Index: /releases/1.0.1/INSTALL
===================================================================
--- /releases/1.0.1/INSTALL	(revision 275)
+++ /releases/1.0.1/INSTALL	(revision 275)
@@ -0,0 +1,35 @@
+RegLookup Installation
+======================
+
+Prerequisites
+-------------
+
+RegLookup and its associated libraries have the following build dependencies:
+
+ - SCons (package "scons" in most popular distributions or from http://www.scons.org/)
+ - GCC
+ - talloc 2.x (under Debian, "libtalloc2" and "libtalloc-dev")
+ - Python 2 (2.6+) or Python 3  (Python is required for SCons anyway)
+ - Doxygen (optional, only needed to build developer documentation)
+
+Note that iconv support is required, as specified in IEEE Std 1003.1 
+(POSIX.1-2001).  Some platforms still do not contain support for this 
+natively, in which case you may need to install libiconv from:
+  http://www.gnu.org/software/libiconv/
+
+
+Survival Commands
+-----------------
+
+scons
+# and as root
+scons install
+
+
+Advanced Installation
+---------------------
+By default, most RegLookup files are installed in the appropriate
+directories under /usr/local.  In order to override this behavior, set
+the PREFIX environment variable to the desired path.  For example:
+
+$ PREFIX=/home/myuser/reglookup scons install
Index: /releases/1.0.1/LICENSE
===================================================================
--- /releases/1.0.1/LICENSE	(revision 275)
+++ /releases/1.0.1/LICENSE	(revision 275)
@@ -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/1.0.1/SConstruct
===================================================================
--- /releases/1.0.1/SConstruct	(revision 275)
+++ /releases/1.0.1/SConstruct	(revision 275)
@@ -0,0 +1,97 @@
+import sys
+import os
+sys.dont_write_bytecode = True
+from regfi_version import REGFI_VERSION
+
+cflags = '-std=gnu99 -pedantic -Wall -D_FILE_OFFSET_BITS=64 -fvisibility=hidden'
+cflags += ' -DREGFI_VERSION=\'"%s"\'' % REGFI_VERSION
+cflags += ' -ggdb'
+
+lib_src = ['lib/regfi.c',
+           'lib/winsec.c',
+           'lib/range_list.c',
+           'lib/lru_cache.c',
+           'lib/void_stack.c']
+
+cc=os.environ.get('CC', 'gcc')
+env = Environment(ENV=os.environ,
+                  CC=cc,
+                  CFLAGS=cflags,
+                  CPPPATH=['include', '/usr/local/include'],
+                  LIBPATH=['lib', '/usr/local/lib'],
+                  LIBS=['m', 'pthread', 'regfi', 'talloc'])
+
+# Libraries
+libregfi_static = env.Library(lib_src)
+libregfi = env.SharedLibrary(lib_src, LIBS=['m','pthread', 'talloc'])
+
+
+# Executables
+reglookup = env.Program(['src/reglookup.c'])
+reglookup_recover = env.Program(['src/reglookup-recover.c'])
+
+
+# Documentation
+#  This only needs to be run during the release/packaging process
+man_fixup = "|sed 's/.SH DESCRIPTION/\\n.SH DESCRIPTION/'"
+man_builder = Builder(action='docbook2x-man --to-stdout $SOURCE'
+                      + man_fixup + '| gzip -9 > $TARGET',
+                      suffix = '.gz',
+                      src_suffix = '.docbook')
+env['BUILDERS']['ManPage'] = man_builder
+
+man_reglookup = env.ManPage('doc/reglookup.1.docbook')
+man_reglookup_recover = env.ManPage('doc/reglookup-recover.1.docbook')
+man_reglookup_timeline = env.ManPage('doc/reglookup-timeline.1.docbook')
+
+# Installation
+prefix='/usr/local/'
+if 'PREFIX' in os.environ:
+   prefix = os.environ['PREFIX']+'/'
+
+install_items = [prefix+'bin',
+                 prefix+'lib', 
+                 prefix+'include/regfi',
+                 prefix+'man']
+
+env.Install(prefix+'bin', [reglookup, reglookup_recover, 'bin/reglookup-timeline'])
+libinstall = env.Install(prefix+'lib', [libregfi, libregfi_static])
+env.Install(prefix+'include/regfi', Glob('include/*.h'))
+env.Install(prefix+'man/man1', [man_reglookup, man_reglookup_recover,
+                                man_reglookup_timeline])
+env.AddPostAction(libinstall, 'ldconfig')
+
+if sys.version_info[0] == 2:
+   install_items.append('pyregfi2-install.log')
+   env.Command('pyregfi2-install.log', ['python/pyregfi/__init__.py', 
+                                        'python/pyregfi/structures.py', 
+                                        'python/pyregfi/winsec.py'],
+               "python pyregfi-distutils.py install | tee pyregfi2-install.log")
+
+python_path = os.popen('which python3').read()
+if python_path != '':
+   install_items.append('pyregfi3-install.log')
+   env.Command('pyregfi3-install.log', ['python/pyregfi/__init__.py', 
+                                        'python/pyregfi/structures.py', 
+                                        'python/pyregfi/winsec.py'], 
+               "python3 pyregfi-distutils.py install | tee pyregfi3-install.log")
+
+# API documentation
+regfi_doc = env.Command('doc/devel/regfi/index.html', 
+                        Glob('lib/*.c')+Glob('include/*.h')+['doc/devel/Doxyfile.regfi'],
+                        'doxygen doc/devel/Doxyfile.regfi')
+pyregfi_doc = env.Command('doc/devel/pyregfi/index.html', 
+                          Glob('python/pyregfi/*.py')+['doc/devel/Doxyfile.pyregfi', regfi_doc],
+                          'doxygen doc/devel/Doxyfile.pyregfi')
+
+
+# User Friendly Targets
+env.Alias('libregfi', libregfi)
+env.Alias('reglookup', reglookup)
+env.Alias('reglookup-recover', reglookup_recover)
+env.Alias('bin', [reglookup_recover, reglookup])
+env.Alias('doc', [man_reglookup,man_reglookup_recover,man_reglookup_timeline])
+env.Alias('doc-devel', [regfi_doc, pyregfi_doc])
+env.Alias('install', install_items)
+
+Default('bin', libregfi)
Index: /releases/1.0.1/bin/reglookup-timeline
===================================================================
--- /releases/1.0.1/bin/reglookup-timeline	(revision 275)
+++ /releases/1.0.1/bin/reglookup-timeline	(revision 275)
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# This script is a wrapper for reglookup, and reads one or more registry
+# files to produce an MTIME sorted output.  This is helpful when building
+# timelines for investigations.
+#
+# Copyright (C) 2005-2007,2010 Timothy D. Morgan
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+#
+# $Id$
+
+
+usage()
+{
+  echo "Usage: $0 [-H] [-V] <REGISTRY_FILE> [<REGISTRY_FILE> ...]" 1>&2
+  echo "   -H  Omit header line" 1>&2
+  echo "   -V  Include values with parent timestamps" 1>&2
+}
+
+if [ $# -eq 0 ]; then
+  usage
+  echo "ERROR: requires at least one parameter" 1>&2
+  exit 1
+fi
+
+PRINT_HEADER=true
+if [ "$1" = "-H" ]; then
+  PRINT_HEADER=false
+  shift
+fi
+
+OPTS='-t KEY'
+if [ "$1" = "-V" ]; then
+  OPTS='-i'
+  shift
+fi
+
+if [ "$PRINT_HEADER" = "true" ]; then
+  echo "MTIME,FILE,PATH"
+fi
+
+for F in $@; do
+  reglookup $OPTS -H "$F" | awk -F',' '{ printf "%s,'"$F"',%s\n",$4,$1; }'
+done | sort
Index: /releases/1.0.1/doc/devel/Doxyfile.pyregfi
===================================================================
--- /releases/1.0.1/doc/devel/Doxyfile.pyregfi	(revision 275)
+++ /releases/1.0.1/doc/devel/Doxyfile.pyregfi	(revision 275)
@@ -0,0 +1,1570 @@
+# Doxyfile 1.6.2-20100208
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = pyregfi
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc/devel/pyregfi
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = YES
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set
+# FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = YES
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort
+# the (brief and detailed) documentation of class members so that constructors
+# and destructors are listed first. If set to NO (the default) the
+# constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief
+# docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if
+# SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = python/pyregfi
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.py
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = talloc*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = .
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for
+# more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+#  plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted
+# before the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML
+# help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be 
+# implemented using a PHP enabled web server instead of at the web client using
+# Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server based approach is 
+# that it scales better to large projects and allows full text search. The 
+# disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code 
+# with syntax highlighting in the LaTeX output. Note that which sources are
+# shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = doc/devel/regfi/regfi.tags=../regfi
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = NO
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = NO
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
Index: /releases/1.0.1/doc/devel/Doxyfile.regfi
===================================================================
--- /releases/1.0.1/doc/devel/Doxyfile.regfi	(revision 275)
+++ /releases/1.0.1/doc/devel/Doxyfile.regfi	(revision 275)
@@ -0,0 +1,1570 @@
+# Doxyfile 1.6.2-20100208
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = regfi
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc/devel/regfi
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set
+# FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = YES
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort
+# the (brief and detailed) documentation of class members so that constructors
+# and destructors are listed first. If set to NO (the default) the
+# constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief
+# docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if
+# SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = lib include
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.c *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = talloc*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = .
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for
+# more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+#  plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted
+# before the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML
+# help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be 
+# implemented using a PHP enabled web server instead of at the web client using
+# Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server based approach is 
+# that it scales better to large projects and allows full text search. The 
+# disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code 
+# with syntax highlighting in the LaTeX output. Note that which sources are
+# shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+ 
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = doc/devel/regfi/regfi.tags
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = NO
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
Index: /releases/1.0.1/doc/devel/README
===================================================================
--- /releases/1.0.1/doc/devel/README	(revision 275)
+++ /releases/1.0.1/doc/devel/README	(revision 275)
@@ -0,0 +1,5 @@
+This directory contains technical information on the registry format,
+RegLookup design goals and programmer's API usage information. For
+information on how to use the RegLookup tools, refer to the doc
+directory and man pages included in the main release distribution or
+Subversion trunk directory.
Index: /releases/1.0.1/doc/devel/TODO
===================================================================
--- /releases/1.0.1/doc/devel/TODO	(revision 275)
+++ /releases/1.0.1/doc/devel/TODO	(revision 275)
@@ -0,0 +1,62 @@
+$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.
+
+ - It might be nice to have a way to filter results by security 
+   descriptor information.  Maybe by MTIME as well.
+
+ - reglookup-timeline needs to be replaced with something cross-platform.  
+   Perhaps a python script that provides MTIME range filtering capabilities.
+
+ - Need to integrate much of reglookup-recover's algorithms into regfi 
+   and then expose them from the bottom-up to provide building blocks 
+   through regfi and pyregfi.  This should be addressed along with code 
+   to support handling of partial/fragmented registry hives.
+
+ - Testing, testing, and more testing.  reglookup needs to be more
+   heavily tested on all recent Windows platforms.  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.
+
+ - Unicode support still needs improvement.  While parsing strings seems
+   to be decent, UTF-8 output would be nice.
+
+ - Continue to improve regfi/pyregfi APIs as needed.  winsec library needs more
+   flexibility and documentation.
+
+ - Consider adding regfi wrappers for other high-level languages (perl? ruby?).
+
+ - Documentation.  The security descriptor output format needs to be
+   documented.  Also, function contracts should be added to the
+   lower-level functions of regfi.c. Continue adding
+
+ - Consider switching from libiconv to Joachim Metz's libuna for
+   increased portability and easier builds.
+
+ - Grep through the source for 'XXX', and you'll find more.
+
+ - Consider integrating packaging rules for debian/other platforms into trunk.
+
+ - Investigate why file descriptors can't be directly used in Windows
+
+
+1.0 RELEASE
+===========
+
+Testing
+  Full diffs
+  regfi and pyregfi threading
+  valgrind in multiple scenarios for reglookup, reglookup-recover
+  double check man pages
Index: /releases/1.0.1/doc/devel/mingw-build-OBSOLETE.txt
===================================================================
--- /releases/1.0.1/doc/devel/mingw-build-OBSOLETE.txt	(revision 275)
+++ /releases/1.0.1/doc/devel/mingw-build-OBSOLETE.txt	(revision 275)
@@ -0,0 +1,80 @@
+Cross-compiling RegLookup to Windows with MinGW
+===============================================
+
+MinGW can be used to compile Windows binaries from UNIX environments.
+The following instructions outline the steps required to build
+reglookup.exe and reglookup-recover.exe.  This process is experimental
+and Windows binaries have not been well tested.  You have been warned.
+
+Prerequisites
+-------------
+
+- Before you start, ensure you have MinGW installed.  Under Debian,
+  install the `mingw32' package.
+
+- Download pre-compiled libiconv packages from here:
+   http://gnuwin32.sourceforge.net/packages/libiconv.htm
+
+  You will need to download the "Binaries" and "Developer files"
+  packages, whose files are named libiconv-VERSION-bin.zip and
+  libiconv-VERSION-lib.zip respectively.
+
+- Unpack both zip files into a designated top-level directory.
+  Suggested commands:
+  $ mkdir /usr/local/src/libiconv-VERSION-bin /usr/local/src/libiconv-VERSION-lib
+  $ cd /usr/local/src/libiconv-VERSION-bin
+  $ unzip .../path/to/libiconv-VERSION-bin.zip
+  $ cd /usr/local/src/libiconv-VERSION-lib
+  $ unzip .../path/to/libiconv-VERSION-lib.zip
+  
+
+Building
+--------
+
+Review the top level RegLookup Makefile to ensure the settings match
+your environment.  Find the conditional block which looks like:
+
+################################################################################
+# MinGW cross-compiling build settings
+ifdef BUILD_MINGW
+
+## These may need to be changed
+CC=i586-mingw32msvc-cc
+LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib
+
+## These probably do not need to be changed
+BIN_EXT=.exe
+INC:=$(INC) -I$(LIBICONV_PATH)/include
+EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a
+
+endif
+################################################################################
+
+
+If either the CC or LIBICONV_PATH settings are incorrect for your
+system, either update the Makefile, or override these options at build
+time when you run make.  For instance, the above settings in the
+Makefile are correct, you can execute the build by running:
+
+$ make BUILD_MINGW=1
+
+Alternatively, you may override the variables above with:
+
+$ make BUILD_MINGW=1 CC=my-mingw-binary LIBICONV_PATH=.../path/to/libiconv-VERSION-lib
+
+Once the build is complete, you'll find the .exe files under the
+build/bin directory.
+
+
+Installation
+------------
+Naturally, there is no install Makefile target for the MinGW build
+process, since we aren't installing on the local system.  To install
+these binaries on a Windows machine, simply copy over the reglookup.exe
+and reglookup-recover.exe files from the build/bin directory to the
+desired host.  In addition, you will need to install the libiconv2.dll
+file on that host (either in the same directory as the reglookup
+executables, or somewhere in the DLL search path).  This file is
+available in the libiconv-VERSION-bin.zip file you downloaded earlier,
+under the `bin' subdirectory.
+
Index: /releases/1.0.1/doc/devel/references.txt
===================================================================
--- /releases/1.0.1/doc/devel/references.txt	(revision 275)
+++ /releases/1.0.1/doc/devel/references.txt	(revision 275)
@@ -0,0 +1,34 @@
+- 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
+
+- Nigel Williams.  Much of the same information as provided in 'winntreg.txt',
+  but with some code:
+  http://www.wednesday.demon.co.uk/dosreg.html
+
+- 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/1.0.1/doc/devel/winntreg.txt
===================================================================
--- /releases/1.0.1/doc/devel/winntreg.txt	(revision 275)
+++ /releases/1.0.1/doc/devel/winntreg.txt	(revision 275)
@@ -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/1.0.1/doc/reglookup-recover.1.docbook
===================================================================
--- /releases/1.0.1/doc/reglookup-recover.1.docbook	(revision 275)
+++ /releases/1.0.1/doc/reglookup-recover.1.docbook	(revision 275)
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id='reglookup-recover.1'>
+  <!--  $Id$ -->
+  <refmeta>
+    <refentrytitle>reglookup</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo class="sectdesc">File Conversion Utilities</refmiscinfo>
+  </refmeta>
+  <refnamediv id='name'>
+    <refname>reglookup-recover</refname>
+    <refpurpose>Windows NT+ registry deleted data recovery tool</refpurpose>
+  </refnamediv>
+
+  <refsect1 id='synopsis'>
+    <title>SYNOPSIS</title>
+    <para>
+      <command>
+	reglookup-recover [options] <replaceable>registry-file</replaceable>
+      </command> 
+    </para>
+  </refsect1>
+
+  <refsect1 id='description'>
+    <title>DESCRIPTION</title>
+    <para>
+        reglookup-recover attempts to scour a Windows registry hive for 
+        deleted data structures and outputs those found in a CSV-like format.
+    </para>
+  </refsect1>
+
+  <refsect1 id='options'>
+    <title>OPTIONS</title>
+    <para>
+      <command>reglookup-recover</command> accepts the following parameters:
+    </para>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-v</option>
+        </term>
+        <listitem>
+	  <para>
+	    Verbose output.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-h</option>
+        </term>
+        <listitem>
+	  <para>
+	    Enables the printing of a column header row. (default)
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-H</option>
+        </term>
+        <listitem>
+	  <para>
+	    Disables the printing of a column header row.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-l</option>
+        </term>
+        <listitem>
+	  <para>
+            Display cells which could not be interpreted as valid
+            registry structures at the end of the output.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-L</option>
+        </term>
+        <listitem>
+	  <para>
+            Do not display cells which could not be interpreted as valid
+            registry structures.  This is the default behavior.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-r</option>
+        </term>
+        <listitem>
+	  <para>
+            Display raw cell contents for cells which were interpreted as intact
+            data structures.  This additional output will appear on the same
+            line as the interpreted data.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-R</option>
+        </term>
+        <listitem>
+	  <para>
+            Do not display raw cell contents for cells which were interpreted
+            as intact data structures.  This is the default behavior.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option><replaceable>registry-file</replaceable></option>
+        </term>
+        <listitem>
+	  <para>
+	    Required argument.  Specifies the location of the
+	    registry file to read.  The system registry files should be
+	    found under:
+	    <command>%SystemRoot%/system32/config</command>.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id='output'>
+    <title>OUTPUT</title>
+    <para>
+      <!-- XXX: this should be a bit more formal -->
+      <command>reglookup-recover</command> generates a comma-separated values (CSV) 
+      like output and writes it to stdout. For more information on the syntax of 
+      the general format, see <command>reglookup(1)</command>.
+    </para>
+    <para>
+      This tool is new and the output format, particularly the included columns, 
+      may change in future revisions.  When this format stablizes, additional
+      documentation will be included here.
+    </para>
+  </refsect1>
+
+  <refsect1 id='examples'>
+    <title>EXAMPLES</title>
+    <para>
+      To dump the recoverable contents of a system registry hive:
+    </para>
+    <para>
+      <screen>
+	reglookup-recover /mnt/win/c/WINDOWS/system32/config/system
+      </screen>
+    </para>
+    <para>
+      Extract all available unallocated data, including unparsable unallocated
+      space and the raw data associated with parsed cells in a user-specific
+      registry:
+    </para>
+    <para>
+      <screen>
+	reglookup-recover -r -l '/mnt/win/c/Documents and Settings/user/NTUSER.DAT'
+      </screen>
+    </para>
+  </refsect1>
+
+  <refsect1 id='bugs'>
+    <title>BUGS</title>
+    <para>
+      This program has been smoke-tested against most current Windows target 
+      platforms, but a comprehensive test suite has not yet been developed.
+      (Please report results to the development mailing list if you encounter
+       any bugs.  Sample registry files and/or patches are greatly appreciated.)
+    </para>
+    <para>
+      This program is new as of RegLookup release 0.9.0 and should be considered 
+      unstable.
+    </para>
+    <para>
+      For more information on registry format details and the recovery 
+      algorithm, see:
+    </para>
+    <para>
+        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/1.0.1/doc/reglookup-timeline.1.docbook
===================================================================
--- /releases/1.0.1/doc/reglookup-timeline.1.docbook	(revision 275)
+++ /releases/1.0.1/doc/reglookup-timeline.1.docbook	(revision 275)
@@ -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/1.0.1/doc/reglookup.1.docbook
===================================================================
--- /releases/1.0.1/doc/reglookup.1.docbook	(revision 275)
+++ /releases/1.0.1/doc/reglookup.1.docbook	(revision 275)
@@ -0,0 +1,367 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<refentry id='reglookup.1'>
+  <!--  $Id$ -->
+  <refmeta>
+    <refentrytitle>reglookup</refentrytitle>
+    <manvolnum>1</manvolnum>
+    <refmiscinfo class="sectdesc">File Conversion Utilities</refmiscinfo>
+  </refmeta>
+  <refnamediv id='name'>
+    <refname>reglookup</refname>
+    <refpurpose>Windows NT+ registry reader/lookup tool</refpurpose>
+  </refnamediv>
+
+  <refsect1 id='synopsis'>
+    <title>SYNOPSIS</title>
+    <para>
+      <command>
+	reglookup [options] <replaceable>registry-file</replaceable>
+      </command>
+    </para>
+  </refsect1>
+
+  <refsect1 id='description'>
+    <title>DESCRIPTION</title>
+    <para>
+        reglookup is designed to read windows registry elements and
+        print them out to stdout in a CSV-like format.  It has filtering
+        options to narrow the focus of the output.  This tool is
+        designed to work with on Windows NT-based registries.
+    </para>
+  </refsect1>
+
+  <refsect1 id='options'>
+    <title>OPTIONS</title>
+    <para>
+      <command>reglookup</command> accepts the following parameters:
+    </para>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-p <replaceable>prefix-filter</replaceable></option>
+        </term>
+        <listitem>
+          <para>
+	    Specify a path prefix filter.  Only keys/values under
+	    this registry path will be output.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-t <replaceable>type-filter</replaceable></option>
+        </term>
+        <listitem>
+          <para>
+	    Specify a type filter.  Only elements which match this
+	    registry data type will be printed.  Acceptable values
+	    are:
+            <command>
+	      NONE, SZ, EXPAND_SZ, BINARY, DWORD, DWORD_BE,
+	      LINK, MULTI_SZ, RSRC_LIST, RSRC_DESC, RSRC_REQ_LIST, QWORD
+	    </command>
+	    and
+	    <command>
+	      KEY
+	    </command>.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-h</option>
+        </term>
+        <listitem>
+	  <para>
+	    Enables the printing of a column header row. (default)
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+    
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-i</option>
+        </term>
+        <listitem>
+	  <para>
+	    Printed values inherit the timestamp of their parent key, which is
+            printed along with them.  Note that this timestamp is not
+            necessarily meaningful for any given value values because timestamps
+            are saved on keys only and you cannot tell which value has been
+            modified since a change to any value of a given key would update the
+            time stamp.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-H</option>
+        </term>
+        <listitem>
+	  <para>
+	    Disables the printing of a column header row.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-s</option>
+        </term>
+        <listitem>
+	  <para>
+	    Adds five additional columns to output containing 
+	    information from key security descriptors and rarely used
+            fields.  The columns are: owner, group, sacl, dacl, class.
+	    (This feature's output has not been extensively tested.)
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-S</option>
+        </term>
+        <listitem>
+	  <para>
+	    Disables the printing of security descriptor
+	    information. (default)
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option>-v</option>
+        </term>
+        <listitem>
+	  <para>
+	    Verbose output.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+
+    <variablelist remap='IP'>
+      <varlistentry>
+        <term>
+          <option><replaceable>registry-file</replaceable></option>
+        </term>
+        <listitem>
+	  <para>
+	    Required argument.  Specifies the location of the
+	    registry file to read.  The system registry files should be
+	    found under:
+	    <command>%SystemRoot%/system32/config</command>.
+	  </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1 id='output'>
+    <title>OUTPUT</title>
+    <para>
+      <!-- XXX: this should be a bit more formal -->
+      <command>reglookup</command> generates comma-separated values (CSV) 
+      and writes them to stdout.  The format is designed to simplify parsing 
+      algorithms of other tools by quoting CSV special characters using a 
+      common hexadecimal format.  Specifically, special characters or non-ascii 
+      bytes are converted to "%XX" where XX is the hexadecimal value for 
+      the byte.
+    </para>
+    <para>
+      The number of columns or fields in each line is fixed for a given run 
+      of the program, but may vary based on the command line options provided.
+      See the header line for information on which fields are available and 
+      what they contain.
+    </para>
+    <para>
+      Some fields in some lines may contain sub-fields which require additional
+      delimiters.  If these sub-delimiters occur in these sub-fields, they are 
+      also encoded in the same way as commas or other special characters are.  
+      Currently, the second, third, and fourth level delimiters are "|", ":", 
+      and " ", respectively.  These are particularly important to take note of 
+      when security attributes are printed.  Please note that these delimiters
+      may occur in fields that are not sub-delimited, and should not be 
+      interpreted as special.
+    </para>
+    <para>
+      Security attributes of registry keys have a complex structure which is 
+      outlined here.  Each key will generally have an associated ACL (Access 
+      Control List), which is made up of ACEs (Access Control Entries).  Each 
+      ACE is delimited by the secondary delimiter mentioned above, "|".  The 
+      fields within an ACE are delimited by the third-level delimiter, ":", 
+      and consist of a SID, the ACE type (ALLOW, DENY, etc), a list of access 
+      rights, and a list of flags.  The last two fields are delimited by the 
+      fourth-level delimiter " ".  These final lists are simply human-readable 
+      interpretations of bits.  The access rights abbreviations are listed 
+      below along with their Microsoft-assigned names:
+      <screen>
+      QRY_VAL		KEY_QUERY_VALUE
+      SET_VAL		KEY_SET_VALUE
+      CREATE_KEY	KEY_CREATE_SUB_KEY
+      ENUM_KEYS		KEY_ENUMERATE_SUB_KEYS
+      NOTIFY		KEY_NOTIFY
+      CREATE_LNK	KEY_CREATE_LINK
+      WOW64_64		KEY_WOW64_64KEY
+      WOW64_32		KEY_WOW64_32KEY
+      DELETE		DELETE
+      R_CONT		READ_CONTROL
+      W_DAC		WRITE_DAC
+      W_OWNER		WRITE_OWNER
+      SYNC		SYNCHRONIZE
+      SYS_SEC		ACCESS_SYSTEM_SECURITY
+      MAX_ALLWD		MAXIMUM_ALLOWED
+      GEN_A		GENERIC_ALL
+      GEN_X		GENERIC_EXECUTE
+      GEN_W		GENERIC_WRITE
+      GEN_R		GENERIC_READ
+      </screen>
+
+      And the meaning of each flag is:
+      <screen>
+      OI	Object Inherit
+      CI	Container Inherit
+      NP	Non-Propagate
+      IO	Inherit Only
+      IA	Inherited ACE
+      </screen>
+
+      Please see the following references for more information:
+      <screen>
+        http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
+        http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
+        http://msdn2.microsoft.com/en-us/library/aa772242.aspx
+        http://support.microsoft.com/kb/220167
+      </screen>
+    </para>
+    <para>
+      Note that some of the bits listed above have either not been allocated by 
+      Microsoft, or simply aren't documented.  If any bits are set in the above 
+      two fields that aren't recognized, a hexidecimal representation of all of 
+      these mystery bits will be included in the output.  For instance, if the 
+      lowest bit and third lowest bit were not recognized while being set, 
+      the number "0x5" would be included as an element in the list.
+    </para>
+    <para>
+      While the ACL/ACE output format is mostly stable at this point, minor 
+      changes may be introduced in future versions.
+    </para>
+  </refsect1>
+
+  <refsect1 id='examples'>
+    <title>EXAMPLES</title>
+    <para>
+      To read and print the contents of an entire system registry
+      file: 
+    </para>
+    <para>
+      <screen>
+	reglookup /mnt/win/c/WINNT/system32/config/system
+      </screen>
+    </para>
+    <para>
+      To limit the output to just those entries under the Services
+      key: 
+    </para>
+    <para>
+      <screen>
+	reglookup -p /ControlSet002/Services /mnt/win/c/WINNT/system32/config/system
+      </screen>
+    </para>
+    <para>
+      To limit the output to all registry values of type BINARY:
+    </para>
+    <para>
+      <screen>
+	reglookup -t BINARY /mnt/win/c/WINNT/system32/config/system
+      </screen>
+    </para>
+    <para>
+      And to limit the output to BINARY values under the Services key:
+    </para>
+    <para>
+      <screen>
+	reglookup -t BINARY -p /ControlSet002/Services /mnt/win/c/WINNT/system32/config/system
+      </screen>
+    </para>
+  </refsect1>
+
+  <refsect1 id='bugs'>
+    <title>BUGS</title>
+    <para>
+      This program has been smoke-tested against most current Windows target 
+      platforms, but a comprehensive test suite has not yet been developed.
+      (Please report results to the development mailing list if you encounter
+       any bugs.  Sample registry files and/or patches are greatly appreciated.)
+    </para>
+    <para>
+      The SID conversions haven't been carefully checked for accuracy.
+    </para>
+    <para>
+      The MTIME conversions appear correctly produce the stored UTC timestamp.
+      However, due to the periodicity of registry writes, and the complexity
+      of the conversion, a small amount of error (on the order of seconds) may
+      be possible.  The documentation available online from Microsoft on 
+      this field is very poor.
+    </para>
+    <para>
+      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/1.0.1/include/byteorder.h
===================================================================
--- /releases/1.0.1/include/byteorder.h	(revision 275)
+++ /releases/1.0.1/include/byteorder.h	(revision 275)
@@ -0,0 +1,183 @@
+/*
+ * Branched from Samba project Subversion repository, version #2:
+ *  http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/byteorder.h
+ *
+ * Unix SMB/CIFS implementation.
+ * SMB Byte handling
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 1992-1998 Andrew Tridgell 
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#ifndef _BYTEORDER_H
+#define _BYTEORDER_H
+
+/**
+ * @file
+ *
+ * This file implements macros for machine independent short and 
+ * int manipulation
+
+@verbatim
+
+Here is a description of this file that I emailed to the samba list once:
+
+> I am confused about the way that byteorder.h works in Samba. I have
+> looked at it, and I would have thought that you might make a distinction
+> between LE and BE machines, but you only seem to distinguish between 386
+> and all other architectures.
+> 
+> Can you give me a clue?
+
+sure.
+
+The distinction between 386 and other architectures is only there as
+an optimisation. You can take it out completely and it will make no
+difference. The routines (macros) in byteorder.h are totally byteorder
+independent. The 386 optimsation just takes advantage of the fact that
+the x86 processors don't care about alignment, so we don't have to
+align ints on int boundaries etc. If there are other processors out
+there that aren't alignment sensitive then you could also define
+CAREFUL_ALIGNMENT=0 on those processors as well.
+
+Ok, now to the macros themselves. I'll take a simple example, say we
+want to extract a 2 byte integer from a SMB packet and put it into a
+type called uint16_t that is in the local machines byte order, and you
+want to do it with only the assumption that uint16_t is _at_least_ 16
+bits long (this last condition is very important for architectures
+that don't have any int types that are 2 bytes long)
+
+You do this:
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+
+then to extract a uint16_t value at offset 25 in a buffer you do this:
+
+char *buffer = foo_bar();
+uint16_t xx = SVAL(buffer,25);
+
+We are using the byteoder independence of the ANSI C bitshifts to do
+the work. A good optimising compiler should turn this into efficient
+code, especially if it happens to have the right byteorder :-)
+
+I know these macros can be made a bit tidier by removing some of the
+casts, but you need to look at byteorder.h as a whole to see the
+reasoning behind them. byteorder.h defines the following macros:
+
+SVAL(buf,pos) - extract a 2 byte SMB value
+IVAL(buf,pos) - extract a 4 byte SMB value
+SVALS(buf,pos) signed version of SVAL()
+IVALS(buf,pos) signed version of IVAL()
+
+SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
+SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
+SSVALS(buf,pos,val) - signed version of SSVAL()
+SIVALS(buf,pos,val) - signed version of SIVAL()
+
+RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
+RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
+RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
+RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
+RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
+RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
+RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
+
+it also defines lots of intermediate macros, just ignore those :-)
+
+@endverbatim
+
+*/
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right" 
+   byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
+#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */
+#define PVAL(buf,pos) (CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
+
+
+#if CAREFUL_ALIGNMENT
+
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
+#define SVALS(buf,pos) ((int16_t)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32_t)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16_t)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val)))
+
+#else /* CAREFUL_ALIGNMENT */
+
+/* this handles things for architectures like the 386 that can handle
+   alignment errors */
+/*
+   WARNING: This section is dependent on the length of int16_t and int32_t
+   being correct 
+*/
+
+/* get single value from an SMB buffer */
+#define SVAL(buf,pos) (*(const uint16_t *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32_t *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVALS(buf,pos) (*(const int16_t *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32_t *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32_t *)((char *)(buf) + (pos))) /* Non const version of above. */
+
+/* store single value in an SMB buffer */
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16_t)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32_t)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16_t)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32_t)(val))
+
+#endif /* CAREFUL_ALIGNMENT */
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
+#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
+
+/* Alignment macros. */
+#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
+#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
+
+#endif /* _BYTEORDER_H */
Index: /releases/1.0.1/include/compat.h
===================================================================
--- /releases/1.0.1/include/compat.h	(revision 275)
+++ /releases/1.0.1/include/compat.h	(revision 275)
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010-2011 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: regfi.h 252 2011-05-08 17:33:49Z tim $
+ */
+
+#ifndef _COMPAT_H
+#define _COMPAT_H
+
+/* GCC-specific macro for library exports */
+#ifdef _EXPORT
+#undef _EXPORT
+#endif
+#ifdef REGFI_WIN32
+#define _EXPORT() __declspec(dllexport)
+#else
+#define _EXPORT() __attribute__((visibility("default")))
+#endif
+
+#ifndef EOVERFLOW
+# define EOVERFLOW E2BIG
+#endif
+
+#endif /*_COMPAT_H*/
Index: /releases/1.0.1/include/lru_cache.h
===================================================================
--- /releases/1.0.1/include/lru_cache.h	(revision 275)
+++ /releases/1.0.1/include/lru_cache.h	(revision 275)
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2008-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/**
+ * @file
+ *
+ * A data structure which approximates a least recently used (LRU) cache.
+ * Implemented as a basic randomized hash table.
+ */
+
+
+#ifndef LRU_CACHE_H
+#define LRU_CACHE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <talloc.h>
+
+#include "compat.h"
+
+
+struct lru_cache_element;
+typedef struct lru_cache_element lru_cache_element; 
+
+struct lru_cache_element
+{
+  void* index;
+  uint32_t index_len;
+  void* data;
+  lru_cache_element* next;
+  lru_cache_element* older;
+  lru_cache_element* newer;
+};
+
+
+/** XXX: document this. */
+typedef struct _lru_cache
+{
+  uint32_t secret;
+  uint32_t num_keys;
+  uint32_t num_buckets;
+  uint32_t max_keys;
+  lru_cache_element* oldest;
+  lru_cache_element* newest;
+  lru_cache_element** table;
+  bool talloc_data;
+} lru_cache;
+
+
+/**
+ * XXX: finish documenting.
+ */
+_EXPORT()
+lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret);
+
+
+/**
+ * XXX: finish documenting.
+ */
+_EXPORT()
+lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, 
+				uint32_t secret, bool talloc_data);
+
+
+/**
+ * XXX: finish documenting.
+ */
+_EXPORT()
+void lru_cache_destroy(lru_cache* ht);
+
+
+/**
+ * XXX: finish documenting.
+ */
+_EXPORT()
+bool lru_cache_update(lru_cache* ht, const void* index, 
+		      uint32_t index_len, void* data);
+
+/**
+ * XXX: finish documenting.
+ *
+ * @return A pointer to data previously stored at index.
+ *         If no data was found at index, NULL is returned.
+ */
+_EXPORT()
+void* lru_cache_find(lru_cache* ht, const void* index, 
+		     uint32_t index_len);
+
+/**
+ * XXX: finish documenting.
+ *
+ * Removes entry from table at index.
+ *
+ * @return A pointer to data that was there previously or NULL if no entry is
+ *         at index.
+ */
+_EXPORT()
+bool lru_cache_remove(lru_cache* ht, const void* index, 
+		      uint32_t index_len);
+
+#endif
Index: /releases/1.0.1/include/range_list.h
===================================================================
--- /releases/1.0.1/include/range_list.h	(revision 275)
+++ /releases/1.0.1/include/range_list.h	(revision 275)
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2008-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/**
+ * @file 
+ *
+ * A data structure which stores a list of address ranges.
+ * 
+ * range_lists support basic in-place modifications and maintain the address
+ * space in sorted order.  Looking up a range_list_element is implemented 
+ * through binary search.
+ */
+
+#ifndef _RANGE_LIST_H
+#define _RANGE_LIST_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <talloc.h>
+
+#include "compat.h"
+
+typedef struct _range_list_element
+{
+  uint32_t offset;
+  uint32_t length;
+  void* data;
+} range_list_element;
+
+
+/** XXX: document this. */
+typedef struct _range_list
+{
+  range_list_element** elements;
+  uint32_t elem_alloced;
+  uint32_t size;
+} range_list;
+
+
+/** Allocates a new range_list.
+ *
+ * @return A newly allocated range_list, or NULL if an error occurred.
+ */
+_EXPORT()
+range_list* range_list_new();
+
+
+/** Frees the memory associated with a range_list, including the elements, but
+ *  not any data parameters referenced by those elements.  
+ *
+ * If rl is NULL, does nothing.
+ *
+ * @param rl the range_list to be free()d.
+ */
+_EXPORT()
+void range_list_free(range_list* rl);
+
+
+/** Query the current number of elements on a range_list
+ *
+ * @param rl the range_list to query
+ *
+ * @return The number of elements currently in the list.
+ */
+_EXPORT()
+uint32_t range_list_size(const range_list* rl);
+
+
+/** Adds an element to the range_list.  
+ *
+ * The new element must not overlap with others.
+ * NOTE: this is a slow operation.
+ *
+ * @param rl     the range list to update
+ * @param offset the starting point for the range
+ * @param length the length of the range
+ * @param data   misc data associated with this range element
+ *
+ * @return  true on success, false on failure.
+ *
+ * Failures can occur due to memory limitations, max_size limitations,
+ * or if the submitted range overlaps with an existing element.  Other
+ * errors may also be possible.
+ */
+_EXPORT()
+bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data);
+
+
+/** Removes an element from the list.  
+ *
+ * The element data structure will be freed, but the data property will not be.
+ *
+ * @param rl    the range_list to modify
+ * @param index the element index to remove
+ *
+ * @return true if the element was successfully removed, false otherwise.
+ */
+_EXPORT()
+bool range_list_remove(range_list* rl, uint32_t index);
+
+
+/** Retrieves the element for a given index.
+ *
+ * @param rl    the range_list being queried.
+ * @param index the element index desired.
+ * 
+ * @return The element for a given index, or NULL if the element is not 
+ *         available.
+ */
+_EXPORT()
+const range_list_element* range_list_get(const range_list* rl, uint32_t index);
+
+
+/** Attempts to find the unique element whose range encompasses offset.
+ *
+ * @param rl     the range_list being queried.
+ * @param offset the location for which an element is desired.
+ *
+ * @return A matching element index or a negative value if none could be found.
+ */
+_EXPORT()
+int32_t range_list_find(const range_list* rl, uint32_t offset);
+
+
+/** Same as range_list_find(), but returns the data associated with an element.
+ *
+ * @param rl     the range_list being queried.
+ * @param offset the address to search for in the ranges
+ *
+ * @return The data element of the matching element index or NULL if none could
+ *         be found.
+ *
+ *  NOTE: May also return NULL if an element matched but the data
+ *        element was never set.
+ */
+_EXPORT()
+void* range_list_find_data(const range_list* rl, uint32_t offset);
+
+
+/**  Splits an existing element into two elements in place.
+ *
+ * The resulting list will contain an additional element whose offset 
+ * is the one provided and whose length extends to the end of the old element
+ * (the one identified by the index).  The original element's offset will 
+ * remain the same while it's length is shortened such that it is contiguous
+ * with the newly created element.  The newly created element will have an index 
+ * of one more than the current element.
+ *
+ * Both the original element and the newly created element will reference the 
+ * original element's data.
+ *
+ * @param rl     the range_list to modify
+ * @param index  the index of the element to be split
+ * @param offset the at which the element will be split
+ *
+ * @return true if the element was successfully split, false otherwise.
+ */
+_EXPORT()
+bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset);
+
+
+/** Determines whether or not a specified range exists contiguously within the
+ *  range_list.
+ *
+ * @param rl     the range_list to search
+ * @param start  the offset at the beginning of the range
+ * @param length the length of the range
+ *
+ * @return true if the specified range exists and is complete, false otherwise.
+ */
+_EXPORT()
+bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length);
+
+#endif
Index: /releases/1.0.1/include/regfi.h
===================================================================
--- /releases/1.0.1/include/regfi.h	(revision 275)
+++ /releases/1.0.1/include/regfi.h	(revision 275)
@@ -0,0 +1,1773 @@
+/*
+ * Copyright (C) 2005-2011 Timothy D. Morgan
+ * Copyright (C) 2010 Michael Cohen
+ * Copyright (C) 2005 Gerald (Jerry) Carter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/**
+ * @file
+ * Windows NT (and later) read-only registry library
+ *
+ * This library is intended for use in digital forensics investigations, but 
+ * is likely useful in other applications.
+ *
+ * Branched from Samba project Subversion repository, version #6903:
+ *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h?rev=6903&view=auto
+ *
+ * Since then, it has been heavily rewritten, simplified, and improved.
+ */
+
+/**
+ * @mainpage Home
+ *
+ * The regfi library is a read-only NT registry library which serves as the main
+ * engine behind the reglookup tool.  It is designed with digital forensic 
+ * analysis in mind, but it should also be useful in other tools which need to 
+ * efficiently traverse and query registry data structures.
+ *
+ * The library is broken down into four main parts, the 
+ * @ref regfiBase "Base Layer", which any code dependent on the library will 
+ * likely need to rely on, as well as three main functional layers: 
+ * @li @ref regfiIteratorLayer
+ * @li @ref regfiGlueLayer
+ * @li @ref regfiParseLayer
+ *
+ * Most users will find that a combination of the Base Layer and the Iterator Layer 
+ * will be sufficient for accessing registry hive files.  Those who are willing
+ * to dive deep into registry data structures, for instance to recover deleted
+ * data structures or to research Windows registry behavior in detail, will 
+ * find the Parse Layer to be quite useful.
+ */
+
+
+#ifndef _REGFI_H
+#define _REGFI_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <iconv.h>
+#include <pthread.h>
+#include <talloc.h>
+
+/* regfi headers */
+#include "compat.h"
+#include "byteorder.h"
+#include "winsec.h"
+#include "void_stack.h"
+#include "range_list.h"
+#include "lru_cache.h"
+
+/******************************************************************************/
+/* Constants for use while interacting with the library                       */
+/******************************************************************************/
+
+/* regfi library error message types */
+#define REGFI_LOG_INFO  0x0001
+#define REGFI_LOG_WARN  0x0004
+#define REGFI_LOG_ERROR 0x0010
+#define REGFI_DEFAULT_LOG_MASK REGFI_LOG_ERROR|REGFI_LOG_WARN
+
+/* regfi library supported character encodings */
+/* UTF16LE is not supported for output */
+typedef enum {
+  REGFI_ENCODING_DEFAULT  = 0,
+  REGFI_ENCODING_ASCII =   0,
+  REGFI_ENCODING_UTF8  =  1,
+  REGFI_ENCODING_UTF16LE = 2,
+  REGFI_NUM_ENCODINGS  =  3
+} REGFI_ENCODING;
+
+/* Registry data types */
+typedef enum {
+  REG_NONE                   =    0,
+  REG_SZ		     =    1,
+  REG_EXPAND_SZ              =    2,
+  REG_BINARY 	             =    3,
+  REG_DWORD	             =    4,
+  REG_DWORD_LE	             =    4 , /* DWORD, little endian */
+  REG_DWORD_BE	             =    5 , /* DWORD, big endian */
+  REG_LINK                   =    6,
+  REG_MULTI_SZ  	     =    7,
+  REG_RESOURCE_LIST          =    8,
+  REG_FULL_RESOURCE_DESCRIPTOR=   9,
+  REG_RESOURCE_REQUIREMENTS_LIST= 10,
+  REG_QWORD                     = 11, /* 64-bit little endian */
+/* XXX: Has MS defined a REG_QWORD_BE? */
+/* Not a real type in the registry */
+  REG_KEY                 =   0x7FFFFFFF
+} REGFI_DATA_TYPE;
+#define REGFI_OFFSET_NONE          0xffffffff
+
+
+
+/******************************************************************************/
+/* Various resource limits and related constants                              */
+/******************************************************************************/
+
+/* Flags determining how many records to cache internally */
+#define REGFI_CACHE_SK_MAX         64
+#define REGFI_CACHE_NK_MAX         1024
+
+/* This maximum depth is described here:
+ * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+ */
+#define REGFI_MAX_DEPTH		   512
+
+/* This limit defines the maximum number of levels deep that ri subkey list
+ * trees can go.
+ */
+/* XXX: This is totally arbitrary right now.
+ *      The actual limit may need to be discovered by experimentation.
+ */
+#define REGFI_MAX_SUBKEY_DEPTH     255
+
+
+/******************************************************************************/
+/* Symbols for internal use                                                   */
+/******************************************************************************/
+
+/* Global thread-local storage key */
+pthread_key_t regfi_log_key;
+
+/* Header sizes and magic number lengths for various records */
+#define REGFI_HBIN_ALLOC           0x1000 /* Minimum allocation unit for HBINs */
+#define REGFI_REGF_SIZE            0x1000 /* "regf" header block size */
+#define REGFI_REGF_MAGIC_SIZE      4
+#define REGFI_REGF_NAME_SIZE       64
+#define REGFI_REGF_RESERVED1_SIZE  340
+#define REGFI_REGF_RESERVED2_SIZE  3528
+#define REGFI_HBIN_MAGIC_SIZE      4
+#define REGFI_CELL_MAGIC_SIZE      2
+#define REGFI_HBIN_HEADER_SIZE     0x20
+#define REGFI_NK_MIN_LENGTH        0x4C
+#define REGFI_VK_MIN_LENGTH        0x14
+#define REGFI_SK_MIN_LENGTH        0x14
+#define REGFI_SUBKEY_LIST_MIN_LEN  0x4
+#define REGFI_BIG_DATA_MIN_LENGTH  0xC
+
+
+/* Constants used for validation */
+/* XXX: Can we add clock resolution validation as well as range?  It has
+ *      been reported that Windows timestamps are never more than a
+ *      certain granularity (250ms?), which could be used to help
+ *      eliminate false positives.  Would need to verify this and
+ *      perhaps conservatively implement a check.
+ */
+ /* Minimum time is Jan 1, 1990 00:00:00 */
+#define REGFI_MTIME_MIN            0x01B41E6D00000000L
+
+ /* Maximum time is Jan 1, 2290 00:00:00
+  * (We hope no one is using Windows by then...) 
+  */
+#define REGFI_MTIME_MAX            0x0304754300000000L
+
+
+/* Flags for the vk records */
+#define REGFI_VK_FLAG_ASCIINAME    0x0001
+#define REGFI_VK_DATA_IN_OFFSET    0x80000000
+#define REGFI_VK_MAX_DATA_LENGTH   1024*1024  /* XXX: This is arbitrary */
+
+
+/* Known key flags */
+/*******************/
+/* These next two show up on normal-seeming keys in Vista and W2K3 registries */
+#define REGFI_NK_FLAG_UNKNOWN1     0x4000
+#define REGFI_NK_FLAG_UNKNOWN2     0x1000
+
+/* This next one shows up in some Vista "software" registries */
+/* XXX: This shows up in the following two SOFTWARE keys in Vista:
+ *   /Wow6432Node/Microsoft
+ *   /Wow6432Node/Microsoft/Cryptography
+ *  
+ * It comes along with UNKNOWN2 and ASCIINAME for a total flags value of 0x10A0
+ */
+#define REGFI_NK_FLAG_UNKNOWN3     0x0080
+
+/* Predefined handle.  Rumor has it that the valuelist count for this key is 
+ * where the handle is stored.
+ * http://msdn.microsoft.com/en-us/library/ms724836(VS.85).aspx
+ */
+#define REGFI_NK_FLAG_PREDEF_KEY   0x0040
+
+/* The name will be in ASCII if this next bit is set, otherwise UTF-16LE */
+#define REGFI_NK_FLAG_ASCIINAME    0x0020
+
+/* Symlink key.  
+ * See: http://www.codeproject.com/KB/system/regsymlink.aspx 
+ */
+#define REGFI_NK_FLAG_LINK         0x0010
+
+/* This key cannot be deleted */
+#define REGFI_NK_FLAG_NO_RM        0x0008
+
+/* Root of a hive */
+#define REGFI_NK_FLAG_ROOT         0x0004
+
+/* Mount point of another hive.  NULL/(default) value indicates which hive 
+ * and where in the hive it points to. 
+ */
+#define REGFI_NK_FLAG_HIVE_LINK    0x0002 
+
+/* These keys shouldn't be stored on disk, according to:
+ * http://geekswithblogs.net/sdorman/archive/2007/12/24/volatile-registry-keys.aspx
+ */
+#define REGFI_NK_FLAG_VOLATILE     0x0001
+
+/* Useful for identifying unknown flag types */
+#define REGFI_NK_KNOWN_FLAGS       (REGFI_NK_FLAG_PREDEF_KEY\
+				    | REGFI_NK_FLAG_ASCIINAME\
+				    | REGFI_NK_FLAG_LINK\
+				    | REGFI_NK_FLAG_NO_RM\
+				    | REGFI_NK_FLAG_ROOT\
+				    | REGFI_NK_FLAG_HIVE_LINK\
+				    | REGFI_NK_FLAG_VOLATILE\
+				    | REGFI_NK_FLAG_UNKNOWN1\
+				    | REGFI_NK_FLAG_UNKNOWN2\
+                                    | REGFI_NK_FLAG_UNKNOWN3)
+
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#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 REGFI_TIME_FIXUP (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+
+
+/******************************************************************************/
+/* Structures                                                                 */
+/******************************************************************************/
+
+typedef uint64_t REGFI_NTTIME;
+
+typedef struct _regfi_log
+{
+  /* Error/warning/info messages returned by lower layer functions */
+  char* messages;
+
+  /* Mask for error message types that will be stored. */
+  uint16_t msg_mask;
+
+} REGFI_LOG;
+
+
+/** HBIN block information
+ * @ingroup regfiMiddleLayer
+ */
+typedef struct _regfi_hbin 
+{
+  /** Offset of this HBIN in the registry file */
+  uint32_t file_off;
+
+  /** Number of active records pointing to this block (not used currently) */
+  uint32_t ref_count;
+
+  /** Offset from first hbin block */
+  uint32_t first_hbin_off;
+
+  /** Block size of this block Should be a multiple of 4096 (0x1000) */
+  uint32_t block_size;
+
+  /** Relative offset to next block.  
+   * 
+   * @note This value may be unreliable!
+   */
+  uint32_t next_block;
+
+  /** Magic number for the HBIN (should be "hbin"). */
+  uint8_t magic[REGFI_HBIN_MAGIC_SIZE];
+} REGFI_HBIN;
+
+
+/* Subkey List -- list of key offsets and hashed names for consistency */
+typedef struct 
+{
+  /* Virtual offset of NK record or additional subkey list, 
+   * depending on this list's type. 
+   */
+  uint32_t offset;
+
+  uint32_t hash;
+} REGFI_SUBKEY_LIST_ELEM;
+
+
+/** Subkey-list structure
+ * @ingroup regfiMiddleLayer
+ */
+typedef struct _regfi_subkey_list
+{
+  /* Real offset of this record's cell in the file */
+  uint32_t offset;
+
+  uint32_t cell_size;
+  
+  /* Number of immediate children */
+  uint32_t num_children;
+
+  /* Total number of keys referenced by this list and its children */
+  uint32_t num_keys;
+
+  REGFI_SUBKEY_LIST_ELEM* elements;
+  uint8_t magic[REGFI_CELL_MAGIC_SIZE];
+
+  /* Set if the magic indicates this subkey list points to child subkey lists */
+  bool recursive_type;
+} REGFI_SUBKEY_LIST;
+
+
+typedef uint32_t REGFI_VALUE_LIST_ELEM;
+/** Value-list structure
+ * @ingroup regfiMiddleLayer
+ */
+typedef struct _regfi_value_list
+{
+  /* Real offset of this record's cell in the file */
+  uint32_t offset;
+
+  uint32_t cell_size;
+
+  /* Actual number of values referenced by this list.  
+   * May differ from parent key's num_values if there were parsing errors. 
+   */
+  uint32_t num_values;
+
+  REGFI_VALUE_LIST_ELEM* elements;
+} REGFI_VALUE_LIST;
+
+
+/** Class name structure (used in storing SysKeys)
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_classname
+{
+  /** Real offset of this record's cell in the file */
+  uint32_t offset;
+
+  /** As converted to requested REGFI_ENCODING */
+  char* interpreted;
+
+  /** Represents raw buffer read from classname cell.
+   *
+   * Length of this item is specified in the size field.
+   */
+  uint8_t* raw;
+
+  /** Length of the raw data.
+   *
+   * May be shorter than that indicated by parent key.
+   */
+  uint16_t size;
+} REGFI_CLASSNAME;
+
+
+/** Data record structure
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_data
+{
+  /* XXX: this isn't populated yet. Should set it to start of data cell
+   *      or big data cell. 
+   */
+  uint32_t offset;
+
+  /** Data type of this data, as indicated by the referencing VK record. */
+  REGFI_DATA_TYPE type;
+
+  /** Length of the raw data. */
+  uint32_t size;
+
+  /** This is always present, representing the raw data cell contents. */
+  uint8_t* raw;
+
+  /** Represents the length of the interpreted value. Meaning is type-specific. 
+   *  Will be 0 if interpretation failed for any reason.
+   */
+  uint32_t interpreted_size;
+
+  /** These items represent interpreted versions of the REGFI_DATA::raw field.
+   *
+   * Only use the appropriate member according to the REGFI_DATA::type field.
+   * In the event of an unknown type, use only the REGFI_DATA::raw field.
+   */
+  union _regfi_data_interpreted
+  {
+    /** REG_NONE 
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* none; 
+
+    /** REG_SZ 
+     *
+     * Stored as a NUL terminated string.  Converted to the specified
+     * REGFI_ENCODING.
+     */
+    uint8_t* string;
+
+    /** REG_EXPAND_SZ 
+     *
+     * Stored as a NUL terminated string.  Converted to the specified
+     * REGFI_ENCODING.
+     */
+    uint8_t* expand_string;
+
+    /** REG_BINARY
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* binary;
+
+    /** REG_DWORD */
+    uint32_t dword;
+
+    /** REG_DWORD_BE */
+    uint32_t dword_be;
+
+    /** REG_LINK
+     *
+     * Stored as a NUL terminated string.  Converted to the specified
+     * REGFI_ENCODING.
+     */
+    uint8_t* link;
+
+    /** REG_MULTI_SZ 
+     *
+     * Stored as a list of uint8_t* pointers, terminated with a NULL pointer.
+     * Each string element in the list is NUL terminated, and the character set
+     * is determined by the specified REGFI_ENCODING.
+     */
+    uint8_t** multiple_string;
+
+    /** REG_QWORD */
+    uint64_t qword;
+
+    /* The following are treated as binary currently, but this may change in
+     * the future as the formats become better understood.
+     */
+
+    /** REG_RESOURCE_LIST
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* resource_list;
+
+    /** REG_FULL_RESOURCE_DESCRIPTOR
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* full_resource_descriptor;
+
+    /** REG_RESOURCE_REQUIREMENTS_LIST
+     *
+     * Stored as a raw buffer.  Use REGFI_DATA::interpreted_size to determine
+     * length.
+     */
+    uint8_t* resource_requirements_list;
+  } interpreted;
+} REGFI_DATA;
+
+
+/** Value structure
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_vk
+{
+  /** Real offset of this record's cell in the file */
+  uint32_t offset;	
+
+  /** ((start_offset - end_offset) & 0xfffffff8) */
+  uint32_t cell_size;
+
+  /** The name of this value converted to desired REGFI_ENCODING.  
+   *
+   * This conversion typically occurs automatically through REGFI_ITERATOR
+   * settings.  String is NUL terminated.
+   */
+  char* name;
+
+  /** The raw value name
+   *
+   * Length of the buffer is stored in name_length.
+   */
+  uint8_t* name_raw;
+
+  /** Length of name_raw */
+  uint16_t name_length;
+
+  /** Offset from beginning of this hbin block */
+  uint32_t hbin_off;
+  
+  /** Size of the value's data as reported in the VK record.
+   *
+   * May be different than that obtained while parsing the data cell itself.
+   */
+  uint32_t data_size;
+
+  /** Virtual offset of data cell */
+  uint32_t data_off;
+
+  /** Value's data type */
+  REGFI_DATA_TYPE type;
+
+  /** VK record's magic number (should be "vk") */
+  uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
+
+  /** VK record flags */
+  uint16_t flags;
+
+  /* XXX: A 2-byte field of unknown purpose stored in the VK record */
+  uint16_t unknown1;
+
+  /** Whether or not the data record is stored in the VK record's data_off field. 
+   *
+   * This information is derived from the high bit of the raw data size field.
+   */
+  bool     data_in_offset;
+
+  /* XXX: deprecated */
+  REGFI_DATA* data;
+
+} REGFI_VK;
+
+
+/* Key Security */
+struct _regfi_sk;
+
+/** Security structure
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_sk 
+{
+  /** Real file offset of this record */
+  uint32_t offset;
+
+  /** ((start_offset - end_offset) & 0xfffffff8) */
+  uint32_t cell_size;
+
+  /** The stored Windows security descriptor for this SK record */
+  WINSEC_DESC* sec_desc;
+
+  /** Offset of this record from beginning of this hbin block */
+  uint32_t hbin_off;
+  
+  /** Offset of the previous SK record in the linked list of SK records */
+  uint32_t prev_sk_off;
+
+  /** Offset of the next SK record in the linked list of SK records */
+  uint32_t next_sk_off;
+
+  /** Number of keys referencing this SK record */
+  uint32_t ref_count;
+
+  /** Size of security descriptor (sec_desc) */
+  uint32_t desc_size;
+
+  /* XXX: A 2-byte field of unknown purpose */
+  uint16_t unknown_tag;
+
+  /** The magic number for this record (should be "sk") */
+  uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
+} REGFI_SK;
+
+
+/** Key structure
+ * @ingroup regfiBase
+ */
+typedef struct _regfi_nk
+{
+  /** Real offset of this record's cell in the file */
+  uint32_t offset;
+
+  /** Actual or estimated length of the cell.  
+   * Always in multiples of 8. 
+   */
+  uint32_t cell_size;
+
+  /** Preloaded value-list for this key.
+   * This element is loaded automatically when using the iterator interface and
+   * possibly some lower layer interfaces.
+   */
+  REGFI_VALUE_LIST* values;
+
+
+  /** Preloaded subkey-list for this key.
+   * This element is loaded automatically when using the iterator interface and
+   * possibly some lower layer interfaces.
+   */
+  REGFI_SUBKEY_LIST* subkeys;
+  
+  /** Key flags */
+  uint16_t flags;
+
+  /** Magic number of key (should be "nk") */
+  uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
+
+  /** Key's last modification time */
+  REGFI_NTTIME mtime;
+
+  /** Length of name_raw */
+  uint16_t name_length;
+
+  /** Length of referenced classname */
+  uint16_t classname_length;
+
+  /** The name of this key converted to desired REGFI_ENCODING.  
+   *
+   * This conversion typically occurs automatically through REGFI_ITERATOR
+   * settings.  String is NUL terminated.
+   */
+  char* name;
+
+  /** The raw key name
+   *
+   * Length of the buffer is stored in name_length.
+   */
+  uint8_t* name_raw;
+
+  /** Virtual offset of parent key */
+  uint32_t parent_off;
+
+  /** Virtual offset of classname key */
+  uint32_t classname_off;
+  
+  /* XXX: max subkey name * 2 */
+  uint32_t max_bytes_subkeyname;
+
+  /* XXX: max subkey classname length (as if) */
+  uint32_t max_bytes_subkeyclassname;
+
+  /* XXX: max value name * 2 */
+  uint32_t max_bytes_valuename;
+
+  /* XXX: max value data size */
+  uint32_t max_bytes_value;
+  
+  /* XXX: Fields of unknown purpose */
+  uint32_t unknown1;
+  uint32_t unknown2;
+  uint32_t unknown3;
+  uint32_t unk_index;		    /* nigel says run time index ? */
+  
+  /** Number of subkeys */
+  uint32_t num_subkeys;
+
+  /** Virtual offset of subkey-list */
+  uint32_t subkeys_off;
+
+  /** Number of values for this key */
+  uint32_t num_values;
+
+  /** Virtual offset of value-list */
+  uint32_t values_off;
+
+  /** Virtual offset of SK record */
+  uint32_t sk_off;
+} REGFI_NK;
+
+
+typedef struct _regfi_raw_file
+{
+  int64_t  (* seek)(); /* (REGFI_RAW_FILE* self, uint64_t offset, int whence) */
+  ssize_t  (* read)(); /* (REGFI_RAW_FILE* self, void* buf, size_t count) */
+
+  uint64_t cur_off;
+  uint64_t size;
+  void*    state;
+} REGFI_RAW_FILE;
+
+
+/** Registry hive file data structure
+ *
+ * This essential structure stores run-time information about a single open
+ * registry hive as well as file header (REGF block) data.  This structure 
+ * also stores a list of warnings and error messages generated while parsing
+ * the registry hive.  These can be tuned using @ref regfi_log_set_mask.  
+ * Messages may be retrieved using @ref regfi_log_get_str.
+ *
+ * @note If the message mask is set to record any messages, dependent code 
+ *       must use @ref regfi_log_get_str periodically to clear the message
+ *       queue. Otherwise, this structure will grow in size over time as 
+ *       messages queue up.
+ *
+ * @ingroup regfiBase
+ */ 
+typedef struct _regfi_file
+{
+  /* Data parsed from file header */
+  /********************************/
+  uint8_t  magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */
+
+ /* These sequence numbers should match if
+  * the hive was properly synced to disk.
+  */
+  uint32_t sequence1;            
+  uint32_t sequence2;
+
+  REGFI_NTTIME mtime;
+  uint32_t major_version;  /* Set to 1 in all known hives */
+  uint32_t minor_version;  /* Set to 3 or 5 in all known hives */
+  uint32_t type;           /* XXX: Unverified.  Set to 0 in all known hives */
+  uint32_t format;         /* XXX: Unverified.  Set to 1 in all known hives */
+
+  uint32_t root_cell;  /* Offset to root cell in the first (or any?) hbin block */
+  uint32_t last_block; /* Offset to last hbin block in file */
+
+  uint32_t cluster;    /* XXX: Unverified. Set to 1 in all known hives */
+
+  /* Matches hive's base file name. Stored in UTF-16LE */
+  uint8_t file_name[REGFI_REGF_NAME_SIZE];
+
+  WINSEC_UUID* rm_id;       /* XXX: Unverified. */
+  WINSEC_UUID* log_id;      /* XXX: Unverified. */
+  WINSEC_UUID* tm_id;       /* XXX: Unverified. */
+  uint32_t flags;             /* XXX: Unverified. */
+  uint32_t guid_signature;    /* XXX: Unverified. */
+
+  uint32_t checksum;          /* Stored checksum from file */
+  uint32_t computed_checksum; /* Our own calculation of the checksum. 
+			     * (XOR of bytes 0x0000 - 0x01FB) */
+
+  WINSEC_UUID* thaw_tm_id;  /* XXX: Unverified. */
+  WINSEC_UUID* thaw_rm_id;  /* XXX: Unverified. */
+  WINSEC_UUID* thaw_log_id; /* XXX: Unverified. */
+  uint32_t boot_type;         /* XXX: Unverified. */
+  uint32_t boot_recover;      /* XXX: Unverified. */
+
+  /* This seems to include random junk.  Possibly unsanitized memory left over
+   * from when header block was written.  For instance, chunks of nk records 
+   * can be found, though often it's all 0s. */
+  uint8_t reserved1[REGFI_REGF_RESERVED1_SIZE];
+
+  /* This is likely reserved and unusued currently.  (Should be all 0s.)
+   * Included here for easier access in looking for hidden data 
+   * or doing research. */
+  uint8_t reserved2[REGFI_REGF_RESERVED2_SIZE];
+
+
+  /* Run-time information */
+  /************************/
+  /* For sanity checking (not part of the registry header) */
+  uint32_t file_length;
+
+  /** The encoding that all strings are converted to during interpretation.
+   */
+  REGFI_ENCODING string_encoding;
+
+  /* Functions for accessing the file */
+  REGFI_RAW_FILE* cb;
+
+  /* Mutex for all cb access.  This is done to prevent one thread from moving
+   * the file offset while another thread is in the middle of a multi-read
+   * parsing transaction */
+  pthread_mutex_t cb_lock;
+
+  /* Metadata about hbins */
+  range_list* hbins;
+
+  /* Multiple read access allowed, write access is exclusive */
+  pthread_rwlock_t hbins_lock;
+
+  /* Small number of SK records cached */
+  lru_cache* sk_cache;
+
+  /* Need exclusive access for LRUs, since lookups make changes */
+  pthread_mutex_t sk_lock;
+
+  /* Limited number of keys cached */
+  lru_cache* nk_cache;
+
+  /* Need exclusive access for LRUs, since lookups make changes */
+  pthread_mutex_t nk_lock;
+
+  /* Needed to protect various talloc calls */
+  pthread_mutex_t mem_lock;
+
+} REGFI_FILE;
+
+
+typedef struct _regfi_iter_position
+{
+  /* key offset */
+  uint32_t offset;
+
+  /* Index of the current subkey */
+  uint32_t cur_subkey;
+
+  /* Index of the current value */
+  uint32_t cur_value;
+
+  /* The number of subkeys of this key */
+  uint32_t num_subkeys;
+
+  /* The number of values of this key */
+  uint32_t num_values;
+
+} REGFI_ITER_POSITION;
+
+
+/** Registry hive iterator
+ * @ingroup regfiIteratorLayer
+ */
+typedef struct _regfi_iterator
+{
+  /** The registry hive this iterator is associated with */
+  REGFI_FILE* f;
+
+  /** All current parent keys and associated iterator positions */
+  void_stack* key_positions;
+
+  REGFI_ITER_POSITION* cur;
+} REGFI_ITERATOR;
+
+
+
+/** General purpose buffer with stored length
+ * @ingroup regfiBottomLayer
+ */
+typedef struct _regfi_buffer
+{
+  uint8_t* buf;
+  uint32_t len;
+} REGFI_BUFFER;
+
+
+
+/******************************************************************************/
+/** 
+ * @defgroup regfiBase Base Layer: Essential Functions and Data Structures
+ *
+ * These functions are either necessary for normal use of the regfi API or just
+ * don't fit particularly well in any of the other layers.
+ */
+/******************************************************************************/
+
+
+
+/** Returns the current regfi library version
+ *
+ * @return A string indicating the version.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const char* regfi_version();
+
+
+/** Parses file headers of an already open registry hive file and 
+ *  allocates related structures for further parsing.
+ *
+ * @param fd A file descriptor of an already open file.  Must be seekable.
+ *
+ * @param output_encoding Character encoding that strings should be returned in.
+ *                        Only supply the REGFI_ENCODING_* constants, as others 
+ *                        will be rejected.
+ *                        The following values are currently accepted:
+ *                        REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
+ *                        REGFI_ENCODING_ASCII
+ *                        REGFI_ENCODING_UTF8
+ *
+ * @return A reference to a newly allocated REGFI_FILE structure, if successful;
+ *         NULL on error.  Use regfi_free to free the returned REGFI_FILE.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+REGFI_FILE* regfi_alloc(int fd, REGFI_ENCODING output_encoding);
+
+
+/** Parses file headers returned by supplied callback functions.
+ *
+ * This interface is useful if you have a registry hive in memory
+ * or have some other reason to emulate a real file.
+ *
+ * @param file_cb A structure defining the callback functions needed to access the file. 
+ *
+ * @param output_encoding Character encoding that strings should be returned in.
+ *                        Only supply the REGFI_ENCODING_* constants, as others 
+ *                        will be rejected.
+ *                        The following values are currently accepted:
+ *                        REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
+ *                        REGFI_ENCODING_ASCII
+ *                        REGFI_ENCODING_UTF8
+ *
+ * @return A reference to a newly allocated REGFI_FILE structure, if successful;
+ *         NULL on error.  Use regfi_free to free the returned REGFI_FILE.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+REGFI_FILE* regfi_alloc_cb(REGFI_RAW_FILE* file_cb,
+			   REGFI_ENCODING output_encoding);
+
+
+/** Frees a hive's data structures without closing the underlying file.
+ *
+ * @param file The registry structure to free.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+void regfi_free(REGFI_FILE* file);
+
+
+/** Get errors, warnings, and/or verbose information relating to processing of
+ *  the given registry file.
+ *
+ * @return A newly allocated char* which must be free()d by the caller.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+char* regfi_log_get_str();
+
+
+/** Set the verbosity level of messages generated by the library for the 
+ *  current thread.
+ *
+ * @param mask   An integer representing the types of messages desired.
+ *               Acceptable values are created through bitwise ORs of 
+ *               REGFI_LOG_* values.  For instance, if only errors and
+ *               informational messages were desired (but not warnings),
+ *               then one would specify: REGFI_LOG_ERROR|REGFI_LOG_INFO
+ *               By default the message mask is: REGFI_LOG_ERROR|REGFI_LOG_WARN.
+ *
+ * @return       true on success and false on failure.  Failure occurs if 
+ *               underlying pthread functions fail.  errno is set in this case.
+ *
+ * Message masks are set in a thread-specific way.  If one were to set a message
+ * mask in one thread and then spawn a new thread, then the new thread will have
+ * it's message mask reset to the default.  This function may be called at any 
+ * time and will take effect immediately for the current thread.
+ *
+ * @note When a non-zero message mask is set, messages will
+ *       accumulate in memory without limit if they are not fetched using
+ *       @ref regfi_log_get_str and subsequently freed by the caller.  It is
+ *       recommended that messsages be fetched after each regfi API call in
+ *       order to provide the most context.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+bool regfi_log_set_mask(uint16_t mask);
+
+
+/** Fetches a hive's root key.
+ *
+ * @return Returns the root key or NULL on failure.  Key must be freed using
+ *         @ref regfi_free_record.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_NK*       regfi_get_rootkey(REGFI_FILE* file);
+
+
+/** Frees a record previously returned by one of the API functions.
+ *
+ * @param file The file from which the record originated.  
+ *             (This is needed for memory management reasons.)
+ * 
+ * @param record Any of the following record types: REGFI_NK, REGFI_VK, 
+ *        REGFI_SK, REGFI_DATA, and REGFI_CLASSNAME records.
+ *
+ * @note The "const" in the record data type is a bit misleading and is there just for
+ * convenience.  Since records returned previously must not be modified by users
+ * of the API due to internal caching, these are returned as const, so this
+ * function is const to make passing those records back easy.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+void regfi_free_record(REGFI_FILE* file, const void* record);
+
+
+/** Increments reference count on record
+ *
+ * Adds an extra internal reference to specified record, making it necessary to
+ * call regfi_free_record on it an additional time before it is freed.  This is
+ * useful in cases where multiple threads/structures need access to a shared record,
+ * without requiring them to be in sync with when it is freed.
+ *
+ * @param file The file from which the record originated.  
+ *             (This is needed for memory management reasons.)
+ * 
+ * @param record Any of the following record types: REGFI_NK, REGFI_VK, 
+ *        REGFI_SK, REGFI_DATA, and REGFI_CLASSNAME records.
+ *
+ * @return Updated record pointer on success, NULL otherwise
+ *
+ * @note Be sure to use the returned record for further access to the structure
+ *       instead of the previous version of the pointer.  E.g.:
+ *       @code 
+ *       myKey = (const REGFI_NK*)regfi_reference_record(myFile, myKey);
+ *       @endcode
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const void* regfi_reference_record(REGFI_FILE* file, const void* record);
+
+
+/** Retrieves number of subkeys referenced by this key.
+ *
+ * Number of subkeyss in key structure and subkey list structure could differ,
+ * so this provides a standard/sane way of determining the number.
+ *
+ * @param key  the key whose number of subkeys is desired
+ *
+ * @return Returns the number of subkeys referenced by this key.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+uint32_t regfi_fetch_num_subkeys(const REGFI_NK* key);
+
+
+/** Retrieves number of values referenced by this key.
+ *
+ * Number of values in key structure and value list structure could differ,
+ * so this provides a standard/sane way of determining the number.
+ *
+ * @param key  the key whose number of values is desired
+ *
+ * @return Returns the number of values referenced by this key.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+uint32_t regfi_fetch_num_values(const REGFI_NK* key);
+
+
+/** Retrieves classname for a given key.
+ *
+ * @param file the file from which key is derived
+ * @param key the key whose classname is desired
+ *
+ * @return Returns a newly allocated classname structure, or NULL on failure.
+ *         Classname structures must be freed with @ref regfi_free_record.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_CLASSNAME* regfi_fetch_classname(REGFI_FILE* file, 
+					     const REGFI_NK* key);
+
+
+/** Returns the SK (security) record referenced by the supplied key.
+ *
+ * @param file the file from which key is derived
+ * @param key  the key whose SK record is desired
+ * 
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_SK* regfi_fetch_sk(REGFI_FILE* file, const REGFI_NK* key);
+
+
+/** Returns the next SK (security) record referenced by the supplied SK record
+ *
+ * @param file the file from which sk is derived
+ * @param sk   the SK record whose next sibling SK record is desired
+ * 
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @note
+ * SK records are included in a circular, doubly-linked list.
+ * To iterate over all SK records, be sure to check for the repetition of
+ * the SK record you started with to determine when all have been traversed.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_SK* regfi_next_sk(REGFI_FILE* file, const REGFI_SK* sk);
+
+
+/** Returns the previous SK (security) record referenced by the supplied SK record
+ *
+ * @param file the file from which sk is derived
+ * @param sk   the SK record whose previous sibling SK record is desired
+ * 
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @note
+ * SK records are included in a circular, doubly-linked list.
+ * To iterate over all SK records, be sure to check for the repetition of
+ * the SK record you started with to determine when all have been traversed.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_SK* regfi_prev_sk(REGFI_FILE* file, const REGFI_SK* sk);
+
+
+/** Retrieves data for a given value.
+ *
+ * @param file the file from which value is derived
+ * @param value the value whose data is desired
+ *
+ * @return Returns a newly allocated data structure, or NULL on failure.
+ *         Data structures must be freed with @ref regfi_free_record.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_DATA* regfi_fetch_data(REGFI_FILE* file,
+				   const REGFI_VK* value);
+
+
+/** Locates a specific subkey of a given key.
+ *
+ * @param file  the file from which key is derived
+ * @param key   the key whose subkey is desired
+ * @param name  name of the desired subkey (case-insensitive)
+ * @param index a return value: the index of the desired subkey.
+ *              undefined on error
+ *
+ * @return true if the subkey is found, false if an error occurred or if the
+ *         specified name could not be found. If an error occurs, messages
+ *         will be written explaining the issue. (See regfi_log_get_str.)
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+bool regfi_find_subkey(REGFI_FILE* file, const REGFI_NK* key, 
+		       const char* name, uint32_t* index);
+
+
+/** Locates a specific value of a given key.
+ *
+ * @param file  the file from which key is derived
+ * @param key   the key whose value is desired
+ * @param name  name of the desired value (case-insensitive)
+ * @param index a return value: the index of the desired value.  
+ *              undefined on error
+ *
+ * @return true if the value is found, false if an error occurred or if the
+ *         specified name could not be found. If an error occurs, messages
+ *         will be written explaining the issue. (See regfi_log_get_str.)
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+bool regfi_find_value(REGFI_FILE* file, const REGFI_NK* key,
+		      const char* name, uint32_t* index);
+
+
+/** Retrieves a specific subkey of a given key.
+ *
+ * @param file  the file from which key is derived
+ * @param key   the key whose subkey is desired
+ * @param index the index of the desired subkey
+ *
+ * @return the requested subkey or NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_NK* regfi_get_subkey(REGFI_FILE* file, const REGFI_NK* key, 
+				 uint32_t index);
+
+
+/** Retrieves a specific value of a given key.
+ *
+ * @param file  the file from which key is derived
+ * @param key   the key whose value is desired
+ * @param index the index of the desired value
+ *
+ * @return the requested value or NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_VK* regfi_get_value(REGFI_FILE* file, const REGFI_NK* key, 
+				uint32_t index);
+
+
+
+/** Uses a key's parent_off reference to retrieve it's parent.
+ *
+ * @param file  the file from which key is derived
+ * @param key   the key whose parent is desired
+ *
+ * @return the requested subkey or NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_NK* regfi_get_parentkey(REGFI_FILE* file, const REGFI_NK* key);
+
+
+/******************************************************************************/
+/** 
+ * @defgroup regfiIteratorLayer Iterator Layer: Primary regfi Library Interface
+ *
+ * This top layer of API functions provides an iterator interface which makes
+ * traversing registry data structures easy in both single-threaded and 
+ * multi-threaded scenarios.
+ */
+/******************************************************************************/
+
+/** Creates a new iterator for the provided registry file.
+ *
+ * @param file The opened registry file the iterator should be created for.
+ *
+ * @return A newly allocated REGFI_ITERATOR. 
+ *         Must be free()d with regfi_iterator_free.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file);
+
+
+/** Frees a registry file iterator previously created by regfi_iterator_new.
+ *
+ * This does not affect the underlying registry file's allocation status.
+ *
+ * @param i the iterator to be freed
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+void regfi_iterator_free(REGFI_ITERATOR* i);
+
+
+/** Traverse deeper into the registry tree at the current subkey.
+ *
+ * @param i the iterator
+ *
+ * @return  true on success, false on failure.  
+ *          Note that subkey and value indexes are preserved.  That is, if a
+ *          regfi_iterator_up call occurs later (reversing the effect of this
+ *          call) then the subkey and value referenced prior to the
+ *          regfi_iterator_down call will still be referenced.  This  makes
+ *          depth-first iteration particularly easy.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+bool regfi_iterator_down(REGFI_ITERATOR* i);
+
+
+/** Traverse up to the current key's parent key.
+ *
+ * @param i the iterator
+ *
+ * @return  true on success, false on failure.  Any subkey or value state
+ *          associated with the current key is lost.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+bool regfi_iterator_up(REGFI_ITERATOR* i);
+
+
+/** Traverse up to the root key of the hive.
+ *
+ * @param i the iterator
+ *
+ * @return true on success, false on failure.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+bool regfi_iterator_to_root(REGFI_ITERATOR* i);
+
+
+/** Traverse down multiple levels in the registry hive.
+ *
+ * XXX: This currently only accepts ASCII key names.  Need to look into
+ *      accepting other encodings.
+ *
+ * @param i    the iterator
+ * @param path a list of key names representing the path.  This list must
+ *             contain NUL terminated strings.  The list itself is
+ *             terminated with a NULL pointer.  All path elements must be
+ *             keys; value names are not accepted (even as the last
+ *             element).
+ *
+ * @return true on success, false on failure.  If any element of path is not
+ *                 found, false will be returned and the iterator will remain
+ *                 in its original position.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+bool regfi_iterator_descend(REGFI_ITERATOR* i, const char** path);
+
+
+/** Returns the currently referenced key.
+ *
+ * @param i the iterator
+ *
+ * @return A read-only key structure for the current key, or NULL on failure.
+ *         Data structures must be freed with @ref regfi_free_record.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+const REGFI_NK* regfi_iterator_cur_key(REGFI_ITERATOR* i);
+
+
+/** Sets the internal subkey index to the first subkey referenced by the current
+ *  key.
+ *
+ * @param i the iterator
+ *
+ * @return True if the current key has any subkeys, false otherwise.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+bool regfi_iterator_first_subkey(REGFI_ITERATOR* i);
+
+
+/** Returns the currently indexed subkey.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated key structure for the currently referenced subkey,
+ *         or NULL on failure.  Newly allocated keys must be freed with 
+ *         @ref regfi_free_record.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+const REGFI_NK* regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
+
+
+/** Increments the internal subkey index to the next key in the subkey-list.
+ *
+ * @param i the iterator
+ *
+ * @return True if another subkey should exist, false otherwise.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+bool regfi_iterator_next_subkey(REGFI_ITERATOR* i);
+
+
+/** Searches for a subkey with a given name under the current key.
+ *
+ * @param i     the iterator
+ * @param name  subkey name to search for
+ *
+ * @return True if such a subkey was found, false otherwise.  If a subkey is
+ *         found, the current subkey index is set to that subkey.  Otherwise,
+ *         the subkey index remains at the same location as before the call.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* name);
+
+
+/** Sets the internal value index to the first value referenced by the current
+ *  key.
+ *
+ * @param i the iterator
+ *
+ * @return True if the current key has any values, false otherwise.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+bool regfi_iterator_first_value(REGFI_ITERATOR* i);
+
+
+/** Returns the currently indexed value.
+ *
+ * @param i the iterator
+ *
+ * @return A newly allocated value structure for the currently referenced value,
+ *         or NULL on failure.  Newly allocated values must be freed with 
+ *         @ref regfi_free_record.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+const REGFI_VK* regfi_iterator_cur_value(REGFI_ITERATOR* i);
+
+
+/** Increments the internal value index to the next value in the value-list.
+ *
+ * @param i the iterator
+ *
+ * @return True if another value should exist, false otherwise.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+bool regfi_iterator_next_value(REGFI_ITERATOR* i);
+
+
+/** Searches for a value with a given name under the current key.
+ *
+ * @param i     the iterator
+ * @param name  value name to search for
+ *
+ * @return True if such a value was found, false otherwise.  If a value is 
+ *         found, the current value index is set to that value.  Otherwise,
+ *         the value index remains at the same location as before the call.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* name);
+
+
+/** Returns the current key and all parent keys as a list of NK records
+ *
+ * @param i     the iterator
+ *
+ * @return An array of NK record pointers terminated by a NULL pointer.  
+ *         This array may be passed directly to regfi_free_record to free
+ *         the entire array.
+ *
+ * @note In order to use an element of the array independently from the 
+ *       array (that is, to hold a pointer to an individual NK record while 
+ *       freeing the remaining array), callers must first use 
+ *       regfi_reference_record on the elements to be kept.
+ *
+ * @ingroup regfiIteratorLayer
+ */
+_EXPORT()
+const REGFI_NK** regfi_iterator_ancestry(REGFI_ITERATOR* i);
+
+
+/******************************************************************************/
+/** 
+ * @defgroup regfiGlueLayer Glue Layer: Logical Data Structure Loading 
+ */
+/******************************************************************************/
+
+/** Loads a key and associated data structures given a file offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+_EXPORT()
+REGFI_NK* regfi_load_key(REGFI_FILE* file, uint32_t offset, 
+                         bool strict);
+
+
+/** Loads a value at a given file offset alng with associated data structures.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+_EXPORT()
+REGFI_VK* regfi_load_value(REGFI_FILE* file, uint32_t offset, 
+                           bool strict);
+
+
+/** Loads a logical subkey list in its entirety which may span multiple records.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+_EXPORT()
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
+					 uint32_t num_keys, uint32_t max_size,
+					 bool strict);
+
+
+/** Loads a valuelist.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+_EXPORT()
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, 
+				       uint32_t num_values, uint32_t max_size,
+				       bool strict);
+
+
+/** Loads a data record which may be contained in the virtual offset, in a
+ *  single cell, or in multiple cells through big data records.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+_EXPORT()
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
+			     uint32_t length, bool data_in_offset,
+			     bool strict);
+
+
+/** Loads the data associated with a big data record at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+_EXPORT()
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, uint32_t offset, 
+				 uint32_t data_length,uint32_t cell_length,
+				 range_list* used_ranges,
+				 bool strict);
+
+
+/** Given raw data, attempts to interpret the data based on a specified registry
+ *  data type.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+_EXPORT()
+bool regfi_interpret_data(REGFI_FILE* file, 
+			  uint32_t type, REGFI_DATA* data);
+
+
+
+/* These are cached so return values don't need to be freed. */
+
+/** Loads an "sk" security record at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+_EXPORT()
+const REGFI_SK* regfi_load_sk(REGFI_FILE* file, uint32_t offset,
+                              bool strict);
+
+
+
+
+/** Retrieves the HBIN data structure stored at the specified offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiGlueLayer
+ */
+_EXPORT()
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset);
+
+
+
+/******************************************************************************/
+/**
+ * @defgroup regfiParseLayer Parsing Layer: Direct Data Structure Access 
+ */
+/******************************************************************************/
+
+_EXPORT()
+REGFI_FILE* regfi_parse_regf(REGFI_RAW_FILE* file_cb, bool strict);
+
+_EXPORT()
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, 
+			     bool strict);
+
+
+/** Parses an NK record at the specified offset
+ *
+ * @param file     the registry file structure
+ * @param offset   the offset of the cell (not the record) to be parsed.
+ * @param max_size the maximum size the NK cell could be. (for validation)
+ * @param strict   if true, rejects any malformed records.  Otherwise,
+ *                 tries to minimally validate integrity.
+ *
+ * @return A newly allocated NK record structure, or NULL on failure.
+ *
+ * @ingroup regfiParseLayer
+ */
+_EXPORT()
+REGFI_NK* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
+			     uint32_t max_size, bool strict);
+
+
+/** Parses a single cell containing a subkey-list record.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+_EXPORT()
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
+					  uint32_t max_size, bool strict);
+
+
+/** Parses a VK (value) record at the specified offset
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+_EXPORT()
+REGFI_VK* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
+			     uint32_t max_size, bool strict);
+
+
+/** Parses an SK (security) record at the specified offset
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+_EXPORT()
+REGFI_SK* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, 
+			     uint32_t max_size, bool strict);
+
+
+/** Retrieves information on all cells in the registry hive which are 
+ *  currently in the unallocated status.  
+ *
+ * The unallocated status is determined based soley on the cell length sign.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+_EXPORT()
+range_list* regfi_parse_unalloc_cells(REGFI_FILE* file);
+
+
+/** Helper function to parse a cell
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+_EXPORT()
+bool regfi_parse_cell(REGFI_RAW_FILE* file_cb, uint32_t offset,
+		      uint8_t* hdr, uint32_t hdr_len,
+		      uint32_t* cell_length, bool* unalloc);
+
+
+/** Parses a classname cell
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+_EXPORT()
+uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
+			       uint16_t* name_length, 
+			       uint32_t max_size, bool strict);
+
+
+/** Parses a single-cell data record
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+_EXPORT()
+REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
+			      uint32_t length, bool strict);
+
+
+/** Parses a "little data" record which is stored entirely within the 
+ *  provided virtual offset.
+ *
+ * XXX: finish documenting
+ *
+ * @ingroup regfiParseLayer
+ */
+_EXPORT()
+REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset, 
+				     uint32_t length, bool strict);
+
+
+/******************************************************************************/
+/*    Private (and undocumented) Functions                                    */
+/******************************************************************************/
+int64_t               regfi_raw_seek(REGFI_RAW_FILE* self, 
+				     uint64_t offset, int whence);
+ssize_t               regfi_raw_read(REGFI_RAW_FILE* self, 
+				     void* buf, size_t count);
+_EXPORT()
+uint64_t              regfi_seek(REGFI_RAW_FILE* file_cb, 
+				 uint64_t offset, int whence);
+_EXPORT()
+uint32_t              regfi_read(REGFI_RAW_FILE* file_cb, 
+				 uint8_t* buf, uint32_t* length);
+
+_EXPORT()
+const char*           regfi_type_val2str(unsigned int val);
+_EXPORT()
+int                   regfi_type_str2val(const char* str);
+
+_EXPORT()
+char*                 regfi_get_sacl(WINSEC_DESC* sec_desc);
+_EXPORT()
+char*                 regfi_get_dacl(WINSEC_DESC* sec_desc);
+_EXPORT()
+char*                 regfi_get_owner(WINSEC_DESC* sec_desc);
+_EXPORT()
+char*                 regfi_get_group(WINSEC_DESC* sec_desc);
+
+REGFI_SUBKEY_LIST*    regfi_merge_subkeylists(uint16_t num_lists, 
+					      REGFI_SUBKEY_LIST** lists,
+					      bool strict);
+REGFI_SUBKEY_LIST*    regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset,
+						uint32_t max_size, bool strict,
+						uint8_t depth_left);
+void                  regfi_add_message(REGFI_FILE* file, uint16_t msg_type, 
+					const char* fmt, ...);
+REGFI_NK*             regfi_copy_nk(const REGFI_NK* nk);
+REGFI_VK*             regfi_copy_vk(const REGFI_VK* vk);
+_EXPORT()
+int32_t               regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset);
+REGFI_BUFFER          regfi_conv_charset(const char* input_charset, const char* output_charset,
+                                         uint8_t* input, uint32_t input_len);
+_EXPORT()
+REGFI_DATA*           regfi_buffer_to_data(REGFI_BUFFER raw_data);
+
+/* XXX: move to base API and document */
+_EXPORT()
+REGFI_NTTIME          regfi_unix2nt_time(time_t t);
+_EXPORT()
+double                regfi_nt2unix_time(REGFI_NTTIME nt);
+
+
+_EXPORT()
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK* nk, bool strict);
+_EXPORT()
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK* vk, bool strict);
+
+_EXPORT()
+void regfi_init();
+
+
+#endif	/* _REGFI_H */
Index: /releases/1.0.1/include/void_stack.h
===================================================================
--- /releases/1.0.1/include/void_stack.h	(revision 275)
+++ /releases/1.0.1/include/void_stack.h	(revision 275)
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2005,2007,2009-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/** 
+ *@file
+ * 
+ * This is a very simple implementation of a stack which stores chunks of
+ * memory of any type.
+ */
+
+
+#ifndef _VOID_STACK_H
+#define _VOID_STACK_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <talloc.h>
+
+#include "compat.h"
+
+/** XXX: document this. */
+typedef struct _void_stack
+{
+  void** elements;
+  unsigned short max_size;
+  unsigned short top;
+} void_stack;
+
+
+/** XXX: document this. */
+typedef struct _void_stack_iterator
+{
+  const void_stack* stack;
+  unsigned short cur;
+} void_stack_iterator;
+
+
+/** Allocates a new void_stack.
+ *
+ * @param max_size the maxiumum number of elements 
+ *                 which may be pushed onto the stack.
+ *
+ * @return a pointer to the newly allocated void_stack, 
+ *         or NULL if an error occurred.
+ */
+_EXPORT()
+void_stack* void_stack_new(unsigned short max_size);
+
+
+/** Makes a shallow copy of void_stack.
+ *
+ * @param v the stack to make a copy of.
+ *
+ * @return a pointer to the duplicate void_stack, or NULL if an error occurred.
+ */
+_EXPORT()
+void_stack* void_stack_copy(const void_stack* v);
+
+
+/** Makes a shallow copy of void_stack in reverse order.
+ *
+ * @param v the stack to make a copy of.
+ *
+ * @return a pointer to the duplicate void_stack 
+ *         (which will be in reverse order), or NULL if an error occurred.
+ */
+_EXPORT()
+void_stack* void_stack_copy_reverse(const void_stack* v);
+
+
+/** Frees the memory associated with a void_stack, but not the elements held
+ *  on the stack.
+ *
+ * @param stack the stack to be free()d.
+ */
+_EXPORT()
+void void_stack_free(void_stack* stack);
+
+
+/** Frees the memory associated with a void_stack and the elements referenced 
+ *  by the stack.  
+ *
+ * Do not use this function if the elements of the stack 
+ * are also free()d elsewhere, or contain pointers to other memory which 
+ * cannot be otherwise free()d.
+ *
+ * @param stack the stack to be free()d.
+ */
+_EXPORT()
+void void_stack_free_deep(void_stack* stack);
+
+
+/** Query the current number of elements on a void_stack()
+ *
+ * @param stack the void_stack to query
+ *
+ * @return the number of elements currently on the stack.
+ */
+_EXPORT()
+unsigned short void_stack_size(const void_stack* stack);
+
+
+/** Removes the top element on a void_stack and returns a reference to it.
+ *
+ * @param stack the void_stack to pop
+ *
+ * @return a pointer to the popped stack element, or NULL if no elements exist
+ *         on the stack.
+ */
+_EXPORT()
+void* void_stack_pop(void_stack* stack);
+
+
+/** Puts a new element on the top of a void_stack.
+ *
+ * @param stack the void_stack being modified.
+ * @param e the element to be added
+ *
+ * @return true if the element was successfully added, false otherwise.
+ */
+_EXPORT()
+bool void_stack_push(void_stack* stack, void* e);
+
+
+/** Returns a pointer to the current element on the top of the stack.
+ *
+ * @param stack the void_stack being queried.
+ *
+ * @return a pointer to the current element on the top of the stack, or NULL if
+ *         no elements exist in the stack.
+ */
+_EXPORT()
+const void* void_stack_cur(const void_stack* stack);
+
+
+/** Creates a new iterator for the specified void_stack.
+ *
+ * @param stack the void_stack to be referenced by the new iterator
+ *
+ * @return a new void_stack_iterator, or NULL if an error occurred.
+ */
+_EXPORT()
+void_stack_iterator* void_stack_iterator_new(const void_stack* stack);
+
+
+/** Frees a void_stack_iterator.
+ *
+ * Does not affect the void_stack referenced by the iterator.
+ *
+ * @param iter the void_stack_iterator to be free()d.
+ */
+_EXPORT()
+void void_stack_iterator_free(void_stack_iterator* iter);
+
+
+/** Returns a pointer to the the next element in the stack.
+ *
+ * Iterates over elements starting in order from the oldest element (bottom of the stack).
+ *
+ * @param iter the void_stack_iterator used to lookup the next element.
+ *
+ * @return a pointer to the next element.
+ */
+_EXPORT()
+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/1.0.1/include/winsec.h
===================================================================
--- /releases/1.0.1/include/winsec.h	(revision 275)
+++ /releases/1.0.1/include/winsec.h	(revision 275)
@@ -0,0 +1,335 @@
+/* 
+ * Copyright (C) 2005,2009-2011 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team 
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/**
+ * @file
+ *
+ * A small library for interpreting Windows Security Descriptors.
+ * This library was originally based on Samba source from:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * The library has been heavily rewritten and improved based on information
+ * provided by Microsoft at: 
+ *    http://msdn.microsoft.com/en-us/library/cc230366%28PROT.10%29.aspx
+ */
+
+#ifndef _WINSEC_H
+#define _WINSEC_H
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <talloc.h>
+
+#include "compat.h"
+#include "byteorder.h"
+
+
+/* This is the maximum number of subauths in a SID, as defined here:
+ *   http://msdn.microsoft.com/en-us/library/cc230371(PROT.10).aspx
+ */
+#define WINSEC_MAX_SUBAUTHS 15
+
+#define WINSEC_DESC_HEADER_SIZE     (5 * sizeof(uint32_t))
+#define WINSEC_ACL_HEADER_SIZE      (2 * sizeof(uint32_t))
+#define WINSEC_ACE_MIN_SIZE         16
+
+/* XXX: Fill in definitions of other flags */
+/* This self relative flag means offsets contained in the descriptor are relative
+ * to the descriptor's offset.  This had better be true in the registry.
+ */
+#define WINSEC_DESC_SELF_RELATIVE   0x8000
+#define WINSEC_DESC_SACL_PRESENT    0x0010
+#define WINSEC_DESC_DACL_PRESENT    0x0004
+
+#define WINSEC_ACE_OBJECT_PRESENT              0x00000001 
+#define WINSEC_ACE_OBJECT_INHERITED_PRESENT    0x00000002
+#define WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT  0x5
+#define WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT   0x6
+#define WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT    0x7
+#define WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT    0x8
+
+
+/** XXX: document this. */
+typedef struct _winsec_uuid
+{
+  /** XXX: document this. */
+  uint32_t time_low;
+
+  /** XXX: document this. */
+  uint16_t time_mid;
+
+  /** XXX: document this. */
+  uint16_t time_hi_and_version;
+
+  /** XXX: document this. */
+  uint8_t  clock_seq[2];
+
+  /** XXX: document this. */
+  uint8_t  node[6];
+} WINSEC_UUID;
+
+
+/** XXX: document this. */
+typedef struct _winsec_sid
+{
+  /** SID revision number */
+  uint8_t  sid_rev_num;
+
+  /** Number of sub-authorities */
+  uint8_t  num_auths;
+
+  /** Identifier Authority */
+  uint8_t  id_auth[6];
+
+  /** Pointer to sub-authorities.
+   * 
+   * @note The values in these uint32_t's are in *native* byteorder, not
+   * neccessarily little-endian...... JRA.
+   */
+  uint32_t sub_auths[WINSEC_MAX_SUBAUTHS];   /* XXX: Make this dynamically allocated? */
+} WINSEC_DOM_SID;
+
+
+/** XXX: document this. */
+typedef struct _winsec_ace
+{
+  /** xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+  uint8_t type;
+
+  /** xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+  uint8_t flags;
+
+  /** XXX: finish documenting */
+  uint16_t size;
+
+  /** XXX: finish documenting */
+  uint32_t access_mask;
+  
+  /* This stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+
+  /** xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+  uint32_t  obj_flags;
+
+  /** Object GUID */
+  WINSEC_UUID* obj_guid;
+
+  /** Inherited object GUID */
+  WINSEC_UUID* inh_guid;
+
+  /* eof object stuff */
+  
+  /** XXX: finish documenting */
+  WINSEC_DOM_SID* trustee;
+
+} WINSEC_ACE;
+
+
+/** XXX: document this. */
+typedef struct _winsec_acl
+{
+  /** 0x0003 */
+  uint16_t revision;
+
+  /** Size, in bytes, of the entire ACL structure */
+  uint16_t size;
+
+  /** Number of Access Control Entries */
+  uint32_t num_aces;
+  
+  /** XXX: document this. */
+  WINSEC_ACE** aces;
+
+} WINSEC_ACL;
+
+
+/** XXX: document this. */
+typedef struct _winsec_desc
+{
+  /** 0x01 */
+  uint8_t revision;
+
+  /** XXX: better explain this
+   *
+   * "If the Control field has the RM flag set, then this field contains the
+   *  resource manager (RM) control value. ... Otherwise, this field is reserved
+   *  and MUST be set to zero." -- Microsoft.
+   *  See:
+   *   http://msdn.microsoft.com/en-us/library/cc230371%28PROT.10%29.aspx
+   */
+  uint8_t sbz1;
+
+  /** WINSEC_DESC_* flags */
+  uint16_t control;
+  
+  /** Offset to owner sid */
+  uint32_t off_owner_sid;
+
+  /** Offset to group sid */
+  uint32_t off_grp_sid;
+
+  /** Offset to system list of permissions */
+  uint32_t off_sacl;
+
+  /** Offset to list of permissions */
+  uint32_t off_dacl;
+
+  /** XXX: document this */
+  WINSEC_DOM_SID* owner_sid; 
+
+  /** XXX: document this */
+  WINSEC_DOM_SID* grp_sid;
+
+  /** System ACL */
+  WINSEC_ACL* sacl;
+
+  /** User ACL */
+  WINSEC_ACL* dacl;
+
+} WINSEC_DESC;
+
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
+
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+void winsec_free_descriptor(WINSEC_DESC* desc);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
+			       const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+WINSEC_ACL* winsec_parse_acl(void* talloc_ctx, 
+			     const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+WINSEC_ACE* winsec_parse_ace(void* talloc_ctx, 
+			     const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx, 
+				     const uint8_t* buf, uint32_t buf_len);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx, 
+			       const uint8_t* buf, uint32_t buf_len);
+
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+size_t winsec_sid_size(const WINSEC_DOM_SID* sid);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+char* winsec_sid2str(const WINSEC_DOM_SID* sid);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
+bool winsec_ace_object(uint8_t type);
+
+#endif /* _WINSEC_H */
Index: /releases/1.0.1/lib/Makefile
===================================================================
--- /releases/1.0.1/lib/Makefile	(revision 275)
+++ /releases/1.0.1/lib/Makefile	(revision 275)
@@ -0,0 +1,28 @@
+# $Id: Makefile 30 2005-07-16 14:31:27Z tim $
+
+################################################################################
+
+FILES=regfi.o winsec.o void_stack.o range_list.o lru_cache.o talloc.o
+
+all: $(FILES)
+
+regfi.o: regfi.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ regfi.c
+
+winsec.o: winsec.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ winsec.c
+
+void_stack.o: void_stack.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ void_stack.c
+
+range_list.o: range_list.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ range_list.c
+
+lru_cache.o: lru_cache.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ lru_cache.c
+
+talloc.o: talloc.c
+	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ talloc.c
+
+clean:
+	rm -f $(FILES)
Index: /releases/1.0.1/lib/lru_cache.c
===================================================================
--- /releases/1.0.1/lib/lru_cache.c	(revision 275)
+++ /releases/1.0.1/lib/lru_cache.c	(revision 275)
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2008-2009 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/**
+ * @file
+ */
+
+#include "lru_cache.h"
+
+
+#define LRU_CACHE_DEBUG 0
+
+/* XXX: really should replace this with a real universal hash or other
+ *      fast HMAC.
+ */ 
+static uint32_t lru_cache_compute_hash(uint32_t num_buckets,
+				       uint32_t secret,
+				       const void* buf,
+				       uint32_t buf_len)
+{
+  uint32_t i;
+  uint32_t ret_val = 0x243f6a88;
+  unsigned char* s = (unsigned char*)&secret;
+  const unsigned char* b = (unsigned char*)buf;
+
+  for(i=0; i<buf_len; i++)
+    ret_val = (ret_val+(i^s[i%4])*b[i]) % num_buckets;
+  
+  return ret_val;
+}
+
+/* Returns approximately floor(log_2(n)) (log base 2 of n, floored) 
+ * If n == 0, returns 0
+ */
+static uint32_t lru_cache_floor_log2(uint32_t n)
+{
+  uint32_t ret_val;
+  
+  for(ret_val=31; ret_val > 1; ret_val--)
+    if((n & (1 << ret_val)) != 0)
+      return ret_val;
+
+  return 0;
+}
+
+#if 0
+static void lru_cache_print(lru_cache* ht)
+{
+  uint32_t i;
+  lru_cache_element* cur;
+
+  printf("from newest to oldest:\n");
+  for(cur=ht->newest; cur != NULL; cur=cur->older)
+  {
+    /*    write(STDOUT_FILENO, cur->index, cur->index_len);*/
+    printf("%p", (void*)cur);
+    printf("\n");
+    if(cur->older == ht->newest)
+    {
+      printf("??? Loop in LRU list!!");
+      break;
+    }
+  }
+  printf("\n");
+
+  printf("table:\n");
+  for(i=0; i<ht->num_buckets; i++)
+  {
+    printf("%.8X: ", i);
+    for(cur=ht->table[i]; cur != NULL; cur=cur->next)
+    {
+      /*      write(STDOUT_FILENO, cur->index, cur->index_len);*/
+      printf("%p", (void*)cur);
+      printf("|");
+
+      if(cur->next == ht->table[i])
+      {
+	printf("??? Loop in table chain!!");
+	break;
+      }
+    }
+    printf("\n");
+  }
+}
+#endif
+
+
+lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret)
+{
+  return lru_cache_create_ctx(NULL, max_keys, secret, false);
+}
+
+
+lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, 
+				uint32_t secret, bool talloc_data)
+{
+  lru_cache* ret_val;
+
+  ret_val = talloc(talloc_ctx, lru_cache);
+  if(ret_val == NULL)
+    return NULL;
+
+  if(max_keys == 0)
+    ret_val->num_buckets = 1024;
+  else if(max_keys == 1)
+    ret_val->num_buckets = 1;    
+  else
+  {
+    ret_val->num_buckets = max_keys/lru_cache_floor_log2(max_keys);
+    if(ret_val->num_buckets < 1)
+      ret_val->num_buckets = 1;
+  }
+  
+  ret_val->table = talloc_array(ret_val, 
+				lru_cache_element*, ret_val->num_buckets);
+  if(ret_val->table == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  
+  ret_val->oldest = NULL;
+  ret_val->newest = NULL;
+  ret_val->max_keys = max_keys;
+  ret_val->secret = secret;
+  ret_val->talloc_data = talloc_data;
+  ret_val->num_keys = 0;
+  memset(ret_val->table, 0, ret_val->num_buckets*sizeof(lru_cache_element*));
+
+  return ret_val;
+}
+
+
+void lru_cache_destroy(lru_cache* ht)
+{
+  ht->secret = 0;
+  talloc_unlink(NULL, ht);
+}
+
+
+
+bool lru_cache_update(lru_cache* ht, const void* index, 
+		      uint32_t index_len, void* data)
+{
+  uint32_t hash, lru_hash;
+  lru_cache_element* cur;
+  lru_cache_element* last = NULL;
+  lru_cache_element* e = NULL;
+  void* tmp_index;
+
+  hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len);
+  for(cur = ht->table[hash]; cur != NULL && e == NULL; cur=cur->next)
+  {
+    if((index_len == cur->index_len) 
+       && memcmp(cur->index, index, index_len) == 0)
+    { e = cur; }
+  }
+  
+  if(e != NULL)
+  { /* We found the index, so we're going to overwrite the data.
+     * We also need to reposition the element to the newest position,
+     * so remove it from the list for now.
+     */
+    if(ht->talloc_data)
+      talloc_unlink(e, e->data);
+
+    if(e->newer == NULL)
+      ht->newest = e->older;
+    else
+      e->newer->older = e->older;
+
+    if(e->older == NULL)
+      ht->oldest = e->newer;
+    else
+      e->older->newer = e->newer;
+  }
+  else
+  { /* We didn't find an identical index. */
+    
+    if((ht->max_keys != 0) && (ht->num_keys >= ht->max_keys))
+    { /* Eliminate the least recently used item, but reuse the element
+       * structure to minimize reallocation. 
+       */
+      e = ht->oldest;
+      if(ht->newest == ht->oldest)
+      {
+	ht->newest = NULL;
+	ht->oldest = NULL;
+      }
+      else
+      {
+	ht->oldest = e->newer;
+	e->newer->older = NULL;
+      }
+      e->newer = NULL;
+      e->older = NULL;
+
+      last = NULL;
+      lru_hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, 
+					e->index, e->index_len);
+      for(cur = ht->table[lru_hash]; cur != e && cur != NULL; 
+	  last=cur, cur=cur->next)
+      {	continue; }
+
+      if(last == NULL)
+	ht->table[lru_hash] = e->next;
+      else
+	last->next = e->next;
+      e->next = NULL;
+
+      if(ht->talloc_data)
+	talloc_unlink(e, e->data);
+
+      tmp_index = talloc_realloc_size(e, e->index, index_len);
+      if(tmp_index == NULL)
+      {
+	talloc_free(e);
+	return false;
+      }
+      else
+	e->index = tmp_index;
+    }
+    else
+    { /* Brand new element because we have room to spare. */
+
+      e = talloc(ht->table, lru_cache_element);
+      if(e == NULL)
+	return false;
+      
+      e->index = talloc_size(e, index_len);
+      if(e->index == NULL)
+      {
+	talloc_free(e);
+	return false;
+      }
+      
+      /* New entry, increment counters. */
+      ht->num_keys++;
+    }
+    memcpy(e->index, index, index_len);
+    e->index_len = index_len;
+
+    e->next = ht->table[hash];
+    ht->table[hash] = e;
+  }
+  if(ht->talloc_data)
+    data = talloc_reference(e, data);
+  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;
+
+  /* Detach from list */
+  if(cur->newer == NULL)
+    ht->newest = cur->older;
+  else
+    cur->newer->older = cur->older;
+  
+  if(cur->older == NULL)
+    ht->oldest = cur->newer;
+  else
+    cur->older->newer = cur->newer;
+
+  /* Detach from hash table */
+  if(last == NULL)
+    ht->table[hash] = cur->next;
+  else
+    last->next = cur->next;
+
+  talloc_free(cur);
+  
+  /* Removing entry, decrement counters. */
+  ht->num_keys--;
+  
+  return true;
+}
Index: /releases/1.0.1/lib/range_list.c
===================================================================
--- /releases/1.0.1/lib/range_list.c	(revision 275)
+++ /releases/1.0.1/lib/range_list.c	(revision 275)
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2008-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/**
+ * @file
+ */
+
+#include "range_list.h"
+
+
+/*******************/
+/* Private symbols */
+/*******************/
+#define RANGE_LIST_ALLOC_SIZE 256
+
+#if 0 /* For debugging */
+#include <stdio.h>
+static void range_list_print(const range_list* rl)
+{
+  uint32_t i;
+  for(i=0; i<rl->size; i++)
+    fprintf(stderr, " %d=%p,%d,%d,%p", i, (void*)rl->elements[i],
+	    rl->elements[i]->offset, rl->elements[i]->length, 
+	    rl->elements[i]->data);
+  fprintf(stderr, "\n");
+}
+#endif
+
+/*
+ * Inserts elem into rl at the specified index and updates rl->size. 
+ * Memory reallocation of rl->elements is handled when necessary, and
+ * rl->elem_alloced is updated in this case..  Returns false if memory
+ * could not be allocated.  
+ */
+static bool range_list_insert(range_list* rl, range_list_element* elem, uint32_t index)
+{
+  uint32_t i;
+  range_list_element** tmp;
+
+  if(rl->size == rl->elem_alloced)
+  {
+    tmp = talloc_realloc(rl, rl->elements, range_list_element*, 
+			 (rl->elem_alloced+RANGE_LIST_ALLOC_SIZE));
+    if(tmp == NULL)
+      return false;
+    rl->elements = tmp;
+    rl->elem_alloced += RANGE_LIST_ALLOC_SIZE;
+  }
+
+  /* Do the shuffle to the right. */
+  for(i=rl->size; i > index; i--)
+    rl->elements[i] = rl->elements[i-1];
+  rl->elements[index] = elem;
+
+  rl->size++;
+  return true;
+}
+
+/*
+ * Finds the element with the closest offset to that provided, such that
+ * the element's offset <= the provided offset.  If no such element
+ * exists, this returns -1 which indicates that the provided offset
+ * appears before all elements.
+ */
+static int32_t range_list_find_previous(const range_list* rl, uint32_t offset)
+{
+  uint32_t h_idx, l_idx, cur_idx;
+  uint32_t h_val, l_val;
+  range_list_element* cur_elem;
+
+  if((rl->size == 0) || (offset < rl->elements[0]->offset))
+    return -1;
+
+  if(offset >= rl->elements[rl->size-1]->offset)
+    return rl->size-1;
+
+  h_idx = rl->size-1;
+  l_idx = 0;
+  while(h_idx != l_idx)
+  {
+    h_val = rl->elements[h_idx]->offset + rl->elements[h_idx]->length;
+    l_val = rl->elements[l_idx]->offset;
+    /* Make an educated guess as to the "middle" index based on the
+     * ratios of the offset and high/low values.
+     */
+    cur_idx = (uint32_t)ceil((((double)offset-l_val)/(h_val-l_val))*(h_idx-l_idx));
+    if(cur_idx > h_idx)
+      cur_idx = h_idx;
+    if(cur_idx < l_idx)
+      cur_idx = l_idx;
+    cur_elem = rl->elements[cur_idx];
+
+    if((offset >= cur_elem->offset) && (offset < rl->elements[cur_idx+1]->offset))
+      return cur_idx;
+    
+    if(offset < cur_elem->offset)
+      h_idx = cur_idx-1;
+    else
+      l_idx = cur_idx+1;
+  }
+
+  return h_idx;
+}
+
+
+/******************/
+/* Public symbols */
+/******************/
+range_list* range_list_new()
+{
+  range_list* rl;
+
+  rl = talloc(NULL, range_list);
+  if(rl == NULL)
+    return NULL;
+
+  rl->elements = talloc_array(rl, range_list_element*, RANGE_LIST_ALLOC_SIZE);
+  if(rl->elements == NULL)
+  {
+    talloc_free(rl);
+    return NULL;
+  }
+
+  rl->elem_alloced = RANGE_LIST_ALLOC_SIZE;
+  rl->size = 0;
+
+  return rl;
+}
+
+
+void range_list_free(range_list* rl)
+{
+  if(rl != NULL)
+    talloc_free(rl);
+}
+
+
+uint32_t range_list_size(const range_list* rl)
+{
+  return rl->size;
+}
+
+
+
+bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data)
+{
+  uint32_t insert_index;
+  range_list_element* elem;
+  range_list_element* prev_elem;
+  /*fprintf(stderr, "DEBUG: rl->size=%d\n", rl->size);*/
+  /* Sorry, limited to 2**31-1 elements. */
+  if(rl->size >= 0x7FFFFFFF)
+    return false;
+
+  /* 0-length ranges aren't allowed. */
+  if(length == 0)
+    return false;
+  
+  /* Check for integer overflows */
+  if((uint32_t)(offset+length) < offset || (uint32_t)(offset+length) < length)
+    return false;
+
+  /* Find insertion point and validate there are no overlaps */
+  insert_index = range_list_find_previous(rl, offset)+1;
+  
+  /* Does the previous element overlap with this one? */
+  if(insert_index > 0)
+  {
+    prev_elem = rl->elements[insert_index-1];
+    if(offset < prev_elem->length + prev_elem->offset)
+      return false;
+  }
+
+  /* Does this new element overlap with the next one? */
+  if((insert_index+1 < rl->size) 
+     && (offset+length > rl->elements[insert_index+1]->offset))
+    return false;
+
+  elem = talloc(rl->elements, range_list_element);
+  if(elem == NULL)
+    return false;
+  elem->offset = offset;
+  elem->length = length;
+  elem->data = data;
+  
+  if(!range_list_insert(rl, elem, insert_index))
+  {
+    talloc_free(elem);
+    return false;
+  }
+
+  return true;
+}
+
+
+bool range_list_remove(range_list* rl, uint32_t index)
+{
+  uint32_t i;
+  range_list_element** tmp;
+
+  if(index >= rl->size)
+    return false;
+
+  talloc_free(rl->elements[index]);
+
+  /* Do the shuffle to the left. */
+  for(i=index; i < (rl->size-1); i++)
+    rl->elements[i] = rl->elements[i+1];
+  rl->elements[rl->size-1] = NULL;
+  rl->size--;
+
+  /* Try to keep memory usage down */
+  if(rl->size + 2 * RANGE_LIST_ALLOC_SIZE  < rl->elem_alloced)
+  {
+    tmp = talloc_realloc(rl, rl->elements, range_list_element*, 
+			 (rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE));
+    if(tmp != NULL)
+    {
+      rl->elements = tmp;
+      rl->elem_alloced -= 2*RANGE_LIST_ALLOC_SIZE;
+    }
+  }
+
+  return true;
+}
+
+
+const range_list_element* range_list_get(const range_list* rl, uint32_t index)
+{
+  if(index >= rl->size)
+    return NULL;
+
+  return rl->elements[index];
+}
+
+
+int32_t range_list_find(const range_list* rl, uint32_t offset)
+{
+  uint32_t prev_idx;
+  range_list_element* elem;
+
+  if(rl->size == 0)
+    return -1;
+
+  if((offset < rl->elements[0]->offset)
+     || (offset > rl->elements[rl->size-1]->offset 
+	 + rl->elements[rl->size-1]->length))
+    return -2;
+
+  prev_idx = range_list_find_previous(rl, offset);
+  elem = rl->elements[prev_idx];
+  if(offset < elem->offset+elem->length)
+    return prev_idx;
+
+  return -3;
+}
+
+
+void* range_list_find_data(const range_list* rl, uint32_t offset)
+{
+  int32_t index = range_list_find(rl, offset);
+  if(index < 0)
+    return NULL;
+
+  return rl->elements[index]->data;
+}
+
+
+bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset)
+{
+  range_list_element* cur_elem;
+  range_list_element* new_elem;
+
+  if(index >= rl->size)
+    return false;
+
+  cur_elem = rl->elements[index];
+  if((offset <= cur_elem->offset) 
+     || (offset >= cur_elem->offset+cur_elem->length))
+    return false;
+
+  new_elem = talloc(rl->elements, range_list_element);
+  if(new_elem == NULL)
+    return false;
+  
+  new_elem->offset = offset;
+  new_elem->length = cur_elem->offset + cur_elem->length - offset;
+  new_elem->data = cur_elem->data;
+  
+  if(!range_list_insert(rl, new_elem, index+1))
+  {
+    talloc_free(new_elem);
+    return false;
+  }
+
+  cur_elem->length = new_elem->offset - cur_elem->offset;
+
+  return true;
+}
+
+
+bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length)
+{
+  int32_t idx1, idx2;
+
+  idx1 = range_list_find(rl, start);
+  if(idx1 < 0)
+    return false;
+
+  idx2 = range_list_find(rl, start+length);
+  if(idx2 < 0)
+    return false;
+
+  if(idx1 == idx2)
+    return true;
+
+  while(idx1 != idx2)
+  {
+    if(rl->elements[idx1]->offset + rl->elements[idx1]->length 
+       != rl->elements[idx1+1]->offset)
+      return false;
+    idx1++;
+  }
+
+  return true;
+}
Index: /releases/1.0.1/lib/regfi.c
===================================================================
--- /releases/1.0.1/lib/regfi.c	(revision 275)
+++ /releases/1.0.1/lib/regfi.c	(revision 275)
@@ -0,0 +1,3903 @@
+/*
+ * Copyright (C) 2005-2011 Timothy D. Morgan
+ * Copyright (C) 2005 Gerald (Jerry) Carter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/** 
+ * @file
+ *
+ * Windows NT (and later) read-only registry library
+ *
+ * See @ref regfi.h for more information.
+ *
+ * Branched from Samba project Subversion repository, version #7470:
+ *   http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto
+ *
+ * Since then, it has been heavily rewritten, simplified, and improved.
+ */
+
+#include "regfi.h"
+
+/* Library version can be overridden at build time */
+#ifndef REGFI_VERSION
+#define REGFI_VERSION "trunk"
+#endif
+
+
+/* Registry types mapping */
+const unsigned int regfi_num_reg_types = 12;
+static const char* regfi_type_names[] =
+  {"NONE", "SZ", "EXPAND_SZ", "BINARY", "DWORD", "DWORD_BE", "LINK",
+   "MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
+
+const char* regfi_encoding_names[] =
+  {"US-ASCII", "UTF-8", "UTF-16LE"};
+
+
+/* Ensures regfi_init runs only once */
+static pthread_once_t regfi_init_once = PTHREAD_ONCE_INIT;
+
+
+/******************************************************************************
+ ******************************************************************************/
+const char* regfi_version()
+{
+  return REGFI_VERSION;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_log_free(void* ptr)
+{
+  REGFI_LOG* log_info = (REGFI_LOG*)ptr;
+  
+  if(log_info->messages != NULL)
+    free(log_info->messages);
+
+  talloc_free(log_info);
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_init()
+{
+  int err;
+  if((err = pthread_key_create(&regfi_log_key, regfi_log_free)) != 0)
+    fprintf(stderr, "ERROR: key_create: %s\n", strerror(err));
+  errno = err;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_LOG* regfi_log_new()
+{
+  int err;
+  REGFI_LOG* log_info = talloc(NULL, REGFI_LOG);
+  if(log_info == NULL)
+    return NULL;
+
+  log_info->msg_mask = REGFI_DEFAULT_LOG_MASK;
+  log_info->messages = NULL;
+
+  pthread_once(&regfi_init_once, regfi_init);
+
+  if((err = pthread_setspecific(regfi_log_key, log_info)) != 0)
+  {
+    fprintf(stderr, "ERROR: setspecific: %s\n", strerror(err));
+    goto fail;
+  }
+
+  return log_info;
+
+ fail:
+  talloc_free(log_info);
+  errno = err;
+  return NULL;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_log_add(uint16_t msg_type, const char* fmt, ...)
+{
+  /* XXX: Switch internal storage over to a linked list or stack.
+   *      Then add a regfi_log_get function that returns the list in some
+   *      convenient, user-friendly data structure.  regfi_log_get_str should
+   *      stick around and will simply smush the list into a big string when 
+   *      it's called, rather than having messages smushed when they're first
+   *      written to the log.
+   */
+  uint32_t buf_size, buf_used;
+  char* new_msg;
+  REGFI_LOG* log_info;
+  va_list args;
+
+  log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
+  if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
+    return;
+
+  if((log_info->msg_mask & msg_type) == 0)
+    return;
+
+  if(log_info->messages == NULL)
+    buf_used = 0;
+  else
+    buf_used = strlen(log_info->messages);
+  
+  buf_size = buf_used+strlen(fmt)+160;
+  new_msg = realloc(log_info->messages, buf_size);
+  if(new_msg == NULL)
+    /* XXX: should we report this? */
+    return;
+  
+  switch (msg_type)
+  {
+  case REGFI_LOG_INFO:
+    strcpy(new_msg+buf_used, "INFO: ");
+    buf_used += 6;
+    break;
+  case REGFI_LOG_WARN:
+    strcpy(new_msg+buf_used, "WARN: ");
+    buf_used += 6;
+    break;
+  case REGFI_LOG_ERROR:
+    strcpy(new_msg+buf_used, "ERROR: ");
+    buf_used += 7;
+    break;
+  }
+  
+  va_start(args, fmt);
+  vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args);
+  va_end(args);
+  strncat(new_msg, "\n", buf_size-1);
+  
+  log_info->messages = new_msg;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+char* regfi_log_get_str()
+{
+  char* ret_val;
+  REGFI_LOG* log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
+  if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
+    return NULL;
+  
+  ret_val = log_info->messages;
+  log_info->messages = NULL;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+bool regfi_log_set_mask(uint16_t msg_mask)
+{
+  REGFI_LOG* log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
+  if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
+  {
+      return false;
+  }
+
+  log_info->msg_mask = msg_mask;
+  return true;
+}
+
+
+/******************************************************************************
+ * Returns NULL for an invalid e
+ *****************************************************************************/
+static const char* regfi_encoding_int2str(REGFI_ENCODING e)
+{
+  if(e < REGFI_NUM_ENCODINGS)
+    return regfi_encoding_names[e];
+
+  return NULL;
+}
+
+
+/******************************************************************************
+ * Returns NULL for an invalid val
+ *****************************************************************************/
+const char* regfi_type_val2str(unsigned int val)
+{
+  if(val == REG_KEY)
+    return "KEY";
+  
+  if(val >= regfi_num_reg_types)
+    return NULL;
+  
+  return regfi_type_names[val];
+}
+
+
+/******************************************************************************
+ * Returns -1 on error
+ *****************************************************************************/
+int regfi_type_str2val(const char* str)
+{
+  int i;
+
+  if(strcmp("KEY", str) == 0)
+    return REG_KEY;
+
+  for(i=0; i < regfi_num_reg_types; i++)
+    if (strcmp(regfi_type_names[i], str) == 0) 
+      return i;
+
+  if(strcmp("DWORD_LE", str) == 0)
+    return REG_DWORD_LE;
+
+  return -1;
+}
+
+
+/* Security descriptor formatting functions  */
+
+const char* regfi_ace_type2str(uint8_t type)
+{
+  static const char* map[7] 
+    = {"ALLOW", "DENY", "AUDIT", "ALARM", 
+       "ALLOW CPD", "OBJ ALLOW", "OBJ DENY"};
+  if(type < 7)
+    return map[type];
+  else
+    /* XXX: would be nice to return the unknown integer value.  
+     *      However, as it is a const string, it can't be free()ed later on, 
+     *      so that would need to change. 
+     */
+    return "UNKNOWN";
+}
+
+
+/* XXX: need a better reference on the meaning of each flag. */
+/* For more info, see:
+ *   http://msdn2.microsoft.com/en-us/library/aa772242.aspx
+ */
+char* regfi_ace_flags2str(uint8_t flags)
+{
+  static const char* flag_map[32] = 
+    { "OI", /* Object Inherit */
+      "CI", /* Container Inherit */
+      "NP", /* Non-Propagate */
+      "IO", /* Inherit Only */
+      "IA", /* Inherited ACE */
+      NULL,
+      NULL,
+      NULL,
+    };
+
+  char* ret_val = malloc(35*sizeof(char));
+  char* fo = ret_val;
+  uint32_t i;
+  uint8_t f;
+
+  if(ret_val == NULL)
+    return NULL;
+
+  fo[0] = '\0';
+  if (!flags)
+    return ret_val;
+
+  for(i=0; i < 8; i++)
+  {
+    f = (1<<i);
+    if((flags & f) && (flag_map[i] != NULL))
+    {
+      strcpy(fo, flag_map[i]);
+      fo += strlen(flag_map[i]);
+      *(fo++) = ' ';
+      flags ^= f;
+    }
+  }
+  
+  /* Any remaining unknown flags are added at the end in hex. */
+  if(flags != 0)
+    sprintf(fo, "0x%.2X ", flags);
+
+  /* Chop off the last space if we've written anything to ret_val */
+  if(fo != ret_val)
+    fo[-1] = '\0';
+
+  return ret_val;
+}
+
+
+char* regfi_ace_perms2str(uint32_t perms)
+{
+  uint32_t i, p;
+  /* This is more than is needed by a fair margin. */
+  char* ret_val = malloc(350*sizeof(char));
+  char* r = ret_val;
+
+  /* Each represents one of 32 permissions bits.  NULL is for undefined/reserved bits.
+   * For more information, see:
+   *   http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
+   *   http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
+   */
+  static const char* perm_map[32] = 
+    {/* object-specific permissions (registry keys, in this case) */
+      "QRY_VAL",       /* KEY_QUERY_VALUE */
+      "SET_VAL",       /* KEY_SET_VALUE */
+      "CREATE_KEY",    /* KEY_CREATE_SUB_KEY */
+      "ENUM_KEYS",     /* KEY_ENUMERATE_SUB_KEYS */
+      "NOTIFY",        /* KEY_NOTIFY */
+      "CREATE_LNK",    /* KEY_CREATE_LINK - Reserved for system use. */
+      NULL,
+      NULL,
+      "WOW64_64",      /* KEY_WOW64_64KEY */
+      "WOW64_32",      /* KEY_WOW64_32KEY */
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      NULL,
+      /* standard access rights */
+      "DELETE",        /* DELETE */
+      "R_CONT",        /* READ_CONTROL */
+      "W_DAC",         /* WRITE_DAC */
+      "W_OWNER",       /* WRITE_OWNER */
+      "SYNC",          /* SYNCHRONIZE - Shouldn't be set in registries */
+      NULL,
+      NULL,
+      NULL,
+      /* other generic */
+      "SYS_SEC",       /* ACCESS_SYSTEM_SECURITY */
+      "MAX_ALLWD",     /* MAXIMUM_ALLOWED */
+      NULL,
+      NULL,
+      "GEN_A",         /* GENERIC_ALL */
+      "GEN_X",         /* GENERIC_EXECUTE */
+      "GEN_W",         /* GENERIC_WRITE */
+      "GEN_R",         /* GENERIC_READ */
+    };
+
+
+  if(ret_val == NULL)
+    return NULL;
+
+  r[0] = '\0';
+  for(i=0; i < 32; i++)
+  {
+    p = (1<<i);
+    if((perms & p) && (perm_map[i] != NULL))
+    {
+      strcpy(r, perm_map[i]);
+      r += strlen(perm_map[i]);
+      *(r++) = ' ';
+      perms ^= p;
+    }
+  }
+  
+  /* Any remaining unknown permission bits are added at the end in hex. */
+  if(perms != 0)
+    sprintf(r, "0x%.8X ", perms);
+
+  /* Chop off the last space if we've written anything to ret_val */
+  if(r != ret_val)
+    r[-1] = '\0';
+
+  return ret_val;
+}
+
+
+char* regfi_get_acl(WINSEC_ACL* acl)
+{
+  uint32_t i, extra, size = 0;
+  const char* type_str;
+  char* flags_str;
+  char* perms_str;
+  char* sid_str;
+  char* ace_delim = "";
+  char* ret_val = NULL;
+  char* tmp_val = NULL;
+  bool failed = false;
+  char field_delim = ':';
+
+  for (i = 0; i < acl->num_aces && !failed; i++)
+  {
+    sid_str = winsec_sid2str(acl->aces[i]->trustee);
+    type_str = regfi_ace_type2str(acl->aces[i]->type);
+    perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask);
+    flags_str = regfi_ace_flags2str(acl->aces[i]->flags);
+    
+    if(flags_str != NULL && perms_str != NULL 
+       && type_str != NULL && sid_str != NULL)
+    {
+      /* XXX: this is slow */
+      extra = strlen(sid_str) + strlen(type_str) 
+	+ strlen(perms_str) + strlen(flags_str) + 5;
+      tmp_val = realloc(ret_val, size+extra);
+
+      if(tmp_val == NULL)
+      {
+	free(ret_val);
+	ret_val = NULL;
+	failed = true;
+      }
+      else
+      {
+	ret_val = tmp_val;
+	size += sprintf(ret_val+size, "%s%s%c%s%c%s%c%s",
+			ace_delim,sid_str,
+			field_delim,type_str,
+			field_delim,perms_str,
+			field_delim,flags_str);
+	ace_delim = "|";
+      }
+    }
+    else
+      failed = true;
+
+    if(sid_str != NULL)
+      free(sid_str);
+    if(sid_str != NULL)
+      free(perms_str);
+    if(sid_str != NULL)
+      free(flags_str);
+  }
+
+  return ret_val;
+}
+
+
+char* regfi_get_sacl(WINSEC_DESC *sec_desc)
+{
+  if (sec_desc->sacl)
+    return regfi_get_acl(sec_desc->sacl);
+  else
+    return NULL;
+}
+
+
+char* regfi_get_dacl(WINSEC_DESC *sec_desc)
+{
+  if (sec_desc->dacl)
+    return regfi_get_acl(sec_desc->dacl);
+  else
+    return NULL;
+}
+
+
+char* regfi_get_owner(WINSEC_DESC *sec_desc)
+{
+  return winsec_sid2str(sec_desc->owner_sid);
+}
+
+
+char* regfi_get_group(WINSEC_DESC *sec_desc)
+{
+  return winsec_sid2str(sec_desc->grp_sid);
+}
+
+
+bool regfi_read_lock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
+{
+  int lock_ret = pthread_rwlock_rdlock(lock);
+  if(lock_ret != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Error obtaining read lock in"
+		      "%s due to: %s\n", context, strerror(lock_ret));
+    return false;
+  }
+
+  return true;
+}
+
+
+bool regfi_write_lock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
+{
+  int lock_ret = pthread_rwlock_wrlock(lock);
+  if(lock_ret != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Error obtaining write lock in"
+		      "%s due to: %s\n", context, strerror(lock_ret));
+    return false;
+  }
+
+  return true;
+}
+
+
+bool regfi_rw_unlock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
+{
+  int lock_ret = pthread_rwlock_unlock(lock);
+  if(lock_ret != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Error releasing lock in"
+		      "%s due to: %s\n", context, strerror(lock_ret));
+    return false;
+  }
+
+  return true;
+}
+
+
+bool regfi_lock(REGFI_FILE* file, pthread_mutex_t* lock, const char* context)
+{
+  int lock_ret = pthread_mutex_lock(lock);
+  if(lock_ret != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Error obtaining mutex lock in"
+		      "%s due to: %s\n", context, strerror(lock_ret));
+    return false;
+  }
+
+  return true;
+}
+
+
+bool regfi_unlock(REGFI_FILE* file, pthread_mutex_t* lock, const char* context)
+{
+  int lock_ret = pthread_mutex_unlock(lock);
+  if(lock_ret != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Error releasing mutex lock in"
+		      "%s due to: %s\n", context, strerror(lock_ret));
+    return false;
+  }
+
+  return true;
+}
+
+
+int64_t regfi_raw_seek(REGFI_RAW_FILE* self, uint64_t offset, int whence)
+{
+  if(sizeof(off_t) == 4 && offset > 2147483647)
+  {
+    errno = EOVERFLOW;
+    return -1;
+  }
+  return lseek(*(int*)self->state, offset, whence);
+}
+
+ssize_t regfi_raw_read(REGFI_RAW_FILE* self, void* buf, size_t count)
+{
+  return read(*(int*)self->state, buf, count);
+}
+
+
+/*****************************************************************************
+ * Convenience function to wrap up the ugly callback stuff
+ *****************************************************************************/
+uint64_t regfi_seek(REGFI_RAW_FILE* file_cb, uint64_t offset, int whence)
+{
+  return file_cb->seek(file_cb, offset, whence);
+}
+
+
+/*****************************************************************************
+ * 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 the file and write them to
+ * buf.
+ *
+ * On success, 0 is returned.  Upon failure, an errno code is returned.
+ *
+ * The number of bytes successfully read is returned through the length 
+ * parameter by reference.  If both the return value and length parameter are 
+ * returned as 0, then EOF was encountered immediately
+ *****************************************************************************/
+uint32_t regfi_read(REGFI_RAW_FILE* file_cb, uint8_t* buf, uint32_t* length)
+{
+  uint32_t rsize = 0;
+  uint32_t rret = 0;
+
+  do
+  {
+    rret = file_cb->read(file_cb, 
+                         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(REGFI_RAW_FILE* file_cb, uint32_t offset, uint8_t* hdr, 
+		      uint32_t hdr_len, uint32_t* cell_length, bool* unalloc)
+{
+  uint32_t length;
+  int32_t raw_length;
+  uint8_t tmp[4];
+
+  if(regfi_seek(file_cb, offset, SEEK_SET) == -1)
+    return false;
+
+  length = 4;
+  if((regfi_read(file_cb, 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(file_cb, hdr, &length) != 0) || length != hdr_len)
+      return false;
+  }
+
+  return true;
+}
+
+
+/******************************************************************************
+ * Given an offset and an hbin, is the offset within that hbin?
+ * The offset is a virtual file offset.
+ ******************************************************************************/
+static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32_t voffset)
+{
+  if(!hbin)
+    return false;
+
+  if((voffset > hbin->first_hbin_off) 
+     && (voffset < (hbin->first_hbin_off + hbin->block_size)))
+    return true;
+		
+  return false;
+}
+
+
+
+/******************************************************************************
+ * Provide a physical offset and receive the correpsonding HBIN
+ * block for it.  NULL if one doesn't exist.
+ ******************************************************************************/
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset)
+{
+  return (const REGFI_HBIN*)range_list_find_data(file->hbins, offset);
+}
+
+
+/******************************************************************************
+ * Calculate the largest possible cell size given a physical offset.
+ * Largest size is based on the HBIN the offset is currently a member of.
+ * Returns negative values on error.
+ * (Since cells can only be ~2^31 in size, this works out.)
+ ******************************************************************************/
+int32_t regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset)
+{
+  const REGFI_HBIN* hbin = regfi_lookup_hbin(file, offset);
+  if(hbin == NULL)
+    return -1;
+
+  return (hbin->block_size + hbin->file_off) - offset;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset, 
+					 uint32_t num_keys, uint32_t max_size, 
+					 bool strict)
+{
+  REGFI_SUBKEY_LIST* ret_val;
+
+  ret_val = regfi_load_subkeylist_aux(file, offset, max_size, strict, 
+				      REGFI_MAX_SUBKEY_DEPTH);
+  if(ret_val == NULL)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Failed to load subkey list at"
+		      " offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  if(num_keys != ret_val->num_keys)
+  {
+    /*  Not sure which should be authoritative, the number from the 
+     *  NK record, or the number in the subkey list.  Just emit a warning for
+     *  now if they don't match.
+     */
+    regfi_log_add(REGFI_LOG_WARN, "Number of subkeys listed in parent"
+		      " (%d) did not match number found in subkey list/tree (%d)"
+		      " while parsing subkey list/tree at offset 0x%.8X.", 
+		      num_keys, ret_val->num_keys, offset);
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset, 
+					     uint32_t max_size, bool strict,
+					     uint8_t depth_left)
+{
+  REGFI_SUBKEY_LIST* ret_val;
+  REGFI_SUBKEY_LIST** sublists;
+  uint32_t i, num_sublists, off;
+  int32_t sublist_maxsize;
+
+  if(depth_left == 0)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Maximum depth reached"
+		      " while parsing subkey list/tree at offset 0x%.8X.", 
+		      offset);
+    return NULL;
+  }
+
+  ret_val = regfi_parse_subkeylist(file, offset, max_size, strict);
+  if(ret_val == NULL)
+    return NULL;
+
+  if(ret_val->recursive_type)
+  {
+    num_sublists = ret_val->num_children;
+    sublists = (REGFI_SUBKEY_LIST**)malloc(num_sublists 
+					   * sizeof(REGFI_SUBKEY_LIST*));
+    for(i=0; i < num_sublists; i++)
+    {
+      off = ret_val->elements[i].offset + REGFI_REGF_SIZE;
+
+      sublist_maxsize = regfi_calc_maxsize(file, off);
+      if(sublist_maxsize < 0)
+	sublists[i] = NULL;
+      else
+	sublists[i] = regfi_load_subkeylist_aux(file, off, sublist_maxsize, 
+						strict, depth_left-1);
+    }
+    talloc_free(ret_val);
+
+    return regfi_merge_subkeylists(num_sublists, sublists, strict);
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset, 
+					  uint32_t max_size, bool strict)
+{
+  REGFI_SUBKEY_LIST* ret_val = NULL;
+  uint32_t i, cell_length, length, elem_size, read_len;
+  uint8_t* elements = NULL;
+  uint8_t buf[REGFI_SUBKEY_LIST_MIN_LEN];
+  bool unalloc;
+  bool recursive_type;
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_subkeylist"))
+     goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN,
+		       &cell_length, &unalloc))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while "
+		      "parsing subkey-list at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  if(cell_length > max_size)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Cell size longer than max_size"
+		      " while parsing subkey-list at offset 0x%.8X.", offset);
+    if(strict)
+      goto fail_locked;
+    cell_length = max_size & 0xFFFFFFF8;
+  }
+
+  recursive_type = false;
+  if(buf[0] == 'r' && buf[1] == 'i')
+  {
+    recursive_type = true;
+    elem_size = sizeof(uint32_t);
+  }
+  else if(buf[0] == 'l' && buf[1] == 'i')
+  {
+    elem_size = sizeof(uint32_t);
+  }
+  else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
+    elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
+  else
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Unknown magic number"
+		      " (0x%.2X, 0x%.2X) encountered while parsing"
+		      " subkey-list at offset 0x%.8X.", buf[0], buf[1], offset);
+    goto fail_locked;
+  }
+
+  ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
+  if(ret_val == NULL)
+    goto fail_locked;
+
+  ret_val->offset = offset;
+  ret_val->cell_size = cell_length;
+  ret_val->magic[0] = buf[0];
+  ret_val->magic[1] = buf[1];
+  ret_val->recursive_type = recursive_type;
+  ret_val->num_children = SVAL(buf, 0x2);
+
+  if(!recursive_type)
+    ret_val->num_keys = ret_val->num_children;
+
+  length = elem_size*ret_val->num_children;
+  if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t) < length)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Number of elements too large for"
+		      " cell while parsing subkey-list at offset 0x%.8X.", 
+		      offset);
+    if(strict)
+      goto fail_locked;
+    length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t);
+  }
+
+  ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, 
+				   ret_val->num_children);
+  if(ret_val->elements == NULL)
+    goto fail_locked;
+
+  elements = (uint8_t*)malloc(length);
+  if(elements == NULL)
+    goto fail_locked;
+
+  read_len = length;
+  if(regfi_read(file->cb, elements, &read_len) != 0 || read_len!=length)
+    goto fail_locked;
+
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_subkeylist"))
+     goto fail;
+
+  if(elem_size == sizeof(uint32_t))
+  {
+    for (i=0; i < ret_val->num_children; i++)
+    {
+      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
+      ret_val->elements[i].hash = 0;
+    }
+  }
+  else
+  {
+    for (i=0; i < ret_val->num_children; i++)
+    {
+      ret_val->elements[i].offset = IVAL(elements, i*elem_size);
+      ret_val->elements[i].hash = IVAL(elements, i*elem_size+4);
+    }
+  }
+  free(elements);
+
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_subkeylist");
+ fail:
+  if(elements != NULL)
+    free(elements);
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists, 
+					   REGFI_SUBKEY_LIST** lists,
+					   bool strict)
+{
+  uint32_t i,j,k;
+  REGFI_SUBKEY_LIST* ret_val;
+
+  if(lists == NULL)
+    return NULL;
+  ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
+
+  if(ret_val == NULL)
+    return NULL;
+  
+  /* Obtain total number of elements */
+  ret_val->num_keys = 0;
+  for(i=0; i < num_lists; i++)
+  {
+    if(lists[i] != NULL)
+      ret_val->num_keys += lists[i]->num_children;
+  }
+  ret_val->num_children = ret_val->num_keys;
+
+  if(ret_val->num_keys > 0)
+  {
+    ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM,
+				     ret_val->num_keys);
+    k=0;
+
+    if(ret_val->elements != NULL)
+    {
+      for(i=0; i < num_lists; i++)
+      {
+	if(lists[i] != NULL)
+	{
+	  for(j=0; j < lists[i]->num_keys; j++)
+	  {
+	    ret_val->elements[k].hash = lists[i]->elements[j].hash;
+	    ret_val->elements[k++].offset = lists[i]->elements[j].offset;
+	  }
+	}
+      }
+    }
+  }
+  
+  for(i=0; i < num_lists; i++)
+    talloc_free(lists[i]);
+  free(lists);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_SK* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, 
+			     bool strict)
+{
+  REGFI_SK* ret_val = NULL;
+  uint8_t* sec_desc_buf = NULL;
+  uint32_t cell_length, length;
+  uint8_t sk_header[REGFI_SK_MIN_LENGTH];
+  bool unalloc = false;
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_sk"))
+     goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, sk_header, REGFI_SK_MIN_LENGTH,
+		       &cell_length, &unalloc))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse SK record cell"
+		      " at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+   
+  if(sk_header[0] != 's' || sk_header[1] != 'k')
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch in parsing"
+		      " SK record at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  ret_val = talloc(NULL, REGFI_SK);
+  if(ret_val == NULL)
+    goto fail_locked;
+
+  ret_val->offset = offset;
+  /* XXX: Is there a way to be more conservative (shorter) with 
+   *      cell length when cell is unallocated?
+   */
+  ret_val->cell_size = cell_length;
+
+  if(ret_val->cell_size > max_size)
+    ret_val->cell_size = max_size & 0xFFFFFFF8;
+  if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
+     || (strict && (ret_val->cell_size & 0x00000007) != 0))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Invalid cell size found while"
+		      " parsing SK record at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  ret_val->magic[0] = sk_header[0];
+  ret_val->magic[1] = sk_header[1];
+
+  ret_val->unknown_tag = SVAL(sk_header, 0x2);
+  ret_val->prev_sk_off = IVAL(sk_header, 0x4);
+  ret_val->next_sk_off = IVAL(sk_header, 0x8);
+  ret_val->ref_count = IVAL(sk_header, 0xC);
+  ret_val->desc_size = IVAL(sk_header, 0x10);
+
+  if((ret_val->prev_sk_off & 0x00000007) != 0
+     || (ret_val->next_sk_off & 0x00000007) != 0)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "SK record's next/previous offsets"
+		      " are not a multiple of 8 while parsing SK record at"
+		      " offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Security descriptor too large for"
+		      " cell while parsing SK record at offset 0x%.8X.", 
+		      offset);
+    goto fail_locked;
+  }
+
+  sec_desc_buf = (uint8_t*)malloc(ret_val->desc_size);
+  if(sec_desc_buf == NULL)
+    goto fail_locked;
+
+  length = ret_val->desc_size;
+  if(regfi_read(file->cb, sec_desc_buf, &length) != 0 
+     || length != ret_val->desc_size)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to read security"
+		      " descriptor while parsing SK record at offset 0x%.8X.",
+		      offset);
+    goto fail_locked;
+  }
+
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_sk"))
+     goto fail;
+
+  if(!(ret_val->sec_desc = winsec_parse_desc(ret_val, sec_desc_buf, 
+						   ret_val->desc_size)))
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to parse security"
+		      " descriptor while parsing SK record at offset 0x%.8X.",
+		      offset);
+    goto fail;
+  }
+
+  free(sec_desc_buf);
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_sk");
+ fail:
+  if(sec_desc_buf != NULL)
+    free(sec_desc_buf);
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset, 
+					uint32_t num_values, bool strict)
+{
+  REGFI_VALUE_LIST* ret_val = NULL;
+  uint32_t i, cell_length, length, read_len;
+  bool unalloc;
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_valuelist"))
+     goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc))
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to read cell header"
+		      " while parsing value list at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  if((cell_length & 0x00000007) != 0)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Cell length not a multiple of 8"
+		      " while parsing value list at offset 0x%.8X.", offset);
+    if(strict)
+      goto fail_locked;
+    cell_length = cell_length & 0xFFFFFFF8;
+  }
+
+  if((num_values * sizeof(uint32_t)) > cell_length-sizeof(uint32_t))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Too many values found"
+		      " while parsing value list at offset 0x%.8X.", offset);
+    if(strict)
+      goto fail_locked;
+    num_values = cell_length/sizeof(uint32_t) - sizeof(uint32_t);
+  }
+
+  read_len = num_values*sizeof(uint32_t);
+  ret_val = talloc(NULL, REGFI_VALUE_LIST);
+  if(ret_val == NULL)
+    goto fail_locked;
+
+  ret_val->elements = (REGFI_VALUE_LIST_ELEM*)talloc_size(ret_val, read_len);
+  if(ret_val->elements == NULL)
+    goto fail_locked;
+
+  ret_val->offset = offset;
+  ret_val->cell_size = cell_length;
+  ret_val->num_values = num_values;
+
+  length = read_len;
+  if((regfi_read(file->cb, (uint8_t*)ret_val->elements, &length) != 0) 
+     || length != read_len)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to read value pointers"
+		      " while parsing value list at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+  
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_valuelist"))
+     goto fail;
+
+  for(i=0; i < num_values; i++)
+  {
+    /* Fix endianness */
+    ret_val->elements[i] = IVAL(&ret_val->elements[i], 0);
+
+    /* Validate the first num_values values to ensure they make sense */
+    if(strict)
+    {
+      /* XXX: Need to revisit this file length check when we start dealing 
+       *      with partial files. */
+      if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length)
+	 || ((ret_val->elements[i] & 0x00000007) != 0))
+      {
+	regfi_log_add(REGFI_LOG_WARN, "Invalid value pointer"
+			  " (0x%.8X) found while parsing value list at offset"
+			  " 0x%.8X.", ret_val->elements[i], offset);
+	goto fail;
+      }
+    }
+  }
+
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_valuelist");
+ fail:
+  talloc_free(ret_val);
+  return NULL;
+}
+
+/* XXX: should give this boolean return type to indicate errors */
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK* vk, bool strict)
+{
+  /* XXX: Registry value names are supposedly limited to 16383 characters 
+   *      according to:
+   *      http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+   *      Might want to emit a warning if this is exceeded.  
+   *      It is expected that "characters" could be variable width.
+   *      Also, it may be useful to use this information to limit false positives
+   *      when recovering deleted VK records.
+   */
+  REGFI_BUFFER tmp_buf;
+  REGFI_ENCODING from_encoding = (vk->flags & REGFI_VK_FLAG_ASCIINAME)
+    ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
+
+  if(vk->name_length == 0)
+    return;
+
+  if(from_encoding == file->string_encoding)
+  {
+    vk->name_raw[vk->name_length] = '\0';
+    vk->name = (char*)vk->name_raw;
+  }
+  else
+  {
+    tmp_buf = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+                                 regfi_encoding_int2str(file->string_encoding),
+                                 vk->name_raw, vk->name_length);
+    if(tmp_buf.buf == NULL)
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Error occurred while converting"
+                    " value name to encoding %s.  VK offset: 0x%.8X."
+                    "  Error message: %s",
+                    regfi_encoding_int2str(file->string_encoding),
+                    vk->offset, strerror(errno));
+      vk->name = NULL;
+    }
+    else
+    {
+      vk->name = (char*)tmp_buf.buf;
+      talloc_reparent(NULL, vk, vk->name);
+    }
+  }
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_VK* regfi_load_value(REGFI_FILE* file, uint32_t offset, bool strict)
+{
+  REGFI_VK* ret_val = NULL;
+  int32_t max_size;
+
+  max_size = regfi_calc_maxsize(file, offset);
+  if(max_size < 0)
+    return NULL;
+  
+  ret_val = regfi_parse_vk(file, offset, max_size, strict);
+  if(ret_val == NULL)
+    return NULL;
+
+  regfi_interpret_valuename(file, ret_val, strict);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * If !strict, the list may contain NULLs, VK records may point to NULL.
+ ******************************************************************************/
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, 
+				       uint32_t num_values, uint32_t max_size,
+				       bool strict)
+{
+  uint32_t usable_num_values;
+
+  if((num_values+1) * sizeof(uint32_t) > max_size)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Number of values indicated by"
+		      " parent key (%d) would cause cell to straddle HBIN"
+		      " boundary while loading value list at offset"
+		      " 0x%.8X.", num_values, offset);
+    if(strict)
+      return NULL;
+    usable_num_values = max_size/sizeof(uint32_t) - sizeof(uint32_t);
+  }
+  else
+    usable_num_values = num_values;
+
+  return regfi_parse_valuelist(file, offset, usable_num_values, strict);
+}
+
+
+/* XXX: should give this boolean return type to indicate errors */
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK* nk, bool strict)
+{
+  /* XXX: Registry key names are supposedly limited to 255 characters according to:
+   *      http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+   *      Might want to emit a warning if this is exceeded.  
+   *      It is expected that "characters" could be variable width.
+   *      Also, it may be useful to use this information to limit false positives
+   *      when recovering deleted NK records.
+   */
+  REGFI_BUFFER tmp_buf;
+  REGFI_ENCODING from_encoding = (nk->flags & REGFI_NK_FLAG_ASCIINAME) 
+    ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
+
+  if(nk->name_length == 0)
+    return;  
+
+  if(from_encoding == file->string_encoding)
+  {
+    nk->name_raw[nk->name_length] = '\0';
+    nk->name = (char*)nk->name_raw;
+  }
+  else
+  {
+    tmp_buf = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+                                 regfi_encoding_int2str(file->string_encoding),
+                                 nk->name_raw, nk->name_length);
+    if(tmp_buf.buf == NULL)
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Error occurred while converting"
+                    " key name to encoding %s.  NK offset: 0x%.8X."
+                    "  Error message: %s",
+                    regfi_encoding_int2str(file->string_encoding), 
+                    nk->offset, strerror(errno));
+      nk->name = NULL;
+    }
+    else
+    {
+      nk->name = (char*)tmp_buf.buf;
+      talloc_reparent(NULL, nk, nk->name);
+    }
+  }
+}
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_NK* regfi_load_key(REGFI_FILE* file, uint32_t offset, bool strict)
+{
+  REGFI_NK* nk;
+  uint32_t off;
+  int32_t max_size;
+
+  if(file->nk_cache != NULL)
+  {
+    /* First, check to see if we have this key in our cache */
+    if(!regfi_lock(file, &file->mem_lock, "regfi_load_nk"))
+      return NULL;
+    regfi_lock(file, &file->nk_lock, "regfi_load_nk");
+    
+    nk = (REGFI_NK*)lru_cache_find(file->nk_cache, &offset, 4);
+    if(nk != NULL)
+      nk = talloc_reference(NULL, nk);
+
+    regfi_unlock(file, &file->nk_lock, "regfi_load_nk");
+    regfi_unlock(file, &file->mem_lock, "regfi_load_nk");
+    if(nk != NULL)
+      return nk;
+  }
+
+  /* Not cached currently, proceed with loading it */
+  max_size = regfi_calc_maxsize(file, offset);
+  if (max_size < 0) 
+    return NULL;
+
+  /* get the initial nk record */
+  if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Could not load NK record at"
+		  " offset 0x%.8X.", offset);
+    return NULL;
+  }
+
+  regfi_interpret_keyname(file, nk, strict);
+
+  /* get value list */
+  if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) 
+  {
+    off = nk->values_off + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, off);
+    if(max_size < 0)
+    {
+      if(strict)
+      {
+	talloc_free(nk);
+	return NULL;
+      }
+      else
+	nk->values = NULL;
+    }
+    else
+    {
+      nk->values = regfi_load_valuelist(file, off, nk->num_values, 
+					max_size, true);
+      if(nk->values == NULL)
+      {
+	regfi_log_add(REGFI_LOG_WARN, "Could not load value list"
+		      " for NK record at offset 0x%.8X.", offset);
+	if(strict)
+	{
+	  talloc_free(nk);
+	  return NULL;
+	}
+      }
+      talloc_reparent(NULL, nk, nk->values);
+    }
+  }
+
+  /* now get subkey list */
+  if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE)) 
+  {
+    off = nk->subkeys_off + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, off);
+    if(max_size < 0) 
+    {
+      if(strict)
+      {
+	talloc_free(nk);
+	return NULL;
+      }
+      else
+	nk->subkeys = NULL;
+    }
+    else
+    {
+      nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys,
+					  max_size, true);
+
+      if(nk->subkeys == NULL)
+      {
+	regfi_log_add(REGFI_LOG_WARN, "Could not load subkey list"
+		      " while parsing NK record at offset 0x%.8X.", offset);
+	nk->num_subkeys = 0;
+      }
+      talloc_reparent(NULL, nk, nk->subkeys);
+    }
+  }
+
+  if(file->nk_cache != NULL)
+  {
+    /* All is well, so let us cache this key for later */
+    if(!regfi_lock(file, &file->mem_lock, "regfi_load_nk"))
+      return NULL;
+    regfi_lock(file, &file->nk_lock, "regfi_load_nk");
+    
+    lru_cache_update(file->nk_cache, &offset, 4, nk);
+    
+    regfi_unlock(file, &file->nk_lock, "regfi_load_nk");
+    regfi_unlock(file, &file->mem_lock, "regfi_load_nk");
+  }
+
+  return nk;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+const REGFI_SK* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict)
+{
+  REGFI_SK* ret_val = NULL;
+  int32_t max_size;
+  void* failure_ptr = NULL;
+  
+  max_size = regfi_calc_maxsize(file, offset);
+  if(max_size < 0)
+    return NULL;
+
+  if(file->sk_cache == NULL)
+    return regfi_parse_sk(file, offset, max_size, strict);
+
+  if(!regfi_lock(file, &file->mem_lock, "regfi_load_sk"))
+    return NULL;
+  regfi_lock(file, &file->sk_lock, "regfi_load_sk");
+
+  /* First look if we have already parsed it */
+  ret_val = (REGFI_SK*)lru_cache_find(file->sk_cache, &offset, 4);
+
+  /* Bail out if we have previously cached a parse failure at this offset. */
+  if(ret_val == (void*)REGFI_OFFSET_NONE)
+  {
+    ret_val = NULL;
+    goto unlock;
+  }
+
+  if(ret_val == NULL)
+  {
+    ret_val = regfi_parse_sk(file, offset, max_size, strict);
+    if(ret_val == NULL)
+    { /* Cache the parse failure and bail out. */
+      failure_ptr = talloc(NULL, uint32_t);
+      if(failure_ptr == NULL)
+	goto unlock;
+
+      *(uint32_t*)failure_ptr = REGFI_OFFSET_NONE;
+      lru_cache_update(file->sk_cache, &offset, 4, failure_ptr);
+
+      /* Let the cache be the only owner of this */
+      talloc_unlink(NULL, failure_ptr);
+    }
+  }
+  else
+    ret_val = talloc_reference(NULL, ret_val);
+
+ unlock:
+  regfi_unlock(file, &file->sk_lock, "regfi_load_sk");
+  regfi_unlock(file, &file->mem_lock, "regfi_load_sk");
+
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_NK* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin)
+{
+  REGFI_NK* nk = NULL;
+  uint32_t cell_length;
+  uint32_t cur_offset = hbin->file_off+REGFI_HBIN_HEADER_SIZE;
+  uint32_t hbin_end = hbin->file_off+hbin->block_size;
+  bool unalloc;
+
+  while(cur_offset < hbin_end)
+  {
+
+    if(!regfi_lock(file, &file->cb_lock, "regfi_find_root_nk"))
+      return NULL;
+
+    if(!regfi_parse_cell(file->cb, cur_offset, NULL, 0, &cell_length, &unalloc))
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Could not parse cell at offset"
+		    " 0x%.8X while searching for root key.", cur_offset);
+      goto error_locked;
+    }
+
+    if(!regfi_unlock(file, &file->cb_lock, "regfi_find_root_nk"))
+      return NULL;
+
+    if(cell_length == 0)
+      break;
+
+    if(!unalloc)
+    {
+      nk = regfi_load_key(file, cur_offset, true);
+      if(nk != NULL)
+      {
+	if(nk->flags & REGFI_NK_FLAG_ROOT)
+	  return nk;
+      }
+    }
+
+    cur_offset += cell_length;
+  }
+
+  return NULL;
+
+ error_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_find_root_nk");
+  return NULL;
+}
+
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_FILE* regfi_alloc(int fd, REGFI_ENCODING output_encoding)
+{
+  REGFI_FILE* ret_val;
+  REGFI_RAW_FILE* file_cb = talloc(NULL, REGFI_RAW_FILE);
+  if(file_cb == NULL) 
+    return NULL;
+
+  file_cb->state = (void*)talloc(file_cb, int);
+  if(file_cb->state == NULL)
+    goto fail;
+  *(int*)file_cb->state = fd;
+  
+  file_cb->cur_off = 0;
+  file_cb->size = 0;
+  file_cb->read = &regfi_raw_read;
+  file_cb->seek = &regfi_raw_seek;
+  
+  ret_val = regfi_alloc_cb(file_cb, output_encoding);
+  if(ret_val == NULL)
+    goto fail;
+
+  /* In this case, we want file_cb to be freed when ret_val is */
+  talloc_reparent(NULL, ret_val, file_cb);
+  return ret_val;
+
+ fail:
+    talloc_free(file_cb);
+    return NULL;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+static int regfi_free_cb(void* f)
+{
+  REGFI_FILE* file = (REGFI_FILE*)f;
+
+  pthread_mutex_destroy(&file->cb_lock);
+  pthread_rwlock_destroy(&file->hbins_lock);
+  pthread_mutex_destroy(&file->sk_lock);
+  pthread_mutex_destroy(&file->nk_lock);
+  pthread_mutex_destroy(&file->mem_lock);
+
+  return 0;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+REGFI_FILE* regfi_alloc_cb(REGFI_RAW_FILE* file_cb, 
+			   REGFI_ENCODING output_encoding)
+{
+  REGFI_FILE* rb;
+  REGFI_HBIN* hbin = NULL;
+  uint32_t hbin_off, cache_secret;
+  int64_t file_length;
+  bool rla;
+
+  /* Determine file length.  Must be at least big enough for the header
+   * and one hbin.
+   */
+  file_length = regfi_seek(file_cb, 0, SEEK_END);
+  if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "File length (%d) too short to contain a"
+		  " header and at least one HBIN.", file_length);
+    return NULL;
+  }
+  regfi_seek(file_cb, 0, SEEK_SET);
+
+  if(output_encoding != REGFI_ENCODING_UTF8
+     && output_encoding != REGFI_ENCODING_ASCII)
+  { 
+    regfi_log_add(REGFI_LOG_ERROR, "Invalid output_encoding supplied"
+		  " in creation of regfi iterator.");
+    return NULL;
+  }
+
+  /* Read file header */
+  if ((rb = regfi_parse_regf(file_cb, false)) == NULL)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to read REGF block.");
+    return NULL;
+  }
+  rb->file_length = file_length;
+  rb->cb = file_cb;
+  rb->string_encoding = output_encoding;
+
+  if(pthread_mutex_init(&rb->cb_lock, NULL) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create cb_lock mutex.");
+    goto fail;
+  }
+
+  if(pthread_rwlock_init(&rb->hbins_lock, NULL) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create hbins_lock rwlock.");
+    goto fail;
+  }
+
+  if(pthread_mutex_init(&rb->sk_lock, NULL) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create sk_lock mutex.");
+    goto fail;
+  }
+
+  if(pthread_mutex_init(&rb->nk_lock, NULL) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create nk_lock mutex.");
+    goto fail;
+  }
+
+  if(pthread_mutex_init(&rb->mem_lock, NULL) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create mem_lock mutex.");
+    goto fail;
+  }
+
+  rb->hbins = range_list_new();
+  if(rb->hbins == NULL)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create HBIN range_list.");
+    goto fail;
+  }
+  talloc_reparent(NULL, rb, rb->hbins);
+
+  rla = true;
+  hbin_off = REGFI_REGF_SIZE;
+  hbin = regfi_parse_hbin(rb, hbin_off, true);
+  while(hbin && rla)
+  {
+    rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
+    if(rla)
+      talloc_reparent(NULL, rb->hbins, hbin);
+
+    hbin_off = hbin->file_off + hbin->block_size;
+    hbin = regfi_parse_hbin(rb, hbin_off, true);
+  }
+
+  /* This secret isn't very secret, but we don't need a good one.  This 
+   * secret is just designed to prevent someone from trying to blow our
+   * caching and make things slow.
+   */
+  cache_secret = 0x15DEAD05^time(NULL)^(getpid()<<16);
+
+  rb->sk_cache = NULL;
+  if(REGFI_CACHE_SK_MAX > 0)
+    rb->sk_cache = lru_cache_create_ctx(rb, REGFI_CACHE_SK_MAX, 
+                                        cache_secret, true);
+
+  rb->nk_cache = NULL;
+  if(REGFI_CACHE_NK_MAX > 0)
+    rb->nk_cache = lru_cache_create_ctx(rb, REGFI_CACHE_NK_MAX, 
+                                        cache_secret, true);
+
+  /* success */
+  talloc_set_destructor(rb, regfi_free_cb);
+  return rb;
+
+ fail:
+  pthread_mutex_destroy(&rb->cb_lock);
+  pthread_rwlock_destroy(&rb->hbins_lock);
+  pthread_mutex_destroy(&rb->sk_lock);
+  pthread_mutex_destroy(&rb->nk_lock);
+  pthread_mutex_destroy(&rb->mem_lock);
+
+  range_list_free(rb->hbins);
+  talloc_free(rb);
+  return NULL;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_free(REGFI_FILE* file)
+{
+  /* Callback handles cleanup side effects */
+  talloc_free(file);
+}
+
+
+/******************************************************************************
+ * First checks the offset given by the file header, then checks the
+ * rest of the file if that fails.
+ ******************************************************************************/
+const REGFI_NK* regfi_get_rootkey(REGFI_FILE* file)
+{
+  REGFI_NK* nk = NULL;
+  REGFI_HBIN* hbin;
+  uint32_t root_offset, i, num_hbins;
+  
+  if(!file)
+    return NULL;
+
+  root_offset = file->root_cell+REGFI_REGF_SIZE;
+  nk = regfi_load_key(file, root_offset, true);
+  if(nk != NULL)
+  {
+    if(nk->flags & REGFI_NK_FLAG_ROOT)
+      return nk;
+  }
+
+  regfi_log_add(REGFI_LOG_WARN, "File header indicated root key at"
+		" location 0x%.8X, but no root key found."
+		" Searching rest of file...", root_offset);
+  
+  /* If the file header gives bad info, scan through the file one HBIN
+   * block at a time looking for an NK record with a root key type.
+   */
+  
+  if(!regfi_read_lock(file, &file->hbins_lock, "regfi_get_rootkey"))
+    return NULL;
+
+  num_hbins = range_list_size(file->hbins);
+  for(i=0; i < num_hbins && nk == NULL; i++)
+  {
+    hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
+    nk = regfi_find_root_nk(file, hbin);
+  }
+
+  if(!regfi_rw_unlock(file, &file->hbins_lock, "regfi_get_rootkey"))
+    return NULL;
+
+  return nk;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_free_record(REGFI_FILE* file, const void* record)
+{
+  if(!regfi_lock(file, &file->mem_lock, "regfi_free_record"))
+    return;
+
+  talloc_unlink(NULL, (void*)record);
+
+  regfi_unlock(file, &file->mem_lock, "regfi_free_record");
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const void* regfi_reference_record(REGFI_FILE* file, const void* record)
+{
+  const void* ret_val = NULL;
+
+  if(!regfi_lock(file, &file->mem_lock, "regfi_reference_record"))
+    return ret_val;
+
+  ret_val = talloc_reference(NULL, record);
+
+  regfi_unlock(file, &file->mem_lock, "regfi_reference_record");
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+uint32_t regfi_fetch_num_subkeys(const REGFI_NK* key)
+{
+  uint32_t num_in_list = 0;
+  if(key == NULL)
+    return 0;
+
+  if(key->subkeys != NULL)
+    num_in_list = key->subkeys->num_keys;
+
+  if(num_in_list != key->num_subkeys)
+  {
+    regfi_log_add(REGFI_LOG_INFO, "Key at offset 0x%.8X contains %d keys in its"
+		  " subkey list but reports %d should be available.", 
+		  key->offset, num_in_list, key->num_subkeys);
+    return (num_in_list < key->num_subkeys)?num_in_list:key->num_subkeys;
+  }
+  
+  return num_in_list;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+uint32_t regfi_fetch_num_values(const REGFI_NK* key)
+{
+  uint32_t num_in_list = 0;
+  if(key == NULL)
+    return 0;
+
+  if(key->values != NULL)
+    num_in_list = key->values->num_values;
+
+  if(num_in_list != key->num_values)
+  {
+    regfi_log_add(REGFI_LOG_INFO, "Key at offset 0x%.8X contains %d values in"
+		  " its value list but reports %d should be available.",
+		  key->offset, num_in_list, key->num_values);
+    return (num_in_list < key->num_values)?num_in_list:key->num_values;
+  }
+  
+  return num_in_list;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file)
+{
+  REGFI_NK* root;
+  REGFI_ITERATOR* ret_val;
+
+  ret_val = talloc(NULL, REGFI_ITERATOR);
+  if(ret_val == NULL)
+    return NULL;
+  
+  ret_val->cur = talloc(ret_val, REGFI_ITER_POSITION);
+  if(ret_val->cur == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  ret_val->key_positions = void_stack_new(REGFI_MAX_DEPTH);
+  if(ret_val->key_positions == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  talloc_reparent(NULL, ret_val, ret_val->key_positions);
+
+  root = (REGFI_NK*)regfi_get_rootkey(file);
+  if(root == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  ret_val->cur->offset = root->offset;
+  if(root->subkeys_off == REGFI_OFFSET_NONE)
+    ret_val->cur->num_subkeys = 0;
+  else
+    ret_val->cur->num_subkeys = regfi_fetch_num_subkeys(root);
+  
+  if(root->values_off == REGFI_OFFSET_NONE)
+    ret_val->cur->num_values = 0;
+  else
+    ret_val->cur->num_values = regfi_fetch_num_values(root);
+
+  ret_val->cur->cur_subkey = 0;
+  ret_val->cur->cur_value = 0;
+  ret_val->f = file;
+
+  regfi_free_record(ret_val->f, root);
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+void regfi_iterator_free(REGFI_ITERATOR* i)
+{
+  talloc_unlink(NULL, i);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+/* XXX: some way of indicating reason for failure should be added. */
+bool regfi_iterator_down(REGFI_ITERATOR* i)
+{
+  REGFI_NK* subkey;
+  REGFI_ITER_POSITION* pos = talloc(i, REGFI_ITER_POSITION);
+  if(pos == NULL)
+    return false;
+
+  subkey = (REGFI_NK*)regfi_iterator_cur_subkey(i);
+  if(subkey == NULL)
+  {
+    talloc_free(pos);
+    return false;
+  }
+
+  if(!void_stack_push(i->key_positions, i->cur))
+  {
+    talloc_free(pos);
+    regfi_free_record(i->f, subkey);
+    return false;
+  }
+
+  pos->offset = subkey->offset;
+  if(subkey->subkeys_off == REGFI_OFFSET_NONE)
+    pos->num_subkeys = 0;
+  else
+    pos->num_subkeys = regfi_fetch_num_subkeys(subkey);
+
+  if(subkey->values_off == REGFI_OFFSET_NONE)
+    pos->num_values = 0;
+  else
+    pos->num_values = regfi_fetch_num_values(subkey);
+
+  pos->cur_subkey = 0;
+  pos->cur_value = 0;
+  i->cur = pos;
+
+  regfi_free_record(i->f, subkey);
+  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;
+
+  if(!regfi_lock(i->f, &i->f->mem_lock, "regfi_iterator_up"))
+    return false;
+  
+  talloc_unlink(i, i->cur);
+
+  regfi_unlock(i->f, &i->f->mem_lock, "regfi_iterator_up");
+
+  i->cur = 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* name)
+{
+  const REGFI_NK* cur_key;
+  uint32_t new_index;
+  bool ret_val = false;
+
+  cur_key = regfi_iterator_cur_key(i);
+  if(cur_key == NULL)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in find_subkey.");
+    return ret_val;
+  }
+
+  if(regfi_find_subkey(i->f, cur_key, name, &new_index))
+  {
+    i->cur->cur_subkey = new_index;
+    ret_val = true;
+  }
+
+  regfi_free_record(i->f, cur_key);
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_descend(REGFI_ITERATOR* i, const char** path)
+{
+  uint32_t x;
+  if(path == NULL)
+    return false;
+
+  for(x=0; 
+      ((path[x] != NULL) && regfi_iterator_find_subkey(i, path[x])
+       && regfi_iterator_down(i));
+      x++)
+  { continue; }
+
+  if(path[x] == NULL)
+  {
+    return true;
+  }
+
+  /* XXX: is this the right number of times? */
+  for(; x > 0; x--)
+    regfi_iterator_up(i);
+  
+  return false;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK* regfi_iterator_cur_key(REGFI_ITERATOR* i)
+{
+  const REGFI_NK* ret_val = NULL;
+
+  ret_val = regfi_load_key(i->f, i->cur->offset, true);
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_SK* regfi_fetch_sk(REGFI_FILE* file, const REGFI_NK* key)
+{
+  if(key == NULL || key->sk_off == REGFI_OFFSET_NONE)
+    return NULL;
+
+  return regfi_load_sk(file, key->sk_off + REGFI_REGF_SIZE, true);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_SK* regfi_next_sk(REGFI_FILE* file, const REGFI_SK* sk)
+{
+  if(sk == NULL || sk->next_sk_off == REGFI_OFFSET_NONE)
+    return NULL;
+
+  return regfi_load_sk(file, sk->next_sk_off + REGFI_REGF_SIZE, true);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_SK* regfi_prev_sk(REGFI_FILE* file, const REGFI_SK* sk)
+{
+  if(sk == NULL || sk->prev_sk_off == REGFI_OFFSET_NONE)
+    return NULL;
+
+  return regfi_load_sk(file, sk->prev_sk_off + REGFI_REGF_SIZE, true);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_first_subkey(REGFI_ITERATOR* i)
+{
+  i->cur->cur_subkey = 0;
+  return (i->cur->cur_subkey < i->cur->num_subkeys);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
+{
+  const REGFI_NK* cur_key;
+  const REGFI_NK* ret_val;
+  
+  cur_key = regfi_iterator_cur_key(i);
+  if(cur_key == NULL)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in cur_subkey.");
+    return NULL;
+  }
+
+  ret_val = regfi_get_subkey(i->f, cur_key, i->cur->cur_subkey);
+
+  regfi_free_record(i->f, cur_key);
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_next_subkey(REGFI_ITERATOR* i)
+{
+  i->cur->cur_subkey++;
+  return (i->cur->cur_subkey < i->cur->num_subkeys);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* name)
+{
+  const REGFI_NK* cur_key;
+  uint32_t new_index;
+  bool ret_val = false;
+
+  cur_key = regfi_iterator_cur_key(i);
+  if(cur_key == NULL)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in find_value.");
+    return ret_val;
+  }
+
+  if(regfi_find_value(i->f, cur_key, name, &new_index))
+  {
+    i->cur->cur_value = new_index;
+    ret_val = true;
+  }
+
+  regfi_free_record(i->f, cur_key);
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_first_value(REGFI_ITERATOR* i)
+{
+  i->cur->cur_value = 0;
+  return (i->cur->cur_value < i->cur->num_values);
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_VK* regfi_iterator_cur_value(REGFI_ITERATOR* i)
+{
+  const REGFI_NK* cur_key;
+  const REGFI_VK* ret_val = NULL;
+
+  cur_key = regfi_iterator_cur_key(i);
+  if(cur_key == NULL)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in cur_value.");
+    return ret_val;
+  }
+
+  ret_val = regfi_get_value(i->f, cur_key, i->cur->cur_value);
+  
+  regfi_free_record(i->f, cur_key);
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_next_value(REGFI_ITERATOR* i)
+{
+  i->cur->cur_value++;
+  return (i->cur->cur_value < i->cur->num_values);
+}
+
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK** regfi_iterator_ancestry(REGFI_ITERATOR* i)
+{
+  REGFI_NK** ret_val;
+  void_stack_iterator* iter;
+  const REGFI_ITER_POSITION* cur;
+  uint16_t k, num_keys;
+
+  num_keys = void_stack_size(i->key_positions)+1;
+  ret_val = talloc_array(NULL, REGFI_NK*, num_keys+1);
+  if(ret_val == NULL)
+    return NULL;
+
+  iter = void_stack_iterator_new(i->key_positions);
+  if (iter == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  k=0;
+  for(cur=void_stack_iterator_next(iter);
+      cur != NULL; cur=void_stack_iterator_next(iter))
+  { 
+    ret_val[k++] = regfi_load_key(i->f, cur->offset, true); 
+  }
+  ret_val[k] = regfi_load_key(i->f, i->cur->offset, true);
+  void_stack_iterator_free(iter);
+
+  if(!regfi_lock(i->f, &i->f->mem_lock, "regfi_iterator_ancestry"))
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  for(k=0; k<num_keys; k++)
+    talloc_reparent(NULL, ret_val, ret_val[k]);
+
+  regfi_unlock(i->f, &i->f->mem_lock, "regfi_iterator_ancestry");
+
+  ret_val[k] = NULL;
+  return (const REGFI_NK**)ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_CLASSNAME* regfi_fetch_classname(REGFI_FILE* file,
+					     const REGFI_NK* key)
+{
+  REGFI_CLASSNAME* ret_val;
+  uint8_t* raw;
+  REGFI_BUFFER tmp_buf;
+  uint32_t offset;
+  int32_t max_size;
+  uint16_t parse_length;
+
+  if(key->classname_off == REGFI_OFFSET_NONE || key->classname_length == 0)
+    return NULL;
+
+  offset = key->classname_off + REGFI_REGF_SIZE;
+  max_size = regfi_calc_maxsize(file, offset);
+  if(max_size <= 0)
+    return NULL;
+
+  parse_length = key->classname_length;
+  raw = regfi_parse_classname(file, offset, &parse_length, max_size, true);
+  
+  if(raw == NULL)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse class"
+		  " name at offset 0x%.8X for key record at offset 0x%.8X.",
+		  offset, key->offset);
+    return NULL;
+  }
+
+  ret_val = talloc(NULL, REGFI_CLASSNAME);
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->offset = offset;
+  ret_val->raw = raw;
+  ret_val->size = parse_length;
+  talloc_reparent(NULL, ret_val, raw);
+
+  tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+                               regfi_encoding_int2str(file->string_encoding),
+                               raw, parse_length);
+  if(tmp_buf.buf == NULL)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Error occurred while"
+		  " converting classname to charset %s.  Error message: %s",
+		  file->string_encoding, strerror(errno));
+    ret_val->interpreted = NULL;
+  }
+  else
+  {
+    ret_val->interpreted = (char*)tmp_buf.buf;
+    talloc_reparent(NULL, ret_val, tmp_buf.buf);
+  }
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_DATA* regfi_fetch_data(REGFI_FILE* file, 
+				   const REGFI_VK* value)
+{
+  REGFI_DATA* ret_val = NULL;
+  REGFI_BUFFER raw_data;
+
+  if(value->data_size != 0)
+  {
+    raw_data = regfi_load_data(file, value->data_off, value->data_size,
+			       value->data_in_offset, true);
+    if(raw_data.buf == NULL)
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Could not parse data record"
+		    " while parsing VK record at offset 0x%.8X.",
+		    value->offset);
+    }
+    else
+    {
+      ret_val = regfi_buffer_to_data(raw_data);
+
+      if(ret_val == NULL)
+      {
+	regfi_log_add(REGFI_LOG_WARN, "Error occurred in converting"
+		      " data buffer to data structure while interpreting "
+		      "data for VK record at offset 0x%.8X.",
+		      value->offset);
+	talloc_free(raw_data.buf);
+	return NULL;
+      }
+
+      if(!regfi_interpret_data(file, value->type, ret_val))
+      {
+	regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
+		      " interpreting data for VK record at offset 0x%.8X.",
+		      value->offset);
+      }
+    }
+  }
+  
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_find_subkey(REGFI_FILE* file, const REGFI_NK* key, 
+		       const char* name, uint32_t* index)
+{
+  const REGFI_NK* cur;
+  uint32_t i;
+  uint32_t num_subkeys = regfi_fetch_num_subkeys(key);
+  bool found = false;
+
+  /* XXX: should we allow "(default)" subkey names? 
+   *      Do realistically they exist?
+   */
+  if(name == NULL)
+    return false;
+
+  /* XXX: Should lazily build a hash table in memory to index where keys are when
+   *      there are a large number of subkeys.  Attach this to cached keys to
+   *      bound the extra amount of memory used. 
+   */
+  for(i=0; (i < num_subkeys) && (found == false); i++)
+  {
+    cur = regfi_get_subkey(file, key, i);
+    if(cur == NULL)
+      return false;
+
+    /* A NULL name signifies the "(default)" value for a key */
+    if(cur->name != NULL && (strcasecmp(cur->name, name) == 0))
+    {
+      found = true;
+      *index = i;
+    }
+
+    regfi_free_record(file, cur);
+  }
+
+  return found;
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_find_value(REGFI_FILE* file, const REGFI_NK* key, 
+		      const char* name, uint32_t* index)
+{
+  const REGFI_VK* cur;
+  uint32_t i;
+  uint32_t num_values = regfi_fetch_num_values(key);
+  bool found = false;
+
+  /* XXX: Should lazily build a hash table in memory to index where values are when
+   *      there are a large number of them.  Attach this to cached keys to
+   *      bound the extra amount of memory used. 
+   */
+  for(i=0; (i < num_values) && (found == false); i++)
+  {
+    cur = regfi_get_value(file, key, i);
+    if(cur == NULL)
+      return false;
+
+    /* A NULL name signifies the "(default)" value for a key */
+    if(((name == NULL) && (cur->name == NULL))
+       || ((name != NULL) && (cur->name != NULL) 
+           && (strcasecmp(cur->name, name) == 0)))
+    {
+      found = true;
+      *index = i;
+    }
+
+    regfi_free_record(file, cur);
+  }
+
+  return found;
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK* regfi_get_subkey(REGFI_FILE* file, const REGFI_NK* key, 
+				 uint32_t index)
+{
+  if(index < regfi_fetch_num_subkeys(key))
+  {
+    return regfi_load_key(file, 
+                          key->subkeys->elements[index].offset+REGFI_REGF_SIZE, 
+                          true);
+  }
+
+  return NULL;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_VK* regfi_get_value(REGFI_FILE* file, const REGFI_NK* key, 
+				uint32_t index)
+{
+  if(index < regfi_fetch_num_values(key))
+  {
+    return regfi_load_value(file, 
+			    key->values->elements[index]+REGFI_REGF_SIZE,
+                            true);
+  }
+
+  return NULL;  
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK* regfi_get_parentkey(REGFI_FILE* file, const REGFI_NK* key)
+{
+  if(key != NULL && key->parent_off != REGFI_OFFSET_NONE)
+    return regfi_load_key(file, 
+                          key->parent_off+REGFI_REGF_SIZE, true);
+
+  return NULL;
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
+{
+  REGFI_DATA* ret_val;
+
+  if(raw_data.buf == NULL)
+    return NULL;
+
+  ret_val = talloc(NULL, REGFI_DATA);
+  if(ret_val == NULL)
+    return NULL;
+  
+  talloc_reparent(NULL, ret_val, raw_data.buf);
+  ret_val->raw = raw_data.buf;
+  ret_val->size = raw_data.len;
+  ret_val->interpreted_size = 0;
+  ret_val->interpreted.qword = 0;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_interpret_data(REGFI_FILE* file, uint32_t type, REGFI_DATA* data)
+{
+  REGFI_BUFFER tmp_buf;
+  uint8_t** tmp_array;
+  uint32_t i, j;
+
+  if(data == NULL)
+    return false;
+
+  switch (type)
+  {
+  case REG_SZ:
+  case REG_EXPAND_SZ:
+  /* REG_LINK is a symbolic link, stored as a unicode string. */
+  case REG_LINK:
+    tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+                                 regfi_encoding_int2str(file->string_encoding),
+                                 data->raw, data->size);
+    if(tmp_buf.buf == NULL)
+    {
+      regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
+		    " converting data of type %d to string encoding %d."
+                    "  Error message: %s",
+		    type, file->string_encoding, strerror(errno));
+      data->interpreted.string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+
+    data->interpreted.string = tmp_buf.buf;
+    data->interpreted_size = tmp_buf.len;
+    talloc_reparent(NULL, data, tmp_buf.buf);
+    break;
+
+  case REG_DWORD:
+    if(data->size < 4)
+    {
+      data->interpreted.dword = 0;
+      data->interpreted_size = 0;
+      return false;
+    }
+    data->interpreted.dword = IVAL(data->raw, 0);
+    data->interpreted_size = 4;
+    break;
+
+  case REG_DWORD_BE:
+    if(data->size < 4)
+    {
+      data->interpreted.dword_be = 0;
+      data->interpreted_size = 0;
+      return false;
+    }
+    data->interpreted.dword_be = RIVAL(data->raw, 0);
+    data->interpreted_size = 4;
+    break;
+
+  case REG_QWORD:
+    if(data->size < 8)
+    {
+      data->interpreted.qword = 0;
+      data->interpreted_size = 0;
+      return false;
+    }
+    data->interpreted.qword = 
+      (uint64_t)IVAL(data->raw, 0) + (((uint64_t)IVAL(data->raw, 4))<<32);
+    data->interpreted_size = 8;
+    break;
+    
+  case REG_MULTI_SZ:
+    /* Attempt to convert entire string from UTF-16LE to output encoding,
+     * then parse and quote fields individually.
+     */
+    tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+                                 regfi_encoding_int2str(file->string_encoding),
+                                 data->raw, data->size);
+    if(tmp_buf.buf == NULL)
+    {
+      regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
+		    " converting data of type %d to string encoding %d."
+                    "  Error message: %s",
+		    type, file->string_encoding, strerror(errno));
+      data->interpreted.multiple_string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+
+    tmp_array = talloc_array(NULL, uint8_t*, tmp_buf.len+1);
+    if(tmp_array == NULL)
+    {
+      talloc_free(tmp_buf.buf);
+      data->interpreted.string = NULL;
+      data->interpreted_size = 0;
+      return false;
+    }
+
+    tmp_array[0] = tmp_buf.buf;
+    for(i=0,j=1; i < tmp_buf.len && j < tmp_buf.len; i++)
+    {
+      if(tmp_buf.buf[i] == '\0' && (i+1 < tmp_buf.len) 
+         && tmp_buf.buf[i+1] != '\0')
+	tmp_array[j++] = tmp_buf.buf+i+1;
+    }
+    tmp_array[j] = NULL;
+    tmp_array = talloc_realloc(NULL, tmp_array, uint8_t*, j+1);
+    data->interpreted.multiple_string = tmp_array;
+    /* XXX: how meaningful is this?  should we store number of strings instead? */
+    data->interpreted_size = tmp_buf.len;
+    talloc_reparent(NULL, tmp_array, tmp_buf.buf);
+    talloc_reparent(NULL, data, tmp_array);
+    break;
+
+  /* XXX: Dont know how to interpret these yet, just treat as binary */
+  case REG_NONE:
+    data->interpreted.none = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_RESOURCE_LIST:
+    data->interpreted.resource_list = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_FULL_RESOURCE_DESCRIPTOR:
+    data->interpreted.full_resource_descriptor = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_RESOURCE_REQUIREMENTS_LIST:
+    data->interpreted.resource_requirements_list = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  case REG_BINARY:
+    data->interpreted.binary = data->raw;
+    data->interpreted_size = data->size;
+    break;
+
+  default:
+    data->interpreted.qword = 0;
+    data->interpreted_size = 0;
+    return false;
+  }
+
+  data->type = type;
+  return true;
+}
+
+
+/******************************************************************************
+ * Convert string from input_charset to output_charset.
+ * On error, returns a NULL buf attribute and sets the errno.
+ *****************************************************************************/
+REGFI_BUFFER regfi_conv_charset(const char* input_charset, const char* output_charset,
+                                uint8_t* input, uint32_t input_len)
+{
+  iconv_t conv_desc;
+  char* inbuf = (char*)input;
+  char* outbuf;
+  char* retbuf;
+  size_t allocated = (size_t)input_len;
+  size_t in_left = (size_t)input_len;
+  size_t out_left = (size_t)allocated-1;
+  REGFI_BUFFER ret_val;
+  int ret;
+
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  retbuf = talloc_array(NULL, char, allocated);
+  outbuf = retbuf;
+  if(outbuf == NULL)
+  {
+    errno = ENOMEM;
+    return ret_val;
+  }
+
+  /* Set up conversion descriptor. */
+  /* XXX: Consider creating a couple of conversion descriptors earlier,
+   *      storing them on an iterator so they don't have to be recreated
+   *      each time.
+   */
+  conv_desc = iconv_open(output_charset, input_charset);
+
+  ret = 0;
+  do
+  {
+    if(ret == -1)
+    {
+      retbuf = talloc_realloc(NULL, retbuf, char, allocated+(in_left*2));
+      if(retbuf == NULL)
+      {
+        errno = ENOMEM;
+        return ret_val;
+      }
+      outbuf = retbuf+(allocated-1-out_left);
+      out_left += in_left*2;
+      allocated += in_left*2;
+    }
+    ret = iconv(conv_desc, &inbuf, &in_left, &outbuf, &out_left);
+    
+  } while(ret == -1 && errno == E2BIG);
+  
+  if(ret == -1)
+  {
+    iconv_close(conv_desc);
+    return ret_val;
+  }
+
+  /* Save memory */
+  if(out_left > 0)
+  {
+    retbuf = talloc_realloc(NULL, retbuf, char, allocated-out_left);
+    if(retbuf == NULL)
+    {
+      errno = ENOMEM;
+      return ret_val;
+    }
+    allocated -= out_left;
+  }
+  retbuf[allocated-1] = '\0';
+  iconv_close(conv_desc);
+
+  ret_val.buf = (uint8_t*)retbuf;
+  ret_val.len = allocated-1;
+  return ret_val;
+}
+
+
+/*******************************************************************
+ * Computes the checksum of the registry file header.
+ * buffer must be at least the size of a regf header (4096 bytes).
+ *******************************************************************/
+static uint32_t regfi_compute_header_checksum(uint8_t* buffer)
+{
+  uint32_t checksum, x;
+  int i;
+
+  /* XOR of all bytes 0x0000 - 0x01FB */
+
+  checksum = x = 0;
+  
+  for ( i=0; i<0x01FB; i+=4 ) {
+    x = IVAL(buffer, i );
+    checksum ^= x;
+  }
+  
+  return checksum;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+REGFI_FILE* regfi_parse_regf(REGFI_RAW_FILE* file_cb, bool strict)
+{
+  uint8_t file_header[REGFI_REGF_SIZE];
+  uint32_t length;
+  REGFI_FILE* ret_val;
+
+  ret_val = talloc(NULL, REGFI_FILE);
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->sk_cache = NULL;
+  ret_val->hbins = NULL;
+
+  length = REGFI_REGF_SIZE;
+  if((regfi_read(file_cb, file_header, &length)) != 0 
+     || length != REGFI_REGF_SIZE)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Read failed while parsing REGF structure.");
+    goto fail;
+  }
+
+  ret_val->checksum = IVAL(file_header, 0x1FC);
+  ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
+  if (strict && (ret_val->checksum != ret_val->computed_checksum))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Stored header checksum (%.8X) did not equal"
+		  " computed checksum (%.8X).",
+		  ret_val->checksum, ret_val->computed_checksum);
+    if(strict)
+      goto fail;
+  }
+
+  memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE);
+  if(memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Magic number mismatch "
+		  "(%.2X %.2X %.2X %.2X) while parsing hive header",
+		  ret_val->magic[0], ret_val->magic[1], 
+		  ret_val->magic[2], ret_val->magic[3]);
+    goto fail;
+  }
+
+  ret_val->sequence1 = IVAL(file_header, 0x4);
+  ret_val->sequence2 = IVAL(file_header, 0x8);
+  ret_val->mtime = ((uint64_t)IVAL(file_header, 0x10)) << 32;
+  ret_val->mtime |= IVAL(file_header, 0xC);
+  ret_val->major_version = IVAL(file_header, 0x14);
+  ret_val->minor_version = IVAL(file_header, 0x18);
+  ret_val->type = IVAL(file_header, 0x1C);
+  ret_val->format = IVAL(file_header, 0x20);
+  ret_val->root_cell = IVAL(file_header, 0x24);
+  ret_val->last_block = IVAL(file_header, 0x28);
+  ret_val->cluster = IVAL(file_header, 0x2C);
+
+  memcpy(ret_val->file_name, file_header+0x30,  REGFI_REGF_NAME_SIZE);
+
+  ret_val->rm_id = winsec_parse_uuid(ret_val, file_header+0x70, 16);
+  if(ret_val->rm_id == NULL)
+    regfi_log_add(REGFI_LOG_WARN, "Hive header's rm_id failed to parse.");
+
+  ret_val->log_id = winsec_parse_uuid(ret_val, file_header+0x80, 16);
+  if(ret_val->log_id == NULL)
+    regfi_log_add(REGFI_LOG_WARN, "Hive header's log_id failed to parse.");
+
+  ret_val->flags = IVAL(file_header, 0x90);
+
+  ret_val->tm_id = winsec_parse_uuid(ret_val, file_header+0x94, 16);
+  if(ret_val->tm_id == NULL)
+    regfi_log_add(REGFI_LOG_WARN, "Hive header's tm_id failed to parse.");
+
+  ret_val->guid_signature = IVAL(file_header, 0xa4);
+
+  memcpy(ret_val->reserved1, file_header+0xa8, REGFI_REGF_RESERVED1_SIZE);
+  memcpy(ret_val->reserved2, file_header+0x200, REGFI_REGF_RESERVED2_SIZE);
+
+  ret_val->thaw_tm_id = winsec_parse_uuid(ret_val, file_header+0xFC8, 16);
+  ret_val->thaw_rm_id = winsec_parse_uuid(ret_val, file_header+0xFD8, 16);
+  ret_val->thaw_log_id = winsec_parse_uuid(ret_val, file_header+0xFE8, 16);
+  ret_val->boot_type = IVAL(file_header, 0xFF8);
+  ret_val->boot_recover = IVAL(file_header, 0xFFC);
+
+  return ret_val;
+
+ fail:
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+
+/******************************************************************************
+ * Given real file offset, read and parse the hbin at that location
+ * along with it's associated cells.
+ ******************************************************************************/
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict)
+{
+  REGFI_HBIN* hbin = NULL;
+  uint8_t hbin_header[REGFI_HBIN_HEADER_SIZE];
+  uint32_t length;
+  
+  if(offset >= file->file_length)
+    goto fail;
+  
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_hbin"))
+    goto fail;
+
+  if(regfi_seek(file->cb, offset, SEEK_SET) == -1)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Seek failed"
+		  " while parsing hbin at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  length = REGFI_HBIN_HEADER_SIZE;
+  if((regfi_read(file->cb, hbin_header, &length) != 0) 
+     || length != REGFI_HBIN_HEADER_SIZE)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Read failed"
+		  " while parsing hbin at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_hbin"))
+    goto fail;
+
+  hbin = talloc(NULL, REGFI_HBIN);
+  if(hbin == NULL)
+    goto fail;
+  hbin->file_off = offset;
+
+  memcpy(hbin->magic, hbin_header, 4);
+  if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
+  {
+    /* This always seems to happen at the end of a file, so we make it an INFO
+     * message, rather than something more serious.
+     */
+    regfi_log_add(REGFI_LOG_INFO, "Magic number mismatch "
+		  "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
+		  " 0x%.8X.", hbin->magic[0], hbin->magic[1], 
+		  hbin->magic[2], hbin->magic[3], offset);
+    goto fail;
+  }
+
+  hbin->first_hbin_off = IVAL(hbin_header, 0x4);
+  hbin->block_size = IVAL(hbin_header, 0x8);
+  /* this should be the same thing as hbin->block_size, but just in case */
+  hbin->next_block = IVAL(hbin_header, 0x1C);
+
+
+  /* Ensure the block size is a multiple of 0x1000 and doesn't run off 
+   * the end of the file. 
+   */
+  /* XXX: This may need to be relaxed for dealing with 
+   *      partial or corrupt files. 
+   */
+  if((offset + hbin->block_size > file->file_length)
+     || (hbin->block_size & 0xFFFFF000) != hbin->block_size)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "The hbin offset is not aligned"
+		  " or runs off the end of the file"
+		  " while parsing hbin at offset 0x%.8X.", offset);
+    goto fail;
+  }
+
+  return hbin;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_hbin");
+ fail:
+  talloc_free(hbin);
+  return NULL;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+REGFI_NK* regfi_parse_nk(REGFI_FILE* file, uint32_t offset, 
+			 uint32_t max_size, bool strict)
+{
+  uint8_t nk_header[REGFI_NK_MIN_LENGTH];
+  REGFI_NK* ret_val;
+  uint32_t length,cell_length;
+  bool unalloc = false;
+
+  ret_val = talloc(NULL, REGFI_NK);
+  if(ret_val == NULL)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to allocate memory while"
+		  " parsing NK record at offset 0x%.8X.", offset);
+    goto fail;
+  }
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_nk"))
+    goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, nk_header, REGFI_NK_MIN_LENGTH,
+		       &cell_length, &unalloc))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
+		  " while parsing NK record at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch in parsing"
+		  " NK record at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  ret_val->values = NULL;
+  ret_val->subkeys = NULL;
+  ret_val->offset = offset;
+  ret_val->cell_size = cell_length;
+
+  if(ret_val->cell_size > max_size)
+    ret_val->cell_size = max_size & 0xFFFFFFF8;
+  if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
+     || (strict && (ret_val->cell_size & 0x00000007) != 0))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "A length check failed while"
+		  " parsing NK record at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  ret_val->magic[0] = nk_header[0x0];
+  ret_val->magic[1] = nk_header[0x1];
+  ret_val->flags = SVAL(nk_header, 0x2);
+  
+  if((ret_val->flags & ~REGFI_NK_KNOWN_FLAGS) != 0)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Unknown key flags (0x%.4X) while"
+		  " parsing NK record at offset 0x%.8X.", 
+		  (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset);
+  }
+
+  ret_val->mtime = ((uint64_t)IVAL(nk_header, 0x8)) << 32;
+  ret_val->mtime |= IVAL(nk_header, 0x4);
+  /* 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 < REGFI_MTIME_MIN
+	 || ret_val->mtime > REGFI_MTIME_MAX))
+  { goto fail_locked; }
+
+  ret_val->unknown1 = IVAL(nk_header, 0xC);
+  ret_val->parent_off = IVAL(nk_header, 0x10);
+  ret_val->num_subkeys = IVAL(nk_header, 0x14);
+  ret_val->unknown2 = IVAL(nk_header, 0x18);
+  ret_val->subkeys_off = IVAL(nk_header, 0x1C);
+  ret_val->unknown3 = IVAL(nk_header, 0x20);
+  ret_val->num_values = IVAL(nk_header, 0x24);
+  ret_val->values_off = IVAL(nk_header, 0x28);
+  ret_val->sk_off = IVAL(nk_header, 0x2C);
+  ret_val->classname_off = IVAL(nk_header, 0x30);
+
+  ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34);
+  ret_val->max_bytes_subkeyclassname = IVAL(nk_header, 0x38);
+  ret_val->max_bytes_valuename = IVAL(nk_header, 0x3C);
+  ret_val->max_bytes_value = IVAL(nk_header, 0x40);
+  ret_val->unk_index = IVAL(nk_header, 0x44);
+
+  ret_val->name_length = SVAL(nk_header, 0x48);
+  ret_val->classname_length = SVAL(nk_header, 0x4A);
+  ret_val->name = NULL;
+
+  if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
+  {
+    if(strict)
+    {
+      regfi_log_add(REGFI_LOG_ERROR, "Contents too large for cell"
+		    " while parsing NK record at offset 0x%.8X.", offset);
+      goto fail_locked;
+    }
+    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;
+  }
+
+  /* +1 to length in case we decided to use this directly as a string later */
+  ret_val->name_raw = talloc_array(ret_val, uint8_t, ret_val->name_length+1);
+  if(ret_val->name_raw == NULL)
+    goto fail_locked;
+
+  /* Don't need to seek, should be at the right offset */
+  length = ret_val->name_length;
+  if((regfi_read(file->cb, (uint8_t*)ret_val->name_raw, &length) != 0)
+     || length != ret_val->name_length)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to read key name"
+		  " while parsing NK record at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_nk"))
+    goto fail;
+
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_nk");
+ fail:
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset, 
+			       uint16_t* name_length, uint32_t max_size, bool strict)
+{
+  uint8_t* ret_val = NULL;
+  uint32_t length;
+  uint32_t cell_length;
+  bool unalloc = false;
+
+  if(*name_length <= 0 || offset == REGFI_OFFSET_NONE  
+     || (offset & 0x00000007) != 0)
+  { goto fail; }
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_classname"))
+    goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
+		  " while parsing class name at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+  
+  if((cell_length & 0x0000007) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Cell length not a multiple of 8"
+		  " while parsing class name at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+  
+  if(cell_length > max_size)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Cell stretches past hbin "
+		  "boundary while parsing class name at offset 0x%.8X.",
+		  offset);
+    if(strict)
+      goto fail_locked;
+    cell_length = max_size;
+  }
+  
+  if((cell_length - 4) < *name_length)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Class name is larger than"
+		  " cell_length while parsing class name at offset"
+		  " 0x%.8X.", offset);
+    if(strict)
+      goto fail_locked;
+    *name_length = cell_length - 4;
+  }
+  
+  ret_val = talloc_array(NULL, uint8_t, *name_length);
+  if(ret_val != NULL)
+  {
+    length = *name_length;
+    if((regfi_read(file->cb, ret_val, &length) != 0)
+       || length != *name_length)
+    {
+      regfi_log_add(REGFI_LOG_ERROR, "Could not read class name"
+		    " while parsing class name at offset 0x%.8X.", offset);
+      goto fail_locked;
+    }
+  }
+
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_classname"))
+    goto fail;
+
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_classname");
+ fail:
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_VK* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
+			     uint32_t max_size, bool strict)
+{
+  REGFI_VK* ret_val;
+  uint8_t vk_header[REGFI_VK_MIN_LENGTH];
+  uint32_t raw_data_size, length, cell_length;
+  bool unalloc = false;
+
+  ret_val = talloc(NULL, REGFI_VK);
+  if(ret_val == NULL)
+    goto fail;
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_nk"))
+    goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, vk_header, REGFI_VK_MIN_LENGTH,
+		       &cell_length, &unalloc))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
+		  " while parsing VK record at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  ret_val->offset = offset;
+  ret_val->cell_size = cell_length;
+  ret_val->name = NULL;
+  ret_val->name_raw = NULL;
+  
+  if(ret_val->cell_size > max_size)
+    ret_val->cell_size = max_size & 0xFFFFFFF8;
+  if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
+     || (ret_val->cell_size & 0x00000007) != 0)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Invalid cell size encountered"
+		  " while parsing VK record at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  ret_val->magic[0] = vk_header[0x0];
+  ret_val->magic[1] = vk_header[0x1];
+  if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k'))
+  {
+    /* XXX: This does not account for deleted keys under Win2K which
+     *      often have this (and the name length) overwritten with
+     *      0xFFFF. 
+     */
+    regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch"
+		  " while parsing VK record at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  ret_val->name_length = SVAL(vk_header, 0x2);
+  raw_data_size = IVAL(vk_header, 0x4);
+  ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET;
+  /* The data is typically stored in the offset if the size <= 4, 
+   * in which case this flag is set. 
+   */
+  ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET);
+  ret_val->data_off = IVAL(vk_header, 0x8);
+  ret_val->type = IVAL(vk_header, 0xC);
+  ret_val->flags = SVAL(vk_header, 0x10);
+  ret_val->unknown1 = SVAL(vk_header, 0x12);
+
+  if(ret_val->name_length > 0)
+  {
+    if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Name too long for remaining cell"
+		    " space while parsing VK record at offset 0x%.8X.",
+		    offset);
+      if(strict)
+	goto fail_locked;
+      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;
+
+    /* +1 to length in case we decided to use this directly as a string later */
+    ret_val->name_raw = talloc_array(ret_val, uint8_t, ret_val->name_length+1);
+    if(ret_val->name_raw == NULL)
+      goto fail_locked;
+
+    length = ret_val->name_length;
+    if((regfi_read(file->cb, (uint8_t*)ret_val->name_raw, &length) != 0)
+       || length != ret_val->name_length)
+    {
+      regfi_log_add(REGFI_LOG_ERROR, "Could not read value name"
+		    " while parsing VK record at offset 0x%.8X.", offset);
+      goto fail_locked;
+    }
+  }
+  else
+    cell_length = REGFI_VK_MIN_LENGTH + 4;
+
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_nk"))
+    goto fail;
+
+  if(unalloc)
+  {
+    /* If cell_size is still greater, truncate. */
+    if(cell_length < ret_val->cell_size)
+      ret_val->cell_size = cell_length;
+  }
+
+  return ret_val;
+  
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_vk");
+ fail:
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
+			     uint32_t length, bool data_in_offset,
+			     bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint32_t cell_length, offset;
+  int32_t max_size;
+  bool unalloc;
+  
+  /* Microsoft's documentation indicates that "available memory" is 
+   * the limit on value sizes for the more recent registry format version.
+   * This is not only annoying, but it's probably also incorrect, since clearly
+   * value data sizes are limited to 2^31 (high bit used as a flag) and even 
+   * with big data records, the apparent max size is:
+   *   16344 * 2^16 = 1071104040 (~1GB).
+   *
+   * We choose to limit it to 1M which was the limit in older versions and 
+   * should rarely be exceeded unless the file is corrupt or malicious. 
+   * For more info, see:
+   *   http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
+   */
+  /* XXX: add way to skip this check at user discression. */
+  if(length > REGFI_VK_MAX_DATA_LENGTH)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Value data size %d larger than "
+		  "%d, truncating...", length, REGFI_VK_MAX_DATA_LENGTH);
+    length = REGFI_VK_MAX_DATA_LENGTH;
+  }
+
+  if(data_in_offset)
+    return regfi_parse_little_data(file, voffset, length, strict);
+  else
+  {
+    offset = voffset + REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(file, offset);
+    if(max_size < 0)
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Could not find HBIN for data"
+		    " at offset 0x%.8X.", offset);
+      goto fail;
+    }
+    
+    if(!regfi_lock(file, &file->cb_lock, "regfi_load_data"))
+      goto fail;
+
+    if(!regfi_parse_cell(file->cb, offset, NULL, 0,
+			 &cell_length, &unalloc))
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+		    " parsing data record at offset 0x%.8X.", offset);
+      goto fail_locked;
+    }
+
+    if(!regfi_unlock(file, &file->cb_lock, "regfi_load_data"))
+      goto fail;
+
+    if((cell_length & 0x00000007) != 0)
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Cell length not multiple of 8"
+		    " while parsing data record at offset 0x%.8X.",
+		    offset);
+      goto fail;
+    }
+
+    if(cell_length > max_size)
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Cell extends past HBIN boundary"
+		    " while parsing data record at offset 0x%.8X.",
+		    offset);
+      goto fail;
+    }
+
+    if(cell_length - 4 < length)
+    {
+      /* XXX: All big data records thus far have been 16 bytes long.  
+       *      Should we check for this precise size instead of just 
+       *      relying upon the above check?
+       */
+      if (file->major_version >= 1 && file->minor_version >= 5)
+      {
+	/* Attempt to parse a big data record */
+	return regfi_load_big_data(file, offset, length, cell_length, 
+				   NULL, strict);
+      }
+      else
+      {
+	regfi_log_add(REGFI_LOG_WARN, "Data length (0x%.8X) larger than"
+		      " remaining cell length (0x%.8X)"
+		      " while parsing data record at offset 0x%.8X.", 
+		      length, cell_length - 4, offset);
+	if(strict)
+	  goto fail;
+	else
+	  length = cell_length - 4;
+      }
+    }
+
+    ret_val = regfi_parse_data(file, offset, length, strict);
+  }
+
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_load_data");
+ fail:
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses the common case data records stored in a single cell.
+ ******************************************************************************/
+REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
+			      uint32_t length, bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint32_t read_length;
+
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  
+  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+    goto fail;
+  ret_val.len = length;
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_data"))
+    goto fail;
+
+  if(regfi_seek(file->cb, offset+4, SEEK_SET) == -1)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not seek while "
+		  "reading data at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+  
+  read_length = length;
+  if((regfi_read(file->cb, ret_val.buf, &read_length) != 0)
+     || read_length != length)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Could not read data block while"
+		  " parsing data record at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_data"))
+    goto fail;
+
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_data");
+ fail:
+  talloc_free(ret_val.buf);
+  ret_val.buf = NULL;
+  ret_val.buf = 0;
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset,
+				     uint32_t length, bool strict)
+{
+  uint8_t i;
+  REGFI_BUFFER ret_val;
+
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+
+  if(length > 4)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Data in offset but length > 4"
+		  " while parsing data record. (voffset=0x%.8X, length=%d)",
+		  voffset, length);
+    return ret_val;
+  }
+
+  if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
+    return ret_val;
+  ret_val.len = length;
+  
+  for(i = 0; i < length; i++)
+    ret_val.buf[i] = (uint8_t)((voffset >> i*8) & 0xFF);
+
+  return ret_val;
+}
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_parse_big_data_header(REGFI_FILE* file, uint32_t offset, 
+					 uint32_t max_size, bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint32_t cell_length;
+  bool unalloc;
+
+  /* XXX: do something with unalloc? */
+  ret_val.buf = (uint8_t*)talloc_array(NULL, uint8_t, REGFI_BIG_DATA_MIN_LENGTH);
+  if(ret_val.buf == NULL)
+    goto fail;
+
+  if(REGFI_BIG_DATA_MIN_LENGTH > max_size)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Big data header exceeded max_size "
+		  "while parsing big data header at offset 0x%.8X.",offset);
+    goto fail;
+  }
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_header"))
+    goto fail;
+
+
+  if(!regfi_parse_cell(file->cb, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
+		       &cell_length, &unalloc))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+		  " parsing big data header at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_header"))
+    goto fail;
+
+  if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b'))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Unknown magic number"
+		  " (0x%.2X, 0x%.2X) encountered while parsing"
+		  " big data header at offset 0x%.8X.", 
+		  ret_val.buf[0], ret_val.buf[1], offset);
+    goto fail;
+  }
+
+  ret_val.len = REGFI_BIG_DATA_MIN_LENGTH;
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_header");
+ fail:
+  talloc_free(ret_val.buf);
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  return ret_val;
+}
+
+
+
+/******************************************************************************
+ *
+ ******************************************************************************/
+uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
+				      uint16_t num_chunks, bool strict)
+{
+  uint32_t* ret_val;
+  uint32_t indirect_length;
+  int32_t max_size;
+  uint16_t i;
+  bool unalloc;
+
+  /* XXX: do something with unalloc? */
+  max_size = regfi_calc_maxsize(file, offset);
+  if((max_size < 0) || (num_chunks*sizeof(uint32_t) + 4 > max_size))
+    return NULL;
+
+  ret_val = (uint32_t*)talloc_array(NULL, uint32_t, num_chunks);
+  if(ret_val == NULL)
+    goto fail;
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_indirect"))
+    goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, (uint8_t*)ret_val,
+		       num_chunks*sizeof(uint32_t),
+		       &indirect_length, &unalloc))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+		  " parsing big data indirect record at offset 0x%.8X.", 
+		  offset);
+    goto fail_locked;
+  }
+
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_indirect"))
+    goto fail;
+
+  /* Convert pointers to proper endianess, verify they are aligned. */
+  for(i=0; i<num_chunks; i++)
+  {
+    ret_val[i] = IVAL(ret_val, i*sizeof(uint32_t));
+    if((ret_val[i] & 0x00000007) != 0)
+      goto fail;
+  }
+  
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_indirect");
+ fail:
+  talloc_free(ret_val);
+  return NULL;
+}
+
+
+/******************************************************************************
+ * Arguments:
+ *  file       --
+ *  offsets    -- list of virtual offsets.
+ *  num_chunks -- 
+ *  strict     --
+ *
+ * Returns:
+ *  A range_list with physical offsets and complete lengths 
+ *  (including cell headers) of associated cells.  
+ *  No data in range_list elements.
+ ******************************************************************************/
+range_list* regfi_parse_big_data_cells(REGFI_FILE* file, uint32_t* offsets,
+				       uint16_t num_chunks, bool strict)
+{
+  uint32_t cell_length, chunk_offset;
+  range_list* ret_val;
+  uint16_t i;
+  bool unalloc;
+  
+  /* XXX: do something with unalloc? */
+  ret_val = range_list_new();
+  if(ret_val == NULL)
+    goto fail;
+  
+  for(i=0; i<num_chunks; i++)
+  {
+    if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_cells"))
+      goto fail;
+
+    chunk_offset = offsets[i]+REGFI_REGF_SIZE;
+    if(!regfi_parse_cell(file->cb, chunk_offset, NULL, 0,
+			 &cell_length, &unalloc))
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+		    " parsing big data chunk at offset 0x%.8X.", 
+		    chunk_offset);
+      goto fail_locked;
+    }
+
+    if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_cells"))
+      goto fail;
+
+    if(!range_list_add(ret_val, chunk_offset, cell_length, NULL))
+      goto fail;
+  }
+
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_cells");
+ fail:
+  if(ret_val != NULL)
+    range_list_free(ret_val);
+  return NULL;
+}
+
+
+/******************************************************************************
+*******************************************************************************/
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, 
+				 uint32_t offset, uint32_t data_length, 
+				 uint32_t cell_length, range_list* used_ranges,
+				 bool strict)
+{
+  REGFI_BUFFER ret_val;
+  uint16_t num_chunks, i;
+  uint32_t read_length, data_left, tmp_len, indirect_offset;
+  uint32_t* indirect_ptrs = NULL;
+  REGFI_BUFFER bd_header;
+  range_list* bd_cells = NULL;
+  const range_list_element* cell_info;
+
+  ret_val.buf = NULL;
+
+  /* XXX: Add better error/warning messages */
+
+  bd_header = regfi_parse_big_data_header(file, offset, cell_length, strict);
+  if(bd_header.buf == NULL)
+    goto fail;
+
+  /* Keep track of used space for use by reglookup-recover */
+  if(used_ranges != NULL)
+    if(!range_list_add(used_ranges, offset, cell_length, NULL))
+      goto fail;
+
+  num_chunks = SVAL(bd_header.buf, 0x2);
+  indirect_offset = IVAL(bd_header.buf, 0x4) + REGFI_REGF_SIZE;
+  talloc_free(bd_header.buf);
+
+  indirect_ptrs = regfi_parse_big_data_indirect(file, indirect_offset,
+						num_chunks, strict);
+  if(indirect_ptrs == NULL)
+    goto fail;
+
+  if(used_ranges != NULL)
+    if(!range_list_add(used_ranges, indirect_offset, num_chunks*4+4, NULL))
+      goto fail;
+  
+  if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL)
+    goto fail;
+  data_left = data_length;
+
+  bd_cells = regfi_parse_big_data_cells(file, indirect_ptrs, num_chunks, strict);
+  if(bd_cells == NULL)
+    goto fail;
+
+  talloc_free(indirect_ptrs);
+  indirect_ptrs = NULL;
+  
+  for(i=0; (i<num_chunks) && (data_left>0); i++)
+  {
+    cell_info = range_list_get(bd_cells, i);
+    if(cell_info == NULL)
+      goto fail;
+
+    /* XXX: This should be "cell_info->length-4" to account for the 4 byte cell
+     *      length.  However, it has been observed that some (all?) chunks
+     *      have an additional 4 bytes of 0 at the end of their cells that 
+     *      isn't part of the data, so we're trimming that off too. 
+     *      Perhaps it's just an 8 byte alignment requirement...
+     */
+    if(cell_info->length - 8 >= data_left)
+    {
+      if(i+1 != num_chunks)
+      {
+	regfi_log_add(REGFI_LOG_WARN, "Left over chunks detected "
+		      "while constructing big data at offset 0x%.8X "
+		      "(chunk offset 0x%.8X).", offset, cell_info->offset);
+      }
+      read_length = data_left;
+    }
+    else
+      read_length = cell_info->length - 8;
+
+
+    if(read_length > regfi_calc_maxsize(file, cell_info->offset))
+    {
+      regfi_log_add(REGFI_LOG_WARN, "A chunk exceeded the maxsize "
+		    "while constructing big data at offset 0x%.8X "
+		    "(chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail;
+    }
+
+    if(!regfi_lock(file, &file->cb_lock, "regfi_load_big_data"))
+      goto fail;
+
+    if(regfi_seek(file->cb, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1)
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Could not seek to chunk while "
+		    "constructing big data at offset 0x%.8X "
+		    "(chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail_locked;
+    }
+
+    tmp_len = read_length;
+    if(regfi_read(file->cb, ret_val.buf+(data_length-data_left), 
+		  &read_length) != 0 || (read_length != tmp_len))
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Could not read data chunk while"
+		    " constructing big data at offset 0x%.8X"
+		    " (chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail_locked;
+    }
+
+    if(!regfi_unlock(file, &file->cb_lock, "regfi_load_big_data"))
+      goto fail;
+
+    if(used_ranges != NULL)
+      if(!range_list_add(used_ranges, cell_info->offset,cell_info->length,NULL))
+	goto fail;
+
+    data_left -= read_length;
+  }
+  range_list_free(bd_cells);
+
+  ret_val.len = data_length-data_left;
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_load_big_data");
+ fail:
+  talloc_free(ret_val.buf);
+  talloc_free(indirect_ptrs);
+  if(bd_cells != NULL)
+    range_list_free(bd_cells);
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  return ret_val;
+}
+
+
+range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
+{
+  range_list* ret_val;
+  REGFI_HBIN* hbin;
+  const range_list_element* hbins_elem;
+  uint32_t i, num_hbins, curr_off, cell_len;
+  bool is_unalloc;
+
+  ret_val = range_list_new();
+  if(ret_val == NULL)
+    return NULL;
+
+  if(!regfi_read_lock(file, &file->hbins_lock, "regfi_parse_unalloc_cells"))
+  {
+    range_list_free(ret_val);
+    return NULL;
+  }
+
+  num_hbins = range_list_size(file->hbins);
+  for(i=0; i<num_hbins; i++)
+  {
+    hbins_elem = range_list_get(file->hbins, i);
+    if(hbins_elem == NULL)
+      break;
+    hbin = (REGFI_HBIN*)hbins_elem->data;
+
+    curr_off = REGFI_HBIN_HEADER_SIZE;
+    while(curr_off < hbin->block_size)
+    {
+      if(!regfi_lock(file, &file->cb_lock, "regfi_parse_unalloc_cells"))
+	break;
+
+      if(!regfi_parse_cell(file->cb, hbin->file_off+curr_off, NULL, 0,
+			   &cell_len, &is_unalloc))
+      {
+	regfi_unlock(file, &file->cb_lock, "regfi_parse_unalloc_cells");
+	break;
+      }
+
+      if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_unalloc_cells"))
+	break;
+
+      if((cell_len == 0) || ((cell_len & 0x00000007) != 0))
+      {
+	regfi_log_add(REGFI_LOG_ERROR, "Bad cell length encountered"
+		      " while parsing unallocated cells at offset 0x%.8X.",
+		      hbin->file_off+curr_off);
+	break;
+      }
+
+      /* for some reason the record_size of the last record in
+	 an hbin block can extend past the end of the block
+	 even though the record fits within the remaining 
+	 space....aaarrrgggghhhhhh */  
+      if(curr_off + cell_len >= hbin->block_size)
+	cell_len = hbin->block_size - curr_off;
+      
+      if(is_unalloc)
+	range_list_add(ret_val, hbin->file_off+curr_off, 
+		       cell_len, NULL);
+      
+      curr_off = curr_off+cell_len;
+    }
+  }
+
+  if(!regfi_rw_unlock(file, &file->hbins_lock, "regfi_parse_unalloc_cells"))
+  {
+    range_list_free(ret_val);
+    return NULL;
+  }
+
+  return ret_val;
+}
+
+
+/* From lib/time.c */
+
+/****************************************************************************
+ Returns an 8 byte filetime from a time_t
+ This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+REGFI_NTTIME regfi_unix2nt_time(time_t t)
+{
+  double d;
+
+  if (t==0)
+    return 0L;
+  
+  if (t == TIME_T_MAX) 
+    return 0x7fffffffffffffffL;
+  
+  if (t == -1) 
+    return 0xffffffffffffffffL;
+  
+  /* 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) + REGFI_TIME_FIXUP;
+  d *= 1.0e7;
+  /*
+  nt->high = (uint32_t)(d * (1.0/c));
+  nt->low  = (uint32_t)(d - ((double)nt->high) * c);
+  */
+
+  return (REGFI_NTTIME) d;
+}
+
+
+/****************************************************************************
+ 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.
+****************************************************************************/
+double regfi_nt2unix_time(REGFI_NTTIME nt)
+{
+  double ret_val;
+  
+  if (nt == 0 || nt == 0xffffffffffffffffL)
+    return 0;
+  
+  ret_val = (double)(nt) * 1.0e-7;
+  
+  /* now adjust by 369 years to make the secs since 1970 */
+  ret_val -= REGFI_TIME_FIXUP;
+  
+  /* 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_val;
+}
+
+/* End of stuff from lib/time.c */
Index: /releases/1.0.1/lib/void_stack.c
===================================================================
--- /releases/1.0.1/lib/void_stack.c	(revision 275)
+++ /releases/1.0.1/lib/void_stack.c	(revision 275)
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2005,2007,2009-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+/**  
+ * @file
+ */
+
+
+#include "void_stack.h"
+
+void_stack* void_stack_new(unsigned short max_size)
+{
+  void_stack* ret_val = talloc(NULL, void_stack);
+
+  if (ret_val != NULL)
+  {
+    memset(ret_val, 0, sizeof(*ret_val));
+    ret_val->elements = talloc_array(ret_val, void*, max_size);
+    if (ret_val->elements == NULL)
+    {
+      talloc_free(ret_val);
+      ret_val = NULL;
+    }
+    else
+    {
+      memset(ret_val->elements, 0, max_size*sizeof(void*));
+    
+      ret_val->max_size = max_size;
+      ret_val->top = 0;
+    }
+  }
+
+  return ret_val;
+}
+
+
+void_stack* void_stack_copy(const void_stack* v)
+{
+  unsigned int i;
+  void_stack* ret_val;
+  if(v == NULL)
+    return NULL;
+
+  ret_val = void_stack_new(v->max_size);
+  if(ret_val == NULL)
+    return NULL;
+
+  for(i = 0; i < v->top; i++)
+    ret_val->elements[i] = v->elements[i];
+  ret_val->top = v->top;
+
+  return ret_val;
+}
+
+
+void_stack* void_stack_copy_reverse(const void_stack* v)
+{
+  unsigned int i;
+  void_stack* ret_val;
+  if(v == NULL)
+    return NULL;
+
+  ret_val = void_stack_new(v->max_size);
+  if(ret_val == NULL)
+    return NULL;
+
+  for(i = 0; i < v->top; i++)
+    ret_val->elements[i] = v->elements[v->top-i-1];
+  ret_val->top = v->top;
+
+  return ret_val;
+}
+
+
+void void_stack_free(void_stack* stack)
+{
+  talloc_free(stack);
+}
+
+
+void void_stack_free_deep(void_stack* stack)
+{
+  unsigned short i;
+  for(i=0; i < stack->top; i++)
+    free(stack->elements[i]);
+  talloc_free(stack);
+}
+
+
+unsigned short void_stack_size(const void_stack* stack)
+{
+  return stack->top;
+}
+
+
+void* void_stack_pop(void_stack* stack)
+{
+  void* ret_val = NULL;
+
+  if(stack->top > 0)
+  {
+    ret_val = stack->elements[--(stack->top)];
+    stack->elements[stack->top] = NULL;
+  }
+
+  return ret_val;
+}
+
+
+bool void_stack_push(void_stack* stack, void* e)
+{
+  if(stack->top < stack->max_size)
+  {
+    stack->elements[stack->top++] = e;
+    return true;
+  }
+  else
+    return false;
+}
+
+
+const void* void_stack_cur(const void_stack* stack)
+{
+  void* ret_val = NULL;
+
+  if(stack->top > 0)
+    ret_val = stack->elements[stack->top-1];
+
+  return ret_val;
+}
+
+
+void_stack_iterator* void_stack_iterator_new(const void_stack* stack)
+{
+  void_stack_iterator* ret_val = NULL;
+  
+  if(stack != NULL)
+  {
+    ret_val = talloc(stack, void_stack_iterator);
+    if (ret_val != NULL)
+    {
+      ret_val->stack = stack;
+      ret_val->cur = 0;
+    }
+  }
+
+  return ret_val;
+}
+
+
+void void_stack_iterator_free(void_stack_iterator* iter)
+{
+  talloc_free(iter);
+}
+
+
+const void* void_stack_iterator_next(void_stack_iterator* iter)
+{
+  if(iter->cur < iter->stack->top)
+    return iter->stack->elements[iter->cur++];
+  else
+    return NULL;
+}
Index: /releases/1.0.1/lib/winsec.c
===================================================================
--- /releases/1.0.1/lib/winsec.c	(revision 275)
+++ /releases/1.0.1/lib/winsec.c	(revision 275)
@@ -0,0 +1,553 @@
+/*
+ * Copyright (C) 2005-2006,2009-2011 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team 
+ *               (see individual files under Subversion for details.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/** @file */
+
+#include "winsec.h"
+
+
+/******************************************************************************
+ * Non-talloc() interface for parsing a descriptor.
+ ******************************************************************************/
+WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len)
+{
+  return winsec_parse_desc(NULL, buf, buf_len);
+}
+
+
+/******************************************************************************
+ * Free a descriptor.  Not needed if using talloc and a parent context is freed.
+ ******************************************************************************/
+void winsec_free_descriptor(WINSEC_DESC* desc)
+{
+  talloc_free(desc);
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_DESC structure and substructures.
+ ******************************************************************************/
+WINSEC_DESC* winsec_parse_desc(void* talloc_ctx, 
+			       const uint8_t* buf, uint32_t buf_len)
+{
+  WINSEC_DESC* ret_val;
+
+  if (buf == NULL || buf_len <  WINSEC_DESC_HEADER_SIZE)
+    return NULL;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_DESC)) == NULL)
+    return NULL;
+
+  ret_val->revision = buf[0];
+  ret_val->sbz1 = buf[1];
+  ret_val->control = SVAL(buf, 0x2);
+
+  /* XXX: should probably reject any non-self relative */
+  if(!(ret_val->control & WINSEC_DESC_SELF_RELATIVE))
+    fprintf(stderr, "DEBUG: NOT self-relative!\n");
+
+  ret_val->off_owner_sid = IVAL(buf, 0x4);
+  ret_val->off_grp_sid = IVAL(buf, 0x8);
+  ret_val->off_sacl = IVAL(buf, 0xC);
+  ret_val->off_dacl = IVAL(buf, 0x10);
+
+  /* A basic sanity check to ensure our offsets are within our buffer.
+   * Additional length checking is done in secondary parsing functions.
+   */
+  if((ret_val->off_owner_sid >= buf_len)
+     || (ret_val->off_grp_sid >= buf_len)
+     || (ret_val->off_sacl >= buf_len)
+     || (ret_val->off_dacl >= buf_len))
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  if(ret_val->off_owner_sid == 0)
+    ret_val->owner_sid = NULL;
+  else
+  {
+    ret_val->owner_sid = winsec_parse_dom_sid(ret_val, 
+					      buf + ret_val->off_owner_sid,
+					      buf_len - ret_val->off_owner_sid);
+    if(ret_val->owner_sid == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+
+  if(ret_val->off_grp_sid == 0) 
+    ret_val->grp_sid = NULL;
+  else
+  {
+    ret_val->grp_sid = winsec_parse_dom_sid(ret_val, buf + ret_val->off_grp_sid,
+					    buf_len - ret_val->off_grp_sid);
+    if(ret_val->grp_sid == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+
+  if((ret_val->control & WINSEC_DESC_SACL_PRESENT) && ret_val->off_sacl)
+  {
+    ret_val->sacl = winsec_parse_acl(ret_val, buf + ret_val->off_sacl,
+				     buf_len - ret_val->off_sacl);
+    if(ret_val->sacl == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+  else
+    ret_val->sacl = NULL;
+
+  if((ret_val->control & WINSEC_DESC_DACL_PRESENT) && ret_val->off_dacl != 0) 
+  {
+    ret_val->dacl = winsec_parse_acl(ret_val, buf + ret_val->off_dacl,
+				     buf_len - ret_val->off_dacl);
+    if(ret_val->dacl == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+  else
+    ret_val->dacl = NULL;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_ACL structure and all substructures.
+ ******************************************************************************/
+WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
+			     const uint8_t* buf, uint32_t buf_len)
+{
+  uint32_t i, offset;
+  WINSEC_ACL* ret_val;
+
+  /*
+   * Note that the size is always a multiple of 4 bytes due to the
+   * nature of the data structure.
+   */
+  if (buf == NULL || buf_len < 8)
+    return NULL;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_ACL)) == NULL)
+    return NULL;
+  
+  ret_val->revision = SVAL(buf, 0x0);
+  ret_val->size     = SVAL(buf, 0x2);
+  ret_val->num_aces = IVAL(buf, 0x4);
+
+  /* The num_aces can be at most around 4k because anything greater
+   * wouldn't fit in the 16 bit size even if every ace was as small as
+   * possible. 
+   */
+  if((ret_val->size > buf_len) || (ret_val->num_aces > 4095))
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  /* Even if the num_aces is zero, allocate memory as there's a difference
+   * between a non-present DACL (allow all access) and a DACL with no ACE's
+   * (allow no access).
+   */
+  if((ret_val->aces = talloc_array(ret_val, WINSEC_ACE*, 
+				   ret_val->num_aces+1)) == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  offset = 8;
+  for(i=0; i < ret_val->num_aces; i++)
+  {
+    ret_val->aces[i] = winsec_parse_ace(ret_val->aces, 
+					buf+offset, buf_len-offset);
+    if(ret_val->aces[i] == NULL)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+
+    offset += ret_val->aces[i]->size;
+    if(offset > buf_len)
+    {
+      talloc_free(ret_val);
+      return NULL;
+    }
+  }
+  ret_val->aces[ret_val->num_aces] = NULL;
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_ACE structure and all substructures.
+ ******************************************************************************/
+WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
+			     const uint8_t* buf, uint32_t buf_len)
+{
+  uint32_t offset;
+  WINSEC_ACE* ret_val;
+
+  if(buf == NULL || buf_len < WINSEC_ACE_MIN_SIZE)
+    return NULL;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_ACE)) == NULL)
+    return NULL;
+
+  ret_val->type = buf[0];
+  ret_val->flags = buf[1];
+  ret_val->size = SVAL(buf, 0x2);
+  ret_val->access_mask = IVAL(buf, 0x4);
+  ret_val->obj_guid = NULL;
+  ret_val->inh_guid = NULL;
+  
+  offset = 0x8;
+
+  /* check whether object access is present */
+  if (winsec_ace_object(ret_val->type))
+  {
+    ret_val->obj_flags = IVAL(buf, offset);
+    offset += 4;
+
+    if(ret_val->obj_flags & WINSEC_ACE_OBJECT_PRESENT)
+    {
+      ret_val->obj_guid = winsec_parse_uuid(ret_val, 
+					    buf+offset, buf_len-offset);
+      if(ret_val->obj_guid == NULL)
+      {
+	talloc_free(ret_val);
+	return NULL;
+      }
+      offset += sizeof(WINSEC_UUID);
+    }
+
+    if(ret_val->obj_flags & WINSEC_ACE_OBJECT_INHERITED_PRESENT)
+    {
+      ret_val->inh_guid = winsec_parse_uuid(ret_val, 
+					    buf+offset, buf_len-offset);
+      if(ret_val->inh_guid == NULL)
+      {
+	talloc_free(ret_val);
+	return NULL;
+      }
+      offset += sizeof(WINSEC_UUID);
+    }
+  }
+
+  ret_val->trustee = winsec_parse_dom_sid(ret_val, buf+offset, buf_len-offset);
+  if(ret_val->trustee == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_DOM_SID structure.
+ ******************************************************************************/
+WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
+				     const uint8_t* buf, uint32_t buf_len)
+{
+  uint32_t i;
+  WINSEC_DOM_SID* ret_val;
+
+  if(buf == NULL || buf_len < 8)
+    return NULL;
+
+  /*  if((ret_val = (WINSEC_DOM_SID*)zalloc(sizeof(WINSEC_DOM_SID))) == NULL)*/
+  if((ret_val = talloc(talloc_ctx, WINSEC_DOM_SID)) == NULL)
+    return NULL;
+
+  ret_val->sid_rev_num = buf[0];
+  ret_val->num_auths = buf[1];
+  memcpy(ret_val->id_auth, buf+2, 6);
+
+  /* XXX: should really issue a warning here... */
+  if (ret_val->num_auths > WINSEC_MAX_SUBAUTHS)
+    ret_val->num_auths = WINSEC_MAX_SUBAUTHS;
+
+  if(buf_len < ret_val->num_auths*sizeof(uint32_t)+8)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+  
+  for(i=0; i < ret_val->num_auths; i++)
+    ret_val->sub_auths[i] = IVAL(buf, 8+i*sizeof(uint32_t));
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Parses a WINSEC_UUID struct.
+ ******************************************************************************/
+WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
+			       const uint8_t* buf, uint32_t buf_len)
+{
+  WINSEC_UUID* ret_val;
+
+  if(buf == NULL || buf_len < sizeof(WINSEC_UUID))
+    return false;
+
+  if((ret_val = talloc(talloc_ctx, WINSEC_UUID)) == NULL)
+    return NULL;
+  
+  ret_val->time_low = IVAL(buf, 0x0);
+  ret_val->time_mid = SVAL(buf, 0x4);
+  ret_val->time_hi_and_version = SVAL(buf, 0x6);
+  
+  memcpy(ret_val->clock_seq, buf+0x8, 2);
+  memcpy(ret_val->node, buf+0xB, 6);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Calculates the size of a SID.
+ ******************************************************************************/
+size_t winsec_sid_size(const WINSEC_DOM_SID* sid)
+{
+  if (sid == NULL)
+    return 0;
+
+  return sid->num_auths * sizeof(uint32_t) + 8;
+}
+
+
+/******************************************************************************
+ * Compare the auth portion of two SIDs.
+ ******************************************************************************/
+int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+  int i;
+
+  if (sid1 == sid2)
+    return 0;
+  if (!sid1)
+    return -1;
+  if (!sid2)
+    return 1;
+
+  if (sid1->sid_rev_num != sid2->sid_rev_num)
+    return sid1->sid_rev_num - sid2->sid_rev_num;
+
+  for (i = 0; i < 6; i++)
+    if (sid1->id_auth[i] != sid2->id_auth[i])
+      return sid1->id_auth[i] - sid2->id_auth[i];
+
+  return 0;
+}
+
+
+/******************************************************************************
+ * Compare two SIDs.
+ ******************************************************************************/
+int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+  int i;
+
+  if (sid1 == sid2)
+    return 0;
+  if (!sid1)
+    return -1;
+  if (!sid2)
+    return 1;
+
+  /* Compare most likely different rids, first: i.e start at end */
+  if (sid1->num_auths != sid2->num_auths)
+    return sid1->num_auths - sid2->num_auths;
+
+  for (i = sid1->num_auths-1; i >= 0; --i)
+    if (sid1->sub_auths[i] != sid2->sub_auths[i])
+      return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+  return winsec_sid_compare_auth(sid1, sid2);
+}
+
+
+/******************************************************************************
+ * Compare two SIDs.
+ ******************************************************************************/
+bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
+{
+  return winsec_sid_compare(sid1, sid2) == 0;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+char* winsec_sid2str(const WINSEC_DOM_SID* sid)
+{
+  uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
+  uint32_t left = size;
+  uint8_t comps = sid->num_auths;
+  char* ret_val = malloc(size);
+  
+  if(ret_val == NULL)
+    return NULL;
+
+  if(comps > WINSEC_MAX_SUBAUTHS)
+    comps = WINSEC_MAX_SUBAUTHS;
+
+  left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
+
+  for (i = 0; i < comps; i++) 
+    left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
+ * Compares two WINSEC_DESC structures.
+ ******************************************************************************/
+bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2)
+{
+  /* Trivial cases */
+  if (!s1 && !s2)
+    return true;
+  if (!s1 || !s2)
+    return false;
+
+  /* Check top level stuff */
+  if (s1->revision != s2->revision)
+    return false;
+
+  if (s1->control != s2->control)
+    return false;
+
+  /* Check owner and group */
+  if (!winsec_sid_equal(s1->owner_sid, s2->owner_sid))
+    return false;
+
+  if (!winsec_sid_equal(s1->grp_sid, s2->grp_sid)) 
+    return false;
+
+  /* Check ACLs present in one but not the other */
+  if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
+      (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) 
+  { return false; }
+
+  /* Sigh - we have to do it the hard way by iterating over all
+     the ACEs in the ACLs */
+  if(!winsec_acl_equal(s1->dacl, s2->dacl) || !winsec_acl_equal(s1->sacl, s2->sacl)) 
+    return false;
+
+  return true;
+}
+
+
+
+/******************************************************************************
+ * Compares two WINSEC_ACL structures.
+ ******************************************************************************/
+bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2)
+{
+  unsigned int i, j;
+
+  /* Trivial cases */
+  if (!s1 && !s2) 
+    return true;
+  if (!s1 || !s2) 
+    return false;
+
+  /* Check top level stuff */
+  if (s1->revision != s2->revision)
+    return false;
+
+  if (s1->num_aces != s2->num_aces)
+    return false;
+
+  /* The ACEs could be in any order so check each ACE in s1 against 
+     each ACE in s2. */
+
+  for (i = 0; i < s1->num_aces; i++)
+  {
+    bool found = false;
+
+    for (j = 0; j < s2->num_aces; j++) 
+    {
+      if (winsec_ace_equal(s1->aces[i], s2->aces[j])) 
+      {
+	found = true;
+	break;
+      }
+    }
+
+    if (!found)
+      return false;
+  }
+
+  return true;
+}
+
+
+/******************************************************************************
+ * Compares two WINSEC_ACE structures.
+ ******************************************************************************/
+bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2)
+{
+  /* Trivial cases */
+  if (!s1 && !s2) 
+    return true;
+  if (!s1 || !s2) 
+    return false;
+
+  /* Check top level stuff */
+  if (s1->type != s2->type || s1->flags != s2->flags ||
+      s1->access_mask != s2->access_mask)
+  { return false; }
+
+  /* Check SID */
+  if (!winsec_sid_equal(s1->trustee, s2->trustee))
+    return false;
+
+  return true;
+}
+
+
+/******************************************************************************
+ * Check if ACE has OBJECT type.
+ ******************************************************************************/
+bool winsec_ace_object(uint8_t type)
+{
+  if (type == WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+      type == WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+      type == WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+      type == WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) 
+  { return true; }
+
+  return false;
+}
Index: /releases/1.0.1/pyregfi-distutils.py
===================================================================
--- /releases/1.0.1/pyregfi-distutils.py	(revision 275)
+++ /releases/1.0.1/pyregfi-distutils.py	(revision 275)
@@ -0,0 +1,4 @@
+# Called from scons with appropriate python version
+
+from distutils.core import setup
+setup(name='pyregfi', version='1.0', package_dir={'':'python'}, packages=['pyregfi'])
Index: /releases/1.0.1/python/experimental/SConstruct
===================================================================
--- /releases/1.0.1/python/experimental/SConstruct	(revision 275)
+++ /releases/1.0.1/python/experimental/SConstruct	(revision 275)
@@ -0,0 +1,46 @@
+import os, os.path, pdb
+from functools import partial
+import utils
+import config
+import class_parser
+
+def build_python_bindings(target, source, env, initialization=''):
+    """ A command to generate python bindings """
+    module_name = os.path.splitext(os.path.basename(target[0].name))[0]
+    utils.warn("Generating automatic python bindings for module %s" % module_name)
+
+    p = class_parser.HeaderParser(module_name, verbose=config.V)
+    p.module.init_string = initialization
+    p.parse_filenames([s.get_abspath() for s in source])
+
+    fd = open(target[0].get_abspath(), 'w')
+    p.write(fd)
+    fd.close()
+
+
+
+nenv = utils.ExtendedEnvironment()
+
+BOUND_FILES = Split("""
+    ../include/regfi.h
+    regfi/pyregfi.h
+    """)
+
+nenv.config = config
+if config.DEBUG:
+    nenv.Append(CFLAGS = "-std=gnu99 -pedantic -Wall -fPIC -ggdb -O0")
+    nenv.Append(CPPPATH=['../include', 'include'])
+    nenv.python_cppflags = '-I/usr/include/python2.5_d'
+else:
+    nenv.Append(CFLAGS = "-std=gnu99 -pedantic -Wall -fPIC")
+    nenv.Append(CPPPATH=['../include', 'include'])
+    
+nenv.Append(LIBPATH="../lib")
+nenv.Append(LINKFLAGS="")
+# XXX: why should I need to call regfi_init() when it should be called only once automatically? 
+nenv.Command('regfi/pyregfi.c', BOUND_FILES, partial(build_python_bindings,
+                                                     initialization='pyregfi_init();regfi_init();'))
+nenv.Depends('regfi/pyregfi.c', 'class_parser.py')
+
+nenv.PythonModule("pyregfi", ['regfi/pyregfi.c', 'regfi/regfi.c', 'regfi/class.c', 'regfi/error.c'],
+                  LIBS=['regfi', 'talloc'])
Index: /releases/1.0.1/python/experimental/class_parser.py
===================================================================
--- /releases/1.0.1/python/experimental/class_parser.py	(revision 275)
+++ /releases/1.0.1/python/experimental/class_parser.py	(revision 275)
@@ -0,0 +1,2973 @@
+import sys, os, re, pdb, StringIO
+ 
+DEBUG = 0
+
+def log(msg):
+    if DEBUG>0:
+        sys.stderr.write(msg+"\n")
+
+def escape_for_string(string):
+    result = string
+    result = result.encode("string-escape")
+    result = result.replace('"',r'\"')
+
+    return result
+
+class Module:
+    def __init__(self, name):
+        self.name = name
+        self.constants = set()
+        self.classes = {}
+        self.headers = '#include <Python.h>\n'
+
+        self.files = []
+
+    def __str__(self):
+        result = "Module %s\n" % (self.name)
+        l = self.classes.values()
+        l.sort()
+        for attr in l:
+            if attr.is_active():
+                result += "    %s\n" % attr
+
+        l = list(self.constants)
+        l.sort()
+        result += 'Constants:\n'
+        for attr, type in l:
+            result += " %s\n" % attr
+
+        return result
+
+    init_string = ''
+    def initialization(self):
+        result = self.init_string + """
+talloc_set_log_fn((void(*)(const char*))printf);
+//talloc_enable_leak_report();
+"""
+        for cls in self.classes.values():
+            if cls.is_active():
+                result += cls.initialise()
+
+        return result
+
+    def add_constant(self, constant, type="numeric"):
+        """ This will be called to add #define constant macros """
+        self.constants.add((constant, type))
+
+    def add_class(self, cls, handler):
+        self.classes[cls.class_name] = cls
+
+        ## Make a wrapper in the type dispatcher so we can handle
+        ## passing this class from/to python
+        type_dispatcher[cls.class_name] = handler
+
+    def private_functions(self):
+        """ Emits hard coded private functions for doing various things """
+        return """
+/* The following is a static array mapping CLASS() pointers to their
+python wrappers. This is used to allow the correct wrapper to be
+chosen depending on the object type found - regardless of the
+prototype.
+
+This is basically a safer way for us to cast the correct python type
+depending on context rather than assuming a type based on the .h
+definition. For example consider the function
+
+AFFObject Resolver.open(uri, mode)
+
+The .h file implies that an AFFObject object is returned, but this is
+not true as most of the time an object of a derived class will be
+returned. In C we cast the returned value to the correct type. In the
+python wrapper we just instantiate the correct python object wrapper
+at runtime depending on the actual returned type. We use this lookup
+table to do so.
+*/
+static int TOTAL_CLASSES=0;
+
+/* This is a global reference to this module so classes can call each
+   other.
+*/
+static PyObject *g_module = NULL;
+
+static struct python_wrapper_map_t {
+       Object class_ref;
+       PyTypeObject *python_type;
+} python_wrappers[%s];
+
+/** This is a generic wrapper type */
+typedef struct {
+  PyObject_HEAD
+  void *base;
+  void *ctx;
+} Gen_wrapper;
+
+/* Create the relevant wrapper from the item based on the lookup
+table.
+*/
+Gen_wrapper *new_class_wrapper(Object item) {
+   int i;
+   Gen_wrapper *result;
+   Object cls;
+
+   /* Hack to get the current null_context */
+   void* null_context;
+   int* tmp = talloc(NULL, int);
+   null_context = talloc_parent(tmp);
+   talloc_free(tmp);
+
+   // Return None for a NULL pointer
+   if(!item) {
+     Py_INCREF(Py_None);
+     return (Gen_wrapper *)Py_None;
+   }
+
+   // Search for subclasses
+   for(cls=(Object)item->__class__; cls != cls->__super__; cls=cls->__super__) {
+     for(i=0; i<TOTAL_CLASSES; i++) {
+       if(python_wrappers[i].class_ref == cls) {
+         PyErr_Clear();
+
+         result = (Gen_wrapper *)_PyObject_New(python_wrappers[i].python_type);
+         result->ctx = talloc_asprintf(NULL, "new_class_wrapper %%s@%%p",
+                                       NAMEOF(item), (void*)item);
+         result->base = (void *)item;
+
+         /* If its owned by the null_context it means that the function does
+            not want to own the memory - we therefore steal it so it gets freed
+            with the python object. */
+         if(talloc_parent(result->base) == null_context)
+                  talloc_steal(result->ctx, result->base);
+
+         return result;
+       }
+     }
+   }
+
+  PyErr_Format(PyExc_RuntimeError, "Unable to find a wrapper for object %%s", NAMEOF(item));
+  return NULL;
+}
+
+static PyObject *resolve_exception(char **error_buff) {
+  enum _error_type *type = aff4_get_current_error(error_buff);
+  switch(*type) {
+case EProgrammingError:
+    return PyExc_SystemError;
+case EKeyError:
+    return PyExc_KeyError;
+case ERuntimeError:
+    return PyExc_RuntimeError;
+case EWarning:
+    return PyExc_AssertionError;
+default:
+    return PyExc_RuntimeError;
+};
+}
+
+static int type_check(PyObject *obj, PyTypeObject *type) {
+   PyTypeObject *tmp;
+
+   // Recurse through the inheritance tree and check if the types are expected
+   if(obj)
+     for(tmp = obj->ob_type; tmp && tmp != &PyBaseObject_Type; tmp = tmp->tp_base) {
+       if(tmp == type) return 1;
+     }
+
+  return 0;
+}
+
+static int check_error() {
+   if(!CheckError(EZero)) {
+         char *buffer;
+         PyObject *exception = resolve_exception(&buffer);
+
+         PyErr_Format(exception, "%%s", buffer);
+         ClearError();
+         return 1;
+   }
+  return 0;
+}
+
+#define CHECK_ERROR if(check_error()) goto error;
+
+""" % (len(self.classes)+1)
+
+    def initialise_class(self, class_name, out, done = None):
+        if done and class_name in done: return
+
+        done.add(class_name)
+
+        cls = self.classes[class_name]
+        """ Write out class initialisation code into the main init function. """
+        if cls.is_active():
+            base_class = self.classes.get(cls.base_class_name)
+
+            if base_class and base_class.is_active():
+                ## We have a base class - ensure it gets written out
+                ## first:
+                self.initialise_class(cls.base_class_name, out, done)
+
+                ## Now assign ourselves as derived from them
+                out.write(" %s_Type.tp_base = &%s_Type;" % (
+                        cls.class_name, cls.base_class_name))
+            
+            out.write("""
+ %(name)s_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&%(name)s_Type) < 0)
+     return;
+
+ Py_INCREF((PyObject *)&%(name)s_Type);
+ PyModule_AddObject(m, "%(name)s", (PyObject *)&%(name)s_Type);
+""" % {'name': cls.class_name})
+
+    def write(self, out):
+        ## Prepare all classes
+        for cls in self.classes.values():
+            cls.prepare()
+
+        out.write("""
+/**********************************************************************
+     Autogenerated module %s
+
+This module was autogenerated from the following files:
+""" % self.name)
+        for file in self.files:
+            out.write("%s\n" % file)
+
+        out.write("\nThis module implements the following classes:\n")
+        out.write(self.__str__())
+        out.write("""***********************************************************************/
+""")
+        out.write(self.headers)
+        out.write(self.private_functions())
+
+        for cls in self.classes.values():
+            if cls.is_active():
+                out.write("/******************** %s ***********************/" % cls.class_name)
+                cls.struct(out)
+                cls.prototypes(out)
+
+        out.write("/*****************************************************\n             Implementation\n******************************************************/\n\n")
+        for cls in self.classes.values():
+            if cls.is_active():
+                cls.PyMethodDef(out)
+                cls.code(out)
+                cls.PyTypeObject(out)
+
+        ## Write the module initializer
+        out.write("""
+static PyMethodDef %(module)s_methods[] = {
+     {NULL}  /* Sentinel */
+};
+
+PyMODINIT_FUNC init%(module)s(void) {
+   /* Make sure threads are enabled */
+   PyEval_InitThreads();
+
+   /* create module */
+   PyObject *m = Py_InitModule3("%(module)s", %(module)s_methods,
+                                   "%(module)s module.");
+   PyObject *d = PyModule_GetDict(m);
+   PyObject *tmp;
+
+   g_module = m;
+""" % {'module': self.name})
+
+        ## The trick is to initialise the classes in order of their
+        ## inheritance. The following code will order initializations
+        ## according to their inheritance tree
+        done = set()
+        for class_name in self.classes.keys():
+            self.initialise_class(class_name, out, done)
+
+        ## Add the constants in here
+        for constant, type in self.constants:
+            if type == 'integer':
+                out.write(""" tmp = PyLong_FromUnsignedLongLong((int64_t)%s); \n""" % constant)
+            elif type == 'string':
+                out.write(" tmp = PyString_FromString((char *)%s); \n" % constant)
+            else:
+                out.write(" // I dont know how to convert %s type %s\n" % (constant, type))
+                continue
+
+            out.write("""
+ PyDict_SetItemString(d, "%s", tmp);
+ Py_DECREF(tmp);\n""" % (constant))
+
+        out.write(self.initialization())
+        out.write("}\n\n")
+
+class Type:
+    interface = None
+    buildstr = 'O'
+    sense = 'IN'
+    error_value = "return 0;"
+    active = True
+
+    def __init__(self, name, type):
+        self.name = name
+        self.type = type
+        self.attributes = set()
+
+    def comment(self):
+        return "%s %s " % (self.type, self.name)
+
+    def python_name(self):
+        return self.name
+
+    def python_proxy_post_call(self):
+        """ This is called after a proxy call """
+        return ''
+
+    def returned_python_definition(self, *arg, **kw):
+        return self.definition(*arg, **kw)
+
+    def definition(self, default=None, **kw):
+        if default:
+            return "%s %s=%s;\n" % (self.type, self.name, default)
+        else:
+            return "%s __attribute__((unused)) %s;\n" % (self.type, self.name)
+
+    def byref(self):
+        return "&%s" % self.name
+
+    def call_arg(self):
+        return self.name
+
+    def pre_call(self, method):
+        return ''
+
+    def assign(self, call, method, target=None):
+        return "Py_BEGIN_ALLOW_THREADS\n%s = %s;\nPy_END_ALLOW_THREADS\n" % (target or self.name, call)
+
+    def post_call(self, method):
+        if "DESTRUCTOR" in self.attributes:
+            return "talloc_free(self->ctx); self->base = NULL;\n"
+
+        return ''
+
+    def from_python_object(self, source, destination, method, **kw):
+        return ''
+
+    def return_value(self, value):
+        return "return %s;" % value
+
+    def __str__(self):
+        if self.name == 'func_return':
+            return self.type
+        if 'void' in self.type:
+            return ''
+
+        result = "%s : %s" % (self.type, self.name)
+
+        return result
+
+class String(Type):
+    interface = 'string'
+    buildstr = 's'
+    error_value = "return NULL;"
+
+    def __init__(self, name, type):
+        Type.__init__(self, name, type)
+        self.length = "strlen(%s)" % name
+
+    def byref(self):
+        return "&%s" % self.name
+
+    def to_python_object(self, name=None, result='py_result',**kw):
+        name = name or self.name
+
+        result = """PyErr_Clear();
+   if(!%(name)s) { PyErr_Format(PyExc_RuntimeError, "%(name)s is NULL"); goto error; }
+   %(result)s = PyString_FromStringAndSize((char *)%(name)s, %(length)s);\nif(!%(result)s) goto error;
+""" % dict(name=name, result=result,length=self.length)
+
+        if "BORROWED" not in self.attributes and 'BORROWED' not in kw:
+            result += "talloc_unlink(NULL, %s);\n" % name
+
+        return result
+
+    def from_python_object(self, source, destination, method, context='NULL'):
+        method.error_set = True
+        return """
+{
+  char *buff; Py_ssize_t length;
+
+  PyErr_Clear();
+  if(-1==PyString_AsStringAndSize(%(source)s, &buff, &length))
+     goto error;
+
+  %(destination)s = talloc_size(%(context)s, length + 1);
+  memcpy(%(destination)s, buff, length);
+  %(destination)s[length]=0;
+}
+""" % dict(source = source, destination = destination, context =context)
+
+class ZString(String):
+    interface = 'null_terminated_string'
+
+class BorrowedString(String):
+    def to_python_object(self, name=None, result='py_result', **kw):
+        name = name or self.name
+        return "PyErr_Clear();\n" +\
+            "%s = PyString_FromStringAndSize((char *)%(name)s, %(length)s);\n" % dict(
+            name=name, length=self.length, result=result)
+
+class Char_and_Length(Type):
+    interface = 'char_and_length'
+    buildstr = 's#'
+    error_value = "return NULL;"
+
+    def __init__(self, data, data_type, length, length_type):
+        Type.__init__(self, data, data_type)
+
+        self.name = data
+        self.data_type=data_type
+        self.length = length
+        self.length_type = length_type
+
+    def comment(self):
+        return "%s %s, %s %s" % (self.data_type, self.name,
+                                 self.length_type, self.length)
+
+    def definition(self, default = '""', **kw):
+        return "char *%s=%s; Py_ssize_t %s=strlen(%s);\n" % (
+            self.name, default,
+            self.length, default)
+
+    def byref(self):
+        return "&%s, &%s" % (self.name, self.length)
+
+    def call_arg(self):
+        return "(%s)%s, (%s)%s" % (self.data_type, self.name, self.length_type,
+                                   self.length)
+
+    def to_python_object(self, name=None, result='py_result', **kw):
+        return "PyErr_Clear();\n"\
+            "%s = PyString_FromStringAndSize(%s, %s);\nif(!%s) goto error;" % (
+            result, self.name, self.length, result);
+
+class Integer(Type):
+    interface = 'integer'
+    buildstr = 'K'
+
+    def __init__(self, name,type):
+        Type.__init__(self,name,type)
+        self.type = 'uint64_t'
+        self.original_type = type
+
+    def definition(self, default = 0, **kw):
+        return Type.definition(self, default)
+
+    def to_python_object(self, name=None, result='py_result', **kw):
+        name = name or self.name
+        return "PyErr_Clear();\n%s = PyLong_FromLongLong(%s);\n" % (result, name)
+
+    def from_python_object(self, source, destination, method, **kw):
+        return "PyErr_Clear();\n"\
+            "%(destination)s = PyLong_AsUnsignedLong(%(source)s);\n" % dict(
+            source = source, destination= destination)
+
+    def comment(self):
+        return "%s %s " % (self.original_type, self.name)
+
+class Integer32(Integer):
+    buildstr = 'I'
+
+    def __init__(self, name,type):
+        Type.__init__(self,name,type)
+        self.type = 'unsigned int '
+        self.original_type = type
+
+    def to_python_object(self, name=None, result='py_result', **kw):
+        name = name or self.name
+        return "PyErr_Clear();\n%s = PyLong_FromLong(%s);\n" % (result, name)
+
+class Integer64(Integer):
+    buildstr = 'K'
+    type = 'unsigned int'
+
+class Char(Integer):
+    buildstr = "s"
+    interface = 'small_integer'
+
+    def to_python_object(self, name = None, result = 'py_result', **kw):
+        ## We really want to return a string here
+        return """{ char *str_%(name)s = &%(name)s;
+    PyErr_Clear();
+    %(result)s = PyString_FromStringAndSize(str_%(name)s, 1);
+if(!%(result)s) goto error;
+}
+""" % dict(result=result, name = name or self.name)
+
+    def definition(self, default = '"\\x0"', **kw):
+        ## Shut up unused warnings
+        return "char %s __attribute__((unused))=0;\nchar *str_%s __attribute__((unused)) = %s;\n" % (
+            self.name,self.name, default)
+
+    def byref(self):
+        return "&str_%s" % self.name
+
+    def pre_call(self, method):
+        method.error_set = True
+        return """
+if(strlen(str_%(name)s)!=1) {
+  PyErr_Format(PyExc_RuntimeError,
+          "You must only provide a single character for arg %(name)r");
+  goto error;
+}
+
+%(name)s = str_%(name)s[0];
+""" % dict(name = self.name)
+
+class StringOut(String):
+    sense = 'OUT'
+
+class IntegerOut(Integer):
+    sense = 'OUT_DONE'
+    buildstr = ''
+
+    def python_name(self):
+        return None
+
+    def byref(self):
+        return ''
+
+    def call_arg(self):
+        return "&%s" % self.name
+
+class Integer32Out(Integer32):
+    sense = 'OUT_DONE'
+    buildstr = ''
+
+    def python_name(self):
+        return None
+
+    def byref(self):
+        return ''
+
+    def call_arg(self):
+        return "&%s" % self.name
+
+class Char_and_Length_OUT(Char_and_Length):
+    sense = 'OUT_DONE'
+    buildstr = 'l'
+
+    def definition(self, default = 0, **kw):
+        return "char *%s=NULL; Py_ssize_t %s=%s;\n" % (
+            self.name,
+            self.length, default) + "PyObject *tmp_%s;\n" % self.name
+
+    def python_name(self):
+        return self.length
+
+    def byref(self):
+        return "&%s" % self.length
+
+    def pre_call(self, method):
+        return """PyErr_Clear();
+tmp_%s = PyString_FromStringAndSize(NULL, %s);
+if(!tmp_%s) goto error;
+PyString_AsStringAndSize(tmp_%s, &%s, (Py_ssize_t *)&%s);
+""" % (self.name, self.length, self.name, self.name, self.name, self.length)
+
+    def to_python_object(self, name=None, result='py_result', sense='in', **kw):
+        name = name or self.name
+        if 'results' in kw:
+            kw['results'].pop(0)
+
+        if sense=='proxied':
+            return "py_%s = PyLong_FromLong(%s);\n" % (self.name, self.length)
+
+        return  """if(func_return > %(length)s) {
+  func_return = 0;
+}
+
+_PyString_Resize(&tmp_%(name)s, func_return); \n%(result)s = tmp_%(name)s;\n""" % \
+            dict(name= name, result= result, length=self.length)
+
+
+    def python_proxy_post_call(self, result='py_result'):
+        return """
+{
+    char *tmp_buff; int tmp_len;
+    if(-1==PyString_AsStringAndSize(%(result)s, &tmp_buff, &tmp_len)) goto error;
+
+    memcpy(%(name)s,tmp_buff, tmp_len);
+    Py_DECREF(%(result)s);
+    %(result)s = PyLong_FromLong(tmp_len);
+}
+""" % dict(result = result, name=self.name)
+
+class TDB_DATA_P(Char_and_Length_OUT):
+    bare_type = "TDB_DATA"
+
+    def __init__(self, name, type):
+        Type.__init__(self, name, type)
+
+    def definition(self, default=None, **kw):
+        return Type.definition(self)
+
+    def byref(self):
+        return "%s.dptr, &%s.dsize" % (self.name, self.name)
+
+    def pre_call(self, method):
+        return ''
+
+    def call_arg(self):
+        return Type.call_arg(self)
+
+    def to_python_object(self, name=None,result='py_result', **kw):
+        name = name or self.name
+        return "PyErr_Clear();"\
+            "%s = PyString_FromStringAndSize((char *)%s->dptr, %s->dsize);"\
+            "\ntalloc_free(%s);" % (result,
+                                    name, name, name)
+
+    def from_python_object(self, source, destination, method, **kw):
+        method.error_set = True
+        return """
+%(destination)s = talloc(self, %(bare_type)s);
+{ Py_ssize_t tmp; char *buf;
+
+  PyErr_Clear();
+  if(-1==PyString_AsStringAndSize(%(source)s, &buf, &tmp)) {
+  goto error;
+}
+
+  // Take a copy of the python string
+  %(destination)s->dptr = talloc_memdup(%(destination)s, buf, tmp);
+  %(destination)s->dsize = tmp;
+}
+// We no longer need the python object
+Py_DECREF(%(source)s);
+""" % dict(source = source, destination = destination, 
+           bare_type = self.bare_type)
+
+class TDB_DATA(TDB_DATA_P):
+    def to_python_object(self, name = None, result='py_result', **kw):
+        name = name or self.name
+
+        return "PyErr_Clear();\n"\
+            "%s = PyString_FromStringAndSize((char *)%s.dptr, %s.dsize);\n" % (
+            result,
+            name, name)
+
+class Void(Type):
+    buildstr = ''
+    error_value = "return;"
+    original_type = ''
+
+    def __init__(self, *args):
+        Type.__init__(self, None, 'void *')
+
+    def definition(self, default = None, **kw):
+        return ''
+
+    def to_python_object(self, name=None, result = 'py_result', **kw):
+        return "Py_INCREF(Py_None); py_result = Py_None;\n"
+
+    def call_arg(self):
+        return "NULL"
+
+    def byref(self):
+        return None
+
+    def assign(self, call, method, target=None):
+        ## We dont assign the result to anything
+        return "Py_BEGIN_ALLOW_THREADS\n%s;\nPy_END_ALLOW_THREADS\n" % call
+
+    def return_value(self, value):
+        return "return;"
+
+class StringArray(String):
+    interface = 'array'
+    buildstr = 'O'
+
+    def definition(self, default = '""', **kw):
+        return "char **%s=NULL; PyObject *py_%s=NULL;\n" % (
+            self.name, self.name)
+
+    def byref(self):
+        return "&py_%s" % (self.name)
+
+    def from_python_object(self, source, destination, method, context='NULL'):
+        method.error_set = True
+        return """{
+Py_ssize_t i,size=0;
+
+if(%(source)s) {
+   if(!PySequence_Check(%(source)s)) {
+     PyErr_Format(PyExc_ValueError, "%(destination)s must be a sequence");
+     goto error;
+   }
+
+   size = PySequence_Size(%(source)s);
+}
+
+%(destination)s = talloc_zero_array(NULL, char *, size + 1);
+
+for(i=0; i<size;i++) {
+ PyObject *tmp = PySequence_GetItem(%(source)s, i);
+ if(!tmp) goto error;
+ %(destination)s[i] = PyString_AsString(tmp);
+ if(!%(destination)s[i]) {
+   Py_DECREF(tmp);
+   goto error;
+ }
+ Py_DECREF(tmp);
+}
+
+}""" % dict(source = source, destination = destination, context = context)
+
+    def pre_call(self, method):
+        return self.from_python_object("py_%s" % self.name, self.name, method)
+
+    def error_condition(self):
+        return """if(%s) talloc_free(%s);\n""" % (self.name, self.name)
+
+class Wrapper(Type):
+    """ This class represents a wrapped C type """
+    sense = 'IN'
+    error_value = "return NULL;"
+
+    def from_python_object(self, source, destination, method, **kw):
+        return """
+/* First check that the returned value is in fact a Wrapper */
+if(!type_check(%(source)s, &%(type)s_Type)) {
+  PyErr_Format(PyExc_RuntimeError, "function must return an %(type)s instance");
+  goto error;
+}
+
+%(destination)s = ((Gen_wrapper *)%(source)s)->base;
+""" % dict(source = source, destination = destination, type = self.type)
+
+    def to_python_object(self, **kw):
+        return ''
+
+    def returned_python_definition(self, default = 'NULL', sense='in', **kw):
+        return "%s %s;\n" % (self.type, self.name)
+
+    def definition(self, default = 'NULL', sense='in', **kw):
+        result = "Gen_wrapper *%s __attribute__((unused)) = %s;\n" % (self.name, default)
+        if sense == 'in' and not 'OUT' in self.attributes:
+            result += " %s __attribute__((unused)) call_%s;\n" % (self.type, self.name)
+
+        return result
+
+    def call_arg(self):
+        return "call_%s" % self.name
+
+    def pre_call(self, method):
+        if 'OUT' in self.attributes or self.sense == 'OUT':
+            return ''
+
+        return """
+if(!%(name)s || (PyObject *)%(name)s==Py_None) {
+   call_%(name)s = NULL;
+} else if(!type_check((PyObject *)%(name)s,&%(type)s_Type)) {
+     PyErr_Format(PyExc_RuntimeError, "%(name)s must be derived from type %(type)s");
+     goto error;
+} else {
+   call_%(name)s = %(name)s->base;
+}\n""" % self.__dict__
+
+    def assign(self, call, method, target=None):
+        method.error_set = True;
+        args = dict(name = target or self.name, call = call, type = self.type)
+
+        result = """{
+       Object returned_object;
+
+       ClearError();
+
+       Py_BEGIN_ALLOW_THREADS
+       returned_object = (Object)%(call)s;
+       Py_END_ALLOW_THREADS
+
+       CHECK_ERROR;
+""" % args
+
+        ## Is NULL an acceptable return type? In some python code NULL
+        ## can be returned (e.g. in iterators) but usually it should
+        ## be converted to None.
+        if "NULL_OK" in self.attributes:
+            result += """if(returned_object == NULL) {
+     %(name)s = NULL; """ % args
+        else:
+            result += """
+       // A NULL return without errors means we return None
+       if(!returned_object) {
+         %(name)s = (Gen_wrapper *)Py_None;
+         Py_INCREF(Py_None);
+""" % args
+
+        result += """
+       } else {
+         //printf("%%s: Wrapping %%s@%%p\\n", __FUNCTION__, NAMEOF(returned_object), returned_object);
+         %(name)s = new_class_wrapper(returned_object);
+         if(!%(name)s) goto error;
+""" % args
+
+        if "BORROWED" in self.attributes:
+            result += "           talloc_reference(%(name)s->ctx, %(name)s->base);\n" % args
+
+        result += """       }
+    }
+"""
+        return result
+
+    def to_python_object(self, name=None, result = 'py_result', sense='in', **kw):
+        name = name or self.name
+        args = dict(result=result,
+                    name = name)
+
+        if sense=='proxied':
+            return "%(result)s = (PyObject *)new_class_wrapper((Object)%(name)s);\n" % args
+
+        return "%(result)s = (PyObject *)%(name)s;\n" % args
+
+class PointerWrapper(Wrapper):
+    """ A pointer to a wrapped class """
+    def __init__(self, name, type):
+        type = type.split()[0]
+        Wrapper.__init__(self,name, type)
+
+    def definition(self, default = 'NULL', sense='in', **kw):
+        result = "Gen_wrapper *%s = %s;" % (self.name, default)
+        if sense == 'in' and not 'OUT' in self.attributes:
+            result += " %s *call_%s;\n" % (self.type, self.name)
+
+        return result
+
+    def pre_call(self, method):
+        if 'OUT' in self.attributes or self.sense == 'OUT':
+            return ''
+
+        return """
+if(!%(name)s || (PyObject *)%(name)s==Py_None) {
+   call_%(name)s = NULL;
+} else if(!type_check((PyObject *)%(name)s,&%(type)s_Type)) {
+     PyErr_Format(PyExc_RuntimeError, "%(name)s must be derived from type %(type)s");
+     goto error;
+} else {
+   call_%(name)s = (%(type)s *)&%(name)s->base;
+}\n""" % self.__dict__
+
+class StructWrapper(Wrapper):
+    """ A wrapper for struct classes """
+    def assign(self, call, method, target = None):
+        args = dict(name = target or self.name, call = call, type = self.type)
+        result = """
+PyErr_Clear();
+%(name)s = (Gen_wrapper *)PyObject_New(py%(type)s, &%(type)s_Type);
+%(name)s->ctx = talloc_size(NULL, 1);
+%(name)s->base = %(call)s;
+""" % args
+
+        if "NULL_OK" in self.attributes:
+            result += "if(!%(name)s->base) { Py_DECREF(%(name)s); return NULL; }" % args
+
+        result += """
+// A NULL object gets translated to a None
+ if(!%(name)s->base) {
+   Py_DECREF(%(name)s);
+   Py_INCREF(Py_None);
+   %(name)s = (Gen_wrapper *)Py_None;
+ } else {
+""" % args
+
+        if "FOREIGN" in self.attributes:
+            result += '// Not taking references to foreign memory\n'
+        elif "BORROWED" in self.attributes:
+            result += "talloc_reference(%(name)s->ctx, %(name)s->base);\n" % args
+        else:
+            result += "talloc_steal(%(name)s->ctx, %(name)s->base);\n" % args
+
+        result += "}\n"
+
+        return result
+
+    def byref(self):
+        return "&%s" % self.name
+
+    def definition(self, default = 'NULL', sense='in', **kw):
+        result = "Gen_wrapper *%s = %s;" % (self.name, default)
+        if sense == 'in' and not 'OUT' in self.attributes:
+            result += " %s *call_%s;\n" % (self.type, self.name)
+
+        return result;
+
+class PointerStructWrapper(StructWrapper):
+    def __init__(self, name, type):
+        type = type.split()[0]
+        Wrapper.__init__(self,name, type)
+
+class Timeval(Type):
+    """ handle struct timeval values """
+    interface = 'numeric'
+    buildstr = 'f'
+
+    def definition(self, default = None, **kw):
+        return "float %(name)s_flt; struct timeval %(name)s;\n" % self.__dict__
+
+    def byref(self):
+        return "&%s_flt" % self.name
+
+    def pre_call(self, method):
+        return "%(name)s.tv_sec = (int)%(name)s_flt; %(name)s.tv_usec = (%(name)s_flt - %(name)s.tv_sec) * 1e6;\n" % self.__dict__
+
+    def to_python_object(self, name=None, result = 'py_result', **kw):
+        name = name or self.name
+        return """%(name)s_flt = (double)(%(name)s.tv_sec) + %(name)s.tv_usec;
+%(result)s = PyFloat_FromDouble(%(name)s_flt);
+""" % dict(name = name, result=result)
+
+class PyObject(Type):
+    """ Accept an opaque python object """
+    interface = 'opaque'
+    buildstr = 'O'
+    def definition(self, default = 'NULL', **kw):
+        self.default = default
+        return 'PyObject *%(name)s = %(default)s;\n' % self.__dict__
+
+    def byref(self):
+        return "&%s" % self.name
+
+type_dispatcher = {
+    "IN char *": String,
+    "IN unsigned char *": String,
+    "unsigned char *": String,
+    "char *": String,
+    "ZString": ZString,
+
+    "OUT char *": StringOut,
+    "OUT unsigned char *": StringOut,
+    "unsigned int": Integer,
+    'int': Integer,
+    'OUT uint64_t *': IntegerOut,
+    'OUT uint32_t *': Integer32Out,
+    'char': Char,
+    'void': Void,
+    'void *': Void,
+
+    'TDB_DATA *': TDB_DATA_P,
+    'TDB_DATA': TDB_DATA,
+    'uint64_t': Integer,
+    'uint32_t': Integer32,
+    'time_t': Integer32,
+    'uint16_t': Integer,
+    'uint8_t': Integer,
+    'int64_t': Integer,
+    'ssize_t': Integer,
+    'size_t': Integer,
+    'unsigned long int': Integer,
+    'struct timeval': Timeval,
+    'char **': StringArray,
+
+    'PyObject *': PyObject,
+    }
+
+method_attributes = ['BORROWED', 'DESTRUCTOR','IGNORE']
+
+def dispatch(name, type):
+    if not type: return Void()
+
+    m = re.match("struct ([a-zA-Z0-9]+)_t *", type)
+    if m:
+        type = m.group(1)
+
+    type_components = type.split()
+    attributes = set()
+
+    if type_components[0] in method_attributes:
+        attributes.add(type_components.pop(0))
+
+    type = " ".join(type_components)
+    result = type_dispatcher[type](name, type)
+
+    result.attributes = attributes
+
+    return result
+
+class ResultException:
+    value = 0
+    exception = "PyExc_IOError"
+
+    def __init__(self, check, exception, message):
+        self.check = check
+        self.exception = exception
+        self.message = message
+
+    def write(self, out):
+        out.write("\n//Handle exceptions\n")
+        out.write("if(%s) {\n    PyErr_Format(PyExc_%s, %s);\n  goto error; \n}\n\n" % (
+                self.check, self.exception, self.message))
+
+class Method:
+    default_re = re.compile("DEFAULT\(([A-Z_a-z0-9]+)\) =(.+)")
+    exception_re = re.compile("RAISES\(([^,]+),\s*([^\)]+)\) =(.+)")
+    typedefed_re = re.compile(r"struct (.+)_t \*")
+
+    def __init__(self, class_name, base_class_name, method_name, args, return_type,
+                 myclass = None):
+        self.name = method_name
+        ## myclass needs to be a class generator
+        if not isinstance(myclass, ClassGenerator): raise RuntimeError("myclass must be a class generator")
+
+        self.myclass = myclass
+        self.docstring = ''
+        self.defaults = {}
+        self.exception = None
+        self.error_set = False
+        self.class_name = class_name
+        self.base_class_name = base_class_name
+        self.args = []
+        self.definition_class_name = class_name
+        for type,name in args:
+            self.add_arg(type, name)
+
+        try:
+            self.return_type = dispatch('func_return', return_type)
+            self.return_type.attributes.add("OUT")
+            self.return_type.original_type = return_type
+        except KeyError:
+            ## Is it a wrapped type?
+            if return_type:
+                log("Unable to handle return type %s.%s %s" % (self.class_name, self.name, return_type))
+                #pdb.set_trace()
+            self.return_type = Void()
+
+    def clone(self, new_class_name):
+        self.find_optional_vars()
+
+        result = self.__class__(new_class_name, self.base_class_name, self.name,
+                                [], 'void *',
+                                myclass = self.myclass)
+        result.args = self.args
+        result.return_type = self.return_type
+        result.definition_class_name = self.definition_class_name
+        result.defaults = self.defaults
+        result.exception = self.exception
+
+        return result
+
+    def find_optional_vars(self):
+        for line in self.docstring.splitlines():
+            m =self.default_re.search(line)
+            if m:
+                name = m.group(1)
+                value = m.group(2)
+                log("Setting default value for %s of %s" % (m.group(1),
+                                                            m.group(2)))
+                self.defaults[name] = value
+
+            m =self.exception_re.search(line)
+            if m:
+                self.exception = ResultException(m.group(1), m.group(2), m.group(3))
+
+    def write_local_vars(self, out):
+        self.find_optional_vars()
+
+        ## We do it in two passes - first mandatory then optional
+        kwlist = """static char *kwlist[] = {"""
+        ## Mandatory
+        for type in self.args:
+            python_name = type.python_name()
+            if python_name and python_name not in self.defaults:
+                kwlist += '"%s",' % python_name
+
+        for type in self.args:
+            python_name = type.python_name()
+            if python_name and python_name in self.defaults:
+                kwlist += '"%s",' % python_name
+
+        kwlist += ' NULL};\n'
+
+        for type in self.args:
+            python_name = type.python_name()
+            try:
+                out.write(type.definition(default = self.defaults[python_name]))
+            except KeyError:
+                out.write(type.definition())
+
+        ## Make up the format string for the parse args in two pases
+        parse_line = ''
+        for type in self.args:
+            python_name = type.python_name()
+            if type.buildstr and python_name not in self.defaults:
+                parse_line += type.buildstr
+
+        parse_line += '|'
+        for type in self.args:
+            python_name = type.python_name()
+            if type.buildstr and python_name in self.defaults:
+                parse_line += type.buildstr
+
+        if parse_line != '|':
+            ## Now parse the args from python objects
+            out.write(kwlist)
+            out.write("\nif(!PyArg_ParseTupleAndKeywords(args, kwds, \"%s\", kwlist, " % parse_line)
+            tmp = []
+            for type in self.args:
+                ref = type.byref()
+                if ref:
+                    tmp.append(ref)
+
+            out.write(",".join(tmp))
+            self.error_set = True
+            out.write("))\n goto error;\n\n")
+
+    def error_condition(self):
+        result = ""
+        if "DESTRUCTOR" in self.return_type.attributes:
+            result += "talloc_free(self->ctx); self->base = NULL;\n"
+
+        return result +"return NULL;\n";
+
+    def write_definition(self, out):
+        args = dict(method = self.name, class_name = self.class_name)
+        out.write("\n/********************************************************\nAutogenerated wrapper for function:\n")
+        out.write(self.comment())
+        out.write("********************************************************/\n")
+
+        self._prototype(out)
+        out.write("""{
+       PyObject *returned_result, *py_result;
+""" % args)
+
+        out.write(self.return_type.definition())
+
+        self.write_local_vars( out);
+
+        out.write("""// Make sure that we have something valid to wrap
+if(!self->base) return PyErr_Format(PyExc_RuntimeError, "%(class_name)s object no longer valid");
+""" % args)
+
+        ## Precall preparations
+        out.write("// Precall preparations\n")
+        out.write(self.return_type.pre_call(self))
+        for type in self.args:
+            out.write(type.pre_call(self))
+
+        out.write("""// Check the function is implemented
+  {  void *method = ((%(def_class_name)s)self->base)->%(method)s;
+     if(!method || (void *)unimplemented == (void *)method) {
+         PyErr_Format(PyExc_RuntimeError, "%(class_name)s.%(method)s is not implemented");
+         goto error;
+     }
+  }
+""" % dict(def_class_name = self.definition_class_name, method=self.name,
+           class_name = self.class_name))
+
+        out.write("\n// Make the call\n ClearError();")
+        call = "((%s)self->base)->%s(((%s)self->base)" % (self.definition_class_name, self.name, self.definition_class_name)
+        tmp = ''
+        for type in self.args:
+            tmp += ", " + type.call_arg()
+
+        call += "%s)" % tmp
+
+        ## Now call the wrapped function
+        out.write(self.return_type.assign(call, self))
+        if self.exception:
+            self.exception.write(out)
+
+        self.error_set = True
+
+        out.write("\n// Postcall preparations\n")
+        ## Postcall preparations
+        out.write(self.return_type.post_call(self))
+        for type in self.args:
+            out.write(type.post_call(self))
+
+        ## Now assemble the results
+        results = [self.return_type.to_python_object()]
+        for type in self.args:
+            if type.sense == 'OUT_DONE':
+                results.append(type.to_python_object(results = results))
+
+        ## If all the results are returned by reference we dont need
+        ## to prepend the void return value at all.
+        if isinstance(self.return_type, Void) and len(results)>1:
+            results.pop(0)
+
+        out.write("\n// prepare results\n")
+        ## Make a tuple of results and pass them back
+        if len(results)>1:
+            out.write("returned_result = PyList_New(0);\n")
+            for result in results:
+                out.write(result)
+                out.write("PyList_Append(returned_result, py_result); Py_DECREF(py_result);\n");
+            out.write("return returned_result;\n")
+        else:
+            out.write(results[0])
+            ## This useless code removes compiler warnings
+            out.write("returned_result = py_result;\nreturn returned_result;\n");
+
+        ## Write the error part of the function
+        if self.error_set:
+            out.write("\n// error conditions:\n")
+            out.write("error:\n    " + self.error_condition());
+
+        out.write("\n}\n\n")
+
+    def add_arg(self, type, name):
+        try:
+            t = type_dispatcher[type](name, type)
+        except KeyError:
+            ## Sometimes types must be typedefed in advance
+            try:
+                m = self.typedefed_re.match(type)
+                type = m.group(1)
+                log( "Trying %s for %s" % (type, m.group(0)))
+                t = type_dispatcher[type](name, type)
+            except (KeyError, AttributeError):
+                log( "Unable to handle type %s.%s %s" % (self.class_name, self.name, type))
+                return
+
+        ## Here we collapse char * + int type interfaces into a
+        ## coherent string like interface.
+        try:
+            previous = self.args[-1]
+            if t.interface == 'integer' and \
+                    previous.interface == 'string':
+
+                ## We make a distinction between IN variables and OUT
+                ## variables
+                if previous.sense == 'OUT':
+                    cls = Char_and_Length_OUT
+                else:
+                    cls = Char_and_Length
+
+
+                cls = cls(
+                    previous.name,
+                    previous.type,
+                    name, type)
+
+                self.args[-1] = cls
+
+                return
+        except IndexError:
+            pass
+
+        self.args.append(t)
+
+    def comment(self):
+        result = self.return_type.original_type+" "+self.class_name+"."+self.name+"("
+        args = []
+        for type in self.args:
+            args.append( type.comment())
+
+        result += ",".join(args) + ");\n"
+
+        return result
+
+    def prototype(self, out):
+        self._prototype(out)
+        out.write(";\n")
+
+    def _prototype(self, out):
+        out.write("""static PyObject *py%(class_name)s_%(method)s(py%(class_name)s *self, PyObject *args, PyObject *kwds) """ % dict(method = self.name, class_name = self.class_name))
+
+    def __str__(self):
+        result = "def %s %s(%s):" % (
+            self.return_type,
+            self.name, ' , '.join([a.__str__() for a in self.args]))
+        return result
+
+    def PyMethodDef(self, out):
+        docstring = self.comment() + "\n\n" + self.docstring
+        out.write('     {"%s",(PyCFunction)py%s_%s, METH_VARARGS|METH_KEYWORDS, "%s"},\n' % (
+                self.name,
+                self.class_name,
+                self.name, escape_for_string(docstring)))
+
+class IteratorMethod(Method):
+    """ A Method which implements an iterator """
+    def __init__(self, *args, **kw):
+        Method.__init__(self, *args, **kw)
+
+        ## Tell the return type that a NULL python return is ok
+        self.return_type.attributes.add("NULL_OK")
+
+    def _prototype(self, out):
+        out.write("""static PyObject *py%(class_name)s_%(method)s(py%(class_name)s *self)""" % dict(method = self.name, class_name = self.class_name))
+
+    def __str__(self):
+        result = "Iterator returning %s." % (
+            self.return_type)
+        return result
+
+    def PyMethodDef(self, out):
+        ## This method should not go in the method table as its linked
+        ## in directly
+        pass
+
+class SelfIteratorMethod(IteratorMethod):
+    def write_definition(self, out):
+        args = dict(method = self.name, class_name = self.class_name)
+        out.write("\n/********************************************************\nAutogenerated wrapper for function:\n")
+        out.write(self.comment())
+        out.write("********************************************************/\n")
+
+        self._prototype(out)
+        out.write("""{
+          ((%(class_name)s)self->base)->%(method)s((%(class_name)s)self->base);
+          return PyObject_SelfIter((PyObject *)self);
+}
+""" % args)
+
+class ConstructorMethod(Method):
+    ## Python constructors are a bit different than regular methods
+    def _prototype(self, out):
+        out.write("""
+static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds)
+""" % dict(method = self.name, class_name = self.class_name))
+
+    def write_destructor(self, out):
+        ## We make sure that we unlink exactly the reference we need
+        ## (the object will persist if it has some other
+        ## references). Note that if we just used talloc_free here it
+        ## will remove some random reference which may not actually be
+        ## the reference we own (which is NULL).
+        free = """
+    if(self->base) {
+        //printf("Unlinking %s@%p\\n", NAMEOF(self->base), self->base);
+        talloc_free(self->ctx);
+        self->base=NULL;
+    }
+"""
+        out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+%(free)s
+ PyObject_Del(self);
+}\n
+""" % dict(class_name = self.class_name, free=free))
+
+    def error_condition(self):
+        return "return -1;";
+
+    def write_definition(self, out):
+        self._prototype(out)
+        out.write("""{\n""")
+        self.write_local_vars(out)
+
+        ## Precall preparations
+        for type in self.args:
+            out.write(type.pre_call(self))
+
+        ## Now call the wrapped function
+        out.write("\nself->ctx = talloc_strdup(NULL, \"%s\");" % self.class_name)
+        out.write("\n ClearError();\nPy_BEGIN_ALLOW_THREADS\nself->base = CONSTRUCT(%s, %s, %s, self->ctx" % (
+                self.class_name,
+                self.definition_class_name,
+                self.name))
+        tmp = ''
+        for type in self.args:
+            tmp += ", " + type.call_arg()
+
+        self.error_set = True
+        out.write("""%s);\nPy_END_ALLOW_THREADS\n
+       if(!CheckError(EZero)) {
+         char *buffer;
+         PyObject *exception = resolve_exception(&buffer);
+
+         PyErr_Format(exception,
+                    "%%s", buffer);
+         ClearError();
+         goto error;
+  } else if(!self->base) {
+    PyErr_Format(PyExc_IOError, "Unable to construct class %s");
+    goto error;
+  }
+""" % (tmp, self.class_name))
+
+        out.write("  return 0;\n");
+
+        ## Write the error part of the function
+        if self.error_set:
+            out.write("error:\n    " + self.error_condition());
+
+        out.write("\n}\n\n")
+
+class GetattrMethod(Method):
+    def __init__(self, class_name, base_class_name, myclass):
+        self.base_class_name = base_class_name
+        self._attributes = []
+        self.error_set = True
+        self.return_type = Void()
+        self.myclass = myclass
+        self.rename_class_name(class_name)
+
+    def add_attribute(self, attr):
+        if attr.name:
+            self._attributes.append([self.class_name, attr])
+
+    def rename_class_name(self, new_name):
+        """ This allows us to rename the class_name at a later stage.
+        Required for late initialization of Structs whose name is not
+        know until much later on.
+        """
+        self.class_name = new_name
+        self.name = "py%s_getattr" % new_name
+        for x in self._attributes:
+            x[0] = new_name
+
+    def get_attributes(self):
+        for class_name, attr in self._attributes:
+            try:
+                if not self.myclass.module.classes[attr.type].active:
+                    continue
+            except KeyError: pass
+
+            yield class_name, attr
+
+    def __str__(self):
+        result = ""
+        for class_name, attr in self.get_attributes():
+            result += "    %s\n" % attr.__str__()
+
+        return result
+
+
+    def clone(self, class_name):
+        result = self.__class__(class_name, self.base_class_name, self.myclass)
+        result._attributes = self._attributes[:]
+
+        return result
+
+    def prototype(self, out):
+        if self.name:
+            out.write("""
+static PyObject *%(name)s(py%(class_name)s *self, PyObject *name);
+""" % self.__dict__)
+
+    def built_ins(self, out):
+        """ check for some built in attributes we need to support """
+        out.write("""  if(!strcmp(name, "__members__")) {
+     PyObject *result = PyList_New(0);
+     PyObject *tmp;
+     PyMethodDef *i;
+
+     if(!result) goto error;
+""")
+        ## Add attributes
+        for class_name, attr in self.get_attributes():
+            out.write(""" tmp = PyString_FromString("%(name)s");
+    PyList_Append(result, tmp); Py_DECREF(tmp);
+""" % dict(name = attr.name))
+
+        ## Add methods
+        out.write("""
+
+    for(i=%s_methods; i->ml_name; i++) {
+     tmp = PyString_FromString(i->ml_name);
+    PyList_Append(result, tmp); Py_DECREF(tmp);
+    }""" % self.class_name)
+
+        out.write("""
+     return result; 
+   }\n""")
+
+    def write_definition(self, out):
+        if not self.name: return
+        out.write("""
+static PyObject *py%(class_name)s_getattr(py%(class_name)s *self, PyObject *pyname) {
+  char *name;
+  // Try to hand it off to the python native handler first
+  PyObject *result = PyObject_GenericGetAttr((PyObject*)self, pyname);
+
+  if(result) return result;
+
+  PyErr_Clear();
+  // No - nothing interesting was found by python
+  name = PyString_AsString(pyname);
+
+  if(!self->base) return PyErr_Format(PyExc_RuntimeError, "Wrapped object (%(class_name)s.%(name)s) no longer valid");
+  if(!name) return NULL;
+""" % self.__dict__)
+
+        self.built_ins(out)
+
+        for class_name, attr in self.get_attributes():
+            ## what we want to assign
+            if self.base_class_name:
+                call = "(((%s)self->base)->%s)" % (class_name, attr.name)
+            else:
+                call = "(self->base->%s)" % (attr.name)
+
+            out.write("""
+if(!strcmp(name, "%(name)s")) {
+    PyObject *py_result;
+    %(python_def)s
+
+    %(python_assign)s
+    %(python_obj)s
+    return py_result;
+}""" % dict(name = attr.name, python_obj = attr.to_python_object(),
+             python_assign = attr.assign(call, self),
+             python_def = attr.definition(sense='out')))
+
+        out.write("""
+
+  return PyObject_GenericGetAttr((PyObject *)self, pyname);
+""" % self.__dict__)
+
+        ## Write the error part of the function
+        if self.error_set:
+            out.write("error:\n" + self.error_condition());
+
+        out.write("}\n\n")
+
+class ProxiedGetattr(GetattrMethod):
+    def built_ins(self,out):
+        out.write("""  if(!strcmp(name, "__members__")) {
+     PyObject *result;
+     PyObject *tmp;
+     PyMethodDef *i;
+
+     PyErr_Clear();
+     // Get the list of members from our proxied object
+     result  = PyObject_GetAttrString(self->base->proxied, name);
+     if(!result) goto error;
+""")
+        ## Add attributes
+        for class_name, attr in self.get_attributes():
+            out.write(""" tmp = PyString_FromString("%(name)s");
+    PyList_Append(result, tmp); Py_DECREF(tmp);
+""" % dict(name = attr.name))
+
+        ## Add methods
+        out.write("""
+
+    for(i=%s_methods; i->ml_name; i++) {
+     tmp = PyString_FromString(i->ml_name);
+    PyList_Append(result, tmp); Py_DECREF(tmp);
+    } """ % self.class_name)
+
+        out.write("""
+     return result;
+   }\n""")
+
+        out.write(""" /** Just try to get the attribute from our proxied object */  {
+   PyObject *result = PyObject_GetAttrString(self->base->proxied, name);
+   if(result) return result;
+} """)
+
+class ProxiedMethod(Method):
+    def __init__(self, method, myclass):
+        self.name = method.name
+        self.method = method
+        self.myclass = myclass
+        self.class_name = method.class_name
+        self.base_class_name = method.base_class_name
+        self.args = method.args
+        self.definition_class_name = method.definition_class_name
+        self.return_type = method.return_type
+        self.docstring = "Proxy for %s" % self.name
+        self.defaults = {}
+        self.exception = None
+        self.error_set = False
+
+    def get_name(self):
+        return "py%(class_name)s_%(name)s" % dict(class_name =self.myclass.class_name,
+                                                  name = self.name)
+
+    def _prototype(self, out):
+        out.write("""
+static %(return_type)s %(name)s(%(definition_class_name)s self""" % dict(
+                return_type = self.return_type.original_type,
+                class_name = self.myclass.class_name,
+                method = self.name,
+                name = self.get_name(),
+                definition_class_name = self.definition_class_name))
+
+        for arg in self.args:
+            out.write(", %s" % (arg.comment()))
+
+        out.write(")")
+
+    def prototype(self, out):
+        self._prototype(out)
+        out.write(";\n")
+
+    def write_definition(self, out):
+        self._prototype(out)
+        self._write_definition(out)
+
+    def _write_definition(self, out):
+        ## We need to grab the GIL before we do anything
+        out.write("""{
+      //Grab the GIL so we can do python stuff
+      PyGILState_STATE gstate;
+      gstate = PyGILState_Ensure();
+      """)
+
+        out.write("{\nPyObject *py_result=NULL;\n")
+        out.write('PyObject *method_name = PyString_FromString("%s");\n' % self.name)
+        out.write(self.return_type.returned_python_definition())
+
+        for arg in self.args:
+            out.write("PyObject *py_%s=NULL;\n" % arg.name)
+
+        out.write("\n//Obtain python objects for all the args:\n")
+        for arg in self.args:
+            out.write(arg.to_python_object(result = "py_%s" % arg.name,
+                                           sense='proxied', BORROWED=True))
+
+        out.write('if(!((%s)self)->proxied) {\n RaiseError(ERuntimeError, "No proxied object in %s"); goto error;\n}\n' % (self.myclass.class_name, self.myclass.class_name))
+
+        out.write("\n//Now call the method\n")
+        out.write("""PyErr_Clear();
+py_result = PyObject_CallMethodObjArgs(((%s)self)->proxied,method_name,""" % self.myclass.class_name)
+        for arg in self.args:
+            out.write("py_%s," % arg.name)
+
+        ## Sentinal
+        self.error_set = True
+        out.write("""NULL);
+
+/** Check for python errors */
+if(PyErr_Occurred()) {
+   PyObject *exception_t, *exception, *tb;
+   PyObject *str;
+   char *str_c;
+   char *error_str;
+   enum _error_type *error_type = aff4_get_current_error(&error_str);
+
+   // Fetch the exception state and convert it to a string:
+   PyErr_Fetch(&exception_t, &exception, &tb);
+
+   str = PyObject_Repr(exception);
+   str_c = PyString_AsString(str);
+
+   if(str_c) {
+      strncpy(error_str, str_c, BUFF_SIZE-1);
+      error_str[BUFF_SIZE-1]=0;
+      *error_type = ERuntimeError;
+   }
+   Py_DECREF(str);
+   goto error;
+}
+
+""");
+
+        for arg in self.args:
+            out.write(arg.python_proxy_post_call())
+
+        ## Now convert the python value back to a value
+        out.write(self.return_type.from_python_object('py_result',self.return_type.name, self, context = "self"))
+
+        out.write("if(py_result) { Py_DECREF(py_result);}\nPy_DECREF(method_name);\n\n");
+        out.write("PyGILState_Release(gstate);\n")
+
+        ## Decref all our python objects:
+        for arg in self.args:
+            out.write("if(py_%s) { Py_DECREF(py_%s);}\n" %( arg.name, arg.name))
+
+        out.write(self.return_type.return_value('func_return'))
+        if self.error_set:
+            out.write("\nerror:\n")
+            out.write("if(py_result) { Py_DECREF(py_result);}\nPy_DECREF(method_name);\n\n");
+            ## Decref all our python objects:
+            for arg in self.args:
+                out.write("if(py_%s) { Py_DECREF(py_%s);}\n" % (arg.name, arg.name))
+
+            out.write("PyGILState_Release(gstate);\n %s;\n" % self.error_condition())
+
+        out.write("   }\n}\n")
+
+    def error_condition(self):
+        return self.return_type.error_value
+
+class StructConstructor(ConstructorMethod):
+    """ A constructor for struct wrappers - basically just allocate
+    memory for the struct.
+    """
+    def write_definition(self, out):
+        out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {\n""" % dict(method = self.name, class_name = self.class_name))
+        out.write("\nself->ctx = talloc_strdup(NULL, \"%s\");" % self.class_name)
+        out.write("\nself->base = talloc(self->ctx, %s);\n" % self.class_name)
+        out.write("  return 0;\n}\n\n")
+
+    def write_destructor(self, out):
+        out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+   talloc_free(self->ctx);
+}\n
+""" % dict(class_name = self.class_name))
+
+class ProxyConstructor(ConstructorMethod):
+    def write_destructor(self, out):
+        out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+    if(self->base) {
+        // Release the proxied object
+        //Py_DECREF(self->base->proxied);
+        talloc_free(self->ctx);
+        self->base = NULL;
+    }
+}\n
+
+static int %(class_name)s_destructor(void *this) {
+  py%(class_name)s *self = (py%(class_name)s *)this;
+  Py_DECREF(self->base->proxied);
+  return 0;
+}
+""" % dict(class_name = self.class_name))
+
+    def initialise_attributes(self, out):
+        attributes = self.myclass.module.classes[self.base_class_name].attributes.get_attributes()
+        for definition_class_name, attribute in attributes:
+            out.write("""
+{
+  // Converting from %(attribute_name)s
+  PyErr_Clear();
+  PyObject *py_result = PyObject_GetAttrString(self->base->proxied, "%(name)s");
+
+  if(py_result) {
+       %(type)s tmp;
+       %(from_python_object)s;
+       ((%(definition_class_name)s)self->base)->%(name)s = tmp;
+       Py_DECREF(py_result);
+  }
+  PyErr_Clear();
+}""" % dict(definition = attribute.definition(), name=attribute.name,
+             attribute_name = attribute.__class__.__name__,
+             type = attribute.type,
+             definition_class_name = definition_class_name,
+             from_python_object = attribute.from_python_object(
+                        'py_result',"tmp", method=self,
+                        context = 'self->base')))
+
+
+    def write_constructor_proxy(self, out):
+        ## Get the base_class constructor
+        self.base_cons_method = ProxiedMethod(self.myclass.module.classes[self.base_class_name].constructor, self.myclass)
+
+        self.base_cons_method._prototype(out)
+        out.write("{\nPyObject *py_result;\n")
+        out.write('PyObject *method_name;')
+        out.write("%(class_name)s this = (%(class_name)s)self;\n" % self.__dict__)
+        out.write("PyGILState_STATE gstate;\ngstate = PyGILState_Ensure();\n")
+        out.write('method_name = PyString_FromString("__class__");\n')
+        for arg in self.base_cons_method.args:
+            out.write("PyObject *py_%s;\n" % arg.name)
+
+        out.write("\n//Obtain python objects for all the args:\n")
+        for arg in self.base_cons_method.args:
+            out.write(arg.to_python_object(result = "py_%s" % arg.name,
+                                           sense = 'proxied',
+                                           BORROWED=True))
+
+        out.write('if(!((%s)self)->proxied) {\n RaiseError(ERuntimeError, "No proxied object in %s"); goto error;\n}\n' % (self.myclass.class_name, self.myclass.class_name))
+
+        out.write("""
+// Enlarge the object size to accomodate the extended class
+self = talloc_realloc_size(self, self, sizeof(struct %(base_class_name)s_t));
+""" % self.__dict__)
+        out.write("\n//Now call the method\n")
+        out.write("PyErr_Clear();\npy_result = PyObject_CallMethodObjArgs(((%s)self)->proxied,method_name," % self.myclass.class_name)
+
+        call = ''
+        for arg in self.base_cons_method.args:
+            call += "py_%s," % arg.name
+
+        ## Sentinal
+        self.error_set = True
+        call += """NULL"""
+
+        out.write(call + ");\n");
+        out.write("""
+if(!py_result && PyCallable_Check(this->proxied)) {
+   PyErr_Clear();
+   py_result = PyObject_CallFunctionObjArgs(((%(name)s)self)->proxied, %(call)s);
+}
+
+/** Check for python errors */
+if(PyErr_Occurred()) {
+   PyObject *exception_t, *exception, *tb;
+   PyObject *str;
+   char *str_c;
+   char *error_str;
+   enum _error_type *error_type = aff4_get_current_error(&error_str);
+
+   // Fetch the exception state and convert it to a string:
+   PyErr_Fetch(&exception_t, &exception, &tb);
+
+   str = PyObject_Repr(exception);
+   str_c = PyString_AsString(str);
+
+   if(str_c) {
+      strncpy(error_str, str_c, BUFF_SIZE-1);
+      error_str[BUFF_SIZE-1]=0;
+      *error_type = ERuntimeError;
+   }
+   Py_DECREF(str);
+   goto error;
+}
+
+// Take over the proxied object now
+this->proxied = py_result;
+""" % dict(name=self.myclass.class_name, call = call));
+
+        ## Now we try to populate the C struct slots with proxies of
+        ## the python objects
+        for class_name, attr in self.myclass.module.classes[\
+            self.base_class_name].attributes.get_attributes():
+            out.write("""
+// Converting %(name)s from proxy:
+{
+   PyObject *py_result = PyObject_GetAttrString(this->proxied, "%(name)s");
+   if(py_result) {
+""" % dict(name = attr.name))
+            out.write("{ %s " % attr.definition())
+            out.write(attr.from_python_object("py_result",
+                                              "((%s)self)->%s" % (class_name, attr.name),
+                                              self))
+            out.write("}\nPy_DECREF(py_result);\n}\n}\n")
+
+        out.write("PyGILState_Release(gstate);\n")
+        out.write("\n\nreturn self;\n")
+        if self.error_set:
+            out.write("error:\n PyGILState_Release(gstate);\ntalloc_free(self); return NULL;\n")
+
+        out.write("}\n\n")
+
+    def write_definition(self, out):
+        self.write_constructor_proxy(out)
+        out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {
+      PyGILState_STATE gstate = PyGILState_Ensure();
+""" % dict(method = self.name, class_name = self.class_name))
+
+        self.write_local_vars(out)
+
+        ## Precall preparations
+        for type in self.args:
+            out.write(type.pre_call(self))
+
+        ## Make up the call
+        self.call = "talloc_memdup(NULL, &__%(class_name)s, sizeof(__%(class_name)s));\n" % self.__dict__
+
+        ## Now call the wrapped function
+        out.write("""
+ self->base = %(call)s
+
+// Take over a copy of the proxied object
+ self->base->proxied = proxied;
+
+ /* We take a reference to the proxied object, and the proxied base
+ class takes a reference. This way we (the python object) and the
+ proxied C class can be freed independantly and only when both are
+ freed the proxied object is freed.  */
+
+ //Py_INCREF(proxied);
+ Py_INCREF(proxied);
+ talloc_set_destructor((void*)self->base, %(class_name)s_destructor);
+""" % self.__dict__)
+
+        ## Install the handler for the constructor. FIXME - implement
+        ## shortcut bypassing here so if the proxied class is itself a
+        ## python binding to a C class and it does not override
+        ## anything, we call directly into the original C class method
+        ## instead of converting to python, and back.
+        out.write("((%(definition_class_name)s)self->base)->%(name)s = %(func)s;\n" % dict(
+                definition_class_name = self.base_cons_method.definition_class_name,
+                name = self.base_cons_method.name,
+                func = self.base_cons_method.get_name()))
+
+        ## Now iterate over all our methods and install handlers:
+        for method in self.myclass.methods:
+            out.write("""
+   if(1 || PyDict_GetItemString(proxied->ob_type->tp_dict, "%(name)s")) {
+     ((%(definition_class_name)s)self->base)->%(name)s = py%(class_name)s_%(name)s;
+   }
+""" % dict(definition_class_name = method.definition_class_name,
+           name = method.name,
+           class_name = self.myclass.class_name))
+
+        ## Now fill in all attributes from the proxied object. Since
+        ## the C struct attribute access is just memory access its
+        ## difficult to trap it and refill attributes dynamically from
+        ## the python object. Therefore for now we just read all
+        ## attributes initially and populate the C struct with them.
+        self.initialise_attributes(out)
+
+        out.write("\n   PyGILState_Release(gstate);\n  return 0;\n");
+
+        ## Write the error part of the function
+        if self.error_set:
+            out.write("error:\n  PyGILState_Release(gstate);\n  " + self.error_condition());
+
+        out.write("\n}\n\n")
+
+class EmptyConstructor(ConstructorMethod):
+    def write_definition(self, out):
+        out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {\n""" % dict(method = self.name, class_name = self.class_name))
+        out.write("""return 0;}\n\n""")
+
+class ClassGenerator:
+    docstring = ''
+    def __init__(self, class_name, base_class_name, module):
+        self.class_name = class_name
+        self.methods = []
+        self.module = module
+        self.constructor = EmptyConstructor(class_name, base_class_name,
+                                             "Con", [], '', myclass = self)
+
+        self.base_class_name = base_class_name
+        self.attributes = GetattrMethod(self.class_name, self.base_class_name, self)
+        self.modifier = set()
+        self.active = True
+        self.iterator = None
+
+    def prepare(self):
+        """ This method is called just before we need to write the
+        output and allows us to do any last minute fixups.
+        """
+        pass
+
+    def __str__(self):
+        result = "#%s\n" % self.docstring
+
+        result += "Class %s(%s):\n" % (self.class_name, self.base_class_name)
+        result += " Constructor:%s\n" % self.constructor
+        result += " Attributes:\n%s\n" % self.attributes
+        result += " Methods:\n"
+        for a in self.methods:
+            result += "    %s\n" % a.__str__()
+
+        return result
+
+    def is_active(self):
+        """ Returns true if this class is active and should be generated """
+        if not self.active or self.modifier and \
+                ('PRIVATE' in self.modifier or 'ABSTRACT' in self.modifier):
+            log("%s is not active %s" % (self.class_name, self.modifier))
+            return False
+
+        return True
+
+    def clone(self, new_class_name):
+        """ Creates a clone of this class - usefull when implementing
+        class extensions
+        """
+        result = ClassGenerator(new_class_name, self.class_name, self.module)
+        result.constructor = self.constructor.clone(new_class_name)
+        result.methods = [ x.clone(new_class_name) for x in self.methods ]
+        result.attributes = self.attributes.clone(new_class_name)
+
+        return result
+
+    def add_attribute(self, attr_name, attr_type, modifier):
+        try:
+            if not self.module.classes[attr_type].is_active(): return
+        except KeyError: pass
+
+        try:
+            ## All attribute references are always borrowed - that
+            ## means we dont want to free them after accessing them
+            type_class = dispatch(attr_name, "BORROWED "+attr_type)
+        except KeyError:
+            log("Unknown attribute type %s for  %s.%s" % (attr_type,
+                                                          self.class_name,
+                                                          attr_name))
+            return
+
+        type_class.attributes.add(modifier)
+        self.attributes.add_attribute(type_class)
+
+    def add_constructor(self, method_name, args, return_type, docstring):
+        if method_name.startswith("Con"):
+            self.constructor = ConstructorMethod(self.class_name, self.base_class_name,
+                                                 method_name, args, return_type,
+                                                 myclass = self)
+            self.constructor.docstring = docstring
+
+    def struct(self,out):
+        out.write("""\ntypedef struct {
+  PyObject_HEAD
+  %(class_name)s base;
+  void *ctx;
+} py%(class_name)s;\n
+""" % dict(class_name=self.class_name))
+
+    def code(self, out):
+        if not self.constructor:
+            raise RuntimeError("No constructor found for class %s" % self.class_name)
+
+        self.constructor.write_destructor(out)
+        self.constructor.write_definition(out)
+        if self.attributes:
+            self.attributes.write_definition(out)
+
+        for m in self.methods:
+            m.write_definition(out)
+
+    def initialise(self):
+        return "python_wrappers[TOTAL_CLASSES].class_ref = (Object)&__%s;\n" \
+            "python_wrappers[TOTAL_CLASSES++].python_type = &%s_Type;\n" % (
+            self.class_name, self.class_name)
+
+    def PyMethodDef(self, out):
+        out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
+        for method in self.methods:
+            method.PyMethodDef(out)
+
+        out.write("     {NULL}  /* Sentinel */\n};\n")
+
+    def prototypes(self, out):
+        """ Write prototype suitable for .h file """
+        out.write("""staticforward PyTypeObject %s_Type;\n""" % self.class_name)
+        self.constructor.prototype(out)
+
+        if self.attributes:
+            self.attributes.prototype(out)
+        for method in self.methods:
+            method.prototype(out)
+
+    def numeric_protocol_int(self):
+        pass
+
+    def numeric_protocol_nonzero(self):
+        return """
+static int
+%(class_name)s_nonzero(py%(class_name)s *v)
+{
+        return v->base != 0;
+}
+""" % self.__dict__
+
+    def numeric_protocol(self, out):
+        args = {'class':self.class_name}
+        for type, func in [ ('nonzero', self.numeric_protocol_nonzero),
+                            ('int', self.numeric_protocol_int) ]:
+            definition = func()
+            if definition:
+                out.write(definition)
+                args[type] = "%s_%s" % (self.class_name,type)
+            else:
+                args[type] = '0'
+
+        out.write("""
+static PyNumberMethods %(class)s_as_number = {
+        (binaryfunc)    0,       /*nb_add*/
+        (binaryfunc)    0,       /*nb_subtract*/
+        (binaryfunc)    0,       /*nb_multiply*/
+                        0,       /*nb_divide*/
+                        0,       /*nb_remainder*/
+                        0,       /*nb_divmod*/
+                        0,       /*nb_power*/
+        (unaryfunc)     0,       /*nb_negative*/
+        (unaryfunc)     0,       /*tp_positive*/
+        (unaryfunc)     0,       /*tp_absolute*/
+        (inquiry)       %(nonzero)s,   /*tp_nonzero*/
+        (unaryfunc)     0,       /*nb_invert*/
+                        0,       /*nb_lshift*/
+        (binaryfunc)    0,       /*nb_rshift*/
+                        0,       /*nb_and*/
+                        0,       /*nb_xor*/
+                        0,       /*nb_or*/
+                        0,       /*nb_coerce*/
+         (unaryfunc)    %(int)s,       /*nb_int*/
+                        0,       /*nb_long*/
+                        0,       /*nb_float*/
+                        0,       /*nb_oct*/
+                        0,       /*nb_hex*/
+        0,                              /* nb_inplace_add */
+        0,                              /* nb_inplace_subtract */
+        0,                              /* nb_inplace_multiply */
+        0,                              /* nb_inplace_divide */
+        0,                              /* nb_inplace_remainder */
+        0,                              /* nb_inplace_power */
+        0,                              /* nb_inplace_lshift */
+        0,                              /* nb_inplace_rshift */
+        0,                              /* nb_inplace_and */
+        0,                              /* nb_inplace_xor */
+        0,                              /* nb_inplace_or */
+        0,                              /* nb_floor_divide */
+        0,                              /* nb_true_divide */
+        0,                              /* nb_inplace_floor_divide */
+        0,                              /* nb_inplace_true_divide */
+        0,                              /* nb_index */
+};
+"""  % args)
+        return "&%(class)s_as_number" % args
+
+    def PyTypeObject(self, out):
+        args = {'class':self.class_name, 'module': self.module.name, 
+                'iterator': 0,
+                'iternext': 0,
+                'tp_str': 0,
+                'getattr_func': 0,
+                'docstring': "%s: %s" % (self.class_name, 
+                                         escape_for_string(self.docstring))}
+
+        if self.attributes:
+            args['getattr_func'] = self.attributes.name
+
+        args['numeric_protocol'] = self.numeric_protocol(out)
+        if "ITERATOR" in self.modifier:
+            args['iterator'] = "PyObject_SelfIter"
+            args['iternext'] = "py%s_iternext" % self.class_name
+
+        if "SELF_ITER" in self.modifier:
+            args['iterator'] = 'py%s___iter__' % self.class_name
+
+        if "TP_STR" in self.modifier:
+            args['tp_str'] = 'py%s___str__' % self.class_name
+
+        out.write("""
+static PyTypeObject %(class)s_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /* ob_size */
+    "%(module)s.%(class)s",               /* tp_name */
+    sizeof(py%(class)s),            /* tp_basicsize */
+    0,                         /* tp_itemsize */
+    (destructor)%(class)s_dealloc,/* tp_dealloc */
+    0,                         /* tp_print */
+    0,                         /* tp_getattr */
+    0,                         /* tp_setattr */
+    0,                         /* tp_compare */
+    0,                         /* tp_repr */
+    %(numeric_protocol)s,      /* tp_as_number */
+    0,                         /* tp_as_sequence */
+    0,                         /* tp_as_mapping */
+    0,                         /* tp_hash */
+    0,                         /* tp_call */
+    (reprfunc)%(tp_str)s,      /* tp_str */
+    (getattrofunc)%(getattr_func)s,                         /* tp_getattro */
+    0,                         /* tp_setattro */
+    0,                         /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
+    "%(docstring)s",     /* tp_doc */
+    0,	                       /* tp_traverse */
+    0,                         /* tp_clear */
+    0,                         /* tp_richcompare */
+    0,                         /* tp_weaklistoffset */
+    (getiterfunc)%(iterator)s,              /* tp_iter */
+    (iternextfunc)%(iternext)s,/* tp_iternext */
+    %(class)s_methods,         /* tp_methods */
+    0,                         /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)py%(class)s_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    0,                         /* tp_new */
+};
+""" % args )
+
+class StructGenerator(ClassGenerator):
+    """ A wrapper generator for structs """
+    def __init__(self, class_name, module):
+        self.class_name = class_name
+        self.methods = []
+        self.module = module
+        self.base_class_name = None
+        self.active = False
+        self.modifier = set()
+        self.constructor = None
+        self.attributes = GetattrMethod(self.class_name, self.base_class_name, self)
+
+    def prepare(self):
+        ## This is needed for late stage initialization - sometimes
+        ## our class_name is not know until now.
+        if not self.constructor:
+            self.constructor = StructConstructor(self.class_name, self.base_class_name,
+                                                 'Con', [], "void", myclass = self)
+
+            self.attributes.rename_class_name(self.class_name)
+            for x in self.attributes._attributes:
+                x[1].attributes.add('FOREIGN')
+
+    def __str__(self):
+        result = "#%s\n" % self.docstring
+
+        result += "Struct %s:\n" % (self.class_name)
+        result += "%s\n" % self.attributes
+
+        return result
+
+    def struct(self, out):
+        out.write("""\ntypedef struct {
+  PyObject_HEAD
+  %(class_name)s *base;
+  void *ctx;
+} py%(class_name)s;\n
+""" % dict(class_name=self.class_name))
+
+    def initialise(self):
+        return ''
+
+class ProxyClassGenerator(ClassGenerator):
+    def __init__(self, class_name, base_class_name, module, *args, **kwargs):
+        ClassGenerator.__init__(self, class_name, base_class_name, module, *args, **kwargs)
+        self.constructor = ProxyConstructor(self.class_name,
+                                            self.base_class_name, '__init__',
+                                            [('PyObject *', 'proxied')],
+                                            'void', myclass = self)
+        self.module = module
+        self.attributes = ProxiedGetattr(self.class_name, self.base_class_name, self)
+
+
+    def initialise(self):
+        return "INIT_CLASS(%(class_name)s);\n" % self.__dict__ + ClassGenerator.initialise(self)
+
+    def struct(self, out):
+        out.write("""
+// The proxied type is an extension of the wrapped type with a pointer
+// to the proxied PyObject.
+CLASS(%(class_name)s, %(base_class_name)s)
+   uint32_t magic;
+   PyObject *proxied;
+END_CLASS
+
+VIRTUAL(%(class_name)s, %(base_class_name)s) {
+} END_VIRTUAL
+
+typedef struct {
+  PyObject_HEAD
+  %(class_name)s base;
+  void *ctx;
+} py%(class_name)s;\n
+""" % self.__dict__)
+
+    def PyMethodDef(self, out):
+        out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
+        ## For now no methods
+        out.write("     {NULL}  /* Sentinel */\n};\n")
+
+class parser:
+    class_re = re.compile(r"^([A-Z]+)?\s*CLASS\(([A-Z_a-z0-9]+)\s*,\s*([A-Z_a-z0-9]+)\)")
+    method_re = re.compile(r"^\s*([0-9A-Z_a-z ]+\s+\*?)METHOD\(([A-Z_a-z0-9]+),\s*([A-Z_a-z0-9]+),?")
+    enum_start_re = re.compile(r'enum ([0-9A-Z_a-z]+) {')
+    enum_re = re.compile(r'([0-9A-Z_a-z]+) = [^,]+,')
+    enum_end_re = re.compile('}')
+    arg_re = re.compile(r"\s*([0-9A-Z a-z_]+\s+\*?)([0-9A-Za-z_]+),?")
+    constant_re = re.compile(r"#define\s+([A-Z_0-9]+)\s+[^\s]+")
+    struct_re = re.compile(r"([A-Z]+)?\s+(typedef )?struct\s+([A-Z_a-z0-9]+)\s+{")
+    proxy_class_re = re.compile(r"^([A-Z]+)?\s*PROXY_CLASS\(([A-Za-z0-9]+)\)")
+    end_class_re = re.compile("END_CLASS")
+    attribute_re = re.compile(r"^\s*([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z]+)\s*;")
+    comment_re = re.compile(r"^\s*//")
+    comment_start_re = re.compile(r"/\*+")
+    comment_end_re = re.compile(r"\*+/")
+    blank_line_re = re.compile("\s+")
+    typedef_re = re.compile("typedef ([A-Za-z_0-9]+) +([A-Za-z_0-9]+) *;")
+    bind_re = re.compile("BIND\(([a-zA-Z_0-9]+)\);")
+    current_class = None
+
+    def __init__(self, module, verbosity=0):
+        self.module = module
+        self.current_comment = ''
+        self.verbosity = verbosity
+        self.state = 'DEFAULT'
+
+        ## this is a list of objects which are to be bound
+        self.to_bind = []
+
+        global DEBUG
+
+        DEBUG = verbosity
+
+        io = StringIO.StringIO("""
+// Base object
+CLASS(Object, Obj)
+END_CLASS
+""")
+        self.parse_fd(io, '')
+
+    def add_class(self, class_name, base_class_name, class_type, handler, docstring, modifier):
+        try:
+            self.current_class = self.module.classes[base_class_name].clone(class_name)
+        except (KeyError, AttributeError):
+            log("Base class %s is not defined !!!!" % base_class_name)
+            self.current_class = class_type(class_name, base_class_name, self.module)
+
+        ## Now add the new class to the module object
+        self.current_class.docstring = docstring
+        self.current_class.modifier = modifier
+        self.module.add_class(self.current_class, handler)
+
+    def parse_filenames(self, filenames):
+        ## Be quiet for the first pass as many problems will be
+        ## resolved on the second pass anyway.
+        global DEBUG
+        DEBUG = 0
+        for f in filenames:
+            self._parse(f)
+
+        DEBUG = self.verbosity
+        log("Second pass: Consolidating definitions")
+        for f in filenames:
+            self._parse(f)
+
+    def _parse(self, filename):
+        fd = open(filename)
+        self.parse_fd(fd, filename)
+        fd.close()
+
+        if filename not in self.module.files:
+              self.module.headers += '#include "%s"\n' % filename
+              self.module.files.append(filename)
+
+    def parse_fd(self, fd, filename):
+        self.current_class = None
+        self.current_comment = ''
+
+        while 1:
+            line = fd.readline()
+            if not line: break
+
+            ## Handle binds
+            m= self.bind_re.search(line)
+            if m:
+                print "Will bind %s" % m.group(1)
+                self.to_bind.append(m.group(1))
+                continue
+
+            ## Handle enums
+            if self.state == 'DEFAULT':
+                m = self.enum_start_re.search(line)
+                if m:
+                    self.state = 'enum'
+                    type_dispatcher[m.group(1)] = type_dispatcher['int']
+                    continue
+
+            elif self.state == 'enum':
+                m = self.enum_re.search(line)
+                if m:
+                    self.module.add_constant(m.group(1), 'integer')
+
+                if '}' in line:
+                    self.state = 'DEFAULT'
+                    continue
+
+            ## Handle c++ style comments //
+            m = self.comment_re.match(line)
+            if m:
+                self.current_comment = line[m.end():]
+                while 1:
+                    line = fd.readline()
+
+                    m = self.comment_re.match(line)
+                    if not m:
+                        break
+
+                    self.current_comment += line[m.end():]
+
+            ## Multiline C style comments
+            m = self.comment_start_re.search(line)
+            if m:
+                line = line[m.end():]
+                while 1:
+                    m = self.comment_end_re.search(line)
+                    if m:
+                        self.current_comment += line[:m.start()]
+                        line = fd.readline()
+                        break
+                    else:
+                        self.current_comment += line
+
+                    line = fd.readline()
+                    if not line: break
+
+            ## Handle simple typdefs (as if typedef uint64_t my_fancy_type;)
+            m = self.typedef_re.search(line)
+            if m:
+                ## We basically add a new type as a copy of the old
+                ## type
+                old, new = m.group(1), m.group(2)
+                if old in type_dispatcher:
+                    type_dispatcher[new] = type_dispatcher[old]
+
+            ## Handle constant #define
+            m = self.constant_re.search(line)
+            if m:
+                ## We need to determine if it is a string or integer
+                if re.search('"', line):
+                    ## Its a string
+                    self.module.add_constant(m.group(1), 'string')
+                else:
+                    self.module.add_constant(m.group(1), 'integer')
+
+            ## Wrap structs
+            m = self.struct_re.search(line)
+            if m:
+                modifier = m.group(1)
+                class_name = m.group(3)
+                base_class_name = None
+                ## Structs may be refered to as a pointer or absolute
+                ## - its the same thing ultimatley.
+
+                ## We only wrap structures which are explicitely bound
+                if (modifier and 'BOUND' in modifier) or \
+                        class_name in self.to_bind:
+                    self.add_class(class_name, base_class_name, StructGenerator, StructWrapper,
+                                   self.current_comment, modifier)
+                    type_dispatcher["%s *" % class_name] = PointerStructWrapper
+
+                continue
+
+            m = self.class_re.search(line)
+            if m:
+                ## We need to make a new class now... We basically
+                ## need to build on top of previously declared base
+                ## classes - so we try to find base classes, clone
+                ## them if possible:
+                modifier = m.group(1)
+                class_name = m.group(2)
+                base_class_name = m.group(3)
+                self.add_class(class_name, base_class_name, ClassGenerator, Wrapper,
+                               self.current_comment, modifier)
+                type_dispatcher["%s *" % class_name] = PointerWrapper
+
+                continue
+
+            ## Make a proxy class for python callbacks
+            m = self.proxy_class_re.search(line)
+            if m:
+                modifier = m.group(1)
+                base_class_name = m.group(2)
+                class_name = "Proxied%s" % base_class_name
+                try:
+                    proxied_class = self.module.classes[base_class_name]
+                except KeyError:
+                    raise RuntimeError("Need to create a proxy for %s but it has not been defined (yet). You must place the PROXIED_CLASS() instruction after the class definition" % base_class_name)
+                self.current_class = ProxyClassGenerator(class_name,
+                                                         base_class_name, self.module)
+                #self.current_class.constructor.args += proxied_class.constructor.args
+                self.current_class.docstring = self.current_comment
+
+                ## Create proxies for all these methods
+                for method in proxied_class.methods:
+                    self.current_class.methods.append(ProxiedMethod(method, self.current_class))
+
+                self.module.add_class(self.current_class, Wrapper)
+
+                ## Make sure that further lines are not interpreted as part of this class.
+                self.current_class = None
+
+            m = self.method_re.search(line)
+            if self.current_class and m:
+                args = []
+                method_name = m.group(3)
+                return_type = m.group(1).strip()
+                ## Ignore private methods
+                if return_type.startswith("PRIVATE"): continue
+
+                ## Now parse the args
+                offset = m.end()
+                while 1:
+                    m = self.arg_re.match(line[offset:])
+                    if not m:
+                        ## Allow multiline definitions if there is \\
+                        ## at the end of the line
+                        if line.strip().endswith("\\"):
+                            line = fd.readline()
+                            offset = 0
+                            if line:
+                                continue
+
+                        break
+
+                    offset += m.end()
+                    args.append([m.group(1).strip(), m.group(2).strip()])
+
+                if return_type == self.current_class.class_name and \
+                        method_name.startswith("Con"):
+                    self.current_class.add_constructor(method_name, args, return_type,
+                                                       self.current_comment)
+                else:
+                    self.current_class.add_method(method_name, args, return_type,
+                                                  self.current_comment)
+
+            m = self.attribute_re.search(line)
+            if self.current_class and m:
+                type = m.group(1)
+                name = m.group(2)
+                self.current_class.add_attribute(name, type)
+
+            m = self.end_class_re.search(line)
+            if m:
+                ## Just clear the current class context
+                self.current_class = None
+
+            ## If this is a shadow file we do not include it from the
+            ## main module. A shadow file is a way for us to redefine
+            ## other C constructs which will be imported from an
+            ## external header file so we can bind them slightly
+            ## differently.
+            if "this is a shadow file" in self.current_comment \
+                    and filename not in self.module.files:
+                self.module.files.append(filename)
+
+            ## We only care about comment immediately above methods
+            ## etc as we take them to be documentation. If we get here
+            ## the comment is not above anything we care about - so we
+            ## clear it:
+            self.current_comment = ''
+
+    def write(self, out):
+        self.module.write(out)
+
+
+import lexer
+
+class EnumConstructor(ConstructorMethod):
+    def write_destructor(self, out):
+        out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+ Py_DECREF(self->value);
+ PyObject_Del(self);
+}\n
+""" % dict(class_name = self.class_name))
+    def write_definition(self, out):
+        self.myclass.modifier.add("TP_STR")
+        self._prototype(out)
+        out.write("""{
+static char *kwlist[] = {"value", NULL};
+
+if(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &self->value))
+ goto error;
+
+Py_INCREF(self->value);
+
+  return 0;
+error:
+    return -1;
+}
+
+static PyObject *py%(class_name)s___str__(py%(class_name)s *self) {
+  PyObject *result = PyDict_GetItem(%(class_name)s_rev_lookup, self->value);
+
+  if(result) {
+     Py_INCREF(result);
+ } else {
+     result = PyObject_Str(self->value);
+ }
+
+ return result;
+}
+
+""" % self.__dict__)
+
+class Enum(StructGenerator):
+    def __init__(self, name, module):
+        StructGenerator.__init__(self, name, module)
+        self.values = []
+        self.name = name
+        self.attributes = None
+        self.active = True
+
+    def prepare(self):
+        self.constructor = EnumConstructor(self.class_name, self.base_class_name,
+                                           'Con', [], "void", myclass = self)
+        StructGenerator.prepare(self)
+
+    def __str__(self):
+        result = "Enum %s:\n" % (self.name)
+        for attr in self.values:
+            result += "    %s\n" % attr.__str__()
+
+        return result
+
+    def struct(self,out):
+        out.write("""\ntypedef struct {
+  PyObject_HEAD
+  PyObject *value;
+} py%(class_name)s;\n
+
+
+static PyObject *%(class_name)s_Dict_lookup;
+static PyObject *%(class_name)s_rev_lookup;
+""" % dict(class_name=self.class_name))
+
+    def PyMethodDef(self, out):
+        out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
+        out.write("     {NULL}  /* Sentinel */\n};\n")
+
+    def numeric_protocol_nonzero(self):
+        pass
+
+    def numeric_protocol_int(self):
+        return """
+static PyObject *%(class_name)s_int(py%(class_name)s *self) {
+    Py_INCREF(self->value);
+    return self->value;
+}
+""" % self.__dict__
+
+    def initialise(self):
+        result = """
+%(class_name)s_Dict_lookup = PyDict_New();
+%(class_name)s_rev_lookup = PyDict_New();
+""" % self.__dict__
+
+        if self.values:
+            result += "{ PyObject *tmp, *tmp2;\n"
+            for attr in self.values:
+                result += ''' tmp = PyLong_FromLong(%(value)s);
+  tmp2 = PyString_FromString("%(value)s");
+  PyDict_SetItem(%(class_name)s_Dict_lookup, tmp2, tmp);
+  PyDict_SetItem(%(class_name)s_rev_lookup, tmp, tmp2);
+  Py_DECREF(tmp);
+  Py_DECREF(tmp2);
+
+''' % dict(value = attr, class_name=self.class_name)
+            result += "};\n"
+
+        return result
+
+class EnumType(Integer):
+    buildstr = 'i'
+
+    def __init__(self, name, type):
+        Integer.__init__(self, name, type)
+        self.type = type
+
+    def to_python_object(self, name=None, result='py_result', **kw):
+        name = name or self.name
+        return """PyErr_Clear();
+%s = PyObject_CallMethod(g_module, "%s", "K", (uint64_t)%s);
+""" % (result, self.type, name)
+
+    def pre_call(self, method):
+        method.error_set = True
+        return """
+// Check if the integer passed is actually a valid member of the enum
+// Enum value of 0 is always allowed
+if(%(name)s) { PyObject *py_%(name)s = PyLong_FromLong(%(name)s);
+  PyObject *tmp = PyDict_GetItem(%(type)s_rev_lookup, py_%(name)s);
+
+  Py_DECREF(py_%(name)s);
+  if(!tmp) {
+    PyErr_Format(PyExc_RuntimeError, "value %%lu is not valid for Enum %(type)s of arg '%(name)s'", (unsigned long)%(name)s);
+    goto error;
+  }
+  Py_DECREF(tmp);
+}
+""" % self.__dict__
+
+class HeaderParser(lexer.SelfFeederMixIn):
+    tokens = [
+        [ 'INITIAL', r'#define\s+', 'PUSH_STATE', 'DEFINE' ],
+        [ 'DEFINE', r'([A-Za-z_0-9]+)\s+[^\n]+', 'DEFINE,POP_STATE', None ],
+        [ 'DEFINE', r'\n', 'POP_STATE', None],
+
+        ## Recognize ansi c comments
+        [ '.', r'/\*(.)', 'PUSH_STATE', 'COMMENT' ],
+        [ 'COMMENT', r'(.+?)\*/\s+', 'COMMENT_END,POP_STATE', None],
+        [ 'COMMENT', r'(.+)', 'COMMENT', None],
+
+        ## And c++ comments
+        [ '.', r'//([^\n]+)', 'COMMENT', None],
+
+        ## An empty line clears the current comment
+        [ '.', r'\r?\n\r?\n', 'CLEAR_COMMENT', None],
+
+        ## Ignore whitespace
+        [ '.', r'\s+', 'SPACE', None ],
+        [ '.', r'\\\n', 'SPACE', None ],
+
+        ## Recognize CLASS() definitions
+        [ 'INITIAL', r"^([A-Z]+)?\s*CLASS\(([A-Z_a-z0-9]+)\s*,\s*([A-Z_a-z0-9]+)\)",
+                     'PUSH_STATE,CLASS_START', 'CLASS'],
+
+        [ 'CLASS', r"^\s*(FOREIGN|ABSTRACT|PRIVATE)?([0-9A-Z_a-z ]+( |\*))METHOD\(([A-Z_a-z0-9]+),\s*([A-Z_a-z0-9]+),?",
+                     "PUSH_STATE,METHOD_START", "METHOD"],
+        [ 'METHOD', r"\s*([0-9A-Z a-z_]+\s+\*?\*?)([0-9A-Za-z_]+),?", "METHOD_ARG", None ],
+        [ 'METHOD', r'\);', 'POP_STATE,METHOD_END', None],
+
+        [ 'CLASS', r"^\s*(FOREIGN|ABSTRACT)?([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z0-9]+)\s*;",
+                   'CLASS_ATTRIBUTE', None],
+        [ 'CLASS', "END_CLASS", 'END_CLASS,POP_STATE', None],
+
+        ## Recognize struct definitions (With name)
+        [ 'INITIAL', "([A-Z_a-z0-9 ]+)?struct\s+([A-Z_a-z0-9]+)\s+{",
+                     'PUSH_STATE,STRUCT_START', 'STRUCT'],
+
+        ## Without name (using typedef)
+        [ 'INITIAL', "typedef\s+struct\s+{",
+                     'PUSH_STATE,TYPEDEF_STRUCT_START', 'STRUCT'],
+
+        [ 'STRUCT', r"^\s*([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z0-9]+)\s*;",
+                     'STRUCT_ATTRIBUTE', None],
+
+        [ 'STRUCT', r"^\s*([0-9A-Z_a-z ]+)\*\s+([A-Z_a-z0-9]+)\s*;",
+                     'STRUCT_ATTRIBUTE_PTR', None],
+
+        ## Struct ended with typedef
+        [ 'STRUCT', '}\s+([0-9A-Za-z_]+);', 'POP_STATE,TYPEDEF_STRUCT_END', None],
+        [ 'STRUCT', '}', 'POP_STATE,STRUCT_END', None],
+
+        ## Handle recursive struct or union definition (At the moment
+        ## we cant handle them at all)
+        [ '(RECURSIVE_)?STRUCT', '(struct|union)\s+([_A-Za-z0-9]+)?\s*{', 'PUSH_STATE', 'RECURSIVE_STRUCT'],
+        [ 'RECURSIVE_STRUCT', '}\s+[0-9A-Za-z]+', 'POP_STATE', None],
+
+        ## Process enums (2 forms - named and typedefed)
+        [ 'INITIAL', r'enum\s+([0-9A-Za-z_]+)\s+{', 'PUSH_STATE,ENUM_START', 'ENUM' ],
+        ## Unnamed
+        [ 'INITIAL', r'typedef\s+enum\s+{', 'PUSH_STATE,TYPEDEF_ENUM_START', 'ENUM' ],
+        [ 'ENUM', r'([0-9A-Za-z_]+)\s+=[^\n]+', 'ENUM_VALUE', None],
+
+        ## Typedefed ending
+        [ 'ENUM', r'}\s+([0-9A-Za-z_]+);', 'POP_STATE,TYPEDEFED_ENUM_END', None],
+        [ 'ENUM', r'}', 'POP_STATE,ENUM_END', None],
+
+        [ 'INITIAL', r'BIND_STRUCT\(([0-9A-Za-z_ \*]+)\)', 'BIND_STRUCT', None],
+
+        ## A simple typedef of one type for another type:
+        [ 'INITIAL', r"typedef ([A-Za-z_0-9]+) +([^;]+);", 'SIMPLE_TYPEDEF', None],
+
+        ## Handle proxied directives
+        [ 'INITIAL', r"PROXY_CLASS\(([A-Za-z0-9]+)\);", 'PROXY_CLASS', None],
+
+        ]
+
+    def __init__(self, name, verbose = 1):
+        self.module = Module(name)
+        lexer.SelfFeederMixIn.__init__(self, verbose = 0)
+
+        io = StringIO.StringIO("""
+// Base object
+CLASS(Object, Obj)
+END_CLASS
+""")
+        self.parse_fd(io)
+
+    current_comment = ''
+    def COMMENT(self, t, m):
+        self.current_comment += m.group(1) + "\n"
+
+    def COMMENT_END(self, t, m):
+        self.current_comment += m.group(1)
+
+    def CLEAR_COMMENT(self, t, m):
+        self.current_comment = ''
+
+    def DEFINE(self, t, m):
+        line = m.group(0)
+        line = line.split('/*')[0]
+        if '"' in line:
+            type = 'string'
+        else:
+            type = 'integer'
+
+        name = m.group(1).strip()
+        if len(name)>3 and name[0]!='_' and name==name.upper():
+            self.module.add_constant(name, type)
+
+    current_class = None
+    def CLASS_START(self, t, m):
+        class_name = m.group(2).strip()
+        base_class_name = m.group(3).strip()
+
+        try:
+            self.current_class = self.module.classes[base_class_name].clone(class_name)
+        except (KeyError, AttributeError):
+            log("Base class %s is not defined !!!!" % base_class_name)
+            self.current_class = ClassGenerator(class_name, base_class_name, self.module)
+
+        self.current_class.docstring = self.current_comment
+        self.current_class.modifier.add(m.group(1))
+        self.module.add_class(self.current_class, Wrapper)
+        type_dispatcher["%s *" % class_name] = PointerWrapper
+
+    current_method = None
+    def METHOD_START(self, t, m):
+        return_type = m.group(2).strip()
+        method_name = m.group(5).strip()
+        modifier = m.group(1) or ''
+
+        if 'PRIVATE' in modifier: return
+
+        ## Is it a regular method or a constructor?
+        self.current_method = Method
+        if return_type == self.current_class.class_name and \
+                method_name.startswith("Con"):
+            self.current_method = ConstructorMethod
+        elif method_name == 'iternext':
+            self.current_method = IteratorMethod
+            self.current_class.modifier.add("ITERATOR")
+        elif method_name == '__iter__':
+            self.current_method = SelfIteratorMethod
+            self.current_class.modifier.add("SELF_ITER")
+        elif method_name == '__str__':
+            self.current_class.modifier.add("TP_STR")
+
+        self.current_method = self.current_method(self.current_class.class_name,
+                                                  self.current_class.base_class_name,
+                                                  method_name, [], return_type,
+                                                  myclass = self.current_class)
+        self.current_method.docstring = self.current_comment
+        self.current_method.modifier = modifier
+
+    def METHOD_ARG(self, t, m):
+        name = m.group(2).strip()
+        type = m.group(1).strip()
+        if self.current_method:
+            self.current_method.add_arg(type, name)
+
+    def METHOD_END(self, t, m):
+        if not self.current_method: return
+
+        if isinstance(self.current_method, ConstructorMethod):
+            self.current_class.constructor = self.current_method
+        else:
+            found = False
+            for i in range(len(self.current_class.methods)):
+                ## Try to replace existing methods with this new method
+                method = self.current_class.methods[i]
+                if method.name == self.current_method.name:
+                    self.current_class.methods[i] = self.current_method
+                    self.current_method = None
+                    return
+
+            ## Method does not exist, just add to the end
+            self.current_class.methods.append(self.current_method)
+
+        self.current_method = None
+
+    def CLASS_ATTRIBUTE(self, t, m):
+        modifier = m.group(1) or ''
+        type = m.group(2).strip()
+        name = m.group(3).strip()
+        self.current_class.add_attribute(name, type, modifier)
+
+    def END_CLASS(self, t, m):
+        self.current_class = None
+
+    current_struct = None
+    def STRUCT_START(self, t, m):
+        self.current_struct = StructGenerator(m.group(2).strip(), self.module)
+        self.current_struct.docstring = self.current_comment
+        self.current_struct.modifier.add(m.group(1))
+
+    def TYPEDEF_STRUCT_START(self, t, m):
+        self.current_struct = StructGenerator(None, self.module)
+        self.current_struct.docstring = self.current_comment
+
+    def STRUCT_ATTRIBUTE(self, t, m):
+        name = m.group(2).strip()
+        type = m.group(1).strip()
+        self.current_struct.add_attribute(name, type, '')
+
+    def STRUCT_ATTRIBUTE_PTR(self, t, m):
+        type = "%s *" % m.group(1).strip()
+        name = m.group(2).strip()
+        self.current_struct.add_attribute(name, type, '')
+
+    def STRUCT_END(self, t, m):
+        self.module.add_class(self.current_struct, StructWrapper)
+        type_dispatcher["%s *" % self.current_struct.class_name] = PointerStructWrapper
+        self.current_struct = None
+
+    def TYPEDEF_STRUCT_END(self, t, m):
+        self.current_struct.class_name = m.group(1).strip()
+
+        self.STRUCT_END(t, m)
+
+    current_enum = None
+    def ENUM_START(self, t, m):
+        self.current_enum = Enum(m.group(1).strip(), self.module)
+
+    def TYPEDEF_ENUM_START(self, t, m):
+        self.current_enum = Enum(None, self.module)
+
+    def ENUM_VALUE(self, t, m):
+        self.current_enum.values.append(m.group(1).strip())
+
+    def ENUM_END(self, t, m):
+        self.module.classes[self.current_enum.name] = self.current_enum
+
+        ## For now we just treat enums as an integer, and also add
+        ## them to the constant table. In future it would be nice to
+        ## have them as a proper python object so we can override
+        ## __str__ and __int__.
+        for attr in self.current_enum.values:
+            self.module.add_constant(attr, 'integer')
+
+        #type_dispatcher[self.current_enum.name] = Integer
+        type_dispatcher[self.current_enum.name] = EnumType
+        self.current_enum = None
+
+    def TYPEDEFED_ENUM_END(self, t, m):
+        self.current_enum.name = self.current_enum.class_name = m.group(1)
+        self.ENUM_END(t, m)
+
+    def BIND_STRUCT(self, t, m):
+        self.module.classes[m.group(1)].active = True
+
+    def SIMPLE_TYPEDEF(self, t, m):
+        ## We basically add a new type as a copy of the old
+        ## type
+        old, new = m.group(1).strip(), m.group(2).strip()
+        if old in type_dispatcher:
+            type_dispatcher[new] = type_dispatcher[old]
+
+    def PROXY_CLASS(self, t, m):
+        base_class_name = m.group(1).strip()
+        class_name = "Proxied%s" % base_class_name
+        try:
+            proxied_class = self.module.classes[base_class_name]
+        except KeyError:
+            raise RuntimeError("Need to create a proxy for %s but it has not been defined (yet). You must place the PROXIED_CLASS() instruction after the class definition" % base_class_name)
+        current_class = ProxyClassGenerator(class_name,
+                                            base_class_name, self.module)
+        #self.current_class.constructor.args += proxied_class.constructor.args
+        current_class.docstring = self.current_comment
+
+        ## Create proxies for all these methods
+        for method in proxied_class.methods:
+            current_class.methods.append(ProxiedMethod(method, current_class))
+
+        self.module.add_class(current_class, Wrapper)
+
+    def parse_filenames(self, filenames):
+        for f in filenames:
+            self._parse(f)
+
+        ## Second pass
+        for f in filenames:
+            self._parse(f)
+
+    def _parse(self, filename):
+        fd = open(filename)
+        self.parse_fd(fd)
+        fd.close()
+
+        if filename not in self.module.files:
+              self.module.headers += '#include "%s"\n' % filename
+              self.module.files.append(filename)
+
+    def write(self, out):
+        self.module.write(out)
+
+
+if __name__ == '__main__':
+    p = HeaderParser('pyaff4', verbose = 1)
+    for arg in sys.argv[1:]:
+        p.parse_fd(open(arg))
+
+    log("second parse")
+    for arg in sys.argv[1:]:
+        p.parse_fd(open(arg))
+
+    pdb.set_trace()
+    p.write(sys.stdout)
+
+#    p = parser(Module("pyaff4"))
+#    for arg in sys.argv[1:]:
+#        p.parse(arg)
+#        log("second parse")
+#        p.parse(arg)
+
+#    p.write(sys.stdout)
+
Index: /releases/1.0.1/python/experimental/config.py
===================================================================
--- /releases/1.0.1/python/experimental/config.py	(revision 275)
+++ /releases/1.0.1/python/experimental/config.py	(revision 275)
@@ -0,0 +1,4 @@
+MINGW_XCOMPILE = False
+V = True
+
+DEBUG = True
Index: /releases/1.0.1/python/experimental/include/aff4_errors.h
===================================================================
--- /releases/1.0.1/python/experimental/include/aff4_errors.h	(revision 275)
+++ /releases/1.0.1/python/experimental/include/aff4_errors.h	(revision 275)
@@ -0,0 +1,67 @@
+/*
+** aff4_errors.h
+** 
+** Made by mic
+** Login   <mic@laptop>
+** 
+** Started on  Sat Mar  6 20:54:25 2010 mic
+** Last update Sat Mar  6 20:54:25 2010 mic
+*/
+
+#ifndef   	AFF4_ERRORS_H_
+# define   	AFF4_ERRORS_H_
+
+// Some helpful little things
+#define ERROR_BUFFER_SIZE 1024
+
+/** This is used for error reporting. This is similar to the way
+    python does it, i.e. we set the error flag and return NULL.
+*/
+enum _error_type {
+  EZero,EGeneric,EOverflow,EWarning,
+  EUnderflow,EIOError, ENoMemory, EInvalidParameter, ERuntimeError, EKeyError,
+
+  // Reserved for impossible conditions
+  EProgrammingError
+};
+
+void *raise_errors(enum _error_type t, char *string,  ...);
+
+/** We only set the error state if its not already set */
+#define RaiseError(t, message, ...)                                     \
+  if(*aff4_get_current_error(NULL) == EZero) {                          \
+    raise_errors(t, "%s: (%s:%d) " message, __FUNCTION__, __FILE__, __LINE__, ## __VA_ARGS__); \
+  };
+
+#define LogWarnings(format, ...)		\
+  do {						\
+    RaiseError(EWarning, format, ## __VA_ARGS__);	\
+    PrintError();				\
+  } while(0);
+  
+#define ClearError()				\
+  do {*aff4_get_current_error(NULL) = EZero;} while(0);
+
+#define PrintError()				\
+  do {char *error_str; if(*aff4_get_current_error(&error_str)) fprintf(stdout, "%s", error_str); fflush(stdout); ClearError(); }while(0);
+
+#define CheckError(error)			\
+  (*aff4_get_current_error(NULL) == error)
+
+/** The current error state is returned by this function.
+
+    This is done in a thread safe manner.
+ */
+enum _error_type *aff4_get_current_error(char **error_str);
+
+
+// These macros are used when we need to do something which might
+// change the error state on the error path of a function.
+#define PUSH_ERROR_STATE { enum _error_type *tmp_error_p = aff4_get_current_error(NULL); enum _error_type tmp_error = *tmp_error_p; enum _error_type exception __attribute__((unused));
+
+#define POP_ERROR_STATE *tmp_error_p = tmp_error;};
+
+
+void error_init();
+
+#endif 	    /* !AFF4_ERRORS_H_ */
Index: /releases/1.0.1/python/experimental/include/class.h
===================================================================
--- /releases/1.0.1/python/experimental/include/class.h	(revision 275)
+++ /releases/1.0.1/python/experimental/include/class.h	(revision 275)
@@ -0,0 +1,447 @@
+/***************************************************
+  Classes and objects in C
+
+  This file makes it easy to implement classes and objects in C. To
+  define a class we need to perform three steps:
+
+  Define the class prototype. This is suitable to go in a .h file for
+  general use by other code.
+
+  Note all classes extend Object.
+
+  Example::
+
+CLASS(Foo, Object)
+    int x;
+    int y;
+
+    //This declares a method of a class Foo, called Con returning a
+    //Foo object. In other words it is a constructor.
+    Foo METHOD(Foo, Con, int x, int y);
+    int METHOD(Foo, add);
+
+END_CLASS
+
+Now we need to define some functions for the constructor and
+methods. Note that the constuctor is using ALLOCATE_CLASS to allocate
+space for the class structures. Callers may call with self==NULL to
+force allocation of a new class. Note that we do not call the
+constructor of our superclass implicitly here. (Calling the sperclass
+constructor is optional, but ALLOCATE_CLASS is not.).
+
+Foo Foo_Con(Foo self,int x,int y) {
+  self->x = x;
+  self->y = y;
+
+  return self;
+};
+
+int Foo_add(Foo this) {
+  return (this->x + this->y);
+};
+
+Now we need to define the Virtual function table - These are those
+functions and attributes which are defined in this class (over its
+superclass). Basically these are all those things in the class
+definition above, with real function names binding them. (Note that by
+convention we preceed the name of the method with the name of the
+class):
+
+VIRTUAL(Foo,Object)
+   VMETHOD(Con) = Foo_Con;
+   VMETHOD(add) = Foo_add;
+END_VIRTUAL
+
+We can use inheritance too:
+
+CLASS(Bar, Foo)
+   Bar METHOD(Bar, Con, char *something)
+END_CLASS
+
+Here Bar extends Foo and defines a new constructor with a different prototype:
+
+VIRTUAL(Bar,Foo)
+   VMETHOD(Con) = Bar_Con
+END_VIRTUAL
+
+If there is a function which expects a Foo, we will need to over ride
+the Foo constructor in the Bar, so the function will not see the
+difference between the Foo and Bar:
+
+CLASS(Bar,Foo)
+  int bar_attr;
+END_CLASS
+
+Foo Bar_Con(Foo self, int x, int y) {
+...
+}
+
+VIRTUAL(Bar, Foo)
+  VMETHOD(super.Con) = Bar_Con
+END_VIRTUAL
+
+Note that in this case we are over riding the Con method defined in
+Foo while creating derived Bar classes. The notation in the VIRTUAL
+table is to use super.Con, because Foo's Con method (the one we are
+over riding), can be located by using super.Con inside a Bar object.
+
+Imagine now that in Bar_Con we wish to use methods and attributes
+defined in Bar. Since Bar_Con over rides Bar's base class (Foo) it
+must have the prototype described above. Since self is of type Foo its
+impossible to use self->bar_attr (There is no bar_attr in Foo - its in
+Bar).
+
+In this case, we need to make a type cast to convice C that self is
+actually a Bar not a Foo:
+
+Foo Bar_Con(Foo self, int x, int y) {
+   Bar this = (Bar)self;
+
+   this->bar_attr=1
+};
+
+This allows us to access bars attributes.
+
+This is a general oddity with C style classes, which C++ and Java
+hide. In C we must always know which class defines which method and
+attribute and reference the right class's method. So for example if we
+want to call a Bar's add method:
+
+Bar a;
+
+a->super.add()
+
+because add is defined in Bar's super class (Foo). Constract this with
+C++ or Java which hide where methods are defined and simply make all
+methods appear like they were defined inside the derived class. This
+takes a while to get used to but the compiler will ensure that the
+references are correct - otherwise things will generally not compile
+properly.
+
+This difference can be used for good and bad. It is possible in C to
+call the base class's version of the method at any time (despite the
+fact it was over ridden). 
+
+For example:
+
+CLASS(Derived, Foo)
+      int METHOD(Derived, add);
+END_CLASS
+
+VIRTUAL(Derived, Foo)
+   VMETHOD(add) = Derived_add
+END_VIRTUAL
+
+If d is a Derived object, we can call Foo's version like this:
+d->super.add()
+
+But Derived's version is accessed by:
+d->add()
+
+Sometimes a derived class may want to over ride the base class's
+methods as well, in this case the VIRTUAL section should over ride
+super.add as well.
+
+*/
+/******************************************************
+# Copyright 2004: Commonwealth of Australia.
+#
+# Developed by the Computer Network Vulnerability Team,
+# Information Security Group.
+# Department of Defence.
+#
+# Michael Cohen <scudette@users.sourceforge.net>
+#
+# ******************************************************
+#  Version: FLAG  $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$
+# ******************************************************
+#
+# * 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 2
+# * 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, write to the Free Software
+# * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# ******************************************************/
+#ifndef __CLASS_H__
+#define __CLASS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef min
+#undef min
+#endif
+#define min(X, Y)  ((X) < (Y) ? (X) : (Y))
+
+#ifdef max
+#undef max
+#endif
+#define max(X, Y)  ((X) > (Y) ? (X) : (Y))
+
+
+#include <talloc.h>
+
+#define CLASS(class,super_class)                                 \
+  typedef struct class ## _t *class;                             \
+  int class ## _init(Object self);                               \
+  extern struct class ## _t __ ## class;                         \
+  struct class ## _t { struct super_class ## _t super;		 \
+  class   __class__;                                             \
+  super_class  __super__;
+
+
+#define METHOD(cls, name, ... )		\
+  (* name)(cls self, ## __VA_ARGS__ )
+
+  // Class methods are attached to the class but are not called with
+  // an instance. This is similar to the python class method or java
+  // static methods.
+#define CLASS_METHOD(name, ... )                \
+  (*name)(__VA_ARGS__)
+
+/***************************************************
+   This is a convenience macro which may be used if x if really large
+
+***************************************************/
+#define CALL(x, method, ... )			\
+  (x)->method((x), ## __VA_ARGS__)
+
+#define END_CLASS };
+
+/***************************************************
+   This is used to set the classes up for use:
+
+   class_init = checks the class template (__class) to see if it has
+   been allocated. otherwise allocates it in the global context.
+
+   class_Alloc = Allocates new memory for an instance of the
+   class. This is a recursive function calling each super class in
+   turn and setting the currently over ridden defaults. So for eample
+   suppose this class (foo) derives from bar, we first fill the
+   template with bars methods, and attributes. Then we over write
+   those with foos methods and attributes.
+
+**********************************************************/
+#define VIRTUAL(class,superclass)				\
+  struct class ## _t __ ## class;                                       \
+                                                                        \
+  int class ## _init(Object this) {                                    \
+  class self = (class)this;                                             \
+  if(self->__super__) return 1;                                         \
+  superclass ##_init(this);                                             \
+  this->__class__ = (Object)&__ ## class;                               \
+  self->__class__ = (class)&__ ## class;                               \
+  this->__super__ = (Object)&__ ## superclass;                          \
+  self->__super__ = (superclass)&__ ## superclass;                      \
+  this->__size = sizeof(struct class ## _t);                            \
+  this->__name__ = #class;
+
+#define SET_DOCSTRING(string)			\
+  ((Object)self)->__doc__ = string
+
+#define END_VIRTUAL return 1; }
+
+#define VMETHOD(method)				\
+  (self)->method
+
+#define VMETHOD_BASE(base, method)		\
+  (((base)self)->method)
+
+#define CLASS_ATTR(self, base, method)		\
+  (((base)self)->method)
+
+#define VATTR(attribute)			\
+  (self)->attribute
+
+#define NAMEOF(obj)				\
+  ((Object)obj)->__name__
+
+#define SIZEOF(obj)                             \
+  ((Object)obj)->__size
+
+#define DOCSTRING(obj)				\
+  ((Object)obj)->__doc__
+
+#define INIT_CLASS(class)                       \
+  class ## _init((Object)&__ ## class)
+
+/*************************************************************
+   This MACRO is used to construct a new Class using a constructor.
+
+    This is done to try and hide the bare (unbound) method names in
+    order to prevent name space pollution. (Bare methods may be
+    defined as static within the implementation file). This macro
+    ensures that class structures are initialised properly before
+    calling their constructors.
+
+   We require the following args:
+    class - the type of class to make
+    virt_class - The class where the method was defined
+    constructors - The constructor method to use
+    context - a talloc context to use.
+
+
+    Note that the class and virt_class do not have to be the same if
+    the method was not defined in the current class. For example
+    suppose Foo extends Bar, but method is defined in Bar but
+    inherited in Foo:
+
+    CONSTRUCT(Foo, Bar, super.method, context)
+
+    virt_class is Bar because thats where method was defined.
+*************************************************************/
+
+// The following only initialises the class if the __super__ element
+// is NULL. This is fast as it wont call the initaliser unnecessaily
+#define CONSTRUCT(class, virt_class, constructor, context, ... )        \
+  (class)( __## class.__super__ == NULL ?                               \
+           class ## _init((Object)&__ ## class) : 0,                    \
+           __## virt_class.__super__ == NULL ?                          \
+           virt_class ## _init((Object)&__ ## virt_class): 0,           \
+             ((virt_class)(&__ ## class))->constructor(                 \
+                       (virt_class)_talloc_memdup(context, &__ ## class, sizeof(struct class ## _t),  __location__ "(" #class ")"), \
+				   ## __VA_ARGS__) )
+
+/** This variant is useful when all we have is a class reference
+    (GETCLASS(Foo)) or &__Foo
+*/
+#define CONSTRUCT_FROM_REFERENCE(class, constructor, context, ... )	\
+  ( (class)->constructor(						\
+                       (void *)_talloc_memdup(context, ((Object)class), ((Object)class)->__size,  __location__ "(" #class "." #constructor ")"), \
+		      ## __VA_ARGS__) )
+
+/** Finds the size of the class in x */
+#define CLASS_SIZE(class)			\
+  ((Object)class)->__size
+
+typedef struct Object_t *Object;
+
+struct Object_t {
+  //A reference to a class instance - this is useful to be able to
+  //tell which class an object really belongs to:
+  Object __class__;
+
+  //And its super class:
+  Object __super__;
+
+  char *__name__;
+
+  /** Objects may have a doc string associated with them. */
+  char *__doc__;
+
+  //How large the class is:
+  int __size;
+};
+
+#define SUPER(base, imp, method, ...)            \
+  ((base)&__ ## imp)->method((base)self, ## __VA_ARGS__)
+
+#define GETCLASS(class)				\
+  (Object)&__ ## class
+
+// Returns true if the obj belongs to the class
+#define ISINSTANCE(obj,class)			\
+  (((Object)obj)->__class__ == GETCLASS(class))
+
+// This is a string comparison version of ISINSTANCE which works
+// across different shared objects.
+#define ISNAMEINSTANCE(obj, class)		\
+  (obj && !strcmp(class, NAMEOF(obj)))
+
+// We need to ensure that class was properly initialised:
+#define ISSUBCLASS(obj,class)			\
+  issubclass((Object)obj, (Object)&__ ## class)
+
+#define CLASSOF(obj)				\
+  ((Object)obj)->__class__
+
+void Object_init(Object);
+
+extern struct Object_t __Object;
+
+int issubclass(Object obj, Object class);
+
+extern void unimplemented(Object self);
+
+#define UNIMPLEMENTED(class, method)             \
+  ((class)self)->method = (void *)unimplemented;
+
+#define ZSTRING_NO_NULL(str) str , (strlen(str))
+#define ZSTRING(str) str , (strlen(str)+1)
+
+  // These dont do anything but are useful to indicate when a function
+  // parameter is used purely to return a value. They are now used to
+  // assist the python binding generator in generating the right sort
+  // of code
+#define OUT
+#define IN
+
+  // This modifier before a class means that the class is abstract and
+  // does not have an implementation - we do not generate bindings for
+  // that class then.
+#define ABSTRACT
+
+  // This modifier indicates that the following pointer is pointing to
+  // a borrowed reference - callers must not free the memory after use.
+#define BORROWED
+
+  // This tells the autobinder to generated bindings to this struct
+#define BOUND
+
+  // This tells the autobinder to ignore this class as it should be
+  // private to the implementation - external callers should not
+  // access this.
+#define PRIVATE
+
+  // This attribute of a method means that this method is a
+  // desctructor - the object is no longer valid after this method is
+  // run
+#define DESTRUCTOR
+
+  // including this after an argument definition will cause the
+  // autogenerator to assign default values to that parameter and make
+  // it optional
+#define DEFAULT(x)
+
+  // This explicitely denote that the type is a null terminated char
+  // ptr as opposed to a pointer to char and length.
+#define ZString char *
+
+  /* The following is a direction for the autogenerator to proxy the
+     given class. This is done in the following way:
+
+1) a new python type is created called Proxy_class_name() with a
+constructor which takes a surrogate object.
+
+2) The proxy class contains a member "base" of the type of the proxied
+C class.
+
+3) The returned python object may be passed to any C functions which
+expect the proxied class, and internal C calls will be converted to
+python method calls on the proxied object.
+  */
+#define PROXY_CLASS(name)
+
+  /* This signals the autogenerator to bind the named struct */
+#define BIND_STRUCT(name)
+
+  // This means that the memory owned by this pointer is managed
+  // externally (not using talloc). It is dangerous to use this
+  // keyword too much because we are unable to manage its memory
+  // appropriately and it can be free'd from under us.
+#define FOREIGN
+
+#endif
+#ifdef __cplusplus
+} /* closing brace for extern "C" */
+#endif
Index: /releases/1.0.1/python/experimental/inspect.py
===================================================================
--- /releases/1.0.1/python/experimental/inspect.py	(revision 275)
+++ /releases/1.0.1/python/experimental/inspect.py	(revision 275)
@@ -0,0 +1,278 @@
+#!/usr/bin/python
+""" Module to inspect the contents of an AFF4 volume """
+import sys, pdb, os
+import re, cmd, readline
+import optparse
+import pyaff4
+import shlex
+import fnmatch, time
+import subprocess
+import readline
+
+time.sleep(1)
+
+## Set more sane completer delimiters
+readline.set_completer_delims(' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?')
+
+## Configure colors
+colors = {}
+colors['cyan']   = '\033[96m'
+colors['purple'] = '\033[95m'
+colors['blue']   = '\033[94m'
+colors['green']  = '\033[92m'
+colors['yellow'] = '\033[93m'
+colors['red']    = '\033[91m'
+colors['end']    = '\033[0m'
+
+#If the output is not a terminal, remove the colors
+if not sys.stdout.isatty():
+   for key, value in colors.iteritems():
+      colors[key] = ''
+
+parser = optparse.OptionParser()
+(options, args) = parser.parse_args()
+
+## Load the specified args as volumes
+oracle = pyaff4.Resolver()
+VOLUMES = []
+STREAMS = {}
+
+for arg in args:
+    volume = pyaff4.RDFURN()
+    volume.set(arg)
+    if oracle.load(volume):
+        VOLUMES.append(volume)
+        urn = pyaff4.RDFURN()
+        iter = oracle.get_iter(volume, pyaff4.AFF4_CONTAINS)
+        while oracle.iter_next(iter, urn):
+           print urn.value
+           if not urn: break
+
+           ## We store is as a simplified path
+           path = "/%s%s" % (urn.parser.netloc, urn.parser.query)
+           STREAMS[path] = urn
+
+class Inspector(cmd.Cmd):
+    prompt = "/:>"
+    intro = "Inspecting the volumes %s. Type 'help' for help." % args
+    CWD = "/"
+
+    def cmdloop(self, *args):
+        while 1:
+            try:
+                if not cmd.Cmd.cmdloop(self, *args):
+                    break
+            except KeyboardInterrupt,e:
+                print "Type %sexit%s to exit, %shelp%s for help" % (
+                    colors['yellow'],
+                    colors['end'],
+                    colors['cyan'],
+                    colors['end'])
+
+    def do_EOF(self, args):
+        """ Exit this program """
+        print "%sGoodbye%s" % (colors['yellow'],
+                               colors['end'])
+        return True
+
+    def do_cd(self, line):
+        """ Change directory """
+        args = shlex.split(line)
+        if not args[0].startswith("/"):
+            args[0] = self.CWD + args[0]
+
+        try:
+            potential_cd = os.path.normpath(args[0])
+            if not potential_cd.endswith('/'):
+                potential_cd += "/"
+
+            for s in STREAMS:
+                if s.startswith(potential_cd):
+                    self.CWD = potential_cd
+                    self.prompt = "%s:>" % self.CWD
+                    return
+
+            raise IOError("No such directory %s" % potential_cd)
+        except IndexError:
+            print "CWD: %s" % self.CWD
+
+    def complete_cd(self, *args):
+        return self.complete_stream(*args)
+
+    def do_help(self, line):
+        """ Provide help for commands """
+        args = shlex.split(line)
+        if not args:
+            ## List all commands
+            for k in dir(self):
+                if k.startswith("do_"):
+                    method = getattr(self, k)
+                    print "%s%s%s: %s" % (colors['red'],
+                                          k[3:],
+                                          colors['end'],
+                                          method.__doc__)
+            return
+
+        for arg in args:
+            try:
+                method = getattr(self, "do_%s" % arg)
+            except AttributeError:
+                print "%sError:%s Command %s%s%s not found" % (colors['red'],
+                                                               colors['end'],
+                                                               colors['yellow'],
+                                                               arg,
+                                                               colors['end'])
+
+            print "%s: %s" % (arg, method.__doc__)
+
+    def do_ls(self, line):
+        """ List streams matching a glob expression """
+        globs = shlex.split(line)
+        if not globs: globs=[self.CWD]
+
+        result = []
+
+        for s in STREAMS:
+            for g in globs:
+                if fnmatch.fnmatch(s, g + "*"):
+                    if s.startswith(self.CWD):
+                        s = s[len(self.CWD):]
+
+                    path = s.split("/")[0]
+                    if path == s:
+                        decoration = colors['blue']
+                    else:
+                        decoration = colors['end']
+
+                    if path not in result:
+                        print "%s%s%s" % (decoration, path, colors['end'])
+                        result.append(path)
+
+    def do_less(self, line):
+        """ Read the streams specified and pipe them through the pager """
+        globs = shlex.split(line)
+        for s in STREAMS:
+            for g in globs:
+                if not g.startswith("/"):
+                    g = self.CWD + g
+
+                g = os.path.normpath(g)
+                if fnmatch.fnmatch(s, g):
+                    ## Try to open the stream
+                    try:
+                        fd = oracle.open(STREAMS[s], 'r')
+                    except IOError, e:
+                        raise
+
+                    pager = os.environ.get("PAGER","less")
+                    pipe=os.popen(pager,"w")
+
+                    while 1:
+                        data = fd.read(1024 * 1024)
+                        if not data: break
+
+                        pipe.write(data)
+
+                    pipe.close()
+
+    def do_cp(self, line):
+        """ Copy a stream from a source to a destination. """
+        globs = shlex.split(line)
+        src = globs[0]
+        dest = globs[1]
+        if(len(globs) > 2):
+           print "usage: cp src dest"
+           return
+
+        if not src.startswith("/"):
+           src = self.CWD + src
+
+        src = os.path.normpath(src)
+        src_urn = pyaff4.RDFURN()
+        src_urn.set("aff4:/" + src)
+        ## Try to open the stream
+        try:
+           fd = oracle.open(src_urn, 'r')
+        except IOError, e:
+           raise
+
+        dest_fd = open(dest, "w")
+        while 1:
+           data = fd.read(1024 * 1024)
+           if not data: break
+
+           dest_fd.write(data)
+
+        dest_fd.close()
+
+    def complete_cp(self, *args):
+       return self.complete_stream(*args)
+
+    def complete_less(self, *args):
+        return self.complete_stream(*args)
+
+    def _display_attribute(self, iter):
+       while 1:
+          obj = oracle.alloc_from_iter(iter)
+          if not obj: break
+
+          print "    -> type (%s) " % (obj.dataType)
+          print "    -> data (%s) " % (obj.serialise(iter.urn))
+
+
+    def do_resolve(self, line):
+       globs = shlex.split(line)
+       attribute = pyaff4.XSDString()
+       subject = pyaff4.RDFURN()
+       iter = pyaff4.RESOLVER_ITER()
+
+       subject.set(globs[0])
+       try:
+          attribute.set(globs[1])
+          print attribute.value
+          self._display_attribute(iter)
+
+       except IndexError:
+          ## Just display all the attributes
+          while oracle.attributes_iter(subject, attribute, iter):
+             print attribute.value
+             self._display_attribute(iter)
+
+    def complete_stream(self, text, line, begidx, endidx):
+        if not text.startswith("/"):
+            text = self.CWD + text
+
+        if not text:
+            completions = STREAMS.keys()
+
+        completions = [ text + f[len(text):].split("/")[0]
+                        for f in STREAMS.keys()
+                        if f.startswith(text)
+                        ]
+
+        return completions
+
+    def do_pwd(self, line):
+        print "CWD: %s" % self.CWD
+
+    def do_exit(self, *args):
+        """ Exits the program """
+        return self.do_EOF(*args)
+
+    def emptyline(self):
+        return
+
+    def onecmd(self, line):
+        try:
+            return cmd.Cmd.onecmd(self, line)
+        except Exception,e:
+            print "%sError:%s %s%s%s %s" % (colors['red'],
+                                            colors['end'],
+                                            colors['yellow'],
+                                            e.__class__.__name__,
+                                            colors['end'],
+                                            e)
+
+        return None
+
+Inspector().cmdloop()
Index: /releases/1.0.1/python/experimental/lexer.py
===================================================================
--- /releases/1.0.1/python/experimental/lexer.py	(revision 275)
+++ /releases/1.0.1/python/experimental/lexer.py	(revision 275)
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+# ******************************************************
+# Michael Cohen <scudette@users.sourceforge.net>
+#
+# ******************************************************
+#  Version: FLAG $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$
+# ******************************************************
+#
+# * 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 2
+# * 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, write to the Free Software
+# * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# ******************************************************
+""" A simple feed lexer.
+"""
+
+import re,sys
+
+class Lexer:
+    """ A generic feed lexer """
+    ## The following is a description of the states we have and the
+    ## way we move through them: format is an array of
+    ## [ state_re, re, token/action, next state ]
+    tokens = []
+    state = "INITIAL"
+    buffer = ''
+    error = 0
+    verbose = 0
+    state_stack = []
+    processed = 0
+    processed_buffer = ''
+    saved_state = None
+    flags = 0
+    
+    def __init__(self, verbose=0, fd=None):
+        if not self.verbose:
+            self.verbose = verbose
+
+        if len(self.tokens[0])==4:
+            for row in self.tokens:
+                row.append(re.compile(row[0], re.DOTALL))
+                row.append(re.compile(row[1], re.DOTALL | re.M | re.S | self.flags ))
+                
+        self.fd = fd
+
+    def save_state(self, t=None, m=None):
+        """ Returns a dict which represents the current state of the lexer.
+
+        When provided to restore_state, the lexer is guaranteed to be
+        in the same state as when the save_state was called.
+
+        Note that derived classes may need to extend this.
+        """
+        ## Cant save our state if we have errors. We need to guarantee
+        ## that we rewind to a good part of the file.
+        if self.error: return
+        try:
+            end = m.end()
+        except: end = 0
+        
+        self.saved_state = dict(state_stack = self.state_stack[:],
+                                processed = self.processed - end,
+                                processed_buffer = self.processed_buffer,
+                                readptr = self.fd.tell() - len(self.buffer) - end,
+                                state = self.state,
+                                objects = self.objects[:],
+                                error = self.error,
+                                )
+
+        if self.verbose>1:
+            print "Saving state %s" % self.processed
+
+    def restore_state(self):
+        state = self.saved_state
+        if not state: return
+        
+        self.state_stack = state['state_stack']
+        self.processed = state['processed']
+        self.processed_buffer = state['processed_buffer']
+        self.buffer = ''
+        self.fd.seek(state['readptr'])
+        self.state = state['state']
+        self.objects = state['objects']
+        self.error = state['error']
+        if self.verbose>1:
+            print "Restoring state to offset %s" % self.processed
+
+    def next_token(self, end = True):
+        ## Now try to match any of the regexes in order:
+        current_state = self.state
+        for state_re, re_str, token, next, state, regex in self.tokens:
+            ## Does the rule apply for us now?
+            if state.match(current_state):
+                if self.verbose > 2:
+                    print "%s: Trying to match %r with %r" % (self.state, self.buffer[:10], re_str)
+                m = regex.match(self.buffer)
+                if m:
+                    if self.verbose > 3:
+                        print "%s matched %s" % (re_str, m.group(0).encode("utf8"))
+                    ## The match consumes the data off the buffer (the
+                    ## handler can put it back if it likes)
+                    self.processed_buffer += self.buffer[:m.end()]
+                    self.buffer = self.buffer[m.end():]
+                    self.processed += m.end()
+
+                    ## Try to iterate over all the callbacks specified:
+                    for t in token.split(','):
+                        try:
+                            if self.verbose > 0:
+                                print "0x%X: Calling %s %r" % (self.processed, t, m.group(0))
+                            cb = getattr(self, t, self.default_handler)
+                        except AttributeError:
+                            continue
+
+                        ## Is there a callback to handle this action?
+                        next_state = cb(t, m)
+                        if next_state == "CONTINUE":
+                            continue
+                        
+                        elif next_state:
+                            next = next_state
+                            self.state = next
+
+                    
+                    if next:
+                        self.state = next
+                
+                    return token
+
+        ## Check that we are making progress - if we are too full, we
+        ## assume we are stuck:
+        if end and len(self.buffer)>0 or len(self.buffer)>1024:
+            self.processed_buffer += self.buffer[:1]
+            self.buffer = self.buffer[1:]
+            self.ERROR("Lexer Stuck, discarding 1 byte (%r) - state %s" % (self.buffer[:10], self.state))
+            return "ERROR"
+
+        ## No token were found
+        return None
+    
+    def feed(self, data):
+        self.buffer += data
+
+    def empty(self):
+        return not len(self.buffer)
+
+    def default_handler(self, token, match):
+        if self.verbose > 2:
+            print "Default handler: %s with %r" % (token,match.group(0))
+
+    def ERROR(self, message = None, weight =1):
+        if self.verbose > 0 and message:
+            print "Error(%s): %s" % (weight,message)
+
+        self.error += weight
+
+    def PUSH_STATE(self, token = None, match = None):
+        if self.verbose > 1:
+            print "Storing state %s" % self.state
+        self.state_stack.append(self.state)
+
+    def POP_STATE(self, token = None, match = None):
+        try:
+            state = self.state_stack.pop()
+            if self.verbose > 1:
+                print "Returned state to %s" % state
+                
+            return state
+        except IndexError:
+            print "Tried to pop the state but failed - possible recursion error"
+            return None
+
+    def close(self):
+        """ Just a conveniece function to force us to parse all the data """
+        while self.next_token(): pass
+
+class SelfFeederMixIn(Lexer):
+    """ This mixin is used to make a lexer which feeds itself one
+    sector at the time.
+
+    Note that self.fd must be the fd we read from.
+    """
+    def parse_fd(self, fd):
+        self.feed(fd.read())
+        while self.next_token(): pass
+
Index: /releases/1.0.1/python/experimental/regfi/class.c
===================================================================
--- /releases/1.0.1/python/experimental/regfi/class.c	(revision 275)
+++ /releases/1.0.1/python/experimental/regfi/class.c	(revision 275)
@@ -0,0 +1,63 @@
+/******************************************************
+# Copyright 2004: Commonwealth of Australia.
+#
+# Developed by the Computer Network Vulnerability Team,
+# Information Security Group.
+# Department of Defence.
+#
+# Michael Cohen <scudette@users.sourceforge.net>
+#
+# ******************************************************
+#  Version: FLAG  $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$
+# ******************************************************
+#
+# * 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 2
+# * 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, write to the Free Software
+# * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# ******************************************************/
+#include "class.h"
+
+#define BUFF_SIZE 1024
+
+// Noone should instantiate Object directly. this should be already
+// allocated therefore:
+
+void Object_init(Object this) {
+  this->__class__ = &__Object;
+  this->__super__ = NULL;
+}
+
+struct Object_t __Object = {
+  .__class__ = &__Object,
+  .__super__ = &__Object,
+  .__size = sizeof(struct Object_t),
+  .__name__ = "Object"
+};
+
+int issubclass(Object obj, Object class) {
+  obj = obj->__class__;
+  while(1) {
+    if(obj == class->__class__)
+      return 1;
+
+    obj=obj->__super__;
+
+    if(obj == &__Object || obj==NULL) 
+      return 0;
+  };
+}
+
+void unimplemented(Object self) {
+  printf("%s contains unimplemented functions.. is it an abstract class?\n", NAMEOF(self));
+  abort();
+}
Index: /releases/1.0.1/python/experimental/regfi/error.c
===================================================================
--- /releases/1.0.1/python/experimental/regfi/error.c	(revision 275)
+++ /releases/1.0.1/python/experimental/regfi/error.c	(revision 275)
@@ -0,0 +1,84 @@
+/*
+** error.c
+** 
+** Made by (mic)
+** Login   <mic@laptop>
+** 
+** Started on  Mon Mar 15 20:45:09 2010 mic
+** Last update Sun May 12 01:17:25 2002 Speed Blue
+*/
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <talloc.h>
+#include "aff4_errors.h"
+
+/** These slots carry the TLS error keys */
+static pthread_key_t error_str_slot;
+static pthread_key_t error_value_slot;
+
+void error_dest(void *slot) {
+  if(slot) talloc_free(slot);
+}
+
+void *raise_errors(enum _error_type t, char *reason, ...) {
+  char *error_buffer;
+  // This has to succeed:
+  enum _error_type *type = aff4_get_current_error(&error_buffer);
+
+  if(reason) {
+    va_list ap;
+    va_start(ap, reason);
+
+    vsnprintf(error_buffer, ERROR_BUFFER_SIZE-1, reason,ap);
+    error_buffer[ERROR_BUFFER_SIZE-1]=0;
+    va_end(ap);
+  };
+
+  //update the error type
+  *type = t;
+
+  return NULL;
+}
+
+static int error_subsystem_initialised=0;
+
+enum _error_type *aff4_get_current_error(char **error_buffer) {
+  enum _error_type *type;
+
+  if(!error_subsystem_initialised) error_init();
+
+  type = pthread_getspecific(error_value_slot);
+
+  // This is optional
+  if(error_buffer) {
+    *error_buffer = pthread_getspecific(error_str_slot);
+
+  // If TLS buffers are not set we need to create them
+    if(!*error_buffer) {
+      *error_buffer =talloc_size(NULL, ERROR_BUFFER_SIZE);
+      pthread_setspecific(error_str_slot, *error_buffer);
+    };
+  };
+
+  if(!type) {
+    type = talloc(NULL, enum _error_type);
+    pthread_setspecific(error_value_slot, type);
+  };
+
+  return type;
+}
+
+/** Initialise the error subsystem */
+void error_init() {
+  error_subsystem_initialised = 1;
+
+  // We create the error buffer slots
+  if(pthread_key_create(&error_str_slot, error_dest) ||
+     pthread_key_create(&error_value_slot, error_dest)) {
+    printf("Unable to set up TLS variables\n");
+    abort();
+  };
+}
Index: /releases/1.0.1/python/experimental/regfi/pyregfi.h
===================================================================
--- /releases/1.0.1/python/experimental/regfi/pyregfi.h	(revision 275)
+++ /releases/1.0.1/python/experimental/regfi/pyregfi.h	(revision 275)
@@ -0,0 +1,182 @@
+/*
+ * Top-level definitions for pyregfi to be processed by Michael Cohen's 
+ * automated Python bindings generator.
+ *
+ * Copyright (C) 2010 Michael I. Cohen
+ * Copyright (C) 2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: $
+ *
+ */
+
+#ifndef   	PYREGFI_H_
+# define   	PYREGFI_H_
+
+#include "class.h"
+#include "aff4_errors.h"
+#include "regfi.h"
+
+/** Forward declarations */
+struct RegistryFile_t;
+struct RegistryKey_t;
+struct SubkeyIterator_t;
+struct ValueIterator_t;
+struct TreeIterator_t;
+
+BIND_STRUCT(REGFI_NK)
+BIND_STRUCT(REGFI_VK)
+BIND_STRUCT(REGFI_DATA)
+
+/** This is the base class for data objects */
+CLASS(RawData, Object)
+    const REGFI_DATA *data;
+    const REGFI_VK *rec;
+
+    RawData METHOD(RawData, Con, REGFI_DATA *data, REGFI_VK *record);
+
+    /** Return the raw buffer as a string. By default we only return
+        this much data - specify a required length to return more.
+
+        DEFAULT(len) = 4096;
+    */
+    int METHOD(RawData, get_value, OUT char *buffer, int len);
+END_CLASS
+
+CLASS(DataString, RawData)
+     BORROWED char *METHOD(DataString, get_value);
+END_CLASS
+
+CLASS(DWORDData, RawData)
+     uint64_t METHOD(DWORDData, get_value);
+END_CLASS
+
+/** This is an iterator for traversing an entire registry hive */
+CLASS(TreeIterator, Object)
+     PRIVATE REGFI_ITERATOR *iter;
+     PRIVATE struct RegistryFile_t *file;
+     PRIVATE bool root_traversed;
+
+     struct TreeIterator_t *METHOD(TreeIterator, Con, struct RegistryFile_t *file,
+				   char **path, REGFI_ENCODING encoding);
+
+     void METHOD(TreeIterator, __iter__);
+     struct RegistryKey_t *METHOD(TreeIterator, iternext);
+
+
+     int METHOD(TreeIterator, down);
+     int METHOD(TreeIterator, up);
+
+     struct RegistryKey_t *METHOD(TreeIterator, current);
+     int METHOD(TreeIterator, to_root);
+
+END_CLASS
+
+
+/** XXX */
+CLASS(RegistryKey, Object)
+     struct RegistryFile_t *file;
+     const REGFI_NK *key;
+
+     struct RegistryKey_t *METHOD(RegistryKey, Con,
+				  struct RegistryFile_t *file, REGFI_NK *base_key);
+
+     struct SubkeyIterator_t *METHOD(RegistryKey, subkeys);
+     struct ValueIterator_t *METHOD(RegistryKey, values);
+
+END_CLASS
+
+
+/** This is an iterator for reading keys from the registry */
+CLASS(SubkeyIterator, Object)
+     struct RegistryFile_t *file;
+     PRIVATE const REGFI_SUBKEY_LIST *list;
+     PRIVATE uint32_t cur;
+     
+     SubkeyIterator METHOD(SubkeyIterator, Con, 
+			   struct RegistryFile_t *file, REGFI_NK *key);
+
+     void METHOD(SubkeyIterator, __iter__);
+     RegistryKey METHOD(SubkeyIterator, iternext);
+END_CLASS
+
+
+
+/** This is an iterator for reading values from the registry */
+CLASS(ValueIterator, Object)
+     struct RegistryFile_t *file;
+     PRIVATE const REGFI_VALUE_LIST *list;
+     PRIVATE uint32_t cur;
+     
+     ValueIterator METHOD(ValueIterator, Con, 
+			  struct RegistryFile_t *file, REGFI_NK *key);
+
+     void METHOD(ValueIterator, __iter__);
+     REGFI_VK *METHOD(ValueIterator, iternext);
+END_CLASS
+
+
+
+CLASS(RegistryFile, Object)
+  REGFI_FILE *reg;
+  int fd;
+
+  RegistryFile METHOD(RegistryFile, Con, char *filename);
+
+  /* Get an iterator for a specific path in the register if path is
+     specified.
+
+     XXX: can we switch to UTF-8 and have Python properly import that?
+
+     DEFAULT(path) == NULL;
+     DEFAULT(encoding) = REGFI_ENCODING_ASCII;
+  */
+  TreeIterator METHOD(RegistryFile, TreeIterator, char **path, REGFI_ENCODING encoding);
+
+  /** Set the verbosity level of messages generated by the library for the 
+ *  current thread.
+ *
+ * @param mask   An integer representing the types of messages desired.
+ *               Acceptable values are created through bitwise ORs of 
+ *               REGFI_LOG_* values.  For instance, if only errors and
+ *               informational messages were desired (but not warnings),
+ *               then one would specify: REGFI_LOG_ERROR|REGFI_LOG_INFO
+ *               By default the message mask is: REGFI_LOG_ERROR|REGFI_LOG_WARN.
+ *
+ * @return       true on success and false on failure.  Failure occurs if 
+ *               underlying pthread functions fail.  errno is set in this case.
+ *
+ * Message masks are set in a thread-specific way.  If one were to set a message
+ * mask in one thread and then spawn a new thread, then the new thread will have
+ * it's message mask reset to the default.  This function may be called at any 
+ * time and will take effect immediately for the current thread.
+ *
+ * @note When a non-zero message mask is set, messages will
+ *       accumulate in memory without limit if they are not fetched using
+ *       @ref regfi_get_log_str and subsequently freed by the caller.  It is
+ *       recommended that messsages be fetched after each regfi API call in
+ *       order to provide the most context.
+ *
+ * @ingroup regfiBase
+ */
+  int METHOD(RegistryFile, set_log_mask, uint16_t mask);
+
+  
+END_CLASS
+
+
+void pyregfi_init();
+
+#endif 	    /* !PYREGFI_H_ */
Index: /releases/1.0.1/python/experimental/regfi/regfi.c
===================================================================
--- /releases/1.0.1/python/experimental/regfi/regfi.c	(revision 275)
+++ /releases/1.0.1/python/experimental/regfi/regfi.c	(revision 275)
@@ -0,0 +1,443 @@
+/*
+ * pyregfi/libregfi glue code 
+ *
+ * Copyright (C) 2010 Michael I. Cohen
+ * Copyright (C) 2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: $
+ */
+
+#include "pyregfi.h"
+
+static int RegistryFile_dest(void *self) 
+{
+  RegistryFile this = (RegistryFile)self;
+
+  regfi_free(this->reg);
+  close(this->fd);
+
+  return 0;
+}
+
+static RegistryFile RegistryFile_Con(RegistryFile self, char *filename) 
+{
+  self->fd = open(filename, O_RDONLY);
+  if(self->fd < 0) 
+  {
+    RaiseError(EIOError, "Unable to open %s", filename);
+    goto error;
+  }
+
+  self->reg = regfi_alloc(self->fd);
+
+  if(!self->reg) 
+  {
+    RaiseError(ERuntimeError, "REGFI Error: %s", regfi_log_get_str());
+    /*char* e = regfi_log_get_str();*/
+    /*fprintf(stderr, "%p\n", e);*/
+    goto error;
+  }
+
+  talloc_set_destructor((void *)self, RegistryFile_dest);
+  return self;
+
+ error:
+  talloc_free(self);
+  return NULL;
+}
+
+static TreeIterator RegistryFile_TreeIterator(RegistryFile self, char **path, REGFI_ENCODING encoding) 
+{
+  return CONSTRUCT(TreeIterator, TreeIterator, Con, NULL, self, path, encoding);
+}
+
+
+VIRTUAL(RegistryFile, Object) {
+  VMETHOD(Con) = RegistryFile_Con;
+  VMETHOD(TreeIterator) = RegistryFile_TreeIterator;
+} END_VIRTUAL
+
+
+static int RegistryKey_dest(void *self) 
+{
+  RegistryKey this = (RegistryKey)self;
+
+  talloc_unlink(this, (void*)this->key);
+
+  return 0;
+}
+
+static RegistryKey RegistryKey_Con(RegistryKey self, 
+				   RegistryFile file, REGFI_NK* base_key)
+{
+  if(base_key == NULL)
+    goto error;
+
+  self->key = base_key;
+  self->file = file;
+  talloc_reference(self, self->key);
+  talloc_set_destructor((void *)self, RegistryKey_dest);
+
+  return self;
+
+ error:
+  talloc_free(self);
+  return NULL;
+}
+
+/* XXX: would be nice to change this into a property, rather than a function,
+ *      but that would require a custom __getattr__.  Can that be done? */
+static SubkeyIterator RegistryKey_subkeys(RegistryKey self)
+{
+  return CONSTRUCT(SubkeyIterator, SubkeyIterator, Con, NULL, self->file, self->key);
+}
+
+/* XXX: would be nice to change this into a property, rather than a function,
+ *      but that would require a custom __getattr__.  Can that be done? */
+static SubkeyIterator RegistryKey_values(RegistryKey self)
+{
+  return CONSTRUCT(ValueIterator, ValueIterator, Con, NULL, self->file, self->key);
+}
+
+
+VIRTUAL(RegistryKey, Object) {
+  VMETHOD(Con) = RegistryKey_Con;
+  VMETHOD(subkeys) = RegistryKey_subkeys;
+  VMETHOD(values) = RegistryKey_values;
+} END_VIRTUAL
+
+
+static int TreeIterator_dest(void *self) 
+{
+  TreeIterator this = (TreeIterator)self;
+
+  regfi_iterator_free(this->iter);
+  return 0;
+}
+
+static TreeIterator TreeIterator_Con(TreeIterator self, 
+				     RegistryFile file, 
+				     char **path,
+				     REGFI_ENCODING encoding)
+{
+  self->iter = regfi_iterator_new(file->reg, encoding);
+  self->file = file;
+
+  if(!self->iter) 
+  {
+    RaiseError(ERuntimeError, "Error: %s", regfi_log_get_str());
+    goto error;
+  }
+
+  talloc_set_destructor((void*)self, TreeIterator_dest);
+
+  /* Traverse to the path */
+  if(path[0]) 
+  {
+    if(!regfi_iterator_walk_path(self->iter, (const char **)path)) 
+    {
+      RaiseError(ERuntimeError, "Unable to walk down key path");
+      goto error;
+    }
+  }
+
+  self->root_traversed = false;
+
+  return self;
+ error:
+  return NULL;
+}
+
+static void TreeIterator__iter__(TreeIterator self) 
+{
+  return;
+}
+
+
+static RegistryKey TreeIterator_next(TreeIterator self)
+{
+  if(!self->root_traversed)
+    self->root_traversed = true;
+  else if(!regfi_iterator_down(self->iter))
+  {
+    do
+    {
+      if(!regfi_iterator_up(self->iter))
+	return NULL;
+    } while(!regfi_iterator_next_subkey(self->iter));
+
+    /* XXX: This is an error condition.  
+     *      Probably should throw an exception or something. */
+    if(!regfi_iterator_down(self->iter))
+      return NULL;
+  }
+
+  regfi_iterator_first_subkey(self->iter);
+  return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file,
+		   regfi_iterator_cur_key(self->iter));
+}
+
+static int TreeIterator_down(TreeIterator self) 
+{
+  int result = regfi_iterator_down(self->iter);
+  regfi_iterator_first_subkey(self->iter);
+  regfi_iterator_first_value(self->iter);
+  return result;
+}
+
+static int TreeIterator_up(TreeIterator self) 
+{
+  return regfi_iterator_up(self->iter);
+}
+
+static RegistryKey TreeIterator_current(TreeIterator self)
+{
+  return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file,
+		   regfi_iterator_cur_key(self->iter));
+}
+
+static int TreeIterator_to_root(TreeIterator self)
+{
+  return regfi_iterator_to_root(self->iter);
+}
+
+
+VIRTUAL(TreeIterator, Object) {
+  VMETHOD(Con) = TreeIterator_Con;
+  VMETHOD(iternext) = TreeIterator_next;
+  VMETHOD(down) = TreeIterator_down;
+  VMETHOD(up) = TreeIterator_up;
+  VMETHOD(current) = TreeIterator_current;
+  VMETHOD(to_root) = TreeIterator_to_root;
+  VMETHOD(__iter__) = TreeIterator__iter__;
+  /*  VMETHOD(list_values) = TreeIterator_list_values;*/
+} END_VIRTUAL
+
+
+
+static int SubkeyIterator_dest(void *self) 
+{
+  SubkeyIterator this = (SubkeyIterator)self;
+
+  talloc_unlink(this, (void*)this->list);
+
+  return 0;
+}
+
+static SubkeyIterator SubkeyIterator_Con(SubkeyIterator self, 
+					 struct RegistryFile_t* file, 
+					 REGFI_NK* key)
+{
+  /* XXX: add a talloc reference? */
+  self->file = file;
+
+  /* Add a reference to the list */
+  self->list = key->subkeys;
+  talloc_reference(self, self->list);
+
+  self->cur = 0;
+  talloc_set_destructor((void *)self, SubkeyIterator_dest);
+
+  return self;
+}
+
+static void SubkeyIterator__iter__(SubkeyIterator self)
+{
+  return;
+}
+
+static RegistryKey SubkeyIterator_iternext(SubkeyIterator self)
+{
+  const REGFI_NK* nk;
+
+  if(self->list != NULL && self->cur < self->list->num_keys)
+  {
+    /* XXX: can we switch to UTF-8 and have Python properly import that? */
+    nk = regfi_load_key(self->file->reg, 
+			self->list->elements[self->cur].offset+REGFI_REGF_SIZE,
+			REGFI_ENCODING_ASCII, true);
+    self->cur++;
+    return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file, nk);
+  }
+
+  return NULL;
+}
+
+VIRTUAL(SubkeyIterator, Object) {
+  VMETHOD(Con) = SubkeyIterator_Con;
+  VMETHOD(__iter__) = SubkeyIterator__iter__;
+  VMETHOD(iternext) = SubkeyIterator_iternext;
+} END_VIRTUAL
+
+
+
+static int ValueIterator_dest(void *self) 
+{
+  ValueIterator this = (ValueIterator)self;
+
+  if(this->list != NULL)
+    talloc_unlink(this, (void*)this->list);
+
+  return 0;
+}
+
+static ValueIterator ValueIterator_Con(ValueIterator self,
+				       struct RegistryFile_t* file, 
+				       REGFI_NK* key)
+{
+  /* XXX: add a talloc reference? */
+  self->file = file;
+  self->cur = 0;
+
+  /* Add a reference to the list */
+  self->list = key->values;
+  if(self->list != NULL)
+    talloc_reference(self, self->list);
+
+  talloc_set_destructor((void *)self, ValueIterator_dest);
+
+  return self;
+}
+
+static void ValueIterator__iter__(ValueIterator self)
+{
+  return;
+}
+
+static REGFI_VK* ValueIterator_iternext(ValueIterator self)
+{
+  const REGFI_VK* vk;
+
+  if(self->list != NULL && self->cur < self->list->num_values)
+  {
+    /* XXX: can we switch to UTF-8 and have Python properly import that? */
+    vk = regfi_load_value(self->file->reg, 
+			  self->list->elements[self->cur]+REGFI_REGF_SIZE,
+			  REGFI_ENCODING_ASCII, true);
+    self->cur++;
+    /*return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, vk);    */
+    return vk; 
+  }
+
+  return NULL;
+
+
+  /* XXX: shouldn't parse data here every time we walk over a value.  
+   *      Instead, make data fetching a method and parse it then. 
+   */
+  /*
+  data = (REGFI_DATA *)regfi_iterator_fetch_data(self->iter, rec);
+  if(!data) {
+    RaiseError(ERuntimeError, "Unable to fetch data: %s", regfi_log_get_str());
+    goto error;
+  }
+
+  switch(rec->type) {
+  case REG_EXPAND_SZ:
+  case REG_SZ:
+    result = (RawData)CONSTRUCT(DataString, RawData, Con, NULL, data, rec);
+    break;
+
+  case REG_DWORD:
+    result = (RawData)CONSTRUCT(DWORDData, RawData, Con, NULL, data, rec);
+    break;
+
+  case REG_BINARY:
+  default:
+    result = (RawData)CONSTRUCT(RawData, RawData, Con, NULL, data, rec);
+    break;
+  }
+
+  return result;
+ error:
+  talloc_free(self);
+  return NULL;
+  */
+}
+
+VIRTUAL(ValueIterator, Object) {
+  VMETHOD(Con) = ValueIterator_Con;
+  VMETHOD(__iter__) = ValueIterator__iter__;
+  VMETHOD(iternext) = ValueIterator_iternext;
+} END_VIRTUAL
+
+
+
+static int RawData_dest(void *self)
+{
+  RawData this = (RawData)self;
+
+  if(this->data) {
+    regfi_free_record(this->data);
+  };
+
+  if(this->rec) {
+    regfi_free_record(this->rec);
+  };
+
+  return 0;
+}
+
+static RawData RawData_Con(RawData self, REGFI_DATA *data, REGFI_VK *record)
+{
+  self->rec = record;
+  self->data = data;
+
+  talloc_set_destructor((void *)self, RawData_dest);
+
+  return self;
+}
+
+static int RawData_get_value(RawData self, char *buff, int len)
+{
+  int available_to_read = min(len, self->data->interpreted_size);
+
+  memcpy(buff, self->data->raw, available_to_read);
+
+  return available_to_read;
+}
+
+VIRTUAL(RawData, Object) {
+  VMETHOD(Con) = RawData_Con;
+  VMETHOD(get_value) = RawData_get_value;
+} END_VIRTUAL
+
+static char* DataString_get_value(DataString self)
+{
+  RawData this = (RawData)self;
+
+  return (char*)this->data->interpreted.string;
+}
+
+VIRTUAL(DataString, RawData) {
+  VMETHOD(get_value) = DataString_get_value;
+} END_VIRTUAL
+
+static uint64_t DWORDData_get_value(DWORDData self)
+{
+  RawData this = (RawData)self;
+
+  return this->data->interpreted.dword;
+}
+
+VIRTUAL(DWORDData, RawData) {
+  VMETHOD(get_value) = DWORDData_get_value;
+} END_VIRTUAL
+
+void pyregfi_init()
+{
+  INIT_CLASS(RegistryFile);
+}
Index: /releases/1.0.1/python/experimental/utils.py
===================================================================
--- /releases/1.0.1/python/experimental/utils.py	(revision 275)
+++ /releases/1.0.1/python/experimental/utils.py	(revision 275)
@@ -0,0 +1,511 @@
+import os, sys, re, pdb
+import distutils.sysconfig as sysconfig
+import distutils.util
+import platform
+import SCons.SConf as SConf
+import config
+
+# taken from scons wiki
+def CheckPKGConfig(context, version):
+    context.Message( 'Checking for pkg-config version > %s... ' % version)
+    ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
+    context.Result( ret )
+    return ret
+
+def CheckFramework(context, name):
+    ret = 0
+    if (platform.system().lower() == 'darwin'):
+        context.Message( '\nLooking for framework %s... ' % name )
+        lastFRAMEWORKS = context.env['FRAMEWORKS']
+        context.env.Append(FRAMEWORKS = [name])
+        ret = context.TryLink("""
+              int main(int argc, char **argv) {
+                return 0;
+              }
+              """, '.c')
+        if not ret:
+            context.env.Replace(FRAMEWORKS = lastFRAMEWORKS
+)
+
+    return ret
+
+def CheckFink(context):
+    context.Message( 'Looking for fink... ')
+    prog = context.env.WhereIs('fink')
+    if prog:
+        ret = 1
+        prefix = prog.rsplit(os.sep, 2)[0]
+        context.env.Append(LIBPATH = [prefix + os.sep +'lib'],
+                           CPPPATH = [prefix + os.sep +'include'])
+        context.Message( 'Adding fink lib and include path')
+    else:
+        ret = 0
+        
+    context.Result(ret)    
+    return int(ret)
+
+def CheckMacports(context):
+    context.Message( 'Looking for macports... ')
+    prog = context.env.WhereIs('port')
+    if prog:
+        ret = 1
+        prefix = prog.rsplit(os.sep, 2)[0]
+        context.env.Append(LIBPATH = [prefix + os.sep + 'lib'],
+                           CPPPATH = [prefix + os.sep + 'include'])
+        context.Message( 'Adding port lib and include path')
+    else:
+        ret = 0
+        
+    context.Result(ret)    
+    return int(ret)
+
+# TODO: We should use the scons one instead
+def CheckLib(context, name):
+    context.Message( 'Looking for lib %s... ' % name )
+    lastLIBS = context.env['LIBS']
+    context.env.Append(LIBS = [name])
+    ret = context.TryLink("""
+              int main(int argc, char **argv) {
+                return 0;
+              }
+              """,'.c')
+    if not ret:
+        context.env.Replace(LIBS = lastLIBS)
+
+    return ret
+
+def ConfigPKG(context, name):
+    context.Message( '\nUsing pkg-config for %s... ' % name )
+    ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0]
+    context.Result(  ret )
+    if ret:
+        context.env.ParseConfig('pkg-config --cflags --libs \'%s\'' % name)
+    return int(ret)
+
+def CheckPKG(context, name):
+    context.Message( 'Checking for %s... ' % name )
+    if platform.system().lower() == 'windows':
+        return 0 
+    ret = 1
+    if not CheckFramework(context, name):
+        if not ConfigPKG(context, name.lower()):
+            ret = CheckLib(context, name) 
+
+    context.Result(ret)
+    return int(ret)
+
+
+## Configure colors for pretty builds
+colors = {}
+colors['cyan']   = '\033[96m'
+colors['purple'] = '\033[95m'
+colors['blue']   = '\033[94m'
+colors['green']  = '\033[92m'
+colors['yellow'] = '\033[93m'
+colors['red']    = '\033[91m'
+colors['end']    = '\033[0m'
+
+#If the output is not a terminal, remove the colors
+if not sys.stdout.isatty():
+   for key, value in colors.iteritems():
+      colors[key] = ''
+
+def error(msg):
+   print "%s%s%s" % (colors['red'], msg, colors['end'])
+   sys.exit(1)
+
+def warn(msg):
+   print "%s%s%s" % (colors['yellow'], msg, colors['end'])
+
+compile_source_message = '%sCompiling %s==> %s$SOURCE%s' % \
+   (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
+
+compile_shared_source_message = '%sCompiling shared %s==> %s$SOURCE%s' % \
+   (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
+
+compile_python_source_message = '%sCompiling python module %s==> %s$SOURCE%s' % \
+   (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
+
+link_program_message = '%sLinking Program %s==> %s$TARGET%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+link_library_message = '%sLinking Static Library %s==> %s$TARGET%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+ranlib_library_message = '%sRanlib Library %s==> %s$TARGET%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+link_shared_library_message = '%sLinking Shared Library %s==> %s$TARGET%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+link_python_module_message = '%sLinking Native Python module %s==> %s${TARGET}%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+java_library_message = '%sCreating Java Archive %s==> %s$TARGET%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+def install_colors(args):
+    """ Installs colors into an environment """
+    args.update(dict( CXXCOMSTR = compile_source_message,
+                      CCCOMSTR = compile_source_message,
+                      SHCCCOMSTR = compile_shared_source_message,
+                      SHCXXCOMSTR = compile_shared_source_message,
+                      ARCOMSTR = link_library_message,
+                      RANLIBCOMSTR = ranlib_library_message,
+                      SHLINKCOMSTR = link_shared_library_message,
+                      LINKCOMSTR = link_program_message,
+                      JARCOMSTR = java_library_message,
+                      JAVACCOMSTR = compile_source_message,))
+
+import optparse
+
+### This workaround is because scons does not provide access to the
+### parser, and by setting Help() we are unable to generate the option
+### listing from AddOption
+my_parser = optparse.OptionParser()
+
+import SCons.Script.Main as Main
+import SCons.Script as Script
+
+def add_option(arg, option, *args, **kwargs):
+    opt = "--%s" % option
+    Main.AddOption(opt, *args, **kwargs)
+    my_parser.add_option(opt, *args, **kwargs)
+
+    arg[option] = Main.GetOption(option)
+
+def generate_help(vars, env):
+    Script.Help("AFF4 build system configuration.\n\nFollowing are compile time options:\n")
+    Script.Help(my_parser.format_help())
+    Script.Help("\nThe following variables can be used on the command line:\n")
+    Script.Help(vars.GenerateHelpText(env))
+
+HEADERS = {}
+
+def check_size(conf, types):
+    global _DEFAULTS
+
+    for t in types:
+        name = "SIZEOF_" + t.replace(" ","_").upper()
+        HEADERS[name] = conf.CheckTypeSize(
+            t, size = _DEFAULTS[t][0])
+
+def check_type(conf, types):
+    header = None
+    for t in types:
+        if ':' in t:
+            t, header = t.split(':')
+        define = "HAVE_" + t.upper().replace(".","_")
+
+        result = 0
+        if conf.CheckType(t, includes="#include <%s>\n" % header):
+            result = 1
+
+        HEADERS[define] = result
+
+def check_build(conf, message, define, prog):
+    """ Build and links prog and adds define if that succeeds """
+    context = SConf.CheckContext(conf)
+    context.Message("Checking for %s ..." % message)
+    if context.TryLink(prog, ".c"):
+        HEADERS[define] = 1
+        context.Message("yes\n")
+    else:
+        context.Message("no\n")
+
+def check(type, conf, headers, extra_include =''):
+    for header in headers:
+        if ":" in header:
+            define, header = header.split(':')
+        else:
+            if "/" in header:
+                tmp = header.split("/")[-1]
+            else:
+                tmp = header
+
+            define = "HAVE_" + tmp.upper().replace(".","_")
+
+        global HEADERS
+        result = 0
+        if type == 'header':
+            #pdb.set_trace()
+            if conf.CheckCHeader(header): result = 1
+            HEADERS[define] = result
+        elif type == 'func':
+            if conf.CheckFunc(header, header=extra_include): result = 1
+            HEADERS[define] = result
+        elif type == 'lib':
+            if conf.CheckLib(header): result =1
+            HEADERS[define] = result
+
+## Build the config.h file
+def config_h_build(target, source, env):
+    config_h_defines = env.Dictionary()
+    config_h_defines.update(env.config.__dict__)
+    warn("Generating %s" % (target[0].path))
+
+    for a_target, a_source in zip(target, source):
+        config_h = file(str(a_target), "w")
+        config_h_in = file(str(a_source), "r")
+        config_h.write(config_h_in.read() % config_h_defines)
+        config_h_in.close()
+
+    keys = HEADERS.keys()
+    keys.sort()
+
+    for k in keys:
+        if HEADERS[k]:
+            config_h.write("#define %s %s\n" % (k,HEADERS[k]))
+        else:
+            config_h.write("/** %s unset */\n" % k)
+
+    config_h.close()
+
+import SCons.Environment
+
+class ExtendedEnvironment(SCons.Environment.Environment):
+    """ Implementation from Richard Levitte email to
+    org.tigris.scons.dev dated Jan 26, 2006 7:05:10 am."""
+    python_cppflags = "-I"+sysconfig.get_python_inc()
+
+    def PythonModule(self, libname, lib_objs=[], **kwargs):
+        """ This builds a python module which is almost a library but
+        is sometimes named differently.
+
+        We have two modes - a cross compile mode where we do our best
+        to guess the flags. In the native mode we can get the required
+        flags directly from distutils.
+        """
+        if config.MINGW_XCOMPILE:
+            shlib_suffix = ".pyd"
+            cppflags = "-I%s" % config.XCOMPILE_PYTHON_PATH
+            shlink_flags = ['']
+
+        else:
+            platform = self.subst('$PLATFORM')
+            shlib_pre_action = None
+            shlib_suffix = distutils.util.split_quoted(
+                sysconfig.get_config_var('SO'))
+            shlib_post_action = None
+            cppflags = distutils.util.split_quoted(self.python_cppflags)
+            shlink_flags = str(self['LINKFLAGS']).split()
+
+        install_dest = distutils.util.split_quoted(
+            os.path.join(
+                sysconfig.get_config_var('BINLIBDEST'),os.path.dirname(libname)))
+
+        flags = distutils.util.split_quoted(
+            sysconfig.get_config_var('LDSHARED'))
+
+        ## For some stupid reason they include the compiler in LDSHARED
+        shlink_flags.extend([x for x in flags if 'gcc' not in x])
+
+        shlink_flags.append(sysconfig.get_config_var('LOCALMODLIBS'))
+
+        ## TODO cross compile mode
+        kwargs['LIBPREFIX'] = ''
+        kwargs['CPPFLAGS'] = cppflags
+        kwargs['SHLIBSUFFIX'] = shlib_suffix
+        kwargs['SHLINKFLAGS'] = shlink_flags
+
+        if not self.config.V:
+            kwargs['SHCCCOMSTR'] = compile_python_source_message
+            kwargs['SHLINKCOMSTR'] = link_python_module_message
+
+        lib = self.SharedLibrary(libname,lib_objs,
+                                 **kwargs)
+
+        ## Install it to the right spot
+        self.Install(install_dest, lib)
+        self.Alias('install', install_dest)
+
+        return lib
+
+    def VersionedSharedLibrary(self, libname, libversion, lib_objs=[]):
+        """ This creates a version library similar to libtool.
+
+        We name the library with the appropriate soname.
+        """
+        platform = self.subst('$PLATFORM')
+        shlib_pre_action = None
+        shlib_suffix = self.subst('$SHLIBSUFFIX')
+        shlib_post_action = None
+        shlink_flags = SCons.Util.CLVar(self.subst('$SHLINKFLAGS'))
+
+        if platform == 'posix':
+            shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ]
+            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ]
+            shlib_suffix += '.' + libversion
+            shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,-soname=${LIBPREFIX}%s%s' % (
+                    libname, shlib_suffix) ]
+        elif platform == 'aix':
+            shlib_pre_action = [ "nm -Pg $SOURCES > ${TARGET}.tmp1", "grep ' [BDT] ' < ${TARGET}.tmp1 > ${TARGET}.tmp2", "cut -f1 -d' ' < ${TARGET}.tmp2 > ${TARGET}", "rm -f ${TARGET}.tmp[12]" ]
+            shlib_pre_action_output_re = [ '$', '.exp' ]
+            shlib_post_action = [ 'rm -f $TARGET', 'ln -s $SOURCE $TARGET' ]
+            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*' % re.escape(shlib_suffix), shlib_suffix ]
+            shlib_suffix += '.' + libversion
+            shlink_flags += ['-G', '-bE:${TARGET}.exp', '-bM:SRE']
+        elif platform == 'cygwin':
+            shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,--out-implib,${TARGET.base}.a' ]
+        elif platform == 'darwin':
+            shlib_suffix = '.' + libversion + shlib_suffix
+            shlink_flags += [ '-dynamiclib', '-current-version %s' % libversion ]
+
+        lib = self.SharedLibrary(libname,lib_objs,
+                                 SHLIBSUFFIX=shlib_suffix,
+                                 SHLINKFLAGS=shlink_flags)
+
+        if shlib_pre_action:
+            shlib_pre_action_output = re.sub(shlib_pre_action_output_re[0], shlib_pre_action_output_re[1], str(lib[0]))
+            self.Command(shlib_pre_action_output, [ lib_objs ], shlib_pre_action)
+            self.Depends(lib, shlib_pre_action_output)
+
+        if shlib_post_action:
+            shlib_post_action_output = re.sub(shlib_post_action_output_re[0], shlib_post_action_output_re[1], str(lib[0]))
+            self.Command(shlib_post_action_output, lib, shlib_post_action)
+
+        return lib
+
+    def InstallVersionedSharedLibrary(self, destination, lib):
+        platform = self.subst('$PLATFORM')
+        shlib_suffix = self.subst('$SHLIBSUFFIX')
+        shlib_install_pre_action = None
+        shlib_install_post_action = None
+
+        if platform == 'posix':
+            shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ]
+            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ]
+            shlib_install_post_action = shlib_post_action
+            shlib_install_post_action_output_re = shlib_post_action_output_re
+
+        ilib = self.Install(destination,lib)
+
+        if shlib_install_pre_action:
+            shlib_install_pre_action_output = re.sub(shlib_install_pre_action_output_re[0], shlib_install_pre_action_output_re[1], str(ilib[0]))
+            self.Command(shlib_install_pre_action_output, ilib, shlib_install_pre_action)
+            self.Depends(shlib_install_pre_action_output, ilib)
+
+        if shlib_install_post_action:
+            shlib_install_post_action_output = re.sub(shlib_install_post_action_output_re[0], shlib_install_post_action_output_re[1], str(ilib[0]))
+            self.Command(shlib_install_post_action_output, ilib, shlib_install_post_action)
+
+
+import subprocess
+
+def pkg_config(pkg, type):
+    try:
+        result = subprocess.Popen(["%s-config" % pkg, "--%s" % type],
+                                  stdout=subprocess.PIPE).communicate()[0]
+    except:
+        error("Unable to run %s-config - do you have the dev package installed?" % pkg)
+
+    return result.strip()
+
+
+# Sensible default for common types on common platforms.
+_DEFAULTS = {
+    'char': [1,],
+    'short' : [2,],
+    'int' : [4, 2],
+    'long' : [4, 8],
+    'long long' : [8, 4],
+    # Normally, there is no need to check unsigned types, because they are
+    # guaranteed to be of the same size than their signed counterpart.
+    'unsigned char': [1,],
+    'unsigned short' : [2,],
+    'unsigned int' : [4, 2],
+    'unsigned long' : [4, 8],
+    'unsigned long long' : [8, 4],
+    'float' : [4,],
+    'double' : [8,],
+    'long double' : [12,],
+    'size_t' : [4,],
+}
+
+def CheckTypeSize(context, type, includes = None, language = 'C', size = None):
+    """This check can be used to get the size of a given type, or to check whether
+    the type is of expected size.
+
+    Arguments:
+        - type : str
+            the type to check
+        - includes : sequence
+            list of headers to include in the test code before testing the type
+        - language : str
+            'C' or 'C++'
+        - size : int
+            if given, will test wether the type has the given number of bytes.
+            If not given, will test against a list of sizes (all sizes between
+            0 and 16 bytes are tested).
+
+        Returns:
+                status : int
+                        0 if the check failed, or the found size of the type if the check succeeded."""
+    minsz = 0
+    maxsz = 16
+
+    if includes:
+        src = "\n".join([r"#include <%s>\n" % i for i in includes])
+    else:
+        src = ""
+
+    if language == 'C':
+        ext = '.c'
+    elif language == 'C++':
+        ext = '.cpp'
+    else:
+        raise NotImplementedError("%s is not a recognized language" % language)
+
+    # test code taken from autoconf: this is a pretty clever hack to find that
+    # a type is of a given size using only compilation. This speeds things up
+    # quite a bit compared to straightforward code using TryRun
+    src += r"""
+typedef %s scons_check_type;
+
+int main()
+{
+    static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) <= %d)];
+    test_array[0] = 0;
+
+    return 0;
+}
+"""
+
+    if size:
+        # Only check if the given size is the right one
+        context.Message('Checking %s is %d bytes... ' % (type, size))
+        st = context.TryCompile(src % (type, size), ext)
+        context.Result(st)
+
+        if st:
+            return size
+        else:
+            return 0
+    else:
+        # Only check if the given size is the right one
+        context.Message('Checking size of %s ... ' % type)
+
+        # Try sensible defaults first
+        try:
+            szrange = _DEFAULTS[type]
+        except KeyError:
+            szrange = []
+        szrange.extend(xrange(minsz, maxsz))
+        st = 0
+
+        # Actual test
+        for sz in szrange:
+            st = context.TryCompile(src % (type, sz), ext)
+            if st:
+                break
+
+        if st:
+            context.Result('%d' % sz)
+            return sz
+        else:
+            context.Result('Failed !')
+            return 0
+
+#For example, to check wether long is 4 bytes on your platform, you can do:
+#config.CheckTypeSize('long', size = 4).
+## Now check the sizes
Index: /releases/1.0.1/python/pyregfi/__init__.py
===================================================================
--- /releases/1.0.1/python/pyregfi/__init__.py	(revision 275)
+++ /releases/1.0.1/python/pyregfi/__init__.py	(revision 275)
@@ -0,0 +1,1143 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2010-2011 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: $
+
+## @package pyregfi
+# Python interface to the regfi library.
+#
+
+## @mainpage API Documentation
+#
+# The pyregfi module provides a Python interface to the @ref regfi Windows 
+# registry library.  
+#
+# The library operates on registry hives, each of which is contained within a
+# single file.  The quickest way to get started, is to use the @ref openHive() 
+# function to obtain a Hive object.  For example:
+# @code
+# >>> import pyregfi
+# >>> myHive = pyregfi.openHive('/mnt/win/c/WINDOWS/system32/config/system')
+# @endcode
+#
+# Using this Hive object, one can begin investigating what top-level keys
+# exist by starting with the root Key attribute:
+# @code
+# >>> for key in myHive.root.subkeys:
+# ...   print(key.name)
+# ControlSet001
+# ControlSet003
+# LastKnownGoodRecovery
+# MountedDevices
+# Select
+# Setup
+# WPA
+# @endcode
+#
+# From there, accessing subkeys and values by name is a simple matter of:
+# @code
+# >>> myKey = myHive.root.subkeys['Select']
+# >>> myValue = myKey.values['Current']
+# @endcode
+#
+# The data associated with a Value can be obtained through the fetch_data()
+# method:
+# @code
+# >>> print(myValue.fetch_data())
+# 1
+# @endcode
+# 
+# While useful for simple exercises, using the subkeys object for deeply nested
+# paths is not efficient and doesn't make for particularly attractive code.  
+# Instead, a special-purpose HiveIterator class is provided for simplicity of
+# use and fast access to specific known paths:
+# @code
+# >>> myIter = pyregfi.HiveIterator(myHive)
+# >>> myIter.descend(['ControlSet001','Control','NetworkProvider','HwOrder'])
+# >>> myKey = myIter.current_key()
+# >>> print(myKey.values['ProviderOrder'].fetch_data())
+# RDPNP,LanmanWorkstation,WebClient
+# @endcode
+# 
+# The first two lines above can be simplified in some "syntactic sugar" provided
+# by the Hive.subtree() method.  Also, as one might expect, the HiveIterator 
+# also acts as an iterator, producing keys in a depth-first order.
+# For instance, to traverse all keys under the ControlSet003\\Services key, 
+# printing their names as we go, we could do:
+# @code
+# >>> for key in Hive.subtree(['ControlSet003','Services']):
+# >>>   print(key.name)
+# Services
+# Abiosdsk
+# abp480n5
+# Parameters
+# PnpInterface
+# ACPI
+# [...]
+# @endcode
+#
+# Note that "Services" was printed first, since the subtree is traversed as a 
+# "preordering depth-first" search starting with the HiveIterator's current_key().  
+# As one might expect, traversals of subtrees stops when all elements in a 
+# specific subtree (and none outside of it) have been traversed.
+#
+# For more information, peruse the various attributes and methods available on 
+# the Hive, HiveIterator, Key, Value, and Security classes.
+#
+# @note @ref regfi is a read-only library by design and there 
+# are no plans to implement write support.
+# 
+# @note At present, pyregfi has been tested with Python versions 2.6 and 3.1
+#
+# @note Developers strive to make pyregfi thread-safe.
+# 
+import sys
+import time
+import ctypes
+import ctypes.util
+import threading
+from pyregfi.structures import *
+
+
+## An enumeration of registry Value data types
+#
+# @note This is a static class, there is no need to instantiate it. 
+#       Just access its attributes directly as DATA_TYPES.SZ, etc
+class DATA_TYPES(object):
+    # XXX: add dictionary lookup attributes to convert both directions between
+    #      the integers and typenames
+
+    ## None / Unknown
+    NONE                       =  0
+    ## String
+    SZ                         =  1
+    ## String with %...% expansions
+    EXPAND_SZ                  =  2
+    ## Binary buffer
+    BINARY                     =  3
+    ## 32 bit integer (little endian)
+    DWORD                      =  4 # DWORD, little endian
+    ## 32 bit integer (little endian)
+    DWORD_LE                   =  4
+    ## 32 bit integer (big endian)
+    DWORD_BE                   =  5 # DWORD, big endian
+    ## Symbolic link
+    LINK                       =  6
+    ## List of strings
+    MULTI_SZ                   =  7
+    ## Unknown structure
+    RESOURCE_LIST              =  8
+    ## Unknown structure
+    FULL_RESOURCE_DESCRIPTOR   =  9
+    ## Unknown structure
+    RESOURCE_REQUIREMENTS_LIST = 10
+    ## 64 bit integer
+    QWORD                      = 11 # 64-bit little endian
+
+
+## An enumeration of log message types
+#
+# @note This is a static class, there is no need to instantiate it. 
+#       Just access its attributes directly as LOG_TYPES.INFO, etc
+class LOG_TYPES(object):
+    ## Informational messages, useful in debugging
+    INFO  =  0x01
+    ## Non-critical problems in structure parsing or intepretation
+    WARN  =  0x04
+    ## Major failures
+    ERROR =  0x10
+
+
+def _buffer2bytearray(char_pointer, length):
+    if length == 0 or char_pointer == None:
+        return None
+    
+    ret_val = bytearray(length)
+    for i in range(0,length):
+        ret_val[i] = char_pointer[i][0]
+
+    return ret_val
+
+
+def _strlist2charss(str_list):
+    ret_val = []
+    for s in str_list:
+        ret_val.append(s.encode('utf-8', 'replace'))
+
+    ret_val = (ctypes.c_char_p*(len(str_list)+1))(*ret_val)
+    # Terminate the char** with a NULL pointer
+    ret_val[-1] = 0
+
+    return ret_val
+
+
+def _charss2strlist(chars_pointer):
+    ret_val = []
+    i = 0
+    s = chars_pointer[i]
+    while s:
+        ret_val.append(s.decode('utf-8', 'replace'))
+        i += 1
+        s = chars_pointer[i]
+
+    return ret_val
+
+
+
+## Returns the (py)regfi library version
+#
+# @return A string indicating the version
+def getVersion():
+    return regfi.regfi_version()
+
+
+## Retrieves messages produced by regfi during parsing and interpretation
+#
+# The regfi C library may generate log messages stored in a special thread-safe
+# global data structure.  These messages should be retrieved periodically or 
+# after each major operation by callers to determine if any errors or warnings
+# should be reported to the user.  Failure to retrieve these could result in 
+# excessive memory consumption.
+def getLogMessages():
+    msgs = regfi.regfi_log_get_str()
+    if not msgs:
+        return ''
+    return msgs.decode('utf-8')
+
+
+## Sets the types of log messages to record
+#
+# @param log_types A sequence of message types that regfi should generate.
+#                  Message types can be found in the LOG_TYPES enumeration.
+#
+# @return True on success, False on failure.  Failures are rare, but could
+#         indicate that global logging is not operating as expected.
+# 
+# Example:
+# @code
+# setLogMask((LOG_TYPES.ERROR, LOG_TYPES.WARN, LOG_TYPES.INFO))
+# @endcode
+#
+# The message mask is a global (all hives, iterators), thread-specific value.
+# For more information, see @ref regfi_log_set_mask.
+#
+def setLogMask(log_types):
+    mask = 0
+    for m in log_types:
+        mask |= m
+    return regfi.regfi_log_set_mask(mask)
+
+
+## Opens a file as a registry hive
+#
+# @param path The file path of a hive, as one would provide to the 
+#             open() built-in
+#
+# @return A new Hive instance
+def openHive(path):
+    fh = open(path, 'rb')
+    return Hive(fh)
+
+
+## Abstract class for most objects returned by the library
+class _StructureWrapper(object):
+    _hive = None
+    _base = None
+
+    def __init__(self, hive, base):
+        if not hive:
+            raise Exception("Could not create _StructureWrapper,"
+                            + " hive is NULL.  Current log:\n"
+                            + getLogMessages())
+        if not base:
+            raise Exception("Could not create _StructureWrapper,"
+                            + " base is NULL.  Current log:\n"
+                            + getLogMessages())
+        self._hive = hive
+        self._base = base
+
+
+    # Memory management for most regfi structures is taken care of here
+    def __del__(self):
+        if self._base:
+            regfi.regfi_free_record(self._hive.file, self._base)
+
+
+    # Any attribute requests not explicitly defined in subclasses gets passed
+    # to the equivalent REGFI_* structure defined in structures.py
+    def __getattr__(self, name):
+        return getattr(self._base.contents, name)
+
+    
+    ## Test for equality
+    #
+    # Records returned by pyregfi may be compared with one another.  For example:
+    # @code
+    #  >>> key2 = key1.subkeys['child']
+    #  >>> key1 == key2
+    #  False
+    #  >>> key1 != key2
+    #  True
+    #  >>> key1 == key2.get_parent()
+    #  True
+    # @endcode
+    def __eq__(self, other):
+        return (type(self) == type(other)) and (self.offset == other.offset)
+
+
+    def __ne__(self, other):
+        return (not self.__eq__(other))
+
+
+class Key():
+    pass
+
+
+class Value():
+    pass
+
+
+
+## Represents a registry SK record which contains a security descriptor
+# 
+class Security(_StructureWrapper):
+    ## Number of registry Keys referencing this SK record
+    ref_count = 1
+
+    ## The absolute file offset of the SK record's cell in the Hive file
+    offset = 0xCAFEBABE
+
+    ## The @ref winsec.SecurityDescriptor for this SK record
+    descriptor = object()
+
+    def __init__(self, hive, base):
+        super(Security, self).__init__(hive, base)
+        # XXX: add checks for NULL pointers
+        self.descriptor = winsec.SecurityDescriptor(base.contents.sec_desc.contents)
+
+    ## Loads the "next" Security record in the hive
+    #
+    # @note
+    # SK records are included in a circular, doubly-linked list.
+    # To iterate over all SK records, be sure to check for the repetition of
+    # the SK record you started with to determine when all have been traversed.
+    def next_security(self):
+        return Security(self._hive,
+                        regfi.regfi_next_sk(self._hive.file, self._base))
+
+    ## Loads the "previous" Security record in the hive
+    #
+    # @note
+    # SK records are included in a circular, doubly-linked list.
+    # To iterate over all SK records, be sure to check for the repetition of
+    # the SK record you started with to determine when all have been traversed.
+    def prev_security(self):
+        return Security(self._hive,
+                        regfi.regfi_prev_sk(self._hive.file, self._base))
+
+
+## Abstract class for ValueList and SubkeyList
+class _GenericList(object):
+    # XXX: consider implementing keys(), values(), items() and other dictionary methods
+    _hive = None
+    _key_base = None
+    _length = None
+    _current = None
+
+    # implementation-specific functions for SubkeyList and ValueList
+    _fetch_num = None
+    _find_element = None
+    _get_element = None
+    _constructor = None
+
+    def __init__(self, key):
+        if not key:
+            raise Exception("Could not create _GenericList; key is NULL."
+                            + "Current log:\n" + getLogMessages())
+
+        base = regfi.regfi_reference_record(key._hive.file, key._base)
+        if not base:
+            raise Exception("Could not create _GenericList; memory error."
+                            + "Current log:\n" + getLogMessages())
+        self._key_base = cast(base, type(key._base))
+        self._length = self._fetch_num(self._key_base)
+        self._hive = key._hive
+
+    
+    def __del__(self):
+        regfi.regfi_free_record(self._hive.file, self._key_base)
+
+
+    ## Length of list
+    def __len__(self):
+        return self._length
+
+
+    ## Retrieves a list element by name
+    #
+    # @param name The name of the subkey or value desired.  
+    #             This is case-insensitive.
+    #
+    # @note The registry format does not inherently prevent multiple
+    #       subkeys or values from having the same name, having a key
+    #       and a value with the same name, or having the same name in
+    #       different cases that could both match. 
+    #       This interface simply returns the first match in the list.
+    #       Lookups using this method could also fail due to incorrectly
+    #       encoded strings stored as names.
+    #       To identify any duplicates or elements with malformed names,
+    #       use the iterator interface to check every list element.
+    #
+    # @return the first element whose name matches, or None if the element
+    #         could not be found
+    def __getitem__(self, name):
+        # XXX: Consider interpreting integer names as offsets in the underlying list
+        index = ctypes.c_uint32()
+        if isinstance(name, str):
+            name = name.encode('utf-8')
+
+        if name != None:
+            name = create_string_buffer(bytes(name))
+
+        if self._find_element(self._hive.file, self._key_base, 
+                              name, byref(index)):
+            return self._constructor(self._hive,
+                                     self._get_element(self._hive.file,
+                                                       self._key_base,
+                                                       index))
+        raise KeyError('')
+
+
+    ## Fetches the requested element by name, or the default value if the lookup
+    #  fails.
+    # 
+    def get(self, name, default):
+        try:
+            return self[name]
+        except KeyError:
+            return default
+    
+    def __iter__(self):
+        self._current = 0
+        return self
+    
+    def __next__(self):
+        if self._current >= self._length:
+            raise StopIteration('')
+
+        elem = self._get_element(self._hive.file, self._key_base,
+                                 ctypes.c_uint32(self._current))
+        self._current += 1
+        return self._constructor(self._hive, elem)
+    
+    # For Python 2.x
+    next = __next__
+
+
+## The list of subkeys associated with a Key
+#
+# This attribute is both iterable:
+# @code
+#   for k in myKey.subkeys: 
+#     ...
+# @endcode
+# and accessible as a dictionary:
+# @code
+#   mySubkey = myKey.subkeys["keyName"]
+# @endcode
+#
+# You may also request the len() of a subkeys list. 
+# However keys(), values(), items() and similar methods are not currently 
+# implemented.
+class SubkeyList(_GenericList):
+    _fetch_num = regfi.regfi_fetch_num_subkeys
+    _find_element = regfi.regfi_find_subkey
+    _get_element = regfi.regfi_get_subkey
+
+
+## The list of values associated with a Key
+#
+# This attribute is both iterable:
+# @code
+#   for v in myKey.values: 
+#     ...
+# @endcode
+# and accessible as a dictionary:
+# @code
+#   myValue = myKey.values["valueName"]
+# @endcode
+#
+# You may also request the len() of a values list. 
+# However keys(), values(), items() and similar methods are not currently 
+# implemented.
+class ValueList(_GenericList):
+    _fetch_num = regfi.regfi_fetch_num_values
+    _find_element = regfi.regfi_find_value
+    _get_element = regfi.regfi_get_value
+
+
+## Registry key 
+# These represent registry keys (@ref REGFI_NK records) and provide
+# access to their subkeys, values, and other metadata.
+#
+# @note Key instances may provide access to more attributes than are
+#       documented here.  However, undocumented attributes may change over time
+#       and are not officially supported.  If you need access to an attribute 
+#       not shown here, see @ref pyregfi.structures.
+class Key(_StructureWrapper):
+    ## A @ref ValueList object representing the list of Values 
+    #  stored on this Key
+    values = None
+
+    ## A @ref SubkeyList object representing the list of subkeys 
+    #  stored on this Key
+    subkeys = None
+
+    ## The raw Key name as an uninterpreted bytearray
+    name_raw = (b"...")
+    
+    ## The name of the Key as a (unicode) string
+    name = "..."
+    
+    ## The string encoding used to store the Key's name ("ascii" or "utf-16-le")
+    name_encoding = "ascii"
+
+    ## The absolute file offset of the Key record's cell in the Hive file
+    offset = 0xCAFEBABE
+
+    ## This Key's last modified time represented as the number of seconds 
+    #  since the UNIX epoch in UTC; similar to what time.time() returns
+    modified = 1300000000.123456
+
+    ## The NK record's flags field
+    flags = 0x10110001
+
+    def __init__(self, hive, base):
+        super(Key, self).__init__(hive, base)
+        self.values = ValueList(self)
+        self.subkeys = SubkeyList(self)
+
+    def __getattr__(self, name):
+        if name == "name":
+            ret_val = super(Key, self).__getattr__(name)
+
+            if not ret_val:
+                ret_val = self.name_raw
+                if ret_val != None:
+                    ret_val = ret_val.decode(self.name_encoding, 'replace')
+            else:
+                ret_val = ret_val.decode('utf-8', 'replace')
+                
+        elif name == "name_encoding":
+            flags = super(Key, self).__getattr__("flags")
+            if (flags & structures.REGFI_NK_FLAG_ASCIINAME) > 0:
+                ret_val = "ascii"
+            ret_val = "utf-16-le"
+
+        elif name == "name_raw":
+            ret_val = super(Key, self).__getattr__(name)
+            length = super(Key, self).__getattr__('name_length')
+            ret_val = _buffer2bytearray(ret_val, length)
+
+        elif name == "modified":
+            ret_val = regfi.regfi_nt2unix_time(self._base.contents.mtime)
+
+        else:
+            ret_val = super(Key, self).__getattr__(name)
+
+        return ret_val
+
+
+    ## Retrieves the Security properties for this key
+    def fetch_security(self):
+        return Security(self._hive,
+                        regfi.regfi_fetch_sk(self._hive.file, self._base))
+
+
+    ## Retrieves the class name for this key
+    #
+    # Class names are typically stored as UTF-16LE strings, so these are decoded
+    # into proper python (unicode) strings.  However, if this fails, a bytearray
+    # is instead returned containing the raw buffer stored for the class name.
+    #
+    # @return The class name as a string or bytearray.  None if a class name
+    #         doesn't exist or an unrecoverable error occurred during retrieval.
+    def fetch_classname(self):
+        ret_val = None
+        cn_p = regfi.regfi_fetch_classname(self._hive.file, self._base)
+        if cn_p:
+            cn_struct = cn_p.contents
+            if cn_struct.interpreted:
+                ret_val = cn_struct.interpreted.decode('utf-8', 'replace')
+            else:
+                ret_val = _buffer2bytearray(cn_struct.raw,
+                                            cn_struct.size)
+            regfi.regfi_free_record(self._hive.file, cn_p)
+
+        return ret_val
+
+
+    ## Retrieves this key's parent key
+    #
+    # @return The parent's Key instance or None if current key is root 
+    #         (or an error occured) 
+    def get_parent(self):
+        if self.is_root():
+            return None
+        parent_base = regfi.regfi_get_parentkey(self._hive.file, self._base)
+        if parent_base:
+            return Key(self._hive, parent_base)
+        return None
+
+
+    ## Checks to see if this Key is the root of its Hive
+    #
+    #  @return True if it is, False otherwise
+    def is_root(self):
+        return (self._hive.root == self)
+
+
+## Registry value (metadata)
+#
+# These represent registry values (@ref REGFI_VK records) and provide
+# access to their associated data.
+#
+# @note Value instances may provide access to more attributes than are
+#       documented here.  However, undocumented attributes may change over time
+#       and are not officially supported.  If you need access to an attribute
+#       not shown here, see @ref pyregfi.structures.
+class Value(_StructureWrapper):
+    ## The raw Value name as an uninterpreted bytearray
+    name_raw = (b"...")
+    
+    ## The name of the Value as a (unicode) string
+    name = "..."
+    
+    ## The string encoding used to store the Value's name ("ascii" or "utf-16-le")
+    name_encoding = "ascii"
+
+    ## The absolute file offset of the Value record's cell in the Hive file
+    offset = 0xCAFEBABE
+
+    ## The length of data advertised in the VK record
+    data_size = 0xCAFEBABE
+
+    ## An integer which represents the data type for this Value's data
+    # Typically this value is one of 12 types defined in @ref DATA_TYPES,
+    # but in some cases (the SAM hive) it may be used for other purposes
+    type = DATA_TYPES.NONE
+
+    ## The VK record's flags field
+    flags = 0x10110001
+
+    ## Retrieves the Value's data according to advertised type
+    #
+    # Data is loaded from its cell(s) and then interpreted based on the data
+    # type recorded in the Value.  It is not uncommon for data to be stored with
+    # the wrong type or even with invalid types.  If you have difficulty 
+    # obtaining desired data here, use @ref fetch_raw_data().
+    #
+    # @return The interpreted representation of the data as one of several
+    #         possible Python types, as listed below.  None if any failure 
+    #         occurred during extraction or conversion.
+    #
+    # @retval string for SZ, EXPAND_SZ, and LINK
+    # @retval int for DWORD, DWORD_BE, and QWORD
+    # @retval list(string) for MULTI_SZ
+    # @retval bytearray for NONE, BINARY, RESOURCE_LIST, 
+    #         FULL_RESOURCE_DESCRIPTOR, and RESOURCE_REQUIREMENTS_LIST
+    #
+    def fetch_data(self):
+        ret_val = None
+        data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
+        if not data_p:
+            return None
+        data_struct = data_p.contents
+
+        if data_struct.interpreted_size == 0:
+            ret_val = None
+        elif data_struct.type in (DATA_TYPES.SZ, DATA_TYPES.EXPAND_SZ, DATA_TYPES.LINK):
+            # Unicode strings
+            ret_val = data_struct.interpreted.string.decode('utf-8', 'replace')
+        elif data_struct.type in (DATA_TYPES.DWORD, DATA_TYPES.DWORD_BE):
+            # 32 bit integers
+            ret_val = data_struct.interpreted.dword
+        elif data_struct.type == DATA_TYPES.QWORD:
+            # 64 bit integers
+            ret_val = data_struct.interpreted.qword
+        elif data_struct.type == DATA_TYPES.MULTI_SZ:
+            ret_val = _charss2strlist(data_struct.interpreted.multiple_string)
+        elif data_struct.type in (DATA_TYPES.NONE, DATA_TYPES.RESOURCE_LIST,
+                                  DATA_TYPES.FULL_RESOURCE_DESCRIPTOR,
+                                  DATA_TYPES.RESOURCE_REQUIREMENTS_LIST,
+                                  DATA_TYPES.BINARY):
+            ret_val = _buffer2bytearray(data_struct.interpreted.none,
+                                        data_struct.interpreted_size)
+
+        regfi.regfi_free_record(self._hive.file, data_p)
+        return ret_val
+    
+
+    ## Retrieves raw representation of Value's data
+    #
+    # @return A bytearray containing the data
+    #
+    def fetch_raw_data(self):
+        ret_val = None
+        # XXX: should we load the data without interpretation instead?
+        data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
+        if not data_p:
+            return None
+
+        data_struct = data_p.contents
+        ret_val = _buffer2bytearray(data_struct.raw,
+                                    data_struct.size)
+        regfi.regfi_free_record(self._hive.file, data_p)
+        return ret_val
+
+
+    def __getattr__(self, name):
+        ret_val = None
+        if name == "name":
+            ret_val = super(Value, self).__getattr__(name)
+            if not ret_val:
+                ret_val = self.name_raw
+                if ret_val != None:
+                    ret_val = ret_val.decode(self.name_encoding, 'replace')
+            else:
+                ret_val = ret_val.decode('utf-8', 'replace')
+
+        elif name == "name_encoding":
+            flags = super(Value, self).__getattr__("flags")
+            if (flags & structures.REGFI_VK_FLAG_ASCIINAME) > 0:
+                ret_val = "ascii"
+            else:
+                ret_val = "utf-16-le"
+
+        elif name == "name_raw":
+            ret_val = super(Value, self).__getattr__(name)
+            length = super(Value, self).__getattr__('name_length')
+            ret_val = _buffer2bytearray(ret_val, length)
+
+        else:
+            ret_val = super(Value, self).__getattr__(name)
+
+        return ret_val
+
+
+# Avoids chicken/egg class definitions.
+# Also makes for convenient code reuse in these lists' parent classes.
+SubkeyList._constructor = Key
+ValueList._constructor = Value
+
+
+
+## Represents a single registry hive (file)
+class Hive():
+    file = None
+    raw_file = None
+    _fh = None
+    #_root = None
+
+
+    ## The root Key of this Hive
+    root = None
+
+    ## This Hives's last modified time represented as the number of seconds 
+    #  since the UNIX epoch in UTC; similar to what time.time() returns
+    modified = 1300000000.123456
+
+    ## First sequence number
+    sequence1 = 12345678
+
+    ## Second sequence number
+    sequence2 = 12345678
+
+    ## Major version
+    major_version = 1
+
+    ## Minor version
+    minor_version = 5
+
+    ## Constructor
+    #
+    # Initialize a new Hive based on a Python file object.  To open a file by 
+    # path, see @ref openHive.
+    #
+    # @param fh A Python file object.  The constructor first looks for a valid
+    #           fileno attribute on this object and uses it if possible.  
+    #           Otherwise, the seek and read methods are used for file
+    #           access.
+    #
+    # @note Supplied file must be seekable.  Do not perform any operation on
+    #       the provided file object while a Hive is using it.  Do not 
+    #       construct multiple Hive instances from the same file object.
+    #       If a file must be accessed by separate code and pyregfi 
+    #       simultaneously, use a separate file descriptor.  Hives are 
+    #       thread-safe, so multiple threads may use a single Hive object.
+    def __init__(self, fh):
+        # The fileno method may not exist, or it may throw an exception
+        # when called if the file isn't backed with a descriptor.
+        self._fh = fh
+        fn = None
+        try:
+            # XXX: Native calls to Windows filenos don't seem to work.  
+            #      Need to investigate why.
+            if not is_win32 and hasattr(fh, 'fileno'):
+                fn = fh.fileno()
+        except:
+            pass
+
+        if fn != None:
+            self.file = regfi.regfi_alloc(fn, REGFI_ENCODING_UTF8)
+            if not self.file:
+                # XXX: switch to non-generic exception
+                raise Exception("Could not open registry file.  Current log:\n"
+                                + getLogMessages())
+        else:
+            fh.seek(0)
+            self.raw_file = structures.REGFI_RAW_FILE()
+            self.raw_file.fh = fh
+            self.raw_file.seek = seek_cb_type(self.raw_file.cb_seek)
+            self.raw_file.read = read_cb_type(self.raw_file.cb_read)
+            self.file = regfi.regfi_alloc_cb(pointer(self.raw_file), REGFI_ENCODING_UTF8)
+            if not self.file:
+                # XXX: switch to non-generic exception
+                raise Exception("Could not open registry file.  Current log:\n"
+                                + getLogMessages())
+
+
+    def __getattr__(self, name):
+        if name == "root":
+            # XXX: This creates reference loops.  Need to cache better inside regfi
+            #if self._root == None:
+            #    self._root = Key(self, regfi.regfi_get_rootkey(self.file))
+            #return self._root
+            return Key(self, regfi.regfi_get_rootkey(self.file))
+
+        elif name == "modified":
+            return regfi.regfi_nt2unix_time(self._base.contents.mtime)
+
+        return getattr(self.file.contents, name)
+
+    
+    def __del__(self):
+        if self.file:
+            regfi.regfi_free(self.file)
+
+    def __iter__(self):
+        return HiveIterator(self)
+
+
+    ## Creates a @ref HiveIterator initialized at the specified path in
+    #  the hive.
+    #
+    # @param path A list of Key names which represent an absolute path within
+    #             the Hive
+    #
+    # @return A @ref HiveIterator which is positioned at the specified path.
+    # 
+    # @exception Exception If the path could not be found/traversed
+    def subtree(self, path):
+        hi = HiveIterator(self)
+        hi.descend(path)
+        return hi
+
+
+## A special purpose iterator for registry hives
+#
+# Iterating over an object of this type causes all keys in a specific
+# hive subtree to be returned in a depth-first manner. These iterators
+# are typically created using the @ref Hive.subtree() function on a @ref Hive
+# object.
+#
+# HiveIterators can also be used to manually traverse up and down a
+# registry hive as they retain information about the current position in
+# the hive, along with which iteration state for subkeys and values for
+# every parent key.  See the @ref up and @ref down methods for more
+# information.
+class HiveIterator():
+    _hive = None
+    _iter = None
+    _iteration_root = None
+    _lock = None
+
+    def __init__(self, hive):
+        self._iter = regfi.regfi_iterator_new(hive.file)
+        if not self._iter:
+            raise Exception("Could not create iterator.  Current log:\n"
+                            + getLogMessages())
+        self._hive = hive
+        self._lock = threading.RLock()
+    
+    def __getattr__(self, name):
+        self._lock.acquire()
+        ret_val = getattr(self._iter.contents, name)
+        self._lock.release()
+        return ret_val
+
+    def __del__(self):
+        self._lock.acquire()
+        regfi.regfi_iterator_free(self._iter)
+        self._lock.release()
+
+    def __iter__(self):
+        self._lock.acquire()
+        self._iteration_root = None
+        self._lock.release()
+        return self
+
+    def __next__(self):
+        self._lock.acquire()
+        if self._iteration_root == None:
+            self._iteration_root = self.current_key().offset
+        elif not regfi.regfi_iterator_down(self._iter):
+            up_ret = regfi.regfi_iterator_up(self._iter)
+            while (up_ret and
+                   not regfi.regfi_iterator_next_subkey(self._iter)):
+                if self._iteration_root == self.current_key().offset:
+                    self._iteration_root = None
+                    self._lock.release()
+                    raise StopIteration('')
+                up_ret = regfi.regfi_iterator_up(self._iter)
+
+            if not up_ret:
+                self._iteration_root = None
+                self._lock.release()
+                raise StopIteration('')
+            
+            # XXX: Use non-generic exception
+            if not regfi.regfi_iterator_down(self._iter):
+                self._lock.release()
+                raise Exception('Error traversing iterator downward.'+
+                                ' Current log:\n'+ getLogMessages())
+
+        regfi.regfi_iterator_first_subkey(self._iter)
+        ret_val = self.current_key()
+        self._lock.release()
+
+        return ret_val
+
+
+    # For Python 2.x
+    next = __next__
+
+    # XXX: Should add sanity checks on some of these traversal functions
+    #      to throw exceptions if a traversal/retrieval *should* have worked
+    #      but failed for some reason.
+
+    ## Descends the iterator to a subkey
+    #
+    # Descends the iterator one level to the current subkey, or a subkey
+    # specified by name.
+    #
+    # @param subkey_name If specified, locates specified subkey by name
+    #                    (via find_subkey()) and descends to it.
+    #
+    # @return True if successful, False otherwise
+    def down(self, subkey_name=None):
+        ret_val = None
+        if subkey_name == None:
+            self._lock.acquire()
+            ret_val = regfi.regfi_iterator_down(self._iter)
+        else:
+            if name != None:
+                name = name.encode('utf-8')
+            self._lock.acquire()
+            ret_val = (regfi.regfi_iterator_find_subkey(self._iter, name) 
+                       and regfi.regfi_iterator_down(self._iter))
+        
+        self._lock.release()
+        return ret_val
+
+
+    ## Causes the iterator to ascend to the current Key's parent
+    #
+    # @return True if successful, False otherwise
+    #
+    # @note The state of current subkeys and values at this level in the tree
+    #       is lost as a side effect.  That is, if you go up() and then back
+    #       down() again, current_subkey() and current_value() will return 
+    #       default selections.
+    def up(self):
+        self._lock.acquire()
+        ret_val = regfi.regfi_iterator_up(self._iter)
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects first subkey of current key
+    #
+    # @return A Key instance for the first subkey.  
+    #         None on error or if the current key has no subkeys.
+    def first_subkey(self):
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_first_subkey(self._iter):
+            ret_val = self.current_subkey()
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects first value of current Key
+    #
+    # @return A Value instance for the first value.  
+    #         None on error or if the current key has no values.
+    def first_value(self):
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_first_value(self._iter):
+            ret_val = self.current_value()
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects the next subkey in the current Key's list
+    #
+    # @return A Key instance for the next subkey.
+    #         None if there are no remaining subkeys or an error occurred.
+    def next_subkey(self):
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_next_subkey(self._iter):
+            ret_val = self.current_subkey()
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects the next value in the current Key's list
+    #  
+    # @return A Value instance for the next value.
+    #         None if there are no remaining values or an error occurred.
+    def next_value(self):
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_next_value(self._iter):
+            ret_val = self.current_value()
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects the first subkey which has the specified name
+    #
+    # @return A Key instance for the selected key. 
+    #         None if it could not be located or an error occurred.
+    def find_subkey(self, name):
+        if name != None:
+            name = name.encode('utf-8')
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_find_subkey(self._iter, name):
+            ret_val = self.current_subkey()
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects the first value which has the specified name
+    #
+    # @return A Value instance for the selected value.
+    #         None if it could not be located or an error occurred.
+    def find_value(self, name):
+        if name != None:
+            name = name.encode('utf-8')
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_find_value(self._iter, name):
+            ret_val = self.current_value()
+        self._lock.release()
+        return ret_val
+
+    ## Retrieves the currently selected subkey
+    #
+    # @return A Key instance of the current subkey
+    def current_subkey(self):
+        self._lock.acquire()
+        ret_val = Key(self._hive, regfi.regfi_iterator_cur_subkey(self._iter))
+        self._lock.release()
+        return ret_val
+
+    ## Retrieves the currently selected value
+    #
+    # @return A Value instance of the current value
+    def current_value(self):
+        self._lock.acquire()
+        ret_val = Value(self._hive, regfi.regfi_iterator_cur_value(self._iter))
+        self._lock.release()
+        return ret_val
+
+    ## Retrieves the current key
+    #
+    # @return A Key instance of the current position of the iterator
+    def current_key(self):
+        self._lock.acquire()
+        ret_val = Key(self._hive, regfi.regfi_iterator_cur_key(self._iter))
+        self._lock.release()
+        return ret_val
+
+    ## Traverse downward multiple levels
+    #
+    # This is more efficient than calling down() multiple times
+    #
+    # @param path A list of Key names which represent the path to descend
+    #
+    # @exception Exception If path could not be located
+    def descend(self, path):
+        cpath = _strlist2charss(path)
+
+        self._lock.acquire()
+        result = regfi.regfi_iterator_descend(self._iter, cpath)
+        self._lock.release()
+        if not result:
+            # XXX: Use non-generic exception
+            raise Exception('Could not locate path.\n'+getLogMessages())
+
+    ## Obtains a list of the current key's ancestry
+    #
+    # @return A list of all parent keys starting with the root Key and ending
+    #         with the current Key
+    def ancestry(self):
+        self._lock.acquire()
+        result = regfi.regfi_iterator_ancestry(self._iter)
+        self._lock.release()
+
+        ret_val = []
+        i = 0
+        k = result[i]
+        while k:
+            k = cast(regfi.regfi_reference_record(self._hive.file, k), POINTER(REGFI_NK))
+            ret_val.append(Key(self._hive, k))
+            i += 1
+            k = result[i]
+
+        regfi.regfi_free_record(self._hive.file, result)
+        return ret_val
+
+    ## Obtains the current path of the iterator
+    #
+    # @return A list of key names starting with the root up to and
+    #         including the current key
+    #
+    def current_path(self):
+        ancestry = self.ancestry()
+        return [a.name for a in ancestry]
+
+
+# Freeing symbols defined for the sake of documentation
+del Value.name,Value.name_encoding,Value.name_raw,Value.offset,Value.data_size,Value.type,Value.flags
+del Key.name,Key.name_encoding,Key.name_raw,Key.offset,Key.modified,Key.flags
+del Hive.root,Hive.modified,Hive.sequence1,Hive.sequence2,Hive.major_version,Hive.minor_version
+del Security.ref_count,Security.offset,Security.descriptor
Index: /releases/1.0.1/python/pyregfi/structures.py
===================================================================
--- /releases/1.0.1/python/pyregfi/structures.py	(revision 275)
+++ /releases/1.0.1/python/pyregfi/structures.py	(revision 275)
@@ -0,0 +1,368 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2010-2011 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: $
+
+## @package pyregfi.structures
+# Low-level data structures and C API mappings.
+#
+# Most users need not venture here.  For more information, see the source.
+
+import sys
+import os
+import traceback
+import ctypes
+import ctypes.util
+from ctypes import *
+
+is_win32 = hasattr(ctypes, 'windll')
+
+# XXX: can we always be sure enums are this size?
+REGFI_ENCODING = c_uint32
+REGFI_ENCODING_UTF8 = REGFI_ENCODING(1)
+
+REGFI_NK_FLAG_ASCIINAME = 0x0020
+REGFI_VK_FLAG_ASCIINAME = 0x0001
+
+REGFI_DATA_TYPE = c_uint32
+REGFI_NTTIME = c_uint64
+
+REGFI_REGF_SIZE = 0x1000
+
+# Prototype everything first so we don't have to worry about reference order
+class REGFI_VK(Structure):
+    pass
+
+class REGFI_SK(Structure):
+    pass
+
+class REGFI_SUBKEY_LIST(Structure):
+    pass
+
+class REGFI_VALUE_LIST(Structure):
+    pass
+
+class REGFI_CLASSNAME(Structure):
+    pass
+
+class REGFI_DATA(Structure):
+    pass
+
+class REGFI_NK(Structure):
+    pass
+
+class REGFI_ITERATOR(Structure):
+    pass
+
+class REGFI_FILE(Structure):
+    pass
+
+class REGFI_RAW_FILE(Structure):
+    fh = None
+    
+    def cb_seek(self, raw_file, offset, whence):
+        try:
+            self.fh.seek(offset, whence)
+        except Exception:
+            traceback.print_exc()
+            set_errno(74) # os.EX_IOERR
+            return -1
+
+        return self.fh.tell()
+
+
+    def cb_read(self, raw_file, buf, count):
+        try:
+            # XXX: anyway to do a readinto() here?
+            tmp = self.fh.read(count)
+            memmove(buf,tmp,len(tmp))
+
+        except Exception:
+            traceback.print_exc()
+            set_errno(74) # os.EX_IOERR
+            return -1
+        return len(tmp)
+
+
+# Load libregfi according to platform
+regfi = None
+if is_win32:
+    # XXX: Using C calling conventions on cross-compiled DLLs seems to work fine
+    #      on Windows, but I'm not sure if stdcall symbols are supported 
+    #      correctly for native Windows binaries...
+    #regfi = ctypes.windll.libregfi
+    #CB_FACTORY = ctypes.WINFUNCTYPE
+    regfi = ctypes.CDLL('libregfi.dll', use_errno=True)
+    CB_FACTORY = ctypes.CFUNCTYPE
+else:
+    regfi = ctypes.CDLL(ctypes.util.find_library('regfi'), use_errno=True)
+    CB_FACTORY = ctypes.CFUNCTYPE
+
+seek_cb_type = CB_FACTORY(c_int64, POINTER(REGFI_RAW_FILE), c_uint64, c_int, use_errno=True)
+read_cb_type = CB_FACTORY(c_int64, POINTER(REGFI_RAW_FILE), POINTER(c_char), c_size_t, use_errno=True)
+
+
+from .winsec import *
+
+REGFI_VK._fields_ = [('offset', c_uint32),
+                     ('cell_size', c_uint32),
+                     ('name', c_char_p),
+                     ('name_raw', POINTER(c_char)),
+                     ('name_length', c_uint16),
+                     ('hbin_off', c_uint32),
+                     ('data_size', c_uint32),
+                     ('data_off', c_uint32),
+                     ('type', REGFI_DATA_TYPE),
+                     ('magic', c_char * 2),
+                     ('flags', c_uint16),
+                     ('unknown1', c_uint16),
+                     ('data_in_offset', c_bool),
+                     ]
+
+
+REGFI_SK._fields_ = [('offset', c_uint32),
+                     ('cell_size', c_uint32),
+                     ('sec_desc', POINTER(WINSEC_DESC)),
+                     ('hbin_off', c_uint32),
+                     ('prev_sk_off', c_uint32),
+                     ('next_sk_off', c_uint32),
+                     ('ref_count', c_uint32),
+                     ('desc_size', c_uint32),
+                     ('unknown_tag', c_uint16),
+                     ('magic', c_char * 2),
+                     ]
+
+
+REGFI_NK._fields_ = [('offset', c_uint32),
+                     ('cell_size', c_uint32),
+                     ('values', POINTER(REGFI_VALUE_LIST)),
+                     ('subkeys', POINTER(REGFI_SUBKEY_LIST)),
+                     ('flags', c_uint16),
+                     ('magic', c_char * 2),
+                     ('mtime', REGFI_NTTIME),
+                     ('name_length', c_uint16),
+                     ('classname_length', c_uint16),
+                     ('name', c_char_p),
+                     ('name_raw', POINTER(c_char)),
+                     ('parent_off', c_uint32),
+                     ('classname_off', c_uint32),
+                     ('max_bytes_subkeyname', c_uint32),
+                     ('max_bytes_subkeyclassname', c_uint32),
+                     ('max_bytes_valuename', c_uint32),
+                     ('max_bytes_value', c_uint32),
+                     ('unknown1', c_uint32),
+                     ('unknown2', c_uint32),
+                     ('unknown3', c_uint32),
+                     ('unk_index', c_uint32),
+                     ('num_subkeys', c_uint32),
+                     ('subkeys_off', c_uint32),
+                     ('num_values', c_uint32),
+                     ('values_off', c_uint32),
+                     ('sk_off', c_uint32),
+                     ]
+
+
+REGFI_SUBKEY_LIST._fields_ = [('offset', c_uint32),
+                              ('cell_size', c_uint32),
+                              ('num_children', c_uint32),
+                              ('num_keys', c_uint32),
+                              ('elements', c_void_p),
+                              ('magic', c_char * 2),
+                              ('recursive_type', c_bool),
+                              ]
+
+
+REGFI_VALUE_LIST._fields_ = [('offset', c_uint32),
+                             ('cell_size', c_uint32),
+                             ('num_values', c_uint32),
+                             ('elements', c_void_p),
+                             ]
+
+REGFI_CLASSNAME._fields_ = [('offset', c_uint32),
+                            ('interpreted', c_char_p),
+                            ('raw', POINTER(c_char)),
+                            ('size', c_uint16),
+                            ]
+
+
+class REGFI_DATA__interpreted(Union):
+    _fields_ = [('none',POINTER(c_char)),
+                ('string', c_char_p),
+                ('expand_string', c_char_p),
+                ('binary',POINTER(c_char)),
+                ('dword', c_uint32),
+                ('dword_be', c_uint32),
+                ('link', c_char_p),
+                ('multiple_string', POINTER(c_char_p)),
+                ('qword', c_uint64),
+                ('resource_list',POINTER(c_char)),
+                ('full_resource_descriptor',POINTER(c_char)),
+                ('resource_requirements_list',POINTER(c_char)),
+                ]
+REGFI_DATA._fields_ = [('offset', c_uint32),
+                       ('type', REGFI_DATA_TYPE),
+                       ('size', c_uint32),
+                       ('raw', POINTER(c_char)),
+                       ('interpreted_size', c_uint32),
+                       ('interpreted', REGFI_DATA__interpreted),
+                       ]
+
+
+REGFI_FILE._fields_ = [('magic', c_char * 4),
+                       ('sequence1', c_uint32),
+                       ('sequence2', c_uint32),
+                       ('mtime', REGFI_NTTIME),
+                       ('major_version', c_uint32),
+                       ('minor_version', c_uint32),
+                       ('type', c_uint32),
+                       ('format', c_uint32),
+                       ('root_cell', c_uint32),
+                       ('last_block', c_uint32),
+                       ('cluster', c_uint32),
+                       ]
+
+
+REGFI_RAW_FILE._fields_ = [('seek', seek_cb_type),
+                           ('read', read_cb_type),
+                           ('cur_off', c_uint64),
+                           ('size', c_uint64),
+                           ('state', c_void_p),
+                           ]
+
+
+# Define function prototypes
+regfi.regfi_version.argtypes = []
+regfi.regfi_version.restype = c_char_p
+
+regfi.regfi_alloc.argtypes = [c_int, REGFI_ENCODING]
+regfi.regfi_alloc.restype = POINTER(REGFI_FILE)
+
+regfi.regfi_alloc_cb.argtypes = [POINTER(REGFI_RAW_FILE), REGFI_ENCODING]
+regfi.regfi_alloc_cb.restype = POINTER(REGFI_FILE)
+
+regfi.regfi_free.argtypes = [POINTER(REGFI_FILE)]
+regfi.regfi_free.restype = None
+
+regfi.regfi_log_get_str.argtypes = []
+regfi.regfi_log_get_str.restype = c_char_p
+
+regfi.regfi_log_set_mask.argtypes = [c_uint16]
+regfi.regfi_log_set_mask.restype = c_bool
+
+regfi.regfi_get_rootkey.argtypes = [POINTER(REGFI_FILE)]
+regfi.regfi_get_rootkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_free_record.argtypes = [POINTER(REGFI_FILE), c_void_p]
+regfi.regfi_free_record.restype = None
+
+regfi.regfi_reference_record.argtypes = [POINTER(REGFI_FILE), c_void_p]
+regfi.regfi_reference_record.restype = c_void_p
+
+regfi.regfi_fetch_num_subkeys.argtypes = [POINTER(REGFI_NK)]
+regfi.regfi_fetch_num_subkeys.restype = c_uint32
+
+regfi.regfi_fetch_num_values.argtypes = [POINTER(REGFI_NK)]
+regfi.regfi_fetch_num_values.restype = c_uint32
+
+regfi.regfi_fetch_classname.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
+regfi.regfi_fetch_classname.restype = POINTER(REGFI_CLASSNAME)
+
+regfi.regfi_fetch_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
+regfi.regfi_fetch_sk.restype = POINTER(REGFI_SK)
+
+regfi.regfi_next_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_SK)]
+regfi.regfi_next_sk.restype = POINTER(REGFI_SK)
+
+regfi.regfi_prev_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_SK)]
+regfi.regfi_prev_sk.restype = POINTER(REGFI_SK)
+
+regfi.regfi_fetch_data.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_VK)]
+regfi.regfi_fetch_data.restype = POINTER(REGFI_DATA)
+
+regfi.regfi_find_subkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+                                    c_char_p, POINTER(c_uint32)]
+regfi.regfi_find_subkey.restype = c_bool
+
+regfi.regfi_find_value.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+                                   c_char_p, POINTER(c_uint32)]
+regfi.regfi_find_value.restype = c_bool
+
+regfi.regfi_get_subkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+                                   c_uint32]
+regfi.regfi_get_subkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_get_value.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+                                  c_uint32]
+regfi.regfi_get_value.restype = POINTER(REGFI_VK)
+
+regfi.regfi_get_parentkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
+regfi.regfi_get_parentkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_nt2unix_time.argtypes = [REGFI_NTTIME]
+regfi.regfi_nt2unix_time.restype = c_double
+
+regfi.regfi_iterator_new.argtypes = [POINTER(REGFI_FILE)]
+regfi.regfi_iterator_new.restype = POINTER(REGFI_ITERATOR)
+
+regfi.regfi_iterator_free.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_free.restype = None
+
+regfi.regfi_iterator_down.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_down.restype = c_bool
+
+regfi.regfi_iterator_up.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_up.restype = c_bool
+
+regfi.regfi_iterator_to_root.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_to_root.restype = c_bool
+
+regfi.regfi_iterator_descend.argtypes = [POINTER(REGFI_ITERATOR), POINTER(c_char_p)]
+regfi.regfi_iterator_descend.restype = c_bool
+
+regfi.regfi_iterator_cur_key.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_cur_key.restype = POINTER(REGFI_NK)
+
+regfi.regfi_iterator_first_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_first_subkey.restype = c_bool
+
+regfi.regfi_iterator_cur_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_cur_subkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_iterator_next_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_next_subkey.restype = c_bool
+
+regfi.regfi_iterator_find_subkey.argtypes = [POINTER(REGFI_ITERATOR), c_char_p]
+regfi.regfi_iterator_find_subkey.restype = c_bool
+
+regfi.regfi_iterator_first_value.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_first_value.restype = c_bool
+
+regfi.regfi_iterator_cur_value.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_cur_value.restype = POINTER(REGFI_VK)
+
+regfi.regfi_iterator_next_value.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_next_value.restype = c_bool
+
+regfi.regfi_iterator_find_value.argtypes = [POINTER(REGFI_ITERATOR), c_char_p]
+regfi.regfi_iterator_find_value.restype = c_bool
+
+regfi.regfi_iterator_ancestry.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_ancestry.restype = POINTER(POINTER(REGFI_NK))
+
+regfi.regfi_init.argtypes = []
+regfi.regfi_init.restype = None
+regfi.regfi_init()
Index: /releases/1.0.1/python/pyregfi/winsec.py
===================================================================
--- /releases/1.0.1/python/pyregfi/winsec.py	(revision 275)
+++ /releases/1.0.1/python/pyregfi/winsec.py	(revision 275)
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2011 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: $
+
+## @package pyregfi.winsec
+# Low-level data structures for winsec library
+#
+
+import sys
+import os
+import uuid
+import ctypes
+import ctypes.util
+from ctypes import *
+from .structures import regfi
+
+is_win32 = hasattr(ctypes, 'windll')
+WINSEC_MAX_SUBAUTHS = 15
+
+if is_win32:
+    libc = cdll.msvcrt
+else:
+    libc = cdll.LoadLibrary("libc.so.6")
+
+class WINSEC_UUID(Structure):
+    pass
+
+class WINSEC_DOM_SID(Structure):
+    pass
+
+class WINSEC_ACE(Structure):
+    pass
+
+class WINSEC_ACL(Structure):
+    pass
+
+class WINSEC_DESC(Structure):
+    pass
+
+WINSEC_UUID._fields_ = [('time_low', c_uint32),
+                        ('time_mid', c_uint16),
+                        ('time_hi_and_version', c_uint16),
+                        ('clock_seq', c_uint8*2),
+                        ('node', c_uint8*6),
+                        ]
+
+WINSEC_DOM_SID._fields_ = [('sid_rev_num', c_uint8),
+                           ('num_auths', c_uint8),
+                           ('id_auths', c_uint8*6),
+                           ('sub_auths', c_uint32*WINSEC_MAX_SUBAUTHS),
+                           ]
+
+WINSEC_ACE._fields_ = [('type', c_uint8),
+                       ('flags', c_uint8),
+                       ('size', c_uint16),
+                       ('access_mask', c_uint32),
+                       ('obj_flags', c_uint32),
+                       ('obj_guid', POINTER(WINSEC_UUID)),
+                       ('inh_guid', POINTER(WINSEC_UUID)),
+                       ('trustee', POINTER(WINSEC_DOM_SID)),
+                       ]
+
+WINSEC_ACL._fields_ = [('revision', c_uint16),
+                       ('size', c_uint16),
+                       ('num_aces', c_uint32),
+                       ('aces', POINTER(POINTER(WINSEC_ACE))),
+                       ]
+
+WINSEC_DESC._fields_ = [('revision', c_uint8),
+                        ('sbz1', c_uint8),
+                        ('control', c_uint16),
+                        ('off_owner_sid', c_uint32),
+                        ('off_grp_sid', c_uint32),
+                        ('off_sacl', c_uint32),
+                        ('off_dacl', c_uint32),
+                        ('owner_sid', POINTER(WINSEC_DOM_SID)),
+                        ('grp_sid', POINTER(WINSEC_DOM_SID)),
+                        ('sacl', POINTER(WINSEC_ACL)),
+                        ('dacl', POINTER(WINSEC_ACL)),
+                        ]
+regfi.winsec_sid2str.argtypes = [POINTER(WINSEC_DOM_SID)]
+regfi.winsec_sid2str.restype = POINTER(c_char)
+
+
+def _guid2uuid(guid):
+    if not guid:
+        return None
+    return uuid.UUID(fields=(guid.contents.time_low,
+                             guid.contents.time_mid,
+                             guid.contents.time_hi_and_version,
+                             guid.contents.clock_seq[0],
+                             guid.contents.clock_seq[1],
+                             guid.contents.node[0]<<40
+                             ^ guid.contents.node[1]<<32
+                             ^ guid.contents.node[2]<<24
+                             ^ guid.contents.node[3]<<16
+                             ^ guid.contents.node[4]<<8
+                             ^ guid.contents.node[5]))
+
+## Represents a Microsoft access control entry, which are elements of access
+#  control lists.  For more information, see: 
+#    http://msdn.microsoft.com/en-us/library/aa374868%28v=vs.85%29.aspx
+#
+#  @note
+#  This interface is subject to change
+class ACE(object):
+    ## The type of entry as an integer
+    type = 1234
+    
+    ## The flags as an integer
+    flags = 0x1234
+
+    ## The access mask/permissions as an integer
+    access_mask = 0x1234
+
+    ## The trustee's SID as a string
+    trustee = "S-1-2..."
+    
+    ## The object GUID as a Python UUID
+    # May be None
+    object = uuid.UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
+
+    ## The inherited object GUID as a Python UUID
+    # May be None
+    inherited_object = uuid.UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
+
+    def __init__(self, ace):
+        # Just copy all of the values out so we don't need to manage memory
+        self.object = _guid2uuid(ace.obj_guid)
+        self.inherited_object = _guid2uuid(ace.inh_guid)
+
+        c_str = regfi.winsec_sid2str(ace.trustee)
+        self.trustee = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace')
+        libc.free(c_str)
+
+        self.type = int(ace.type)
+        self.flags = int(ace.flags)
+        self.access_mask = int(ace.access_mask)
+
+
+## A Microsoft security descriptor
+# For more information, see:
+#   http://msdn.microsoft.com/en-us/library/aa379563%28v=vs.85%29.aspx
+#
+class SecurityDescriptor(object):
+    ## The security descriptor's owner SID, as a string
+    owner = "S-1-2-..."
+
+    ## The security descriptor's group SID, as a string
+    group = "S-1-2-..."
+
+    ## The system access control list represented as a list of @ref ACE objects.
+    # 
+    # Is set to None if a sacl isn't defined
+    sacl = []
+
+    ## The discretionary access control list represented as a list of @ref ACE objects
+    #
+    # Is set to None if a dacl isn't defined
+    dacl = []
+
+    def __init__(self, sec_desc):
+        c_str = regfi.winsec_sid2str(sec_desc.owner_sid)
+        self.owner = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace')
+        libc.free(c_str)
+        
+        c_str = regfi.winsec_sid2str(sec_desc.grp_sid)
+        self.group = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace')
+        libc.free(c_str)
+
+        self.sacl = None
+        if sec_desc.sacl:
+            self.sacl = []
+            for i in range(0,sec_desc.sacl.contents.num_aces):
+                self.sacl.append(ACE(sec_desc.sacl.contents.aces[i].contents))
+
+        self.dacl = None
+        if sec_desc.dacl:
+            self.dacl = []
+            for i in range(0,sec_desc.dacl.contents.num_aces):
+                self.dacl.append(ACE(sec_desc.dacl.contents.aces[i].contents))
+
+
+# Free class objects used for documentation
+del ACE.type,ACE.flags,ACE.access_mask,ACE.object,ACE.inherited_object
+del SecurityDescriptor.owner,SecurityDescriptor.group,SecurityDescriptor.sacl,SecurityDescriptor.dacl
Index: /releases/1.0.1/regfi_version.py
===================================================================
--- /releases/1.0.1/regfi_version.py	(revision 275)
+++ /releases/1.0.1/regfi_version.py	(revision 275)
@@ -0,0 +1,2 @@
+SVN_REV = "$Rev$"
+REGFI_VERSION = "svn-%s" % SVN_REV.split(' ')[1]
Index: /releases/1.0.1/src/common.c
===================================================================
--- /releases/1.0.1/src/common.c	(revision 275)
+++ /releases/1.0.1/src/common.c	(revision 275)
@@ -0,0 +1,375 @@
+/*
+ * This file stores code common to the command line tools.
+ * XXX: This should be converted to a proper library.
+ *
+ * Copyright (C) 2005-2008,2011 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_EXIT_OK       0
+#define REGLOOKUP_EXIT_OSERR   71
+#define REGLOOKUP_EXIT_USAGE   64
+#define REGLOOKUP_EXIT_DATAERR 65
+#define REGLOOKUP_EXIT_NOINPUT 66
+
+
+/* Windows is lame */
+#ifdef O_BINARY
+#define REGLOOKUP_OPEN_FLAGS O_RDONLY|O_BINARY
+#else
+#define REGLOOKUP_OPEN_FLAGS O_RDONLY
+#endif
+
+
+void bailOut(int code, char* message)
+{
+  fprintf(stderr, "%s", message);
+  exit(code);
+}
+
+void printMsgs()
+{
+  char* msgs = regfi_log_get_str();
+  if(msgs != NULL)
+  {
+    fprintf(stderr, "%s", msgs);
+    free(msgs);
+  }
+}
+
+void clearMsgs()
+{
+  char* msgs = regfi_log_get_str();
+  if(msgs != NULL)
+    free(msgs);
+}
+
+
+/* Returns a newly malloc()ed string which contains original buffer,
+ * except for non-printable or special characters are quoted in hex
+ * with the syntax '%QQ' where QQ is the hex ascii value of the quoted
+ * character.  A null terminator is added, since only ascii, not binary,
+ * is returned.
+ */
+static char* quote_buffer(const unsigned char* str, 
+			  unsigned int len, const char* special)
+{
+  unsigned int i, added_len;
+  unsigned int num_written = 0;
+
+  unsigned int buf_len = sizeof(char)*(len+1);
+  char* ret_val = NULL; 
+  char* tmp_buf;
+
+  if(buf_len > 0) 
+    ret_val = malloc(buf_len);
+  if(ret_val == NULL)
+    return NULL;
+
+  for(i=0; i<len; i++)
+  {
+    if(buf_len <= (num_written+5))
+    {
+      /* Expand the buffer by the memory consumption rate seen so far 
+       * times the amount of input left to process.  The expansion is bounded 
+       * below by a minimum safety increase, and above by the maximum possible 
+       * output string length.  This should minimize both the number of 
+       * reallocs() and the amount of wasted memory.
+       */
+      added_len = (len-i)*num_written/(i+1);
+      if((buf_len+added_len) > (len*4+1))
+	buf_len = len*4+1;
+      else
+      {
+	if (added_len < 5)
+	  buf_len += 5;
+	else
+	  buf_len += added_len;
+      }
+
+      tmp_buf = realloc(ret_val, buf_len);
+      if(tmp_buf == NULL)
+      {
+	free(ret_val);
+	return NULL;
+      }
+      ret_val = tmp_buf;
+    }
+    
+    if(str[i] < 32 || str[i] > 126 
+       || str[i] == '%' || strchr(special, str[i]) != NULL)
+    {
+      num_written += snprintf(ret_val + num_written, buf_len - num_written,
+			      "%%%.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 '%QQ' where QQ is the hex ascii value of the quoted
+ * character.
+ */
+static char* quote_string(const char* str, const char* special)
+{
+  unsigned int len;
+
+  if(str == NULL)
+    return NULL;
+
+  len = strlen(str);
+  return quote_buffer((const unsigned char*)str, len, special);
+}
+
+
+/*
+ * Convert a data value to a string for display.  Returns NULL on error,
+ * and the string to display if there is no error, or a non-fatal
+ * error.  On any error (fatal or non-fatal) occurs, (*error_msg) will
+ * be set to a newly allocated string, containing an error message.  If
+ * a memory allocation failure occurs while generating the error
+ * message, both the return value and (*error_msg) will be NULL.  It
+ * is the responsibility of the caller to free both a non-NULL return
+ * value, and a non-NULL (*error_msg).
+ */
+static char* data_to_ascii(const REGFI_DATA* data, char** error_msg)
+{
+  char* ret_val;
+  char* cur_quoted;
+  char* tmp_ptr;
+  char* delim;
+  uint32_t ret_val_left, i, tmp_len;
+
+  if(data == NULL || data->size == 0)
+  {
+    *error_msg = (char*)malloc(37);
+    if(*error_msg == NULL)
+      return NULL;
+    strcpy(*error_msg, "Data pointer was NULL or size was 0.");
+    return NULL;
+  }
+  *error_msg = NULL;
+
+
+  if(data->interpreted_size == 0)
+  {
+    *error_msg = (char*)malloc(51);
+    if(*error_msg == NULL)
+      return NULL;
+    strcpy(*error_msg, "Data could not be interpreted, quoting raw buffer.");
+    return quote_buffer(data->raw, data->size, subfield_special_chars);
+  }
+
+  switch (data->type) 
+  {
+  case REG_SZ:
+    ret_val = quote_string((char*)data->interpreted.string, common_special_chars);
+    if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+
+    return ret_val;
+    break;
+
+    
+  case REG_EXPAND_SZ:
+    ret_val = quote_string((char*)data->interpreted.expand_string, 
+			   common_special_chars);
+    if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+
+    return ret_val;
+    break;
+
+  case REG_LINK:
+    ret_val = quote_string((char*)data->interpreted.link, common_special_chars);
+    if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL)
+	strcpy(*error_msg, "Buffer could not be quoted due to unknown error.");
+
+    return ret_val;
+    break;
+
+  case REG_DWORD:
+    ret_val = malloc(sizeof(char)*(8+2+1));
+    if(ret_val == NULL)
+      return NULL;
+
+    sprintf(ret_val, "0x%.8X", data->interpreted.dword);
+    return ret_val;
+    break;
+
+  case REG_DWORD_BE:
+    ret_val = malloc(sizeof(char)*(8+2+1));
+    if(ret_val == NULL)
+      return NULL;
+
+    sprintf(ret_val, "0x%.8X", data->interpreted.dword_be);
+    return ret_val;
+    break;
+
+  case REG_QWORD:
+    ret_val = malloc(sizeof(char)*(16+2+1));
+    if(ret_val == NULL)
+      return NULL;
+
+    sprintf(ret_val, "0x%.16llX", 
+	    (long long unsigned int)data->interpreted.qword);
+    return ret_val;
+    break;
+
+  case REG_MULTI_SZ:
+    ret_val_left = data->interpreted_size*4+1;
+    ret_val = malloc(ret_val_left);
+    if(ret_val == NULL)
+      return NULL;
+
+    tmp_ptr = ret_val;
+    tmp_ptr[0] = '\0';
+    delim = "";
+    for(i=0; data->interpreted.multiple_string[i] != NULL; i++)
+    {
+      cur_quoted = quote_string((char*)data->interpreted.multiple_string[i],
+				subfield_special_chars);
+      if(cur_quoted != NULL)
+      {
+	tmp_len = snprintf(tmp_ptr, ret_val_left, "%s%s",delim, cur_quoted);
+	tmp_ptr += tmp_len;
+	ret_val_left -= tmp_len;
+	free(cur_quoted);
+      }
+      delim = "|";
+    }
+
+    return ret_val;
+    break;
+
+    
+  case REG_NONE:
+    return quote_buffer(data->interpreted.none, data->interpreted_size,
+			common_special_chars);
+
+    break;
+
+  case REG_RESOURCE_LIST:
+    return quote_buffer(data->interpreted.resource_list, data->interpreted_size,
+			common_special_chars);
+
+    break;
+
+  case REG_FULL_RESOURCE_DESCRIPTOR:
+    return quote_buffer(data->interpreted.full_resource_descriptor, 
+			data->interpreted_size, common_special_chars);
+
+    break;
+
+  case REG_RESOURCE_REQUIREMENTS_LIST:
+    return quote_buffer(data->interpreted.resource_requirements_list,
+			data->interpreted_size, common_special_chars);
+
+    break;
+
+  case REG_BINARY:
+    return quote_buffer(data->interpreted.binary, data->interpreted_size,
+			common_special_chars);
+
+    break;
+
+  default:
+    /* This shouldn't happen, since the regfi routines won't interpret 
+     * unknown types, but just as a safety measure against library changes... 
+     */
+    *error_msg = (char*)malloc(65);
+    if(*error_msg == NULL)
+      return NULL;
+    sprintf(*error_msg,
+	    "Unrecognized registry data type (0x%.8X); quoting as binary.",
+	    data->type);
+    return quote_buffer(data->raw, data->size, common_special_chars);
+  }
+    
+  return NULL;
+}
+
+
+static char* get_quoted_keyname(const REGFI_NK* nk)
+{
+  char* ret_val;
+
+  if(nk->name == NULL)
+    ret_val = quote_buffer(nk->name_raw, nk->name_length, key_special_chars);
+  else
+    ret_val = quote_string(nk->name, key_special_chars);
+
+  return ret_val;
+}
+
+
+static char* get_quoted_valuename(const REGFI_VK* vk)
+{
+  char* ret_val;
+
+  if(vk->name == NULL)
+    ret_val = quote_buffer(vk->name_raw, vk->name_length, 
+			   key_special_chars);
+  else
+    ret_val = quote_string(vk->name, key_special_chars);
+
+  return ret_val;
+}
+
+
+int openHive(const char* filename)
+{
+  int ret_val;
+
+  /* open an existing file */
+  if ((ret_val = open(filename, REGLOOKUP_OPEN_FLAGS)) == -1)
+  {
+    fprintf(stderr, "ERROR: Failed to open hive.  Error returned: %s\n", 
+	    strerror(errno));
+    return -1;
+  }
+
+  return ret_val;
+}
+
+
+void formatTime(REGFI_NTTIME nttime, char* output)
+{
+  time_t tmp_time[1];
+  struct tm* tmp_time_s = NULL;
+
+  *tmp_time = (time_t)regfi_nt2unix_time(nttime);
+  tmp_time_s = gmtime(tmp_time);
+  strftime(output, 
+	   (4+1+2+1+2)+1+(2+1+2+1+2)+1, 
+              "%Y-%m-%d %H:%M:%S", 
+	   tmp_time_s);
+}
Index: /releases/1.0.1/src/reglookup-recover.c
===================================================================
--- /releases/1.0.1/src/reglookup-recover.c	(revision 275)
+++ /releases/1.0.1/src/reglookup-recover.c	(revision 275)
@@ -0,0 +1,1000 @@
+/*
+ * This program attempts to recover deleted data structures in a registry hive.
+ *
+ * Copyright (C) 2008-2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "talloc.h"
+#include "regfi.h"
+#include "range_list.h"
+#include "lru_cache.h"
+
+
+/* Globals, influenced by command line parameters */
+bool print_verbose = false;
+bool print_security = false;
+bool print_header = true;
+bool print_leftover = false;
+bool print_parsedraw = false;
+const char* registry_file = NULL;
+
+#include "common.c"
+
+
+char* getQuotedData(REGFI_RAW_FILE* file_cb, uint32_t offset, uint32_t length)
+{
+  uint8_t* buf;
+  char* quoted_buf;
+  uint32_t len;
+
+  if((regfi_seek(file_cb, offset, SEEK_SET)) == -1)
+    return NULL;
+
+  buf = (uint8_t*)malloc(length);
+  if(buf == NULL)
+    return NULL;
+
+  len = length;
+  if((regfi_read(file_cb, buf, &length) != 0) || length != len)
+  {
+    free(buf);
+    return NULL;
+  }
+
+  quoted_buf = quote_buffer(buf, length, common_special_chars);
+  free(buf);
+
+  return quoted_buf;
+}
+
+/* XXX: Somewhere in here, need to start looking for and handling classnames */
+void printKey(REGFI_FILE* f, REGFI_NK* nk, const char* prefix)
+{
+  char mtime[24];
+  char* quoted_name = NULL;
+  char* quoted_raw = "";
+
+  formatTime(nk->mtime, mtime);
+  
+  /* XXX: Add command line option to choose output encoding */
+  regfi_interpret_keyname(f, nk, true);
+
+  quoted_name = get_quoted_keyname(nk);
+  if (quoted_name == NULL)
+  {
+    quoted_name = malloc(1*sizeof(char));
+    if(quoted_name == NULL)
+      bailOut(REGLOOKUP_EXIT_OSERR, 
+	      "ERROR: Could not allocate sufficient memory.\n");
+    quoted_name[0] = '\0';
+
+    fprintf(stderr, "WARN: NULL key name in NK record at offset %.8X.\n",
+	    nk->offset);
+  }
+
+  if(print_parsedraw)
+    quoted_raw = getQuotedData(f->cb, nk->offset, nk->cell_size);
+
+  printf("%.8X,%.8X,KEY,%s,%s,%s,%d,,,,,,,,%s\n", nk->offset, nk->cell_size,
+	 prefix, quoted_name, mtime, nk->num_values, quoted_raw);
+  
+  if(print_parsedraw)
+    free(quoted_raw);
+  free(quoted_name);
+}
+
+
+void printValue(REGFI_FILE* f, REGFI_VK* vk, const char* prefix)
+{
+  char* quoted_value = NULL;
+  char* quoted_name = NULL;
+  char* quoted_raw = "";
+  char* conv_error = NULL;
+  const char* str_type = NULL;
+
+  /* XXX: Add command line option to choose output encoding */
+  regfi_interpret_valuename(f, vk, true);
+  
+  quoted_name = get_quoted_valuename(vk);
+  if (quoted_name == NULL)
+  { /* Value names are NULL when we're looking at the "(default)" value.
+     * Currently we just return a 0-length string to try an eliminate 
+     * ambiguity with a literal "(default)" value.  The data type of a line
+     * in the output allows one to differentiate between the parent key and
+     * this value.
+     */
+    quoted_name = strdup("");
+    if(quoted_name == NULL)
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+  }
+
+  /* XXX: Add command line option to choose output encoding */
+  if(vk->data != NULL 
+     && !regfi_interpret_data(f, vk->type, vk->data))
+  {
+    fprintf(stderr, "WARN: Error occurred while interpreting data for VK record"
+	    " at offset 0x%.8X.\n", vk->offset);
+  }
+  printMsgs(f);
+
+  quoted_value = data_to_ascii(vk->data, &conv_error);
+  if(quoted_value == NULL)
+  {
+    quoted_value = malloc(1*sizeof(char));
+    if(quoted_value == NULL)
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+    quoted_value[0] = '\0';
+
+    if(conv_error == NULL)
+      fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
+	      "Memory allocation failure likely.\n", prefix, quoted_name);
+    else if(print_verbose)
+      fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
+	      "Returned error: %s\n", prefix, quoted_name, conv_error);
+  }
+  /* XXX: should these always be printed? */
+  else if(conv_error != NULL && print_verbose)
+    fprintf(stderr, "INFO: While quoting value for '%s/%s', "
+	    "warning returned: %s\n", prefix, quoted_name, conv_error);
+
+
+  if(print_parsedraw)
+    quoted_raw = getQuotedData(f->cb, vk->offset, vk->cell_size);
+
+  str_type = regfi_type_val2str(vk->type);
+  if(str_type == NULL)
+    printf("%.8X,%.8X,VALUE,%s,%s,,,0x%.8X,%s,%d,,,,,%s\n", 
+	   vk->offset, vk->cell_size, prefix, quoted_name, 
+	   vk->type, quoted_value, vk->data_size, quoted_raw);
+  else
+    printf("%.8X,%.8X,VALUE,%s,%s,,,%s,%s,%d,,,,,%s\n", 
+	   vk->offset, vk->cell_size, prefix, quoted_name, 
+	   str_type, quoted_value, vk->data_size, quoted_raw);
+
+  if(print_parsedraw)
+    free(quoted_raw);
+  if(quoted_value != NULL)
+    free(quoted_value);
+  if(quoted_name != NULL)
+    free(quoted_name);
+  if(conv_error != NULL)
+    free(conv_error);
+}
+
+
+void printSK(REGFI_FILE* f, REGFI_SK* 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->cb, sk->offset, sk->cell_size);
+
+  if(owner == NULL)
+    owner = empty_str;
+  if(group == NULL)
+    group = empty_str;
+  if(sacl == NULL)
+    sacl = empty_str;
+  if(dacl == NULL)
+    dacl = empty_str;
+
+  printf("%.8X,%.8X,SK,,,,,,,,%s,%s,%s,%s,%s\n", sk->offset, sk->cell_size,
+	 owner, group, sacl, dacl, quoted_raw);
+  
+  if(owner != empty_str)
+    free(owner);
+  if(group != empty_str)
+    free(group);
+  if(sacl != empty_str)
+    free(sacl);
+  if(dacl != empty_str)
+    free(dacl);
+
+  if(print_parsedraw)
+    free(quoted_raw);
+}
+
+
+int printCell(REGFI_FILE* f, uint32_t offset)
+{
+  char* quoted_buf;
+  uint32_t cell_length;
+  bool unalloc;
+
+  if(!regfi_parse_cell(f->cb, offset, NULL, 0, &cell_length, &unalloc))
+    return 1;
+
+  quoted_buf = getQuotedData(f->cb, offset, cell_length);
+  if(quoted_buf == NULL)
+    return 2;
+
+  printf("%.8X,%.8X,RAW,,,,,,,,,,,,%s\n", offset, cell_length, quoted_buf);
+
+  free(quoted_buf);
+  return 0;
+}
+
+
+/* This function returns a properly quoted parent path or partial parent 
+ * path for a given key.  Returns NULL on error, "" if no path was available.
+ * Paths returned must be free()d.
+ */
+/* XXX: This is not terribly efficient, as it may reparse many keys 
+ *      repeatedly.  Should try to add caching.
+ */
+char* getParentPath(REGFI_FILE* f, REGFI_NK* nk)
+{
+  void_stack* path_stack = void_stack_new(REGFI_MAX_DEPTH);
+  REGFI_NK* cur_ancestor;
+  char* ret_val;
+  uint32_t virt_offset, i, stack_size, ret_val_size, ret_val_used, offset;
+  int32_t max_size;
+  REGFI_BUFFER* path_element;
+  
+  /* The path_stack size limit should guarantee that we don't recurse forever. */
+  virt_offset = nk->parent_off;
+  ret_val_size = 1; /* NUL */
+  while(virt_offset != REGFI_OFFSET_NONE)
+  {
+    offset = virt_offset+REGFI_REGF_SIZE;
+    max_size = regfi_calc_maxsize(f, offset);
+    if(max_size < 0)
+      virt_offset = REGFI_OFFSET_NONE;
+    else
+    {
+      cur_ancestor = regfi_parse_nk(f, offset, max_size, true);
+      printMsgs(f);
+
+      if(cur_ancestor == NULL)
+	virt_offset = REGFI_OFFSET_NONE;
+      else
+      {
+	if(cur_ancestor->flags & REGFI_NK_FLAG_ROOT)
+	  virt_offset = REGFI_OFFSET_NONE;
+	else
+	  virt_offset = cur_ancestor->parent_off;
+	
+	path_element = talloc(path_stack, REGFI_BUFFER);
+	if(path_element != NULL)
+	{
+	  /* XXX: Add command line option to choose output encoding */
+	  regfi_interpret_keyname(f, cur_ancestor, true);
+	  
+	  path_element->buf = (uint8_t*)get_quoted_keyname(cur_ancestor);
+	}
+ 
+	if(path_element == NULL || path_element->buf == NULL 
+	   || !void_stack_push(path_stack, path_element))
+	{
+	  /* XXX: Need to add a warning here */
+	  regfi_free_record(f, cur_ancestor);
+	  void_stack_free(path_stack);
+	  return NULL;
+	}
+
+	/* Path element and preceeding delimiter
+	 * Note that this integer can't overflow since key name lengths are
+	 * 16 bits and the max depth is 512.
+	 */
+	path_element->len = strlen((char*)path_element->buf);
+	ret_val_size += path_element->len + 1;
+
+	regfi_free_record(f, cur_ancestor);
+      }
+    }
+  }
+  
+  stack_size = void_stack_size(path_stack);
+  ret_val_used = 0;
+  ret_val = malloc(ret_val_size);
+  if(ret_val == NULL)
+  {
+    void_stack_free(path_stack);
+    return NULL;
+  }
+  ret_val[0] = '\0';
+
+  for(i=0; i<stack_size; i++)
+  {
+    path_element = void_stack_pop(path_stack);
+    snprintf(ret_val+ret_val_used, ret_val_size-ret_val_used, 
+	     "/%s", path_element->buf);
+    ret_val_used += path_element->len + 1;
+    free(path_element->buf);
+    talloc_free(path_element);
+  }
+  void_stack_free(path_stack);
+
+  return ret_val;
+}
+
+
+static void usage(void)
+{
+  fprintf(stderr, "Usage: reglookup-recover [options] <REGISTRY_FILE>\n");
+  fprintf(stderr, "Version: %s\n", regfi_version());
+  fprintf(stderr, "Options:\n");
+  fprintf(stderr, "\t-v\t sets verbose mode.\n");
+  fprintf(stderr, "\t-h\t enables header row. (default)\n");
+  fprintf(stderr, "\t-H\t disables header row.\n");
+  fprintf(stderr, "\t-l\t enables leftover(raw) cell output.\n");
+  fprintf(stderr, "\t-L\t disables leftover(raw) cell output. (default)\n");
+  fprintf(stderr, "\t-r\t enables raw cell output for parsed cells.\n");
+  fprintf(stderr, "\t-R\t disables raw cell output for parsed cells. (default)\n");
+  fprintf(stderr, "\n");
+}
+
+
+bool removeRange(range_list* rl, uint32_t offset, uint32_t length)
+{
+  int32_t rm_idx;
+  const range_list_element* cur_elem;
+
+  rm_idx = range_list_find(rl, offset);
+  if(rm_idx < 0)
+  {
+    fprintf(stderr, "DEBUG: removeRange: rm_idx < 0; (%d)\n", rm_idx);
+    return false;
+  }
+
+  cur_elem = range_list_get(rl, rm_idx);
+  if(cur_elem == NULL)
+  {
+    fprintf(stderr, "DEBUG: removeRange: cur_elem == NULL.  rm_idx=%d\n", rm_idx);
+    return false;
+  }
+
+  if(offset > cur_elem->offset)
+  {
+    if(!range_list_split_element(rl, rm_idx, offset))
+    {
+      fprintf(stderr, "DEBUG: removeRange: first split failed\n");
+      return false;
+    }
+    rm_idx++;
+    cur_elem = range_list_get(rl, rm_idx);
+    if(cur_elem == NULL)
+    {
+      fprintf(stderr, 
+	      "DEBUG: removeRange: cur_elem == NULL after first split.  rm_idx=%d\n",
+	      rm_idx);
+      return false;
+    }
+  }
+  
+  if(offset+length < cur_elem->offset+cur_elem->length)
+  {
+    if(!range_list_split_element(rl, rm_idx, offset+length))
+    {
+      fprintf(stderr, "DEBUG: removeRange: second split failed\n");
+      return false;
+    }
+  }
+  
+  if(!range_list_remove(rl, rm_idx))
+  {
+    fprintf(stderr, "DEBUG: removeRange: remove failed\n");
+    return false;
+  }
+
+  return true;
+}
+
+
+int extractVKs(REGFI_FILE* f,
+	       range_list* unalloc_cells,
+	       range_list* unalloc_values)
+{
+  const range_list_element* cur_elem;
+  REGFI_VK* vk;
+  uint32_t i, j;
+
+  for(i=0; i < range_list_size(unalloc_cells); i++)
+  {
+    printMsgs(f);
+    cur_elem = range_list_get(unalloc_cells, i);
+    for(j=0; j <= cur_elem->length; j+=8)
+    {
+      vk = regfi_parse_vk(f, cur_elem->offset+j, 
+			   cur_elem->length-j, false);
+      printMsgs(f);
+
+      if(vk != NULL)
+      {
+	if(!range_list_add(unalloc_values, vk->offset,
+			   vk->cell_size, vk))
+	{
+	  fprintf(stderr, "ERROR: Couldn't add value to unalloc_values.\n");
+	  return 20;
+	}
+	j+=vk->cell_size-8;
+      }
+    }
+  }
+
+  /* Remove value ranges from the unalloc_cells before we continue. */
+  for(i=0; i<range_list_size(unalloc_values); i++)
+  {
+    cur_elem = range_list_get(unalloc_values, i);
+    if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
+      return 30;
+  }
+
+  return 0;
+}
+
+
+int extractDataCells(REGFI_FILE* file,
+		     range_list* unalloc_cells,
+		     range_list* unalloc_values)
+{
+  const range_list_element* cur_elem;
+  REGFI_VK* vk;
+  range_list* bd_cells;
+  REGFI_BUFFER data;
+  uint32_t i, j, offset, cell_length, length;
+  int32_t max_size;
+  bool unalloc;
+
+  bd_cells = range_list_new();
+  if(bd_cells == NULL)
+    return 10;
+
+  data.buf = NULL;
+  data.len = 0;
+  for(i=0; i<range_list_size(unalloc_values); i++)
+  {
+    cur_elem = range_list_get(unalloc_values, i);
+    vk = (REGFI_VK*)cur_elem->data;
+    if(vk == NULL)
+      return 11;
+
+    length = vk->data_size;
+    vk->data = NULL;
+    if(vk->data_size != 0)
+    {
+      offset = vk->data_off+REGFI_REGF_SIZE;
+
+      if(vk->data_in_offset)
+	data = regfi_parse_little_data(file, vk->data_off, 
+				       length, false);
+      else
+      {
+	max_size = regfi_calc_maxsize(file, offset);
+	if(max_size >= 0 
+	   && regfi_parse_cell(file->cb, offset, NULL, 0,
+			       &cell_length, &unalloc)
+	   && (cell_length & 0x00000007) == 0
+	   && cell_length <= max_size)
+	{
+	  if(cell_length - 4 < length)
+	  {
+	    /* Multi-cell "big data" */
+
+	    /* XXX: All big data records thus far have been 16 bytes long.  
+	     *      Should we check for this precise size instead of just 
+	     *      relying upon the above check?
+	     */
+	    if (file->major_version >= 1 && file->minor_version >= 5)
+	    {
+	      /* Attempt to parse a big data record */
+	      data = regfi_load_big_data(file, offset, length, 
+					 cell_length, bd_cells, false);
+
+	      /* XXX: if this turns out NULL, should fall back to truncating cell */
+	      if(data.buf != NULL)
+	      {
+		for(j=0; j<range_list_size(bd_cells); j++)
+		{
+		  cur_elem = range_list_get(bd_cells, j);
+		  if(cur_elem == NULL)
+		    return 20;
+		  if(!range_list_has_range(unalloc_cells,
+					   cur_elem->offset, 
+					   cur_elem->length))
+		  {
+		    fprintf(stderr, 
+			    "WARN: Successfully parsed big data at offset"
+			    " 0x%.8X was rejected because some substructure"
+			    " (offset=0x%.8X) is allocated or used in other"
+			    " recovered structures.\n",
+			    offset, cur_elem->offset);
+		    talloc_free(data.buf);
+		    data.buf = NULL;
+		    data.len = 0;
+		    break;
+		  }
+		}
+		
+		if(data.buf != NULL)
+		{
+		  for(j=0; j<range_list_size(bd_cells); j++)
+		  {
+		    cur_elem = range_list_get(bd_cells, j);
+		    if(cur_elem == NULL)
+		      return 21;
+		    
+		    if(!removeRange(unalloc_cells, 
+				    cur_elem->offset,
+				    cur_elem->length))
+		    { return 22; }
+		  }
+		}
+	      }
+
+	    }
+	    else
+	    {
+	      fprintf(stderr, 
+		      "WARN: Data length (0x%.8X)"
+		      " larger than remaining cell length (0x%.8X)"
+		      " while parsing data record at offset 0x%.8X."
+		      " Truncating...\n",
+		      length, cell_length - 4, offset);
+	       length = cell_length - 4;
+	    }
+	  }
+	  
+	  /* Typical 1-cell data */
+	  if(range_list_has_range(unalloc_cells, offset, length))
+	  {
+	    data = regfi_parse_data(file, offset, length, false);
+	    if(data.buf != NULL)
+	      if(!removeRange(unalloc_cells, offset, length))
+		return 30;
+	  }
+	}
+      }
+      /* XXX: Need to come up with a different way to link these so the
+       *      vk->data item can be removed from the structure.
+       */
+      vk->data = regfi_buffer_to_data(data);
+      talloc_reparent(NULL, vk, vk->data);
+    }
+  }
+
+  range_list_free(bd_cells);
+  return 0;
+}
+
+
+/* NOTE: unalloc_keys should be an empty range_list. */
+int extractKeys(REGFI_FILE* f, 
+		range_list* unalloc_cells, 
+		range_list* unalloc_keys)
+{
+  const range_list_element* cur_elem;
+  REGFI_NK* key = NULL;
+  uint32_t i, j;
+  int error_code = 0;
+
+  for(i=0; i < range_list_size(unalloc_cells); i++)
+  {
+    printMsgs(f);
+    cur_elem = range_list_get(unalloc_cells, i);
+    for(j=0; cur_elem->length > REGFI_NK_MIN_LENGTH 
+	  && j <= cur_elem->length-REGFI_NK_MIN_LENGTH; j+=8)
+    {
+      key = regfi_parse_nk(f, cur_elem->offset+j,
+			   cur_elem->length-j, false);
+      printMsgs(f);
+
+      if(key != NULL)
+      {
+	if(!range_list_add(unalloc_keys, key->offset, 
+			   key->cell_size, key))
+	{
+	  fprintf(stderr, "ERROR: Couldn't add key to unalloc_keys.\n");
+	  error_code = 20;
+	  goto fail;
+	}
+	talloc_reparent(NULL, unalloc_keys, key);
+	j+=key->cell_size-8;
+      }
+    }
+  }
+
+  for(i=0; i<range_list_size(unalloc_keys); i++)
+  {
+    cur_elem = range_list_get(unalloc_keys, i);
+    if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
+    {
+      error_code = 30;
+      goto fail;
+    }
+  }
+
+  return 0;
+
+ fail:
+  regfi_free_record(f, key);
+  return error_code;
+}
+
+int extractValueLists(REGFI_FILE* f,
+		      range_list* unalloc_cells,
+		      range_list* unalloc_keys,
+		      range_list* unalloc_linked_values)
+{
+  REGFI_NK* nk;
+  REGFI_VK* vk;
+  const range_list_element* cur_elem;
+  uint32_t i, j, num_keys, off, values_length;
+  int32_t max_size;
+
+  num_keys=range_list_size(unalloc_keys);
+  for(i=0; i<num_keys; i++)
+  {
+    cur_elem = range_list_get(unalloc_keys, i);
+    if(cur_elem == NULL)
+      return 10;
+    nk = cur_elem->data;
+
+    if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE))
+    {
+      off = nk->values_off + REGFI_REGF_SIZE;
+      max_size = regfi_calc_maxsize(f, off);
+      if(max_size >= 0)
+      {
+	nk->values = regfi_load_valuelist(f, off, nk->num_values, 
+					  max_size, false);
+	if(nk->values != NULL && nk->values->elements != NULL)
+	{
+	  /* Number of elements in the value list may be shorter than advertised 
+	   * by NK record due to cell truncation.  We'll consider this valid and 
+	   * only throw out the whole value list if it bleeds into an already 
+	   * parsed structure.
+	   */
+	  values_length = (nk->values->num_values+1)*sizeof(uint32_t);
+	  if(values_length != (values_length & 0xFFFFFFF8))
+	    values_length = (values_length & 0xFFFFFFF8) + 8;
+
+	  if(!range_list_has_range(unalloc_cells, off, values_length))
+	  { /* We've parsed a values-list which isn't in the unallocated list,
+	     * so prune it.
+	     */
+	    talloc_free(nk->values);
+	    nk->values = NULL;
+	  }
+	  else
+	  { /* Values-list was recovered.  Remove from unalloc_cells and 
+	     * inspect values. 
+	     */
+	    if(!removeRange(unalloc_cells, off, values_length))
+	      return 20;
+
+	    for(j=0; j < nk->values->num_values; j++)
+	    {
+	      /* Don't bother to restrict cell length here, since we'll
+	       * check our unalloc_cells range_list later. 
+	       */
+	      vk = regfi_parse_vk(f, nk->values->elements[j]+REGFI_REGF_SIZE,
+				  0x7FFFFFFF, false);
+	      printMsgs(f);
+	      
+	      if(vk != NULL)
+	      {
+		if(range_list_has_range(unalloc_cells, 
+					vk->offset, vk->cell_size))
+		{
+		  if(!range_list_add(unalloc_linked_values, vk->offset,
+				     vk->cell_size, vk))
+		  {
+		    talloc_free(vk);
+		    return 30;
+		  }
+
+		  if(!removeRange(unalloc_cells, vk->offset, vk->cell_size))
+		    return 40;
+		}
+		else
+		  talloc_free(vk);
+	      }
+	    }
+	  }
+	}
+      }
+    }
+  }
+
+  return 0;
+}
+
+
+
+/* NOTE: unalloc_sks should be an empty range_list. */
+int extractSKs(REGFI_FILE* f, 
+	       range_list* unalloc_cells,
+	       range_list* unalloc_sks)
+{
+  const range_list_element* cur_elem;
+  REGFI_SK* sk;
+  uint32_t i, j;
+
+  for(i=0; i < range_list_size(unalloc_cells); i++)
+  {
+    printMsgs(f);
+    cur_elem = range_list_get(unalloc_cells, i);
+    for(j=0; j <= cur_elem->length; j+=8)
+    {
+      sk = regfi_parse_sk(f, cur_elem->offset+j, 
+			  cur_elem->length-j, false);
+      printMsgs(f);
+
+      if(sk != NULL)
+      {
+	if(!range_list_add(unalloc_sks, sk->offset,
+			   sk->cell_size, sk))
+	{
+	  fprintf(stderr, "ERROR: Couldn't add sk to unalloc_sks.\n");
+	  return 20;
+	}
+	talloc_reparent(NULL, unalloc_sks, sk);
+	j+=sk->cell_size-8;
+      }
+    }
+  }
+
+  for(i=0; i<range_list_size(unalloc_sks); i++)
+  {
+    cur_elem = range_list_get(unalloc_sks, i);
+    if(!removeRange(unalloc_cells, cur_elem->offset, cur_elem->length))
+      return 30;
+  }
+
+  return 0;
+}
+
+
+int main(int argc, char** argv)
+{ 
+  REGFI_FILE* f;
+  const range_list_element* cur_elem;
+  range_list* unalloc_cells;
+  range_list* unalloc_keys;
+  range_list* unalloc_linked_values;
+  range_list* unalloc_values;
+  range_list* unalloc_sks;
+  char** parent_paths;
+  char* tmp_name;
+  char* tmp_path;
+  REGFI_NK* tmp_key;
+  REGFI_VK* tmp_value;
+  uint32_t argi, arge, i, j, ret, num_unalloc_keys;
+  int fd;
+
+  /* Process command line arguments */
+  if(argc < 2)
+  {
+    usage();
+    bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n");
+  }
+  
+  arge = argc-1;
+  for(argi = 1; argi < arge; argi++)
+  {
+    if (strcmp("-v", argv[argi]) == 0)
+      print_verbose = true;
+    else if (strcmp("-h", argv[argi]) == 0)
+      print_header = true;
+    else if (strcmp("-H", argv[argi]) == 0)
+      print_header = false;
+    else if (strcmp("-l", argv[argi]) == 0)
+      print_leftover = true;
+    else if (strcmp("-L", argv[argi]) == 0)
+      print_leftover = false;
+    else if (strcmp("-r", argv[argi]) == 0)
+      print_parsedraw = true;
+    else if (strcmp("-R", argv[argi]) == 0)
+      print_parsedraw = false;
+    else
+    {
+      usage();
+      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
+      bailOut(REGLOOKUP_EXIT_USAGE, "");
+    }
+  }
+  registry_file = argv[argi];
+
+  fd = openHive(registry_file);
+  if(fd < 0)
+  {
+    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
+    bailOut(REGLOOKUP_EXIT_NOINPUT, "");
+  }
+
+  if(print_verbose)
+    regfi_log_set_mask(REGFI_LOG_ERROR|REGFI_LOG_WARN|REGFI_LOG_INFO);
+  else
+    regfi_log_set_mask(REGFI_LOG_ERROR);
+
+  /* XXX: add command line option to choose output encoding */
+  f = regfi_alloc(fd, REGFI_ENCODING_ASCII);
+  if(f == NULL)
+  {
+    close(fd);
+    bailOut(REGLOOKUP_EXIT_NOINPUT, "ERROR: Failed to create REGFI_FILE structure.\n");
+  }
+
+  if(print_header)
+    printf("OFFSET,REC_LENGTH,REC_TYPE,PATH,NAME,"
+	   "NK_MTIME,NK_NVAL,VK_TYPE,VK_VALUE,VK_DATA_LEN,"
+	   "SK_OWNER,SK_GROUP,SK_SACL,SK_DACL,RAW_CELL\n");
+
+  unalloc_cells = regfi_parse_unalloc_cells(f);
+  if(unalloc_cells == NULL)
+  {
+    fprintf(stderr, "ERROR: Could not obtain list of unallocated cells.\n");
+    return 1;
+  }
+
+  unalloc_keys = range_list_new();
+  if(unalloc_keys == NULL)
+    return 10;
+
+  unalloc_linked_values = range_list_new();
+  if(unalloc_linked_values == NULL)
+    return 10;
+
+  unalloc_values = range_list_new();
+  if(unalloc_values == NULL)
+    return 10;
+
+  unalloc_sks = range_list_new();
+  if(unalloc_sks == NULL)
+    return 10;
+
+  ret = extractKeys(f, unalloc_cells, unalloc_keys);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractKeys() failed with %d.\n", ret);
+    return ret;
+  }
+
+  ret = extractValueLists(f, unalloc_cells, unalloc_keys,unalloc_linked_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractValueLists() failed with %d.\n", ret);
+    return ret;
+  }
+
+  /* Carve any orphan values */
+  ret = extractVKs(f, unalloc_cells, unalloc_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractVKs() failed with %d.\n", ret);
+    return ret;
+  }
+
+  /* Carve any data associated with VK records */
+  ret = extractDataCells(f, unalloc_cells, unalloc_linked_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret);
+    return ret;
+  }
+  ret = extractDataCells(f, unalloc_cells, unalloc_values);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret);
+    return ret;
+  }
+  
+  /* Carve any SK records */
+  ret = extractSKs(f, unalloc_cells, unalloc_sks);
+  if(ret != 0)
+  {
+    fprintf(stderr, "ERROR: extractSKs() failed with %d.\n", ret);
+    return ret;
+  }
+
+  /* Now that we're done carving, associate recovered keys with parents, 
+   * if at all possible.
+   */
+  num_unalloc_keys = range_list_size(unalloc_keys);
+  parent_paths = (char**)malloc(sizeof(char*)*num_unalloc_keys);
+  if(parent_paths == NULL)
+    return 10;
+
+  for(i=0; i < num_unalloc_keys; i++)
+  {
+    cur_elem = range_list_get(unalloc_keys, i);
+    tmp_key = (REGFI_NK*)cur_elem->data;
+
+    if(tmp_key == NULL)
+      return 20;
+    
+    parent_paths[i] = getParentPath(f, tmp_key);
+    if(parent_paths[i] == NULL)
+      return 20;
+  }
+  
+  /* Now start the output */
+  for(i=0; i < num_unalloc_keys; i++)
+  {
+    cur_elem = range_list_get(unalloc_keys, i);
+    tmp_key = (REGFI_NK*)cur_elem->data;
+
+    printKey(f, tmp_key, parent_paths[i]);
+    if(tmp_key->num_values > 0 && tmp_key->values != NULL)
+    {
+      /* XXX: Add command line option to choose output encoding */
+      regfi_interpret_keyname(f, tmp_key, true);
+
+      tmp_name = get_quoted_keyname(tmp_key);
+      tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2);
+      if(tmp_path == NULL)
+      {
+	free(tmp_name);
+	return 10;
+      }
+
+      sprintf(tmp_path, "%s/%s", parent_paths[i], tmp_name);
+      for(j=0; j < tmp_key->values->num_values; j++)
+      {
+	tmp_value = 
+	  (REGFI_VK*)range_list_find_data(unalloc_linked_values, 
+					  tmp_key->values->elements[j]
+					  + REGFI_REGF_SIZE);
+	if(tmp_value != NULL)
+	  printValue(f, tmp_value, tmp_path);
+      }
+      free(tmp_path);
+      free(tmp_name);
+      free(parent_paths[i]);
+    }
+  }
+  free(parent_paths);
+
+  /* Print out orphaned values */
+  for(i=0; i < range_list_size(unalloc_values); i++)
+  {
+    cur_elem = range_list_get(unalloc_values, i);
+    tmp_value = (REGFI_VK*)cur_elem->data; 
+
+    printValue(f, tmp_value, "");
+  }
+  
+  if(print_leftover)
+  {
+    for(i=0; i < range_list_size(unalloc_cells); i++)
+    {
+      cur_elem = range_list_get(unalloc_cells, i);
+      printCell(f, cur_elem->offset);
+    }
+  }
+
+  range_list_free(unalloc_cells);
+  range_list_free(unalloc_keys);
+  range_list_free(unalloc_linked_values);
+  range_list_free(unalloc_values);
+  range_list_free(unalloc_sks);
+
+  regfi_free(f);
+  close(fd);
+
+  return 0;
+}
Index: /releases/1.0.1/src/reglookup.c
===================================================================
--- /releases/1.0.1/src/reglookup.c	(revision 275)
+++ /releases/1.0.1/src/reglookup.c	(revision 275)
@@ -0,0 +1,700 @@
+/*
+ * A utility to read a Windows NT and later registry files.
+ *
+ * Copyright (C) 2005-2011 Timothy D. Morgan
+ * Copyright (C) 2010 Tobias Mueller (portions of '-i' code)
+ * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include "regfi.h"
+#include "void_stack.h"
+
+/* Globals, influenced by command line parameters */
+bool print_value_mtime = false;
+bool print_verbose = false;
+bool print_security = false;
+bool print_header = true;
+bool path_filter_enabled = false;
+bool type_filter_enabled = false;
+char* path_filter = NULL;
+int type_filter;
+const char* registry_file = NULL;
+
+/* Other globals */
+REGFI_FILE* f;
+
+
+/* XXX: A hack to share some functions with reglookup-recover.c.
+ *      Should move these into a proper library at some point.
+ */
+#include "common.c"
+
+
+static bool keysEqual(const REGFI_NK* x, const REGFI_NK* y)
+{
+  return (x != NULL && y != NULL && x->offset == y->offset);
+}
+
+void printValue(REGFI_ITERATOR* iter, const REGFI_VK* vk, char* prefix)
+{
+  const REGFI_NK* cur_key;
+  const REGFI_DATA* data;
+  char* quoted_value = NULL;
+  char* quoted_name = NULL;
+  char* conv_error = NULL;
+  const char* str_type = NULL;
+  char mtime[20];
+  time_t tmp_time[1];
+  struct tm* tmp_time_s = NULL;
+
+  quoted_name = get_quoted_valuename(vk);
+  if (quoted_name == NULL)
+  { /* Value names are NULL when we're looking at the "(default)" value.
+     * Currently we just return a 0-length string to try an eliminate 
+     * ambiguity with a literal "(default)" value.  The data type of a line
+     * in the output allows one to differentiate between the parent key and
+     * this value.
+     */
+    quoted_name = malloc(1*sizeof(char));
+    if(quoted_name == NULL)
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n");
+    quoted_name[0] = '\0';
+  }
+  
+  data = regfi_fetch_data(iter->f, vk);
+
+  printMsgs(iter->f);
+  if(data != NULL)
+  {
+    quoted_value = data_to_ascii(data, &conv_error);
+    if(quoted_value == NULL)
+    {
+      if(conv_error == NULL)
+	fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
+		"Memory allocation failure likely.\n", prefix, quoted_name);
+      else
+	fprintf(stderr, "WARN: Could not quote value for '%s/%s'.  "
+		"Returned error: %s\n", prefix, quoted_name, conv_error);
+    }
+    else if(conv_error != NULL)
+      fprintf(stderr, "WARN: While quoting value for '%s/%s', "
+	      "warning returned: %s\n", prefix, quoted_name, conv_error);
+    regfi_free_record(iter->f, data);
+  }
+
+  if(print_value_mtime)
+  {
+    cur_key = regfi_iterator_cur_key(iter);
+    *tmp_time = regfi_nt2unix_time(cur_key->mtime);
+    tmp_time_s = gmtime(tmp_time);
+    strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+    regfi_free_record(iter->f, cur_key);
+  }
+  else
+    mtime[0] = '\0';
+
+  str_type = regfi_type_val2str(vk->type);
+  if(print_security)
+  {
+    if(str_type == NULL)
+      printf("%s/%s,0x%.8X,%s,%s,,,,\n", prefix, quoted_name,
+	     vk->type, quoted_value, mtime);
+    else
+      printf("%s/%s,%s,%s,%s,,,,\n", prefix, quoted_name,
+	     str_type, quoted_value, mtime);
+  }
+  else
+  {
+    if(str_type == NULL)
+      printf("%s/%s,0x%.8X,%s,%s\n", prefix, quoted_name,
+	     vk->type, quoted_value, mtime);
+    else
+      printf("%s/%s,%s,%s,%s\n", prefix, quoted_name,
+	     str_type, quoted_value, mtime);
+  }
+
+  if(quoted_value != NULL)
+    free(quoted_value);
+  if(quoted_name != NULL)
+    free(quoted_name);
+  if(conv_error != NULL)
+    free(conv_error);
+}
+
+
+char** splitPath(const char* s)
+{
+  char** ret_val;
+  const char* cur = s;
+  char* next = NULL;
+  char* copy;
+  uint32_t ret_cur = 0;
+
+  ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**));
+  if (ret_val == NULL)
+    return NULL;
+  ret_val[0] = NULL;
+
+  /* We return a well-formed, 0-length, path even when input is icky. */
+  if (s == NULL)
+    return ret_val;
+  
+  while((next = strchr(cur, '/')) != NULL)
+  {
+    if ((next-cur) > 0)
+    {
+      copy = (char*)malloc((next-cur+1)*sizeof(char));
+      if(copy == NULL)
+	bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
+	  
+      memcpy(copy, cur, next-cur);
+      copy[next-cur] = '\0';
+      ret_val[ret_cur++] = copy;
+      if(ret_cur < (REGFI_MAX_DEPTH+1+1))
+	ret_val[ret_cur] = NULL;
+      else
+	bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
+    }
+    cur = next+1;
+  }
+
+  /* Grab last element, if path doesn't end in '/'. */
+  if(strlen(cur) > 0)
+  {
+    copy = strdup(cur);
+    ret_val[ret_cur++] = copy;
+    if(ret_cur < (REGFI_MAX_DEPTH+1+1))
+      ret_val[ret_cur] = NULL;
+    else
+      bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n");
+  }
+
+  return ret_val;
+}
+
+
+void freePath(char** path)
+{
+  uint32_t i;
+
+  if(path == NULL)
+    return;
+
+  for(i=0; path[i] != NULL; i++)
+    free(path[i]);
+
+  free(path);
+}
+
+
+/* Returns a quoted path of the current iterator's position */
+char* iter2Path(REGFI_ITERATOR* i)
+{
+  const REGFI_NK** path;
+  uint32_t k;
+  uint32_t buf_left = 127;
+  uint32_t buf_len = buf_left+1;
+  uint32_t name_len = 0;
+  uint32_t grow_amt;
+  char* buf;
+  char* new_buf;
+  char* name;
+  
+  buf = (char*)malloc((buf_len)*sizeof(char));
+  if (buf == NULL)
+    return NULL;
+  buf[0] = '\0';
+
+  path = regfi_iterator_ancestry(i);
+  if(path == NULL)
+  {
+    free(buf);
+    return NULL;
+  }
+
+  for(k=0; path[k] != NULL; k++)
+  {
+    /* skip root element's name */
+    if(k == 0)
+    {
+      buf[0] = '/';
+      buf[1] = '\0';
+    }
+    else
+    {
+      name = get_quoted_keyname(path[k]);
+
+      buf[buf_len-buf_left-1] = '/';
+      buf_left -= 1;
+      name_len = strlen(name);
+      if(name_len+1 > buf_left)
+      {
+        grow_amt = (uint32_t)(buf_len/2);
+        buf_len += name_len+1+grow_amt-buf_left;
+        if((new_buf = realloc(buf, buf_len)) == NULL)
+        {
+          regfi_free_record(i->f, path);
+          free(name);
+          free(buf);
+          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);
+    }
+  }
+
+  regfi_free_record(i->f, path);
+  return buf;
+}
+
+
+void printValueList(REGFI_ITERATOR* iter, char* prefix)
+{
+  const REGFI_VK* value;
+
+  regfi_iterator_first_value(iter);
+  while((value = regfi_iterator_cur_value(iter)) != NULL)
+  {
+    if(!type_filter_enabled || (value->type == type_filter))
+      printValue(iter, value, prefix);
+    regfi_free_record(iter->f, value);
+    regfi_iterator_next_value(iter);
+    printMsgs(iter->f);
+  }
+}
+
+
+void printKey(REGFI_ITERATOR* iter, char* full_path)
+{
+  static char empty_str[1] = "";
+  char* owner = NULL;
+  char* group = NULL;
+  char* sacl = NULL;
+  char* dacl = NULL;
+  char mtime[24];
+  char* quoted_classname;
+  const REGFI_SK* sk;
+  const REGFI_NK* key = regfi_iterator_cur_key(iter);
+  const REGFI_CLASSNAME* classname;
+
+  formatTime(key->mtime, mtime);
+
+  if(print_security && (sk=regfi_fetch_sk(iter->f, key)))
+  {
+    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);
+    regfi_free_record(iter->f, sk);
+
+    if(owner == NULL)
+      owner = empty_str;
+    if(group == NULL)
+      group = empty_str;
+    if(sacl == NULL)
+      sacl = empty_str;
+    if(dacl == NULL)
+      dacl = empty_str;
+
+    classname = regfi_fetch_classname(iter->f, key);
+    printMsgs(iter->f);
+    if(classname != NULL)
+    {
+      if(classname->interpreted == NULL)
+      {
+	fprintf(stderr, "WARN: Could not convert class name"
+		" charset for key '%s'.  Quoting raw...\n", full_path);
+	quoted_classname = quote_buffer(classname->raw, classname->size,
+					key_special_chars);
+      }
+      else
+	quoted_classname = quote_string(classname->interpreted, 
+					key_special_chars);
+
+      if(quoted_classname == NULL)
+      {
+	fprintf(stderr, "ERROR: Could not quote classname"
+		" for key '%s' due to unknown error.\n", full_path);
+	quoted_classname = empty_str;
+      }
+    }
+    else
+      quoted_classname = empty_str;
+    regfi_free_record(iter->f, classname);
+
+    printMsgs(iter->f);
+    printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime, 
+	   owner, group, sacl, dacl, quoted_classname);
+
+    if(owner != empty_str)
+      free(owner);
+    if(group != empty_str)
+      free(group);
+    if(sacl != empty_str)
+      free(sacl);
+    if(dacl != empty_str)
+      free(dacl);
+    if(quoted_classname != empty_str)
+      free(quoted_classname);
+  }
+  else
+    printf("%s,KEY,,%s\n", full_path, mtime);
+
+  regfi_free_record(iter->f, key);
+}
+
+
+void printKeyTree(REGFI_ITERATOR* iter)
+{
+  const REGFI_NK* root = NULL;
+  const REGFI_NK* cur = NULL;
+  const REGFI_NK* sub = NULL;
+  char* path = NULL;
+  int key_type = regfi_type_str2val("KEY");
+  bool print_this = true;
+
+  root = regfi_iterator_cur_key(iter);
+  cur = root = regfi_reference_record(iter->f, root);
+  regfi_iterator_first_subkey(iter);
+  sub = regfi_iterator_cur_subkey(iter);
+  printMsgs(iter->f);
+
+  if(root == NULL)
+    bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: root cannot be NULL.\n");
+  
+  do
+  {
+    if(print_this)
+    {
+      path = iter2Path(iter);
+      if(path == NULL)
+	bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not construct iterator's path.\n");
+
+      if(!type_filter_enabled || (key_type == type_filter))
+	printKey(iter, path);
+      if(!type_filter_enabled || (key_type != type_filter))
+	printValueList(iter, path);
+      
+      free(path);
+    }
+    
+    if(sub == NULL)
+    {
+      if(!keysEqual(cur, root))
+      {
+        regfi_free_record(iter->f, cur);
+        cur = NULL;
+	/* We're done with this sub-tree, going up and hitting other branches. */
+	if(!regfi_iterator_up(iter))
+	{
+	  printMsgs(iter->f);
+	  bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator upward.\n");
+	}
+
+	cur = regfi_iterator_cur_key(iter);
+	if(cur == NULL)
+	{
+	  printMsgs(iter->f);
+	  bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: unexpected NULL for key.\n");
+	}
+	
+	regfi_iterator_next_subkey(iter);
+	sub = regfi_iterator_cur_subkey(iter);
+      }
+      print_this = false;
+    }
+    else
+    { /* We have unexplored sub-keys.  
+       * Let's move down and print this first sub-tree out. 
+       */
+      regfi_free_record(iter->f, cur);
+      cur = NULL;
+      if(!regfi_iterator_down(iter))
+      {
+	printMsgs(iter->f);
+	bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator downward.\n");
+      }
+
+      cur = regfi_iterator_cur_key(iter);
+      regfi_free_record(iter->f, sub);
+      regfi_iterator_first_subkey(iter);
+      sub = regfi_iterator_cur_subkey(iter);
+      print_this = true;
+    }
+    printMsgs(iter->f);
+  } while(!(keysEqual(cur, root) && (sub == NULL)));
+  if(cur != NULL)
+    regfi_free_record(iter->f, cur);
+  regfi_free_record(iter->f, root);
+
+  if(print_verbose)
+    fprintf(stderr, "INFO: Finished printing key tree.\n");
+}
+
+
+/* XXX: What if there is BOTH a value AND a key with that name?? 
+ *      What if there are multiple keys/values with the same name?? 
+ */
+/*
+ * Returns 0 if path was not found.
+ * Returns 1 if path was found as value.
+ * Returns 2 if path was found as key.
+ * Returns less than 0 on other error.
+ */
+int retrievePath(REGFI_ITERATOR* iter, char** path)
+{
+  const REGFI_VK* value;
+  char* tmp_path_joined;
+  const char** tmp_path;
+  uint32_t i;
+  
+  if(path == NULL)
+    return -1;
+
+  /* One extra for any value at the end, and one more for NULL */
+  tmp_path = (const char**)malloc(sizeof(const char**)*(REGFI_MAX_DEPTH+1+1));
+  if(tmp_path == NULL)
+    return -2;
+
+  /* Strip any potential value name at end of path */
+  for(i=0; 
+      (path[i] != NULL) && (path[i+1] != NULL) && (i < REGFI_MAX_DEPTH+1);
+      i++)
+  { tmp_path[i] = path[i]; }
+  tmp_path[i] = NULL;
+
+  if(print_verbose)
+    fprintf(stderr, "INFO: Attempting to retrieve specified path: %s\n",
+	    path_filter);
+
+  /* Special check for '/' path filter */
+  if(path[0] == NULL)
+  {
+    if(print_verbose)
+      fprintf(stderr, "INFO: Found final path element as root key.\n");
+    free(tmp_path);
+    return 2;
+  }
+
+  if(!regfi_iterator_descend(iter, tmp_path))
+  {
+    printMsgs(iter->f);
+    free(tmp_path);
+    return 0;
+  }
+
+  if(regfi_iterator_find_value(iter, path[i]))
+  {
+    if(print_verbose)
+      fprintf(stderr, "INFO: Found final path element as value.\n");
+
+    value = regfi_iterator_cur_value(iter);
+    printMsgs(iter->f);
+    tmp_path_joined = iter2Path(iter);
+
+    if((value == NULL) || (tmp_path_joined == NULL))
+      bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Unexpected error before printValue.\n");
+
+    if(!type_filter_enabled || (value->type == type_filter))
+      printValue(iter, value, tmp_path_joined);
+
+    regfi_free_record(iter->f, value);
+    free(tmp_path);
+    free(tmp_path_joined);
+    return 1;
+  }
+  else if(regfi_iterator_find_subkey(iter, path[i]))
+  {
+    printMsgs(iter->f);
+    if(print_verbose)
+      fprintf(stderr, "INFO: Found final path element as key.\n");
+
+    if(!regfi_iterator_down(iter))
+    {
+      printMsgs(iter->f);
+      bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n");
+    }
+
+    return 2;
+  }
+  printMsgs(iter->f);
+
+  if(print_verbose)
+    fprintf(stderr, "INFO: Could not find last element of path.\n");
+
+  return 0;
+}
+
+
+static void usage(void)
+{
+  fprintf(stderr, "Usage: reglookup [-v] [-s]"
+	  " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
+	  " <REGISTRY_FILE>\n");
+  fprintf(stderr, "Version: %s\n", regfi_version());
+  fprintf(stderr, "Options:\n");
+  fprintf(stderr, "\t-v\t sets verbose mode.\n");
+  fprintf(stderr, "\t-h\t enables header row. (default)\n");
+  fprintf(stderr, "\t-H\t disables header row.\n");
+  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
+  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
+  fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
+  fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
+  fprintf(stderr, "\t-i\t includes parent key modification times with child values.\n");
+  fprintf(stderr, "\n");
+}
+
+
+int main(int argc, char** argv)
+{
+  char** path = NULL;
+  REGFI_ITERATOR* iter;
+  int retr_path_ret, fd;
+  uint32_t argi, arge;
+
+  /* Process command line arguments */
+  if(argc < 2)
+  {
+    usage();
+    bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n");
+  }
+  
+  arge = argc-1;
+  for(argi = 1; argi < arge; argi++)
+  {
+    if (strcmp("-p", argv[argi]) == 0)
+    {
+      if(++argi >= arge)
+      {
+	usage();
+	bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-p' option requires parameter.\n");
+      }
+      if((path_filter = strdup(argv[argi])) == NULL)
+	bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
+
+      path_filter_enabled = true;
+    }
+    else if (strcmp("-t", argv[argi]) == 0)
+    {
+      if(++argi >= arge)
+      {
+	usage();
+	bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-t' option requires parameter.\n");
+      }
+      if((type_filter = regfi_type_str2val(argv[argi])) < 0)
+      {
+	fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
+	bailOut(REGLOOKUP_EXIT_USAGE, "");
+      }
+      type_filter_enabled = true;
+    }
+    else if (strcmp("-h", argv[argi]) == 0)
+      print_header = true;
+    else if (strcmp("-H", argv[argi]) == 0)
+      print_header = false;
+    else if (strcmp("-s", argv[argi]) == 0)
+      print_security = true;
+    else if (strcmp("-S", argv[argi]) == 0)
+      print_security = false;
+    else if (strcmp("-v", argv[argi]) == 0)
+      print_verbose = true;
+    else if (strcmp("-i", argv[argi]) == 0)
+      print_value_mtime = true;
+    else
+    {
+      usage();
+      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
+      bailOut(REGLOOKUP_EXIT_USAGE, "");
+    }
+  }
+  registry_file = argv[argi];
+
+  if(print_verbose)
+    regfi_log_set_mask(REGFI_LOG_INFO|REGFI_LOG_WARN|REGFI_LOG_ERROR);
+
+  fd = openHive(registry_file);
+  if(fd < 0)
+  {
+    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
+    bailOut(REGLOOKUP_EXIT_NOINPUT, "");
+  }
+    
+  /* XXX: add command line option to choose output encoding */
+  f = regfi_alloc(fd, REGFI_ENCODING_ASCII);
+  if(f == NULL)
+  {
+    close(fd);
+    bailOut(REGLOOKUP_EXIT_NOINPUT, "ERROR: Failed to create REGFI_FILE structure.\n");
+  }
+
+  iter = regfi_iterator_new(f);
+  if(iter == NULL)
+  {
+    printMsgs(f);
+    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Couldn't create registry iterator.\n");
+  }
+
+  if(print_header)
+  {
+    if(print_security)
+      printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL,CLASS\n");
+    else
+      printf("PATH,TYPE,VALUE,MTIME\n");
+  }
+
+  if(path_filter_enabled && path_filter != NULL)
+    path = splitPath(path_filter);
+
+  if(path != NULL)
+  {
+    retr_path_ret = retrievePath(iter, path);
+    printMsgs(iter->f);
+    freePath(path);
+
+    if(retr_path_ret == 0)
+      fprintf(stderr, "WARN: Specified path '%s' not found.\n", path_filter);
+    else if (retr_path_ret == 2)
+      printKeyTree(iter);
+    else if(retr_path_ret < 0)
+    {
+      fprintf(stderr, "ERROR: retrievePath() returned %d.\n", 
+	      retr_path_ret);
+      bailOut(REGLOOKUP_EXIT_DATAERR,
+	      "ERROR: Unknown error occurred in retrieving path.\n");
+    }
+  }
+  else
+    printKeyTree(iter);
+
+  regfi_iterator_free(iter);
+  regfi_free(f);
+  close(fd);
+
+  return 0;
+}
