Index: releases/0.2.1/INSTALL
===================================================================
--- releases/0.2.1/INSTALL	(revision 57)
+++ releases/0.2.1/INSTALL	(revision 57)
@@ -0,0 +1,45 @@
+RegLookup Installation
+======================
+
+Prerequisites
+-------------
+
+This package doesn't require much for installation.  Just what typically
+comes with any free operating system.  Be sure you have:
+
+ - GNU Make
+ - GCC
+
+
+Survival Commands
+-----------------
+
+make
+# and as root
+make install
+
+If GNU Make isn't your default, you may need to run `gmake' instead.
+
+
+Advanced Installation
+---------------------
+To install in a custom directory, simply change one or more of the
+following make variables to suit your needs:
+
+ PREFIX      Top level install directory.  Used as base for rest unless
+             they too are overridden. (This defaults to /usr/local)
+
+ BIN_PREFIX  Location for executable programs to be installed.
+
+ DOC_PREFIX  Location for documentation.
+
+ MAN_PREFIX  A path in the MANPATH.
+
+
+Use the following syntax (with GNU Make) to override a variable:
+
+# as root
+make VAR=value install
+
+
+For more information, see the Makefiles.
Index: releases/0.2.1/LICENSE
===================================================================
--- releases/0.2.1/LICENSE	(revision 57)
+++ releases/0.2.1/LICENSE	(revision 57)
@@ -0,0 +1,284 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  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.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE 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.
+
+		     END OF TERMS AND CONDITIONS
Index: releases/0.2.1/Makefile
===================================================================
--- releases/0.2.1/Makefile	(revision 57)
+++ releases/0.2.1/Makefile	(revision 57)
@@ -0,0 +1,47 @@
+# $Id$
+
+# Installation prefixes.  Change to install elsewhere.
+
+PREFIX=/usr/local
+BIN_PREFIX=$(PREFIX)/bin
+DOC_PREFIX=$(PREFIX)/share/doc/reglookup
+MAN_PREFIX=$(PREFIX)/man
+
+################################################################################
+
+CC=gcc
+OPTS=-std=gnu89 -pedantic -Wall
+
+BUILD=$(CURDIR)/build
+BUILD_BIN=$(BUILD)/bin
+BUILD_DOC=$(BUILD)/doc
+
+BUILD_TREE=$(BUILD_BIN) $(BUILD_DOC)
+SUB_DIRS=lib src doc
+
+FILES=$(REGLOOKUP)
+.PHONY: $(SUB_DIRS) clean
+export
+
+
+all: $(BUILD_TREE) $(SUB_DIRS)
+
+#XXX: This should be more generalized.
+install: all
+	mkdir -p $(BIN_PREFIX)
+	mkdir -p $(DOC_PREFIX)
+	mkdir -p $(MAN_PREFIX)/man1
+	$(MAKE) -C src install
+	$(MAKE) -C doc install
+
+
+$(SUB_DIRS):
+	$(MAKE) -C $@
+
+$(BUILD_TREE):
+	mkdir -p $@
+
+clean:
+	$(MAKE) -C src clean
+	$(MAKE) -C lib clean
+	rm -rf $(BUILD)/*
Index: releases/0.2.1/doc/Makefile
===================================================================
--- releases/0.2.1/doc/Makefile	(revision 57)
+++ releases/0.2.1/doc/Makefile	(revision 57)
@@ -0,0 +1,19 @@
+# $Id$
+
+BUILD_FILES=$(BUILD_DOC)/man/man1/reglookup.1.gz
+
+default: $(BUILD_FILES)
+
+$(BUILD_DOC)/man/man1/reglookup.1.gz: man/man1/reglookup.1.gz
+	mkdir -p $(BUILD_DOC)/man/man1
+	cp man/man1/reglookup.1.gz $@
+
+install:
+	cp -r $(BUILD_DOC)/* $(DOC_PREFIX)
+	ln -sf $(DOC_PREFIX)/man/man1/*  $(MAN_PREFIX)/man1
+
+
+#XXX: Used during release only
+release:
+	txt2tags --mask-email -t man -o man/man1/reglookup.1 reglookup.1.t2t
+	cd man/man1 && gzip -9 reglookup.1
Index: releases/0.2.1/doc/devel/TODO
===================================================================
--- releases/0.2.1/doc/devel/TODO	(revision 57)
+++ releases/0.2.1/doc/devel/TODO	(revision 57)
@@ -0,0 +1,30 @@
+$Id$
+
+If you are interested in contributing to this project, here's a few
+things you could look into:
+
+ - Testing, testing, and more testing.  reglookup needs to be tested on 
+   NT/XP/2k3/Vista.  A regression test suite would be nice too.
+
+ - Build system.  I do not wish to use automake/autoconf in this
+   project.  I have also now decided that make is painful to use for
+   everything.  I'd like to switch to a suite of shell scripts driven by
+   minimal make rules.  If you got any ideas on this, shoot them my way.
+
+ - Unicode support.  I have never internationalized or localized any
+   software before.  This would be a big help for the SZ, EXPAND_SZ, and
+   MULTI_SZ data types.
+
+ - The interface between reglookup.c and regfio.c needs to be improved.
+   Many functions from reglookup should be moved to the library, and I
+   want to move to using iterator structs in the library.  This will the
+   library more threadsafe and may better support lazy parsing of many
+   structures which are currently parsed up front.
+
+ - The stuff in smb_deps.h and smb_deps.c needs to be cleaned up.  The
+   eventual goal is to have it all either integrated into regfio, or to
+   be eliminated, or broken out into small supporting libraries, as
+   necessary.  It is currently just a jumble of old Samba code that I
+   haven't decided where to put yet.
+
+ - Grep through the source for 'XXX', and you'll find more.
Index: releases/0.2.1/doc/devel/references.txt
===================================================================
--- releases/0.2.1/doc/devel/references.txt	(revision 57)
+++ releases/0.2.1/doc/devel/references.txt	(revision 57)
@@ -0,0 +1,4 @@
+
+- 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
Index: releases/0.2.1/doc/devel/winntreg.txt
===================================================================
--- releases/0.2.1/doc/devel/winntreg.txt	(revision 57)
+++ releases/0.2.1/doc/devel/winntreg.txt	(revision 57)
@@ -0,0 +1,268 @@
+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
+0x0010      D-Word      Offset of Owner/Parent key
+0x0014      D-Word      number of sub-Keys
+0x001C      D-Word      Offset of the sub-key lf-Records
+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"-record
+===============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"lf" = 0x666C
+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 sensitiv!
+
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the 
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+(This is just a self-relative security descriptor in the data. R Sharpe.) 
+
+
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"sk" = 0x6B73
+0x0002      Word      Unused
+0x0004      D-Word      Offset of previous "sk"-Record
+0x0008      D-Word      Offset of next "sk"-Record
+0x000C      D-Word      usage-counter
+0x0010      D-Word      Size of "sk"-record in bytes
+????                                             //standard self
+relative security desciptor. Nigel
+????  ????      Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I dont know how accurate it really is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+                      +----->+------------+
+| first sub |   |                      |      | Usage cnt. |
+| name      |   |  +-->+------------+  |      | length     |
+| value     |   |  |   | next rec.  |  |      | text       |------->+-------+
++-----------+   |  |   | name rec.  |--+      +------------+        | xxxxx |
+   +------------+  |   | value rec. |-------->+------------+        +-------+
+   v               |   +------------+         | Usage cnt. |
++-----------+      |                          | length     |
+| next rec. |      |                          | text       |------->+-------+
+| first sub |------+                          +------------+        | xxxxx |
+| name      |                                                       +-------+
+| value     |
++-----------+    
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+   
++---------------------------------------------------------------+
+|                                                               |
+v                                                               |
++---------+     +---------->+-----------+  +----->+---------+   |
+| "nk"    |     |           | lf-rec.   |  |      | nk-rec. |   |
+| ID      |     |           | # of keys |  |      | parent  |---+
+| Date    |     |           | 1st key   |--+      | ....    |
+| parent  |     |           +-----------+         +---------+
+| suk-keys|-----+
+| values  |--------------------->+----------+
+| SK-rec. |---------------+      | 1. value |--> +----------+
+| class   |--+            |      +----------+    | vk-rec.  |
++---------+  |            |                      | ....     |
+             v            |                      | data     |--> +-------+
+      +------------+      |                      +----------+    | xxxxx |
+      | Class name |      |                                      +-------+
+      +------------+      |
+                          v
+          +---------+    +---------+
+   +----->| next sk |--->| Next sk |--+
+   |  +---| prev sk |<---| prev sk |  |
+   |  |   | ....    |    | ...     |  |
+   |  |   +---------+    +---------+  |
+   |  |                    ^          |
+   |  |                    |          |
+   |  +--------------------+          |
+   +----------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps....  (Although it was *fun* for me to uncover this things,
+                  it took me several sleepless nights ;)
+
+            B.D.
Index: releases/0.2.1/doc/reglookup.1.t2t
===================================================================
--- releases/0.2.1/doc/reglookup.1.t2t	(revision 57)
+++ releases/0.2.1/doc/reglookup.1.t2t	(revision 57)
@@ -0,0 +1,105 @@
+reglookup
+
+
+=NAME=
+	reglookup - windows NT+ registry reader/lookup tool
+
+
+=SYNOPSIS=
+	reglookup [options] <REGISTRY_FILE>
+
+
+=DESCRIPTION=
+	reglookup is designed to read windows registry elements and
+	print them out to stdout in a CSV-like format.  It has filtering
+	options to narrow the focus of the output.  This tool is
+	designed to work with on windows NT/2K/XP/2K3 registries, though
+	your mileage may vary.
+
+
+=OPTIONS=
+	-p  <PREFIX_FILTER>	
+		Specify a path prefix filter.  Only keys/values under
+		this registry path will be output.
+
+	-t  <TYPE_FILTER>
+		Specify a type filter.  Only elements which match this
+		registry data type will be printed.  Acceptable values
+		are: SZ,EXPAND_SZ,BINARY,DWORD,DWORD_BE,LINK,MULTI_SZ,
+		     RSRC_LIST,RSRC_DESC,RSRC_REQ_LIST,KEY
+
+	-h	Enables the printing of a column header row. (default)
+
+	-H	Disables the printing of a column header row.
+
+	-s	Adds four additional columns to output containing 
+		information from key security descriptors.  The columns 
+		are: owner, group, sacl, dacl.
+		(This feature's output probably contains bugs right now.)
+
+	-S	Disables the printing of security descriptor
+		information. (default)
+
+	-v	Verbose output.
+		(Currently does little to nothing.)
+
+
+	REGISTRY_FILE
+		Required argument.  Specifies the location of the
+		registry file to read.  Typically, these files will be
+		found on a NTFS partition under
+		%SystemRoot%/system32/config. 
+
+
+=EXAMPLES=
+	To read and print the contents of an entire system registry
+	file: 
+
+	  **reglookup /mnt/win/c/WINNT/system32/config/system**
+
+	To limit the output to just those entries under the Services
+	key: 
+
+	  **reglookup -p /ControlSet002/Services /mnt/win/c/WINNT/system32/config/system**
+
+	To limit the output to all registry values of type BINARY:
+
+	  **reglookup -t BINARY /mnt/win/c/WINNT/system32/config/system**
+	
+	And to limit the output to BINARY values under the Services key:
+
+	  **reglookup -t BINARY -p /ControlSet002/Services /mnt/win/c/WINNT/system32/config/system**
+
+
+=BUGS=
+	The current incarnation of this program has only been tested on
+	windows 2K/XP.
+
+	Verbose output is not working.  MTIME and SID conversions
+	haven't been checked for accuracy.
+
+	You'll notice that registry paths aren't all the same as the
+	ones the equivalents you see in the windows registry editor.
+	Don't ask me why that is.  I just work here.
+
+	This software should be considered unstable at this time.
+
+
+=CREDITS=
+	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.
+
+	Please see source code for a full list of copyrights.
+
+
+=LICENSE=
+	Please see the file "LICENSE" included with this software
+	distribution.
+
+	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 2 for more details.
Index: releases/0.2.1/include/byteorder.h
===================================================================
--- releases/0.2.1/include/byteorder.h	(revision 57)
+++ releases/0.2.1/include/byteorder.h	(revision 57)
@@ -0,0 +1,177 @@
+/* 
+ * Branched from Samba project Subversion repository, version #2:
+ *  http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/byteorder.h
+ *
+ * Unix SMB/CIFS implementation.
+ * SMB Byte handling
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 1992-1998 Andrew Tridgell 
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 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$
+ */
+
+#ifndef _BYTEORDER_H
+#define _BYTEORDER_H
+
+/*
+   This file implements macros for machine independent short and 
+   int manipulation
+
+Here is a description of this file that I emailed to the samba list once:
+
+> I am confused about the way that byteorder.h works in Samba. I have
+> looked at it, and I would have thought that you might make a distinction
+> between LE and BE machines, but you only seem to distinguish between 386
+> and all other architectures.
+> 
+> Can you give me a clue?
+
+sure.
+
+The distinction between 386 and other architectures is only there as
+an optimisation. You can take it out completely and it will make no
+difference. The routines (macros) in byteorder.h are totally byteorder
+independent. The 386 optimsation just takes advantage of the fact that
+the x86 processors don't care about alignment, so we don't have to
+align ints on int boundaries etc. If there are other processors out
+there that aren't alignment sensitive then you could also define
+CAREFUL_ALIGNMENT=0 on those processors as well.
+
+Ok, now to the macros themselves. I'll take a simple example, say we
+want to extract a 2 byte integer from a SMB packet and put it into a
+type called uint16 that is in the local machines byte order, and you
+want to do it with only the assumption that uint16 is _at_least_ 16
+bits long (this last condition is very important for architectures
+that don't have any int types that are 2 bytes long)
+
+You do this:
+
+#define CVAL(buf,pos) (((unsigned char *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos))
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+
+then to extract a uint16 value at offset 25 in a buffer you do this:
+
+char *buffer = foo_bar();
+uint16 xx = SVAL(buffer,25);
+
+We are using the byteoder independence of the ANSI C bitshifts to do
+the work. A good optimising compiler should turn this into efficient
+code, especially if it happens to have the right byteorder :-)
+
+I know these macros can be made a bit tidier by removing some of the
+casts, but you need to look at byteorder.h as a whole to see the
+reasoning behind them. byteorder.h defines the following macros:
+
+SVAL(buf,pos) - extract a 2 byte SMB value
+IVAL(buf,pos) - extract a 4 byte SMB value
+SVALS(buf,pos) signed version of SVAL()
+IVALS(buf,pos) signed version of IVAL()
+
+SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer
+SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer
+SSVALS(buf,pos,val) - signed version of SSVAL()
+SIVALS(buf,pos,val) - signed version of SIVAL()
+
+RSVAL(buf,pos) - like SVAL() but for NMB byte ordering
+RSVALS(buf,pos) - like SVALS() but for NMB byte ordering
+RIVAL(buf,pos) - like IVAL() but for NMB byte ordering
+RIVALS(buf,pos) - like IVALS() but for NMB byte ordering
+RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering
+RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering
+RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering
+
+it also defines lots of intermediate macros, just ignore those :-)
+
+*/
+
+#undef CAREFUL_ALIGNMENT
+
+/* we know that the 386 can handle misalignment and has the "right" 
+   byteorder */
+#ifdef __i386__
+#define CAREFUL_ALIGNMENT 0
+#endif
+
+#ifndef CAREFUL_ALIGNMENT
+#define CAREFUL_ALIGNMENT 1
+#endif
+
+#define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos]))
+#define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */
+#define PVAL(buf,pos) (CVAL(buf,pos))
+#define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val))
+
+
+#if CAREFUL_ALIGNMENT
+
+#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8)
+#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16)
+#define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8))
+#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16))
+#define SVALS(buf,pos) ((int16)SVAL(buf,pos))
+#define IVALS(buf,pos) ((int32)IVAL(buf,pos))
+#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val)))
+#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val)))
+#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val)))
+#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(val)))
+
+#else /* CAREFUL_ALIGNMENT */
+
+/* this handles things for architectures like the 386 that can handle
+   alignment errors */
+/*
+   WARNING: This section is dependent on the length of int16 and int32
+   being correct 
+*/
+
+/* get single value from an SMB buffer */
+#define SVAL(buf,pos) (*(const uint16 *)((const char *)(buf) + (pos)))
+#define SVAL_NC(buf,pos) (*(uint16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVAL(buf,pos) (*(const uint32 *)((const char *)(buf) + (pos)))
+#define IVAL_NC(buf,pos) (*(uint32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define SVALS(buf,pos) (*(const int16 *)((const char *)(buf) + (pos)))
+#define SVALS_NC(buf,pos) (*(int16 *)((char *)(buf) + (pos))) /* Non const version of above. */
+#define IVALS(buf,pos) (*(const int32 *)((const char *)(buf) + (pos)))
+#define IVALS_NC(buf,pos) (*(int32 *)((char *)(buf) + (pos))) /* Non const version of above. */
+
+/* store single value in an SMB buffer */
+#define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16)(val))
+#define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32)(val))
+#define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16)(val))
+#define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32)(val))
+
+#endif /* CAREFUL_ALIGNMENT */
+
+/* now the reverse routines - these are used in nmb packets (mostly) */
+#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF))
+#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16)))
+
+#define RSVAL(buf,pos) SREV(SVAL(buf,pos))
+#define RSVALS(buf,pos) SREV(SVALS(buf,pos))
+#define RIVAL(buf,pos) IREV(IVAL(buf,pos))
+#define RIVALS(buf,pos) IREV(IVALS(buf,pos))
+#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val))
+#define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val))
+#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val))
+#define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val))
+
+/* Alignment macros. */
+#define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3))
+#define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1))
+
+#endif /* _BYTEORDER_H */
Index: releases/0.2.1/include/regfio.h
===================================================================
--- releases/0.2.1/include/regfio.h	(revision 57)
+++ releases/0.2.1/include/regfio.h	(revision 57)
@@ -0,0 +1,276 @@
+/*
+ * Branched from Samba project, Subversion repository version #6903:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h
+ *
+ * Unix SMB/CIFS implementation.
+ * Windows NT registry I/O library
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 2005 Gerald (Jerry) Carter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id$
+ */
+
+/************************************************************
+ * Most of this information was obtained from 
+ * http://www.wednesday.demon.co.uk/dosreg.html
+ * Thanks Nigel!
+ ***********************************************************/
+
+#ifndef _REGFIO_H
+#define _REGFIO_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "smb_deps.h"
+
+/******************************************************************************/
+/* Macros */
+ 
+/* Registry data types */
+#define REG_NONE                       0
+#define REG_SZ		               1
+#define REG_EXPAND_SZ                  2
+#define REG_BINARY 	               3
+#define REG_DWORD	               4
+#define REG_DWORD_LE	               4  /* DWORD, little endian */
+#define REG_DWORD_BE	               5  /* DWORD, big endian */
+#define REG_LINK                       6
+#define REG_MULTI_SZ  	               7
+#define REG_RESOURCE_LIST              8
+#define REG_FULL_RESOURCE_DESCRIPTOR   9
+#define REG_RESOURCE_REQUIREMENTS_LIST 10
+/* Not a real type in the registry */
+#define REG_KEY                        255
+
+typedef struct _val_str { 
+  unsigned int val;
+  const char * str;
+} VAL_STR;
+
+#define REGF_BLOCKSIZE		0x1000
+#define REGF_ALLOC_BLOCK	0x1000
+
+/* header sizes for various records */
+
+#define REGF_HDR_SIZE		4
+#define HBIN_HDR_SIZE		4
+#define HBIN_HEADER_REC_SIZE	0x24
+#define REC_HDR_SIZE		2
+
+#define REGF_OFFSET_NONE	0xffffffff
+
+/* Flags for the vk records */
+
+#define VK_FLAG_NAME_PRESENT	0x0001
+#define VK_DATA_IN_OFFSET	0x80000000
+
+/* NK record macros */
+
+#define NK_TYPE_LINKKEY		0x0010
+#define NK_TYPE_NORMALKEY	0x0020
+#define NK_TYPE_ROOTKEY		0x002c
+
+#define HBIN_STORE_REF(x, y) { x->hbin = y; y->ref_count++ };
+/* if the count == 0; we can clean up */
+#define HBIN_REMOVE_REF(x, y){ x->hbin = NULL; y->ref_count-- };
+
+
+/* HBIN block */
+struct regf_hbin;
+typedef struct regf_hbin {
+  struct regf_hbin* prev;
+  struct regf_hbin* next;
+  uint32 file_off;       /* my offset in the registry file */
+  uint32 free_off;       /* offset to free space within the hbin record */
+  uint32 free_size;      /* amount of data left in the block */
+  int    ref_count;      /* how many active records are pointing to this
+                          * block (not used currently) 
+			  */
+	
+  uint8  header[HBIN_HDR_SIZE]; /* "hbin" */
+  uint32 first_hbin_off; /* offset from first hbin block */
+  uint32 block_size;     /* block size of this block is
+                          * usually a multiple of 4096Kb 
+			  */
+  prs_struct ps;	 /* data */
+  bool dirty;            /* has this hbin block been modified? */
+} REGF_HBIN;
+
+/* ??? List -- list of key offsets and hashed names for consistency */
+typedef struct {
+  uint32 nk_off;
+  uint8 keycheck[sizeof(uint32)];
+} REGF_HASH_REC;
+
+typedef struct {
+  REGF_HBIN *hbin;       /* pointer to HBIN record (in memory) containing 
+			  * this nk record 
+			  */
+  uint32 hbin_off;	 /* offset from beginning of this hbin block */
+  uint32 rec_size;	 /* ((start_offset - end_offset) & 0xfffffff8) */
+  
+  uint8 header[REC_HDR_SIZE];
+  uint16 num_keys;
+  REGF_HASH_REC *hashes;
+} REGF_LF_REC;
+
+/* Key Value */
+
+typedef struct {
+  REGF_HBIN *hbin;	/* pointer to HBIN record (in memory) containing 
+			 * this nk record 
+			 */
+  uint32 hbin_off;	/* offset from beginning of this hbin block */
+  uint32 rec_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+  uint32 rec_off;	/* offset stored in the value list */
+  
+  uint8  header[REC_HDR_SIZE];
+  char*  valuename;
+  uint32 data_size;
+  uint32 data_off;
+  uint8* data;
+  uint32 type;
+  uint16 flag;
+} REGF_VK_REC;
+
+
+/* Key Security */
+struct _regf_sk_rec;
+
+typedef struct _regf_sk_rec {
+  struct _regf_sk_rec *next, *prev;
+  REGF_HBIN *hbin;	/* pointer to HBIN record (in memory) containing 
+			 * this nk record 
+			 */
+  uint32 hbin_off;	/* offset from beginning of this hbin block */
+  uint32 rec_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+  
+  uint32 sk_off;	/* offset parsed from NK record used as a key
+			 * to lookup reference to this SK record 
+			 */
+  
+  uint8  header[REC_HDR_SIZE];
+  uint32 prev_sk_off;
+  uint32 next_sk_off;
+  uint32 ref_count;
+  uint32 size;
+  SEC_DESC *sec_desc;
+} REGF_SK_REC;
+
+/* Key Name */ 
+
+typedef struct {
+  REGF_HBIN *hbin;	/* pointer to HBIN record (in memory) containing 
+			 * this nk record 
+			 */
+  uint32 hbin_off;	/* offset from beginning of this hbin block */
+  uint32 subkey_index;	/* index to next subkey record to return */
+  uint32 rec_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+  
+  /* header information */
+  uint8  header[REC_HDR_SIZE];
+  uint16 key_type;
+  NTTIME mtime;
+  uint32 parent_off;	/* back pointer in registry hive */
+  uint32 classname_off;	
+  char *classname;
+  char *keyname;
+  
+  /* max lengths */
+  uint32 max_bytes_subkeyname;	    /* max subkey name * 2 */
+  uint32 max_bytes_subkeyclassname; /* max subkey classname length (as if) */
+  uint32 max_bytes_valuename;	    /* max valuename * 2 */
+  uint32 max_bytes_value;           /* max value data size */
+  
+  /* unknowns */
+  uint32 unk_index;		    /* nigel says run time index ? */
+  
+  /* children */
+  uint32 num_subkeys;
+  uint32 subkeys_off;	/* hash records that point to NK records */	
+  uint32 num_values;
+  uint32 values_off;	/* value lists which point to VK records */
+  uint32 sk_off;	/* offset to SK record */
+  
+  /* link in the other records here */
+  REGF_LF_REC subkeys;
+  REGF_VK_REC *values;
+  REGF_SK_REC *sec_desc;
+	
+} REGF_NK_REC;
+
+/* REGF block */
+ 
+typedef struct {
+  /* run time information */
+  int fd;	  /* file descriptor */
+  int open_flags; /* flags passed to the open() call */
+  void *mem_ctx;  /* memory context for run-time file access information */
+  REGF_HBIN *block_list; /* list of open hbin blocks */
+  
+  /* file format information */
+  
+  uint8  header[REGF_HDR_SIZE];	/* "regf" */
+  uint32 data_offset;		/* offset to record in the first (or any?) 
+				 * hbin block 
+				 */
+  uint32 last_block;		/* offset to last hbin block in file */
+  uint32 checksum;		/* XOR of bytes 0x0000 - 0x01FB */
+  NTTIME mtime;
+  
+  REGF_SK_REC *sec_desc_list;	/* list of security descriptors referenced 
+				 * by NK records 
+				 */
+  
+  /* unknowns used to simply writing */
+  uint32 unknown1;
+  uint32 unknown2;
+  uint32 unknown3;
+  uint32 unknown4;
+  uint32 unknown5;
+  uint32 unknown6;
+} REGF_FILE;
+
+
+/******************************************************************************/
+/* Function Declarations */
+
+const char*   regfio_type_val2str(unsigned int val);
+int           regfio_type_str2val(const char* str);
+
+char*         regfio_get_sacl(SEC_DESC *sec_desc);
+char*         regfio_get_dacl(SEC_DESC *sec_desc);
+char*         regfio_get_owner(SEC_DESC *sec_desc);
+char*         regfio_get_group(SEC_DESC *sec_desc);
+
+REGF_FILE*    regfio_open( const char *filename );
+int           regfio_close( REGF_FILE *r );
+
+REGF_NK_REC*  regfio_rootkey( REGF_FILE *file );
+REGF_NK_REC*  regfio_fetch_subkey( REGF_FILE *file, REGF_NK_REC *nk );
+
+#endif	/* _REGFIO_H */
Index: releases/0.2.1/include/smb_deps.h
===================================================================
--- releases/0.2.1/include/smb_deps.h	(revision 57)
+++ releases/0.2.1/include/smb_deps.h	(revision 57)
@@ -0,0 +1,433 @@
+/*
+ * This file contains miscellaneous pieces of code which regfio.c
+ * depends upon, from the Samba Subversion tree.  See:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team 
+ *               (see individual files under Subversion for details.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 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$
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "byteorder.h"
+
+#define DEBUG(lvl,body) 0
+
+void* zalloc(size_t size);
+void* zcalloc(size_t size, unsigned int count);
+
+/* From includes.h */
+
+#define uint8 unsigned char
+#define int16 short
+#define uint16 unsigned short
+#define int32 int
+#define uint32 unsigned int
+
+#define SMB_STRUCT_STAT struct stat
+#define QSORT_CAST (int (*)(const void *, const void *))
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MAX(a,b) ((a)>(b)?(a):(b))
+
+extern int DEBUGLEVEL;
+
+#define DLIST_ADD(list, p) \
+{ \
+        if (!(list)) { \
+		(list) = (p); \
+		(p)->next = (p)->prev = NULL; \
+	} else { \
+		(list)->prev = (p); \
+		(p)->next = (list); \
+		(p)->prev = NULL; \
+		(list) = (p); \
+	}\
+}
+
+/* End of stuff from includes.h */
+
+/* From smb.h */
+
+#define MAXSUBAUTHS 15
+
+typedef struct sid_info
+{
+  uint8  sid_rev_num;             /**< SID revision number */
+  uint8  num_auths;               /**< Number of sub-authorities */
+  uint8  id_auth[6];              /**< Identifier Authority */
+  /*
+   *  Pointer to sub-authorities.
+   *
+   * @note The values in these uint32's are in *native* byteorder, not
+   * neccessarily little-endian...... JRA.
+   */
+  uint32 sub_auths[MAXSUBAUTHS];
+} DOM_SID;
+
+typedef struct nttime_info
+{
+  uint32 low;
+  uint32 high;
+} NTTIME;
+
+/* End of stuff from smb.h */
+
+/* From smb_macros.h */
+
+#define TALLOC_ZERO_P(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
+#define SMB_MALLOC_P(type) (type *)malloc_(sizeof(type))
+#define TALLOC_ARRAY(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
+#define TALLOC_ZERO_ARRAY(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
+#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0)
+
+/* End of stuff from smb_macros.h */
+
+/* From ntdomain.h */
+
+struct uuid {
+       uint32 time_low;
+       uint16 time_mid;
+       uint16 time_hi_and_version;
+       uint8  clock_seq[2];
+       uint8  node[6];
+};
+
+typedef struct _prs_struct {
+	bool io; /* parsing in or out of data stream */
+	/* 
+	 * If the (incoming) data is big-endian. On output we are
+	  * always little-endian.
+	   */ 
+	   bool bigendian_data;
+	   uint8 align; /* data alignment */
+	   bool is_dynamic; /* Do we own this memory or not ? */
+	   uint32 data_offset; /* Current working offset into data. */
+	   uint32 buffer_size; /* Current allocated size of the buffer. */
+	   uint32 grow_size; /* size requested via prs_grow() calls */
+	   char *data_p; /* The buffer itself. */
+	   void *mem_ctx; /* When unmarshalling, use this.... */
+} prs_struct;
+
+#define MARSHALL 0
+#define UNMARSHALL 1
+
+#define RPC_LITTLE_ENDIAN  0
+#define RPC_PARSE_ALIGN    4
+
+
+/* End of stuff from ntdomain.h */
+
+/* From nt_status.h */
+
+typedef uint32 NTSTATUS;
+typedef uint32 WERROR;
+
+/* End of stuff from nt_status.h */
+
+/* From lib/time.c */
+
+#define CHAR_BIT 8
+#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
+		    : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
+#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
+#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
+void unix_to_nt_time(NTTIME *nt, time_t t);
+time_t nt_time_to_unix(NTTIME *nt);
+
+/* End of stuff from lib/time.c */
+
+/* From rpc_dce.h */
+
+#define MAX_PDU_FRAG_LEN 0x10b8 /* this is what w2k sets */
+
+/* End of stuff from rpc_dce.h */
+
+/* From parse_prs.h */
+
+bool prs_grow(prs_struct *ps, uint32 extra_space);
+bool prs_align(prs_struct *ps);
+bool prs_init(prs_struct *ps, uint32 size, void *ctx, bool io);
+char *prs_mem_get(prs_struct *ps, uint32 extra_size);
+bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32);
+bool prs_uint32s(bool charmode, const char *name, prs_struct *ps, 
+		 int depth, uint32 *data32s, int len);
+bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16);
+bool prs_uint16_pre(const char *name, prs_struct *ps, int depth, 
+		    uint16 *data16, uint32 *offset);
+bool prs_uint16_post(const char *name, prs_struct *ps, int depth, 
+		     uint16 *data16, uint32 ptr_uint16, uint32 start_offset);
+bool prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8);
+bool prs_uint8s(bool charmode, const char *name, prs_struct *ps, int depth, 
+		uint8* data8s, int len);
+bool prs_set_offset(prs_struct *ps, uint32 offset);
+
+/* End of stuff from parse_prs.h */
+
+
+
+/* buffer used by \winreg\ calls to fill in arbitrary REG_XXX values.
+   It *may* look like a UNISTR2 but it is *not*.  This is not a goof
+   by the winreg developers.  It is a generic buffer.  buffer length
+   is stored in bytes (not # of uint16's) */
+
+typedef struct {
+	uint32 buf_max_len;
+	uint32 offset;
+	uint32 buf_len;
+	uint16 *buffer;
+} REGVAL_BUFFER;
+
+typedef struct {
+	uint32 buf_len;
+	uint16 *buffer; /* data */
+} BUFFER5;
+
+
+/********************************************************************** 
+ * UNICODE string variations
+ **********************************************************************/
+
+
+typedef struct {		/* UNISTR - unicode string size and buffer */
+	uint16 *buffer;		/* unicode characters. ***MUST*** be 
+				   little-endian. ***MUST*** be null-terminated */
+} UNISTR;
+
+typedef struct {		/* UNISTR2 - unicode string size (in 
+				   uint16 unicode chars) and buffer */
+	uint32 uni_max_len;
+	uint32 offset;
+	uint32 uni_str_len;
+	uint16 *buffer;		/* unicode characters. ***MUST*** be little-endian. 
+				  **must** be null-terminated and the uni_str_len 
+				  should include the NULL character */
+} UNISTR2;
+
+/* i think this is the same as a BUFFER5 used in the spoolss code --jerry */
+/* not sure about how the termination matches between the uint16 buffers thought */
+
+typedef struct {		/* UNISTR3 - XXXX not sure about this structure */
+	uint32 uni_str_len;
+	UNISTR str;
+} UNISTR3;
+
+typedef struct {		/* Buffer wrapped around a UNISTR2 */
+	uint16 length;		/* number of bytes not counting NULL terminatation */
+	uint16 size;		/* number of bytes including NULL terminatation */
+	UNISTR2 *string;
+} UNISTR4;
+
+typedef struct {
+	uint32 count;
+	UNISTR4 *strings;
+} UNISTR4_ARRAY;
+
+
+/********************************************************************** 
+ * String variations
+ **********************************************************************/
+
+typedef struct {		/* STRING2 - string size (in uint8 chars) and buffer */
+	uint32 str_max_len;
+	uint32 offset;
+	uint32 str_str_len;
+	uint8  *buffer; 	/* uint8 characters. **NOT** necessarily null-terminated */
+} STRING2;
+
+
+/* From rpc_secdesc.h */
+
+typedef struct security_info_info
+{
+	uint32 mask;
+
+} SEC_ACCESS;
+
+typedef struct security_ace_info
+{
+	uint8 type;  /* xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */
+	uint8 flags; /* xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */
+	uint16 size;
+
+	SEC_ACCESS info;
+
+	/* this stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */
+	uint32  obj_flags; /* xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */
+	struct uuid obj_guid;  /* object GUID */
+	struct uuid inh_guid;  /* inherited object GUID */		
+        /* eof object stuff */
+
+	DOM_SID trustee;
+
+} SEC_ACE;
+
+typedef struct security_acl_info
+{
+	uint16 revision; /* 0x0003 */
+	uint16 size; /* size in bytes of the entire ACL structure */
+	uint32 num_aces; /* number of Access Control Entries */
+
+	SEC_ACE *ace;
+
+} SEC_ACL;
+
+typedef struct security_descriptor_info
+{
+	uint16 revision; /* 0x0001 */
+	uint16 type;     /* SEC_DESC_xxxx flags */
+
+	uint32 off_owner_sid; /* offset to owner sid */
+	uint32 off_grp_sid  ; /* offset to group sid */
+	uint32 off_sacl     ; /* offset to system list of permissions */
+	uint32 off_dacl     ; /* offset to list of permissions */
+
+	SEC_ACL *dacl; /* user ACL */
+	SEC_ACL *sacl; /* system ACL */
+	DOM_SID *owner_sid; 
+	DOM_SID *grp_sid;
+
+} SEC_DESC;
+
+/* End of stuff from rpc_secdesc.h */
+
+
+/* From pstring.h */
+
+#define PSTRING_LEN 1024
+#define FSTRING_LEN 256
+
+typedef char pstring[PSTRING_LEN];
+typedef char fstring[FSTRING_LEN];
+
+/* End of stuff from pstring.h */
+
+/* From reg_objects.h */
+
+typedef struct _REGISTRY_VALUE {
+	fstring		valuename;
+	uint16		type;
+	/* this should be encapsulated in an RPC_DATA_BLOB */
+	uint32		size;	/* in bytes */
+	uint8           *data_p;
+} REGISTRY_VALUE;
+
+/* container for registry values */
+typedef struct {
+	void      *ctx;
+	uint32          num_values;
+	REGISTRY_VALUE	**values;
+} REGVAL_CTR;
+
+/* container for registry subkey names */
+typedef struct {
+	void	*ctx;
+	uint32          num_subkeys;
+	char            **subkeys;
+} REGSUBKEY_CTR;
+
+/* represent a registry key with all its subkeys and values */
+struct _regobj_key;
+
+typedef struct _regobj_key {
+	void *ctx;
+
+	char *name;
+
+	REGVAL_CTR values;
+	REGSUBKEY_CTR subkeys;
+} REGOBJ_KEY;
+
+/* End of stuff from reg_objects.h */
+
+/* From rpc_secdes.h */
+
+#define SEC_DESC_DACL_PRESENT		0x0004
+#define SEC_DESC_SACL_PRESENT		0x0010
+#define  SEC_DESC_HEADER_SIZE (2 * sizeof(uint16) + 4 * sizeof(uint32))
+   /* thanks for Jim McDonough <jmcd@us.ibm.com> */
+#define SEC_ACE_OBJECT_PRESENT        0x00000001 
+#define SEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002
+
+#define SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT	0x5
+#define SEC_ACE_TYPE_ACCESS_DENIED_OBJECT     	0x6
+#define SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT      	0x7
+#define SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT	0x8
+
+/* End of stuff from rpc_secdes.h */
+
+/* From rpc_parse/parse_misc.c */
+
+bool smb_io_uuid(const char *desc, struct uuid *uuid, 
+		 prs_struct *ps, int depth);
+bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth);
+bool smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth);
+
+/* End of stuff from rpc_parse/parse_misc.c */
+
+/* From lib/util_sid.c */
+
+size_t sid_size(const DOM_SID *sid);
+int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2);
+int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2);
+bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2);
+
+/* End of stuff from lib/util_sid.c */
+
+/* From lib/secace.c */
+
+bool sec_ace_object(uint8 type);
+
+/* End of stuff from lib/secace.c */
+
+/* From rpc_parse/parse_sec.c */
+
+bool sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth);
+bool sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth);
+bool sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth);
+bool sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth);
+
+/* End of stuff from rpc_parse/parse_sec.c */
+
+/* From lib/secace.c */
+
+bool sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2);
+
+/* End of stuff from lib/secace.c */
+
+/* From lib/secacl.c */
+
+bool sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2);
+
+/* End of stuff from lib/secacl.c */
+
+/* From lib/secdesc.c */
+
+bool sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2);
+
+/* End of stuff from lib/secdesc.c */
Index: releases/0.2.1/include/void_stack.h
===================================================================
--- releases/0.2.1/include/void_stack.h	(revision 57)
+++ releases/0.2.1/include/void_stack.h	(revision 57)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2005 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$
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+typedef struct _void_stack
+{
+  void** elements;
+  unsigned short max_size;
+  unsigned short top;
+} void_stack;
+
+typedef struct _void_stack_iterator
+{
+  void_stack* stack;
+  unsigned short cur;
+} void_stack_iterator;
+
+
+void_stack* void_stack_new(unsigned short max_size);
+void_stack* void_stack_copy(const void_stack* v);
+void_stack* void_stack_copy_reverse(const void_stack* v);
+void void_stack_destroy(void_stack* stack);
+void void_stack_destroy_deep(void_stack* stack);
+unsigned short void_stack_size(void_stack* stack);
+void* void_stack_pop(void_stack* stack);
+bool void_stack_push(void_stack* stack, void* e);
+const void* void_stack_cur(void_stack* stack);
+void_stack_iterator* void_stack_iterator_new(void_stack* stack);
+void void_stack_iterator_destroy(void_stack_iterator* iter);
+const void* void_stack_iterator_next(void_stack_iterator* iter);
Index: releases/0.2.1/lib/Makefile
===================================================================
--- releases/0.2.1/lib/Makefile	(revision 57)
+++ releases/0.2.1/lib/Makefile	(revision 57)
@@ -0,0 +1,19 @@
+# $Id: Makefile 30 2005-07-16 14:31:27Z tim $
+
+################################################################################
+
+FILES=regfio.o smb_deps.o void_stack.o
+
+all: $(FILES)
+
+regfio.o: regfio.c
+	$(CC) $(CFLAGS) $(OPTS) -c -o $@ regfio.c
+
+smb_deps.o: smb_deps.c
+	$(CC) $(CFLAGS) $(OPTS) -c -o $@ smb_deps.c
+
+void_stack.o: void_stack.c
+	$(CC) $(CFLAGS) $(OPTS) -c -o $@ void_stack.c
+
+clean:
+	rm -f $(FILES)
Index: releases/0.2.1/lib/regfio.c
===================================================================
--- releases/0.2.1/lib/regfio.c	(revision 57)
+++ releases/0.2.1/lib/regfio.c	(revision 57)
@@ -0,0 +1,1442 @@
+/*
+ * Branched from Samba project Subversion repository, version #7470:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c
+ *
+ * Unix SMB/CIFS implementation.
+ * Windows NT registry I/O library
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 2005 Gerald (Jerry) Carter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ *
+ * $Id$
+ */
+
+#include "../include/regfio.h"
+
+
+
+/* Registry types mapping */
+const VAL_STR reg_type_names[] = 
+{
+  { REG_SZ,                        "SZ"           },
+  { REG_EXPAND_SZ,                 "EXPAND_SZ"    },
+  { REG_BINARY,                    "BINARY"       },
+  { REG_DWORD,                     "DWORD"        },
+  { REG_DWORD_BE,                  "DWORD_BE"     },
+  { REG_LINK,                      "LINK"         },
+  { REG_MULTI_SZ,                  "MULTI_SZ"     },
+  { REG_RESOURCE_LIST,             "RSRC_LIST"    },
+  { REG_FULL_RESOURCE_DESCRIPTOR,  "RSRC_DESC"    },
+  { REG_RESOURCE_REQUIREMENTS_LIST,"RSRC_REQ_LIST"},
+  { REG_KEY,                       "KEY"          },
+  { 0,                             NULL           },
+};
+
+
+/* Returns NULL on error */
+const char* regfio_type_val2str(unsigned int val)
+{
+  int i;
+
+  for(i=0; reg_type_names[i].val && reg_type_names[i].str; i++)
+    if (reg_type_names[i].val == val) 
+      return reg_type_names[i].str;
+
+  return NULL;
+}
+
+
+/* Returns 0 on error */
+int regfio_type_str2val(const char* str)
+{
+  int i;
+
+  for(i=0; reg_type_names[i].val && reg_type_names[i].str; i++) 
+    if (strcmp(reg_type_names[i].str, str) == 0) 
+      return reg_type_names[i].val;
+
+  return 0;
+}
+
+
+/* Security descriptor parsing functions  */
+
+const char* regfio_ace_type2str(uint8 type)
+{
+  static const char* map[7] 
+    = {"ALLOW", "DENY", "AUDIT", "ALARM", 
+       "ALLOW CPD", "OBJ ALLOW", "OBJ DENY"};
+  if(type < 7)
+    return map[type];
+  else
+    /* XXX: would be nice to return the unknown integer value.  
+     *      However, as it is a const string, it can't be free()ed later on, 
+     *      so that would need to change. 
+     */
+    return "UNKNOWN";
+}
+
+
+/* XXX: this could probably be more efficient */
+char* regfio_ace_flags2str(uint8 flags)
+{
+  char* flg_output = malloc(21*sizeof(char));
+  int some = 0;
+
+  if(flg_output == NULL)
+    return NULL;
+
+  flg_output[0] = '\0';
+  if (!flags)
+    return flg_output;
+
+  if (flags & 0x01) {
+    if (some) strcat(flg_output, " ");
+    some = 1;
+    strcat(flg_output, "OI");
+  }
+  if (flags & 0x02) {
+    if (some) strcat(flg_output, " ");
+    some = 1;
+    strcat(flg_output, "CI");
+  }
+  if (flags & 0x04) {
+    if (some) strcat(flg_output, " ");
+    some = 1;
+    strcat(flg_output, "NP");
+  }
+  if (flags & 0x08) {
+    if (some) strcat(flg_output, " ");
+    some = 1;
+    strcat(flg_output, "IO");
+  }
+  if (flags & 0x10) {
+    if (some) strcat(flg_output, " ");
+    some = 1;
+    strcat(flg_output, "IA");
+  }
+  if (flags == 0xF) {
+    if (some) strcat(flg_output, " ");
+    some = 1;
+    strcat(flg_output, "VI");
+  }
+
+  return flg_output;
+}
+
+
+char* regfio_ace_perms2str(uint32 perms)
+{
+  char* ret_val = malloc(9*sizeof(char));
+  if(ret_val == NULL)
+    return NULL;
+
+  /* XXX: this should probably be parsed better */
+  sprintf(ret_val, "%.8X", perms);
+
+  return ret_val;
+}
+
+
+char* regfio_sid2str(DOM_SID* sid)
+{
+  uint32 i, size = MAXSUBAUTHS*11 + 24;
+  uint32 left = size;
+  uint8 comps = sid->num_auths;
+  char* ret_val = malloc(size);
+  
+  if(ret_val == NULL)
+    return NULL;
+
+  if(comps > MAXSUBAUTHS)
+    comps = MAXSUBAUTHS;
+
+  left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
+
+  for (i = 0; i < comps; i++) 
+    left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
+
+  return ret_val;
+}
+
+
+char* regfio_get_acl(SEC_ACL* acl)
+{
+  uint32 i, extra, size = 0;
+  const char* type_str;
+  char* flags_str;
+  char* perms_str;
+  char* sid_str;
+  char* ret_val = NULL;
+  char* ace_delim = "";
+  char field_delim = ':';
+
+  for (i = 0; i < acl->num_aces; i++)
+  {
+    sid_str = regfio_sid2str(&acl->ace[i].trustee);
+    type_str = regfio_ace_type2str(acl->ace[i].type);
+    perms_str = regfio_ace_perms2str(acl->ace[i].info.mask);
+    flags_str = regfio_ace_flags2str(acl->ace[i].flags);
+    
+    if(flags_str == NULL || perms_str == NULL 
+       || type_str == NULL || sid_str == NULL)
+      return NULL;
+
+    /* XXX: this is slow */
+    extra = strlen(sid_str) + strlen(type_str) 
+          + strlen(perms_str) + strlen(flags_str)+5;
+    ret_val = realloc(ret_val, size+extra);
+    if(ret_val == NULL)
+      return NULL;
+    size += snprintf(ret_val+size, extra, "%s%s%c%s%c%s%c%s",
+		     ace_delim,sid_str,
+		     field_delim,type_str,
+		     field_delim,perms_str,
+		     field_delim,flags_str);
+    ace_delim = "|";
+    free(sid_str);
+    free(perms_str);
+    free(flags_str);
+  }
+
+  return ret_val;
+}
+
+
+char* regfio_get_sacl(SEC_DESC *sec_desc)
+{
+  if (sec_desc->sacl)
+    return regfio_get_acl(sec_desc->sacl);
+  else
+    return NULL;
+}
+
+
+char* regfio_get_dacl(SEC_DESC *sec_desc)
+{
+  if (sec_desc->dacl)
+    return regfio_get_acl(sec_desc->dacl);
+  else
+    return NULL;
+}
+
+
+char* regfio_get_owner(SEC_DESC *sec_desc)
+{
+  return regfio_sid2str(sec_desc->owner_sid);
+}
+
+
+char* regfio_get_group(SEC_DESC *sec_desc)
+{
+  return regfio_sid2str(sec_desc->grp_sid);
+}
+
+
+
+/*******************************************************************
+ *******************************************************************/
+static int read_block( REGF_FILE *file, prs_struct *ps, uint32 file_offset, 
+		       uint32 block_size )
+{
+  int bytes_read, returned;
+  char *buffer;
+  SMB_STRUCT_STAT sbuf;
+
+  /* check for end of file */
+
+  if ( fstat( file->fd, &sbuf ) ) {
+    /*DEBUG(0,("read_block: stat() failed! (%s)\n", strerror(errno)));*/
+    return -1;
+  }
+
+  if ( (size_t)file_offset >= sbuf.st_size )
+    return -1;
+	
+  /* if block_size == 0, we are parsnig HBIN records and need 
+     to read some of the header to get the block_size from there */
+	   
+  if ( block_size == 0 ) {
+    uint8 hdr[0x20];
+
+    if ( lseek( file->fd, file_offset, SEEK_SET ) == -1 ) {
+      /*DEBUG(0,("read_block: lseek() failed! (%s)\n", strerror(errno) ));*/
+      return -1;
+    }
+
+    returned = read( file->fd, hdr, 0x20 );
+    if ( (returned == -1) || (returned < 0x20) ) {
+      /*DEBUG(0,("read_block: failed to read in HBIN header. Is the file corrupt?\n"));*/
+      return -1;
+    }
+
+    /* make sure this is an hbin header */
+
+    if ( strncmp( (char*)hdr, "hbin", HBIN_HDR_SIZE ) != 0 ) {
+      /*DEBUG(0,("read_block: invalid block header!\n"));*/
+      return -1;
+    }
+
+    block_size = IVAL( hdr, 0x08 );
+  }
+
+  /*DEBUG(10,("read_block: block_size == 0x%x\n", block_size ));*/
+
+  /* set the offset, initialize the buffer, and read the block from disk */
+
+  if ( lseek( file->fd, file_offset, SEEK_SET ) == -1 ) {
+    /*DEBUG(0,("read_block: lseek() failed! (%s)\n", strerror(errno) ));*/
+    return -1;
+  }
+	
+  prs_init( ps, block_size, file->mem_ctx, UNMARSHALL );
+  buffer = ps->data_p;
+  bytes_read = returned = 0;
+
+  while ( bytes_read < block_size ) 
+  {
+    if((returned = 
+	read(file->fd, buffer+bytes_read, block_size-bytes_read)) == -1)
+    {
+      /*DEBUG(0,("read_block: read() failed (%s)\n", strerror(errno) ));*/
+      return false;
+    }
+    if ((returned == 0) && (bytes_read < block_size)) 
+    {
+      /*DEBUG(0,("read_block: not a vald registry file ?\n" ));*/
+      return false;
+    }	
+
+    bytes_read += returned;
+  }
+	
+  return bytes_read;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool prs_regf_block(const char *desc, prs_struct *ps, 
+			   int depth, REGF_FILE *file)
+{
+  depth++;
+	
+  if(!prs_uint8s(true, "header", ps, depth, file->header, sizeof(file->header)))
+    return false;
+	
+  /* yes, these values are always identical so store them only once */
+	
+  if ( !prs_uint32( "unknown1", ps, depth, &file->unknown1 ))
+    return false;
+  if ( !prs_uint32( "unknown1 (again)", ps, depth, &file->unknown1 ))
+    return false;
+
+  /* get the modtime */
+	
+  if ( !prs_set_offset( ps, 0x0c ) )
+    return false;
+  if ( !smb_io_time( "modtime", &file->mtime, ps, depth ) )
+    return false;
+
+  /* constants */
+	
+  if ( !prs_uint32( "unknown2", ps, depth, &file->unknown2 ))
+    return false;
+  if ( !prs_uint32( "unknown3", ps, depth, &file->unknown3 ))
+    return false;
+  if ( !prs_uint32( "unknown4", ps, depth, &file->unknown4 ))
+    return false;
+  if ( !prs_uint32( "unknown5", ps, depth, &file->unknown5 ))
+    return false;
+
+  /* get file offsets */
+	
+  if ( !prs_set_offset( ps, 0x24 ) )
+    return false;
+  if ( !prs_uint32( "data_offset", ps, depth, &file->data_offset ))
+    return false;
+  if ( !prs_uint32( "last_block", ps, depth, &file->last_block ))
+    return false;
+		
+  /* one more constant */
+	
+  if ( !prs_uint32( "unknown6", ps, depth, &file->unknown6 ))
+    return false;
+		
+  /* get the checksum */
+	
+  if ( !prs_set_offset( ps, 0x01fc ) )
+    return false;
+  if ( !prs_uint32( "checksum", ps, depth, &file->checksum ))
+    return false;
+	
+  return true;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool prs_hbin_block(const char *desc, prs_struct *ps, 
+			   int depth, REGF_HBIN *hbin)
+{
+  uint32 block_size2;
+
+  depth++;
+	
+  if(!prs_uint8s(true, "header", ps, depth, hbin->header, sizeof(hbin->header)))
+    return false;
+
+  if ( !prs_uint32( "first_hbin_off", ps, depth, &hbin->first_hbin_off ))
+    return false;
+
+  /* The dosreg.cpp comments say that the block size is at 0x1c.
+     According to a WINXP NTUSER.dat file, this is wrong.  The block_size
+     is at 0x08 */
+
+  if ( !prs_uint32( "block_size", ps, depth, &hbin->block_size ))
+    return false;
+
+  block_size2 = hbin->block_size;
+  prs_set_offset( ps, 0x1c );
+  if ( !prs_uint32( "block_size2", ps, depth, &block_size2 ))
+    return false;
+
+  if ( !ps->io )
+    hbin->dirty = true;
+	
+
+  return true;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool prs_nk_rec( const char *desc, prs_struct *ps, 
+			int depth, REGF_NK_REC *nk )
+{
+  uint16 class_length, name_length;
+  uint32 start;
+  uint32 data_size, start_off, end_off;
+  uint32 unknown_off = REGF_OFFSET_NONE;
+
+  nk->hbin_off = ps->data_offset;
+  start = nk->hbin_off;
+	
+  depth++;
+	
+  /* back up and get the data_size */	
+  if ( !prs_set_offset( ps, ps->data_offset-sizeof(uint32)) )
+    return false;
+  start_off = ps->data_offset;
+  if ( !prs_uint32( "rec_size", ps, depth, &nk->rec_size ))
+    return false;
+	
+  if (!prs_uint8s(true, "header", ps, depth, nk->header, sizeof(nk->header)))
+    return false;
+		
+  if ( !prs_uint16( "key_type", ps, depth, &nk->key_type ))
+    return false;
+  if ( !smb_io_time( "mtime", &nk->mtime, ps, depth ))
+    return false;
+		
+  if ( !prs_set_offset( ps, start+0x0010 ) )
+    return false;
+  if ( !prs_uint32( "parent_off", ps, depth, &nk->parent_off ))
+    return false;
+  if ( !prs_uint32( "num_subkeys", ps, depth, &nk->num_subkeys ))
+    return false;
+		
+  if ( !prs_set_offset( ps, start+0x001c ) )
+    return false;
+  if ( !prs_uint32( "subkeys_off", ps, depth, &nk->subkeys_off ))
+    return false;
+  if ( !prs_uint32( "unknown_off", ps, depth, &unknown_off) )
+    return false;
+		
+  if ( !prs_set_offset( ps, start+0x0024 ) )
+    return false;
+  if ( !prs_uint32( "num_values", ps, depth, &nk->num_values ))
+    return false;
+  if ( !prs_uint32( "values_off", ps, depth, &nk->values_off ))
+    return false;
+  if ( !prs_uint32( "sk_off", ps, depth, &nk->sk_off ))
+    return false;
+  if ( !prs_uint32( "classname_off", ps, depth, &nk->classname_off ))
+    return false;
+
+  if (!prs_uint32("max_bytes_subkeyname", ps, depth, &nk->max_bytes_subkeyname))
+    return false;
+  if ( !prs_uint32( "max_bytes_subkeyclassname", ps, 
+		    depth, &nk->max_bytes_subkeyclassname))
+  { return false; }
+  if ( !prs_uint32( "max_bytes_valuename", ps, depth, &nk->max_bytes_valuename))
+    return false;
+  if ( !prs_uint32( "max_bytes_value", ps, depth, &nk->max_bytes_value))
+    return false;
+  if ( !prs_uint32( "unknown index", ps, depth, &nk->unk_index))
+    return false;
+
+  name_length = nk->keyname ? strlen(nk->keyname) : 0 ;
+  class_length = nk->classname ? strlen(nk->classname) : 0 ;
+  if ( !prs_uint16( "name_length", ps, depth, &name_length ))
+    return false;
+  if ( !prs_uint16( "class_length", ps, depth, &class_length ))
+    return false;	
+		
+  if ( class_length ) 
+  {
+    ;;
+  }
+	
+  if ( name_length ) 
+  {
+    if(ps->io && !(nk->keyname = (char*)zcalloc(sizeof(char), name_length+1)))
+	return false;
+
+    if(!prs_uint8s(true, "name", ps, depth, (uint8*)nk->keyname, name_length))
+      return false;
+
+    if(ps->io)
+      nk->keyname[name_length] = '\0';
+  }
+
+  end_off = ps->data_offset;
+
+  /* data_size must be divisible by 8 and large enough to hold 
+     the original record */
+
+  data_size = ((start_off - end_off) & 0xfffffff8 );
+  /*if ( data_size > nk->rec_size )
+      DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, nk->rec_size));*/
+
+  if ( !ps->io )
+    nk->hbin->dirty = true;
+  
+  nk->subkey_index = 0;
+  return true;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static uint32 regf_block_checksum( prs_struct *ps )
+{
+  char *buffer = ps->data_p;
+  uint32 checksum, x;
+  int i;
+
+  /* XOR of all bytes 0x0000 - 0x01FB */
+		
+  checksum = x = 0;
+	
+  for ( i=0; i<0x01FB; i+=4 ) {
+    x = IVAL(buffer, i );
+    checksum ^= x;
+  }
+	
+  return checksum;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool read_regf_block( REGF_FILE *file )
+{
+  prs_struct ps;
+  uint32 checksum;
+	
+  /* grab the first block from the file */
+		
+  if ( read_block( file, &ps, 0, REGF_BLOCKSIZE ) == -1 )
+    return false;
+	
+  /* parse the block and verify the checksum */
+	
+  if ( !prs_regf_block( "regf_header", &ps, 0, file ) )
+    return false;	
+		
+  checksum = regf_block_checksum( &ps );
+	
+  if(ps.is_dynamic)
+    SAFE_FREE(ps.data_p);
+  ps.is_dynamic = false;
+  ps.buffer_size = 0;
+  ps.data_offset = 0;
+
+  if ( file->checksum !=  checksum ) {
+    /*DEBUG(0,("read_regf_block: invalid checksum\n" ));*/
+    return false;
+  }
+
+  return true;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static REGF_HBIN* read_hbin_block( REGF_FILE *file, off_t offset )
+{
+  REGF_HBIN *hbin;
+  uint32 record_size, curr_off, block_size, header;
+	
+  if ( !(hbin = (REGF_HBIN*)zalloc(sizeof(REGF_HBIN))) ) 
+    return NULL;
+  hbin->file_off = offset;
+  hbin->free_off = -1;
+		
+  if ( read_block( file, &hbin->ps, offset, 0 ) == -1 )
+    return NULL;
+	
+  if ( !prs_hbin_block( "hbin", &hbin->ps, 0, hbin ) )
+    return NULL;	
+
+  /* this should be the same thing as hbin->block_size but just in case */
+
+  block_size = hbin->ps.buffer_size;
+
+  /* Find the available free space offset.  Always at the end,
+     so walk the record list and stop when you get to the end.
+     The end is defined by a record header of 0xffffffff.  The 
+     previous 4 bytes contains the amount of free space remaining 
+     in the hbin block. */
+
+  /* remember that the record_size is in the 4 bytes preceeding the record itself */
+
+  if ( !prs_set_offset( &hbin->ps, file->data_offset+HBIN_HDR_SIZE-sizeof(uint32) ) )
+    return false;
+
+  record_size = 0;
+  curr_off = hbin->ps.data_offset;
+  while ( header != 0xffffffff ) {
+    /* not done yet so reset the current offset to the 
+       next record_size field */
+
+    curr_off = curr_off+record_size;
+
+    /* 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 >= block_size ) {
+      record_size = -1;
+      curr_off = -1;
+      break;
+    }
+
+    if ( !prs_set_offset( &hbin->ps, curr_off) )
+      return false;
+
+    if ( !prs_uint32( "rec_size", &hbin->ps, 0, &record_size ) )
+      return false;
+    if ( !prs_uint32( "header", &hbin->ps, 0, &header ) )
+      return false;
+		
+    assert( record_size != 0 );
+
+    if ( record_size & 0x80000000 ) {
+      /* absolute_value(record_size) */
+      record_size = (record_size ^ 0xffffffff) + 1;
+    }
+  }
+
+  /* save the free space offset */
+
+  if ( header == 0xffffffff ) {
+
+    /* account for the fact that the curr_off is 4 bytes behind the actual 
+       record header */
+
+    hbin->free_off = curr_off + sizeof(uint32);
+    hbin->free_size = record_size;
+  }
+
+  /*DEBUG(10,("read_hbin_block: free space offset == 0x%x\n", hbin->free_off));*/
+
+  if ( !prs_set_offset( &hbin->ps, file->data_offset+HBIN_HDR_SIZE )  )
+    return false;
+	
+  return hbin;
+}
+
+
+/*******************************************************************
+ Input a randon offset and receive the correpsonding HBIN 
+ block for it
+*******************************************************************/
+static bool hbin_contains_offset( REGF_HBIN *hbin, uint32 offset )
+{
+  if ( !hbin )
+    return false;
+	
+  if ( (offset > hbin->first_hbin_off) && (offset < (hbin->first_hbin_off+hbin->block_size)) )
+    return true;
+		
+  return false;
+}
+
+
+/*******************************************************************
+ Input a randon offset and receive the correpsonding HBIN 
+ block for it
+*******************************************************************/
+static REGF_HBIN* lookup_hbin_block( REGF_FILE *file, uint32 offset )
+{
+  REGF_HBIN *hbin = NULL;
+  uint32 block_off;
+
+  /* start with the open list */
+
+  for ( hbin=file->block_list; hbin; hbin=hbin->next ) {
+    /* DEBUG(10,("lookup_hbin_block: address = 0x%x [0x%x]\n", hbin->file_off, (uint32)hbin ));*/
+    if ( hbin_contains_offset( hbin, offset ) )
+      return hbin;
+  }
+	
+  if ( !hbin ) {
+    /* start at the beginning */
+
+    block_off = REGF_BLOCKSIZE;
+    do {
+      /* cleanup before the next round */
+      if ( hbin )
+      {
+	if(hbin->ps.is_dynamic)
+	  SAFE_FREE(hbin->ps.data_p);
+	hbin->ps.is_dynamic = false;
+	hbin->ps.buffer_size = 0;
+	hbin->ps.data_offset = 0;
+      }
+
+      hbin = read_hbin_block( file, block_off );
+
+      if ( hbin ) 
+	block_off = hbin->file_off + hbin->block_size;
+
+    } while ( hbin && !hbin_contains_offset( hbin, offset ) );
+  }
+
+  if ( hbin )
+    DLIST_ADD( file->block_list, hbin );
+
+  return hbin;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool prs_hash_rec( const char *desc, prs_struct *ps, int depth, REGF_HASH_REC *hash )
+{
+  depth++;
+
+  if ( !prs_uint32( "nk_off", ps, depth, &hash->nk_off ))
+    return false;
+  if ( !prs_uint8s( true, "keycheck", ps, depth, hash->keycheck, sizeof( hash->keycheck )) )
+    return false;
+	
+  return true;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool hbin_prs_lf_records(const char *desc, REGF_HBIN *hbin, 
+				int depth, REGF_NK_REC *nk)
+{
+  int i;
+  REGF_LF_REC *lf = &nk->subkeys;
+  uint32 data_size, start_off, end_off;
+
+  depth++;
+
+  /* check if we have anything to do first */
+	
+  if ( nk->num_subkeys == 0 )
+    return true;
+
+  /* move to the LF record */
+
+  if ( !prs_set_offset( &hbin->ps, nk->subkeys_off + HBIN_HDR_SIZE - hbin->first_hbin_off ) )
+    return false;
+
+  /* backup and get the data_size */
+	
+  if ( !prs_set_offset( &hbin->ps, hbin->ps.data_offset-sizeof(uint32)) )
+    return false;
+  start_off = hbin->ps.data_offset;
+  if ( !prs_uint32( "rec_size", &hbin->ps, depth, &lf->rec_size ))
+    return false;
+
+  if(!prs_uint8s(true, "header", &hbin->ps, depth, 
+		 lf->header, sizeof(lf->header)))
+    return false;
+		
+  if ( !prs_uint16( "num_keys", &hbin->ps, depth, &lf->num_keys))
+    return false;
+
+  if ( hbin->ps.io ) {
+    if ( !(lf->hashes = (REGF_HASH_REC*)zcalloc(sizeof(REGF_HASH_REC), lf->num_keys )) )
+      return false;
+  }
+
+  for ( i=0; i<lf->num_keys; i++ ) {
+    if ( !prs_hash_rec( "hash_rec", &hbin->ps, depth, &lf->hashes[i] ) )
+      return false;
+  }
+
+  end_off = hbin->ps.data_offset;
+
+  /* data_size must be divisible by 8 and large enough to hold the original record */
+
+  data_size = ((start_off - end_off) & 0xfffffff8 );
+  /*  if ( data_size > lf->rec_size )*/
+    /*DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, lf->rec_size));*/
+
+  if ( !hbin->ps.io )
+    hbin->dirty = true;
+
+  return true;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool hbin_prs_sk_rec( const char *desc, REGF_HBIN *hbin, int depth, REGF_SK_REC *sk )
+{
+  prs_struct *ps = &hbin->ps;
+  uint16 tag = 0xFFFF;
+  uint32 data_size, start_off, end_off;
+
+
+  depth++;
+
+  if ( !prs_set_offset( &hbin->ps, sk->sk_off + HBIN_HDR_SIZE - hbin->first_hbin_off ) )
+    return false;
+
+  /* backup and get the data_size */
+	
+  if ( !prs_set_offset( &hbin->ps, hbin->ps.data_offset-sizeof(uint32)) )
+    return false;
+  start_off = hbin->ps.data_offset;
+  if ( !prs_uint32( "rec_size", &hbin->ps, depth, &sk->rec_size ))
+    return false;
+
+  if (!prs_uint8s(true, "header", ps, depth, sk->header, sizeof(sk->header)))
+    return false;
+  if ( !prs_uint16( "tag", ps, depth, &tag))
+    return false;
+
+  if ( !prs_uint32( "prev_sk_off", ps, depth, &sk->prev_sk_off))
+    return false;
+  if ( !prs_uint32( "next_sk_off", ps, depth, &sk->next_sk_off))
+    return false;
+  if ( !prs_uint32( "ref_count", ps, depth, &sk->ref_count))
+    return false;
+  if ( !prs_uint32( "size", ps, depth, &sk->size))
+    return false;
+
+  if ( !sec_io_desc( "sec_desc", &sk->sec_desc, ps, depth )) 
+    return false;
+
+  end_off = hbin->ps.data_offset;
+
+  /* data_size must be divisible by 8 and large enough to hold the original record */
+
+  data_size = ((start_off - end_off) & 0xfffffff8 );
+  /*  if ( data_size > sk->rec_size )*/
+    /*DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, sk->rec_size));*/
+
+  if ( !hbin->ps.io )
+    hbin->dirty = true;
+
+  return true;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool hbin_prs_vk_rec( const char *desc, REGF_HBIN *hbin, int depth, 
+			     REGF_VK_REC *vk, REGF_FILE *file )
+{
+  uint32 offset;
+  uint16 name_length;
+  prs_struct *ps = &hbin->ps;
+  uint32 data_size, start_off, end_off;
+
+  depth++;
+
+  /* backup and get the data_size */
+	
+  if ( !prs_set_offset( &hbin->ps, hbin->ps.data_offset-sizeof(uint32)) )
+    return false;
+  start_off = hbin->ps.data_offset;
+  if ( !prs_uint32( "rec_size", &hbin->ps, depth, &vk->rec_size ))
+    return false;
+
+  if ( !prs_uint8s( true, "header", ps, depth, vk->header, sizeof( vk->header )) )
+    return false;
+
+  if ( !hbin->ps.io )
+    name_length = strlen(vk->valuename);
+
+  if ( !prs_uint16( "name_length", ps, depth, &name_length ))
+    return false;
+  if ( !prs_uint32( "data_size", ps, depth, &vk->data_size ))
+    return false;
+  if ( !prs_uint32( "data_off", ps, depth, &vk->data_off ))
+    return false;
+  if ( !prs_uint32( "type", ps, depth, &vk->type))
+    return false;
+  if ( !prs_uint16( "flag", ps, depth, &vk->flag))
+    return false;
+
+  offset = ps->data_offset;
+  offset += 2;	/* skip 2 bytes */
+  prs_set_offset( ps, offset );
+
+  /* get the name */
+
+  if ( vk->flag&VK_FLAG_NAME_PRESENT ) {
+
+    if ( hbin->ps.io ) {
+      if ( !(vk->valuename = (char*)zcalloc(sizeof(char), name_length+1 )))
+	return false;
+    }
+    if ( !prs_uint8s(true, "name", ps, depth, 
+		     (uint8*)vk->valuename, name_length) )
+      return false;
+  }
+
+  end_off = hbin->ps.data_offset;
+
+  /* get the data if necessary */
+
+  if ( vk->data_size != 0 ) 
+  {
+    bool charmode = false;
+
+    if ( (vk->type == REG_SZ) || (vk->type == REG_MULTI_SZ) )
+      charmode = true;
+
+    /* the data is stored in the offset if the size <= 4 */
+    if ( !(vk->data_size & VK_DATA_IN_OFFSET) ) 
+    {
+      REGF_HBIN *hblock = hbin;
+      uint32 data_rec_size;
+
+      if ( hbin->ps.io ) 
+      {
+	if ( !(vk->data = (uint8*)zcalloc(sizeof(uint8), vk->data_size) ) )
+	  return false;
+      }
+
+      /* this data can be in another hbin */
+      if ( !hbin_contains_offset( hbin, vk->data_off ) ) 
+      {
+	if ( !(hblock = lookup_hbin_block( file, vk->data_off )) )
+	  return false;
+      }
+      if (!(prs_set_offset(&hblock->ps, 
+			   (vk->data_off
+			    + HBIN_HDR_SIZE
+			    - hblock->first_hbin_off)
+			   - sizeof(uint32))))
+      {	return false; }
+
+      if ( !hblock->ps.io ) 
+      {
+	data_rec_size = ( (vk->data_size+sizeof(uint32)) & 0xfffffff8 ) + 8;
+	data_rec_size = ( data_rec_size - 1 ) ^ 0xFFFFFFFF;
+      }
+      if ( !prs_uint32( "data_rec_size", &hblock->ps, depth, &data_rec_size ))
+	return false;
+      if(!prs_uint8s(charmode, "data", &hblock->ps, depth, 
+		     vk->data, vk->data_size))
+	return false;
+
+      if ( !hblock->ps.io )
+	hblock->dirty = true;
+    }
+    else 
+    {
+      if(!(vk->data = zcalloc(sizeof(uint8), 4)))
+	return false;
+      SIVAL( vk->data, 0, vk->data_off );
+    }
+		
+  }
+
+  /* data_size must be divisible by 8 and large enough to hold the original record */
+
+  data_size = ((start_off - end_off ) & 0xfffffff8 );
+  /*if ( data_size !=  vk->rec_size )
+    DEBUG(10,("prs_vk_rec: data_size check failed (0x%x < 0x%x)\n", data_size, vk->rec_size));*/
+
+  if ( !hbin->ps.io )
+    hbin->dirty = true;
+
+  return true;
+}
+
+
+/*******************************************************************
+ read a VK record which is contained in the HBIN block stored 
+ in the prs_struct *ps.
+*******************************************************************/
+static bool hbin_prs_vk_records(const char *desc, REGF_HBIN *hbin, 
+				int depth, REGF_NK_REC *nk, REGF_FILE *file)
+{
+  int i;
+  uint32 record_size;
+
+  depth++;
+	
+  /* check if we have anything to do first */
+  if(nk->num_values == 0)
+    return true;
+		
+  if(hbin->ps.io)
+  {
+    if (!(nk->values = (REGF_VK_REC*)zcalloc(sizeof(REGF_VK_REC), 
+					      nk->num_values )))
+      return false;
+  }
+	
+  /* convert the offset to something relative to this HBIN block */
+  if (!prs_set_offset(&hbin->ps, 
+		      nk->values_off
+		      + HBIN_HDR_SIZE
+		      - hbin->first_hbin_off
+		      - sizeof(uint32)))
+  { return false; }
+
+  if ( !hbin->ps.io ) 
+  { 
+    record_size = ( ( nk->num_values * sizeof(uint32) ) & 0xfffffff8 ) + 8;
+    record_size = (record_size - 1) ^ 0xFFFFFFFF;
+  }
+
+  if ( !prs_uint32( "record_size", &hbin->ps, depth, &record_size ) )
+    return false;
+		
+  for ( i=0; i<nk->num_values; i++ ) 
+  {
+    if ( !prs_uint32( "vk_off", &hbin->ps, depth, &nk->values[i].rec_off ) )
+      return false;
+  }
+
+  for ( i=0; i<nk->num_values; i++ ) 
+  {
+    REGF_HBIN *sub_hbin = hbin;
+    uint32 new_offset;
+	
+    if ( !hbin_contains_offset( hbin, nk->values[i].rec_off ) ) 
+    {
+      sub_hbin = lookup_hbin_block( file, nk->values[i].rec_off );
+      if ( !sub_hbin ) 
+      {
+	/*DEBUG(0,("hbin_prs_vk_records: Failed to find HBIN block containing offset [0x%x]\n", 
+	  nk->values[i].hbin_off));*/
+	return false;
+      }
+    }
+		
+    new_offset = nk->values[i].rec_off 
+      + HBIN_HDR_SIZE 
+      - sub_hbin->first_hbin_off;
+
+    if (!prs_set_offset(&sub_hbin->ps, new_offset))
+      return false;
+    if (!hbin_prs_vk_rec("vk_rec", sub_hbin, depth, &nk->values[i], file))
+      return false;
+  }
+
+  if ( !hbin->ps.io )
+    hbin->dirty = true;
+
+  return true;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static REGF_SK_REC* find_sk_record_by_offset( REGF_FILE *file, uint32 offset )
+{
+  REGF_SK_REC *p_sk;
+	
+  for ( p_sk=file->sec_desc_list; p_sk; p_sk=p_sk->next ) {
+    if ( p_sk->sk_off == offset ) 
+      return p_sk;
+  }
+	
+  return NULL;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static REGF_SK_REC* find_sk_record_by_sec_desc( REGF_FILE *file, SEC_DESC *sd )
+{
+  REGF_SK_REC *p;
+
+  for ( p=file->sec_desc_list; p; p=p->next ) {
+    if ( sec_desc_equal( p->sec_desc, sd ) )
+      return p;
+  }
+
+  /* failure */
+
+  return NULL;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool hbin_prs_key( REGF_FILE *file, REGF_HBIN *hbin, REGF_NK_REC *nk )
+{
+  int depth = 0;
+  REGF_HBIN *sub_hbin;
+	
+  depth++;
+
+  /* get the initial nk record */
+  if (!prs_nk_rec("nk_rec", &hbin->ps, depth, nk))
+    return false;
+
+  /* fill in values */
+  if ( nk->num_values && (nk->values_off!=REGF_OFFSET_NONE) ) 
+  {
+    sub_hbin = hbin;
+    if ( !hbin_contains_offset( hbin, nk->values_off ) ) 
+    {
+      sub_hbin = lookup_hbin_block( file, nk->values_off );
+      if ( !sub_hbin ) 
+      {
+	/*DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing value_list_offset [0x%x]\n", 
+	  nk->values_off));*/
+	return false;
+      }
+    }
+		
+    if(!hbin_prs_vk_records("vk_rec", sub_hbin, depth, nk, file))
+      return false;
+  }
+		
+  /* now get subkeys */
+  if ( nk->num_subkeys && (nk->subkeys_off!=REGF_OFFSET_NONE) ) 
+  {
+    sub_hbin = hbin;
+    if ( !hbin_contains_offset( hbin, nk->subkeys_off ) ) 
+    {
+      sub_hbin = lookup_hbin_block( file, nk->subkeys_off );
+      if ( !sub_hbin ) 
+      {
+	/*DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing subkey_offset [0x%x]\n", 
+	  nk->subkeys_off));*/
+	return false;
+      }
+    }
+		
+    if (!hbin_prs_lf_records("lf_rec", sub_hbin, depth, nk))
+      return false;
+  }
+
+  /* get the to the security descriptor.  First look if we have already parsed it */
+	
+  if ((nk->sk_off!=REGF_OFFSET_NONE) 
+      && !(nk->sec_desc = find_sk_record_by_offset( file, nk->sk_off )))
+  {
+    sub_hbin = hbin;
+    if (!hbin_contains_offset(hbin, nk->sk_off))
+    {
+      sub_hbin = lookup_hbin_block( file, nk->sk_off );
+      if ( !sub_hbin ) {
+	/*DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing sk_offset [0x%x]\n", 
+	  nk->subkeys_off));*/
+	return false;
+      }
+    }
+		
+    if ( !(nk->sec_desc = (REGF_SK_REC*)zalloc(sizeof(REGF_SK_REC) )) )
+      return false;
+    nk->sec_desc->sk_off = nk->sk_off;
+    if ( !hbin_prs_sk_rec( "sk_rec", sub_hbin, depth, nk->sec_desc ))
+      return false;
+			
+    /* add to the list of security descriptors (ref_count has been read from the files) */
+
+    nk->sec_desc->sk_off = nk->sk_off;
+    DLIST_ADD( file->sec_desc_list, nk->sec_desc );
+  }
+		
+  return true;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool next_record( REGF_HBIN *hbin, const char *hdr, bool *eob )
+{
+  uint8 header[REC_HDR_SIZE] = "";
+  uint32 record_size;
+  uint32 curr_off, block_size;
+  bool found = false;
+  prs_struct *ps = &hbin->ps;
+	
+  curr_off = ps->data_offset;
+  if ( curr_off == 0 )
+    prs_set_offset( ps, HBIN_HEADER_REC_SIZE );
+
+  /* assume that the current offset is at the reacord header 
+     and we need to backup to read the record size */
+  curr_off -= sizeof(uint32);
+
+  block_size = ps->buffer_size;
+  record_size = 0;
+  while ( !found ) 
+  {
+    curr_off = curr_off+record_size;
+    if ( curr_off >= block_size ) 
+      break;
+
+    if ( !prs_set_offset( &hbin->ps, curr_off) )
+      return false;
+
+    if ( !prs_uint32( "record_size", ps, 0, &record_size ) )
+      return false;
+    if ( !prs_uint8s( true, "header", ps, 0, header, REC_HDR_SIZE ) )
+      return false;
+
+    if ( record_size & 0x80000000 ) {
+      /* absolute_value(record_size) */
+      record_size = (record_size ^ 0xffffffff) + 1;
+    }
+
+    if ( memcmp( header, hdr, REC_HDR_SIZE ) == 0 ) {
+      found = true;
+      curr_off += sizeof(uint32);
+    }
+  } 
+
+  /* mark prs_struct as done ( at end ) if no more SK records */
+  /* mark end-of-block as true */	
+  if ( !found )
+  {
+    prs_set_offset( &hbin->ps, hbin->ps.buffer_size );
+    *eob = true;
+    return false;
+  }
+
+  if (!prs_set_offset(ps, curr_off))
+    return false;
+
+  return true;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static bool next_nk_record(REGF_FILE *file, REGF_HBIN *hbin, 
+			   REGF_NK_REC *nk, bool *eob)
+{
+  if (next_record(hbin, "nk", eob) 
+      && hbin_prs_key(file, hbin, nk))
+    return true;
+	
+  return false;
+}
+
+
+/*******************************************************************
+ Open the registry file and then read in the REGF block to get the 
+ first hbin offset.
+*******************************************************************/
+REGF_FILE* regfio_open( const char *filename )
+{
+  REGF_FILE *rb;
+  int flags = O_RDONLY;
+
+  if ( !(rb = (REGF_FILE*)malloc(sizeof(REGF_FILE))) ) {
+    /* DEBUG(0,("ERROR allocating memory\n")); */
+    return NULL;
+  }
+  memset(rb, 0, sizeof(REGF_FILE));
+  rb->fd = -1;
+	
+  /*	if ( !(rb->mem_ctx = talloc_init( "read_regf_block" )) ) 
+    {
+    regfio_close( rb );
+    return NULL;
+    }
+  */
+  rb->open_flags = flags;
+	
+  /* open and existing file */
+
+  if ( (rb->fd = open(filename, flags)) == -1 ) {
+    /* DEBUG(0,("regfio_open: failure to open %s (%s)\n", filename, strerror(errno)));*/
+    regfio_close( rb );
+    return NULL;
+  }
+	
+  /* read in an existing file */
+	
+  if ( !read_regf_block( rb ) ) {
+    /* DEBUG(0,("regfio_open: Failed to read initial REGF block\n"));*/
+    regfio_close( rb );
+    return NULL;
+  }
+	
+  /* success */
+	
+  return rb;
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+static void regfio_mem_free( REGF_FILE *file )
+{
+  /* free any zalloc()'d memory */
+	
+  /*	if ( file && file->mem_ctx )
+    free(file->mem_ctx);
+  */
+}
+
+
+/*******************************************************************
+ *******************************************************************/
+int regfio_close( REGF_FILE *file )
+{
+  int fd;
+
+  regfio_mem_free( file );
+
+  /* nothing to do if there is no open file */
+
+  if ( !file || (file->fd == -1) )
+    return 0;
+		
+  fd = file->fd;
+  file->fd = -1;
+  SAFE_FREE( file );
+
+  return close( fd );
+}
+
+
+/*******************************************************************
+ There should be only *one* root key in the registry file based 
+ on my experience.  --jerry
+*******************************************************************/
+REGF_NK_REC* regfio_rootkey( REGF_FILE *file )
+{
+  REGF_NK_REC *nk;
+  REGF_HBIN   *hbin;
+  uint32      offset = REGF_BLOCKSIZE;
+  bool        found = false;
+  bool        eob;
+	
+  if ( !file )
+    return NULL;
+		
+  if ( !(nk = (REGF_NK_REC*)zalloc(sizeof(REGF_NK_REC) )) ) {
+    /*DEBUG(0,("regfio_rootkey: zalloc() failed!\n"));*/
+    return NULL;
+  }
+	
+  /* scan through the file on HBIN block at a time looking 
+     for an NK record with a type == 0x002c.
+     Normally this is the first nk record in the first hbin 
+     block (but I'm not assuming that for now) */
+	
+  while ( (hbin = read_hbin_block( file, offset )) ) {
+    eob = false;
+
+    while ( !eob) {
+      if ( next_nk_record( file, hbin, nk, &eob ) ) {
+	if ( nk->key_type == NK_TYPE_ROOTKEY ) {
+	  found = true;
+	  break;
+	}
+      }
+      if(hbin->ps.is_dynamic)
+	SAFE_FREE(hbin->ps.data_p);
+      hbin->ps.is_dynamic = false;
+      hbin->ps.buffer_size = 0;
+      hbin->ps.data_offset = 0;
+    }
+		
+    if ( found ) 
+      break;
+
+    offset += hbin->block_size;
+  }
+	
+  if ( !found ) {
+    /*DEBUG(0,("regfio_rootkey: corrupt registry file ?  No root key record located\n"));*/
+    return NULL;
+  }
+
+  DLIST_ADD( file->block_list, hbin );
+
+  return nk;		
+}
+
+
+/* XXX: An interator struct should be used instead, and this function
+ *   should operate on it, so the state of iteration isn't stored in the
+ * REGF_NK_REC struct itself.
+ */
+/*******************************************************************
+ This acts as an interator over the subkeys defined for a given 
+ NK record.  Remember that offsets are from the *first* HBIN block.
+*******************************************************************/
+REGF_NK_REC* regfio_fetch_subkey( REGF_FILE *file, REGF_NK_REC *nk )
+{
+  REGF_NK_REC *subkey;
+  REGF_HBIN   *hbin;
+  uint32      nk_offset;
+
+  /* see if there is anything left to report */
+  if (!nk || (nk->subkeys_off==REGF_OFFSET_NONE) 
+      || (nk->subkey_index >= nk->num_subkeys))
+    return NULL;
+
+  /* find the HBIN block which should contain the nk record */
+  if(!(hbin 
+       = lookup_hbin_block(file, nk->subkeys.hashes[nk->subkey_index].nk_off )))
+  {
+    /*DEBUG(0,("hbin_prs_key: Failed to find HBIN block containing offset [0x%x]\n", 
+      nk->subkeys.hashes[nk->subkey_index].nk_off));*/
+    return NULL;
+  }
+	
+  nk_offset = nk->subkeys.hashes[nk->subkey_index].nk_off;
+  if(!prs_set_offset(&hbin->ps, 
+		     (HBIN_HDR_SIZE + nk_offset - hbin->first_hbin_off)))
+    return NULL;
+		
+  nk->subkey_index++;
+  if(!(subkey = (REGF_NK_REC*)zalloc(sizeof(REGF_NK_REC))))
+    return NULL;
+
+  if(!hbin_prs_key(file, hbin, subkey))
+    return NULL;
+
+  return subkey;
+}
Index: releases/0.2.1/lib/smb_deps.c
===================================================================
--- releases/0.2.1/lib/smb_deps.c	(revision 57)
+++ releases/0.2.1/lib/smb_deps.c	(revision 57)
@@ -0,0 +1,1113 @@
+/*
+ * This file contains miscellaneous pieces of code which regfio.c
+ * depends upon, from the Samba Subversion tree.  See:
+ *   http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/
+ *
+ * Copyright (C) 2005 Timothy D. Morgan
+ * Copyright (C) 1992-2005 Samba development team 
+ *               (see individual files under Subversion for details.)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 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$
+ */
+
+#include "../include/smb_deps.h"
+
+
+/* These act as replacements for numerous Samba memory allocation
+ *   functions. 
+ */
+void* zalloc(size_t size)
+{
+  void* ret_val = NULL;
+  if((ret_val = (void*)malloc(size)) != NULL)
+    memset(ret_val, 0, size);
+  return ret_val;
+}
+
+void* zcalloc(size_t size, unsigned int count)
+{
+  return zalloc(size*count);
+}
+
+/* From lib/time.c */
+
+/****************************************************************************
+ Put a 8 byte filetime from a time_t
+ This takes real GMT as input and converts to kludge-GMT
+****************************************************************************/
+void unix_to_nt_time(NTTIME *nt, time_t t)
+{
+	double d;
+
+	if (t==0) {
+		nt->low = 0;
+		nt->high = 0;
+		return;
+	}
+	if (t == TIME_T_MAX) {
+		nt->low = 0xffffffff;
+		nt->high = 0x7fffffff;
+		return;
+	}		
+	if (t == -1) {
+		nt->low = 0xffffffff;
+		nt->high = 0xffffffff;
+		return;
+	}		
+
+	/* this converts GMT to kludge-GMT */
+	/*XXX t -= TimeDiff(t) - get_serverzone(); */
+
+	d = (double)(t);
+	d += TIME_FIXUP_CONSTANT;
+	d *= 1.0e7;
+
+	nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30))));
+	nt->low  = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30));
+}
+
+
+/****************************************************************************
+ Interpret an 8 byte "filetime" structure to a time_t
+ It's originally in "100ns units since jan 1st 1601"
+
+ An 8 byte value of 0xffffffffffffffff will be returned as (time_t)0.
+
+ It appears to be kludge-GMT (at least for file listings). This means
+ its the GMT you get by taking a localtime and adding the
+ serverzone. This is NOT the same as GMT in some cases. This routine
+ converts this to real GMT.
+****************************************************************************/
+time_t nt_time_to_unix(NTTIME *nt)
+{
+	double d;
+	time_t ret;
+	/* The next two lines are a fix needed for the 
+		broken SCO compiler. JRA. */
+	time_t l_time_min = TIME_T_MIN;
+	time_t l_time_max = TIME_T_MAX;
+
+	if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
+		return(0);
+
+	d = ((double)nt->high)*4.0*(double)(1<<30);
+	d += (nt->low&0xFFF00000);
+	d *= 1.0e-7;
+ 
+	/* now adjust by 369 years to make the secs since 1970 */
+	d -= TIME_FIXUP_CONSTANT;
+
+	if (d <= l_time_min)
+		return (l_time_min);
+
+	if (d >= l_time_max)
+		return (l_time_max);
+
+	ret = (time_t)(d+0.5);
+
+	/* this takes us from kludge-GMT to real GMT */
+	/*XXX
+	ret -= get_serverzone();
+	ret += LocTimeDiff(ret);
+	*/
+	return(ret);
+}
+
+
+/* End of stuff from lib/time.c */
+
+/* From parse_prs.c */
+
+/*******************************************************************
+ Attempt, if needed, to grow a data buffer.
+ Also depends on the data stream mode (io).
+ ********************************************************************/
+bool prs_grow(prs_struct *ps, uint32 extra_space)
+{
+	uint32 new_size;
+	char *new_data;
+
+	ps->grow_size = MAX(ps->grow_size, ps->data_offset + extra_space);
+
+	if(ps->data_offset + extra_space <= ps->buffer_size)
+		return true;
+
+	/*
+	 * We cannot grow the buffer if we're not reading
+	 * into the prs_struct, or if we don't own the memory.
+	 */
+
+	if(ps->io || !ps->is_dynamic) {
+		return false;
+	}
+	
+	/*
+	 * Decide how much extra space we really need.
+	 */
+
+	extra_space -= (ps->buffer_size - ps->data_offset);
+	if(ps->buffer_size == 0) {
+		/*
+		 * Ensure we have at least a PDU's length, or extra_space, whichever
+		 * is greater.
+		 */
+
+		new_size = MAX(MAX_PDU_FRAG_LEN,extra_space);
+
+		if((new_data = zalloc(new_size)) == NULL) {
+			return false;
+		}
+		memset(new_data, '\0', (size_t)new_size );
+	} else {
+		/*
+		 * If the current buffer size is bigger than the space needed, just 
+		 * double it, else add extra_space.
+		 */
+		new_size = MAX(ps->buffer_size*2, ps->buffer_size + extra_space);		
+
+		if ((new_data = (char*)realloc(ps->data_p, new_size)) == NULL) {
+			return false;
+		}
+
+		memset(&new_data[ps->buffer_size], '\0', (size_t)(new_size - ps->buffer_size));
+	}
+	ps->buffer_size = new_size;
+	ps->data_p = new_data;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Align a the data_len to a multiple of align bytes - filling with
+ zeros.
+ ********************************************************************/
+bool prs_align(prs_struct *ps)
+{
+	uint32 mod = ps->data_offset & (ps->align-1);
+
+	if (ps->align != 0 && mod != 0) {
+		uint32 extra_space = (ps->align - mod);
+		if(!prs_grow(ps, extra_space))
+			return false;
+		memset(&ps->data_p[ps->data_offset], '\0', (size_t)extra_space);
+		ps->data_offset += extra_space;
+	}
+
+	return true;
+}
+
+
+/**
+ * Initialise an expandable parse structure.
+ *
+ * @param size Initial buffer size.  If >0, a new buffer will be
+ * created with malloc().
+ *
+ * @return false if allocation fails, otherwise true.
+ **/
+
+bool prs_init(prs_struct *ps, uint32 size, void *ctx, bool io)
+{
+  memset(ps, 0, sizeof(prs_struct));
+  ps->io = io;
+  ps->bigendian_data = RPC_LITTLE_ENDIAN;
+  ps->align = RPC_PARSE_ALIGN;
+  ps->is_dynamic = false;
+  ps->data_offset = 0;
+  ps->buffer_size = 0;
+  ps->data_p = NULL;
+  ps->mem_ctx = ctx;
+  
+  if (size != 0) {
+    ps->buffer_size = size;
+    if((ps->data_p = (char *)zalloc((size_t)size)) == NULL) {
+      return false;
+    }
+    memset(ps->data_p, '\0', (size_t)size);
+    ps->is_dynamic = true; /* We own this memory. */
+  }
+  
+  return true;
+}
+
+
+char *prs_mem_get(prs_struct *ps, uint32 extra_size)
+{
+	if(ps->io) {
+		/*
+		 * If reading, ensure that we can read the requested size item.
+		 */
+		if (ps->data_offset + extra_size > ps->buffer_size) {
+			return NULL;
+		}
+	} else {
+		/*
+		 * Writing - grow the buffer if needed.
+		 */
+		if(!prs_grow(ps, extra_size))
+			return NULL;
+	}
+	return &ps->data_p[ps->data_offset];
+}
+
+
+/*******************************************************************
+ Stream a uint32.
+ ********************************************************************/
+bool prs_uint32(const char *name, prs_struct *ps, int depth, uint32 *data32)
+{
+	char *q = prs_mem_get(ps, sizeof(uint32));
+	if (q == NULL)
+		return false;
+
+	if (ps->io) {
+		if (ps->bigendian_data)
+			*data32 = RIVAL(q,0);
+		else
+			*data32 = IVAL(q,0);
+	} else {
+		if (ps->bigendian_data)
+			RSIVAL(q,0,*data32);
+		else
+			SIVAL(q,0,*data32);
+	}
+
+	ps->data_offset += sizeof(uint32);
+
+	return true;
+}
+
+
+/******************************************************************
+ Stream an array of uint32s. Length is number of uint32s.
+ ********************************************************************/
+bool prs_uint32s(bool charmode, const char *name, prs_struct *ps, 
+		 int depth, uint32 *data32s, int len)
+{
+	int i;
+	char *q = prs_mem_get(ps, len * sizeof(uint32));
+	if (q == NULL)
+		return false;
+
+	if (ps->io) {
+		if (ps->bigendian_data) {
+			for (i = 0; i < len; i++)
+				data32s[i] = RIVAL(q, 4*i);
+		} else {
+			for (i = 0; i < len; i++)
+				data32s[i] = IVAL(q, 4*i);
+		}
+	} else {
+		if (ps->bigendian_data) {
+			for (i = 0; i < len; i++)
+				RSIVAL(q, 4*i, data32s[i]);
+		} else {
+			for (i = 0; i < len; i++)
+				SIVAL(q, 4*i, data32s[i]);
+		}
+	}
+
+	ps->data_offset += (len * sizeof(uint32));
+
+	return true;
+}
+
+
+/*******************************************************************
+ Stream a uint16.
+ ********************************************************************/
+bool prs_uint16(const char *name, prs_struct *ps, int depth, uint16 *data16)
+{
+	char *q = prs_mem_get(ps, sizeof(uint16));
+	if (q == NULL)
+		return false;
+
+	if (ps->io) {
+		if (ps->bigendian_data)
+			*data16 = RSVAL(q,0);
+		else
+			*data16 = SVAL(q,0);
+	} else {
+		if (ps->bigendian_data)
+			RSSVAL(q,0,*data16);
+		else
+			SSVAL(q,0,*data16);
+	}
+
+
+	ps->data_offset += sizeof(uint16);
+
+	return true;
+}
+
+
+/*******************************************************************
+ prs_uint16 wrapper. Call this and it sets up a pointer to where the
+ uint16 should be stored, or gets the size if reading.
+ ********************************************************************/
+bool prs_uint16_pre(const char *name, prs_struct *ps, int depth, 
+		    uint16 *data16, uint32 *offset)
+{
+	*offset = ps->data_offset;
+	if (ps->io) {
+		/* reading. */
+		return prs_uint16(name, ps, depth, data16);
+	} else {
+		char *q = prs_mem_get(ps, sizeof(uint16));
+		if(q ==NULL)
+			return false;
+		ps->data_offset += sizeof(uint16);
+	}
+	return true;
+}
+
+
+/*******************************************************************
+ prs_uint16 wrapper.  call this and it retrospectively stores the size.
+ does nothing on reading, as that is already handled by ...._pre()
+ ********************************************************************/
+bool prs_uint16_post(const char *name, prs_struct *ps, int depth, 
+		     uint16 *data16, uint32 ptr_uint16, uint32 start_offset)
+{
+	if (!ps->io) {
+		/* 
+		 * Writing - temporarily move the offset pointer.
+		 */
+		uint16 data_size = ps->data_offset - start_offset;
+		uint32 old_offset = ps->data_offset;
+
+		ps->data_offset = ptr_uint16;
+		if(!prs_uint16(name, ps, depth, &data_size)) {
+			ps->data_offset = old_offset;
+			return false;
+		}
+		ps->data_offset = old_offset;
+	} else {
+		ps->data_offset = start_offset + (uint32)(*data16);
+	}
+	return true;
+}
+
+
+/*******************************************************************
+ Stream a uint8.
+ ********************************************************************/
+bool prs_uint8(const char *name, prs_struct *ps, int depth, uint8 *data8)
+{
+	char *q = prs_mem_get(ps, 1);
+	if (q == NULL)
+		return false;
+
+    if (ps->io)
+		*data8 = CVAL(q,0);
+	else
+		SCVAL(q,0,*data8);
+
+	ps->data_offset += 1;
+
+	return true;
+}
+
+
+/******************************************************************
+ Stream an array of uint8s. Length is number of uint8s.
+ ********************************************************************/
+bool prs_uint8s(bool charmode, const char *name, prs_struct *ps, int depth, 
+		uint8* data8s, int len)
+{
+	int i;
+	char *q = prs_mem_get(ps, len);
+	if (q == NULL)
+		return false;
+
+	if (ps->io) {
+		for (i = 0; i < len; i++)
+			data8s[i] = CVAL(q,i);
+	} else {
+		for (i = 0; i < len; i++)
+			SCVAL(q, i, data8s[i]);
+	}
+
+	ps->data_offset += len;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Set the current offset (external interface).
+ ********************************************************************/
+bool prs_set_offset(prs_struct *ps, uint32 offset)
+{
+	if(offset <= ps->data_offset) {
+		ps->data_offset = offset;
+		return true;
+	}
+
+	if(!prs_grow(ps, offset - ps->data_offset))
+		return false;
+
+	ps->data_offset = offset;
+	return true;
+}
+
+/* End of stuff from parse_prs.c */
+
+/* From rpc_parse/parse_misc.c */
+
+/*******************************************************************
+ Reads or writes a struct uuid
+********************************************************************/
+bool smb_io_uuid(const char *desc, struct uuid *uuid, 
+		 prs_struct *ps, int depth)
+{
+	if (uuid == NULL)
+		return false;
+	depth++;
+
+	if(!prs_uint32 ("data   ", ps, depth, &uuid->time_low))
+		return false;
+	if(!prs_uint16 ("data   ", ps, depth, &uuid->time_mid))
+		return false;
+	if(!prs_uint16 ("data   ", ps, depth, &uuid->time_hi_and_version))
+		return false;
+
+	if(!prs_uint8s (false, "data   ", ps, depth, uuid->clock_seq, sizeof(uuid->clock_seq)))
+		return false;
+	if(!prs_uint8s (false, "data   ", ps, depth, uuid->node, sizeof(uuid->node)))
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Reads or writes an NTTIME structure.
+********************************************************************/
+bool smb_io_time(const char *desc, NTTIME *nttime, prs_struct *ps, int depth)
+{
+	if (nttime == NULL)
+		return false;
+	depth++;
+
+	if(!prs_align(ps))
+		return false;
+	
+	if(!prs_uint32("low ", ps, depth, &nttime->low)) /* low part */
+		return false;
+	if(!prs_uint32("high", ps, depth, &nttime->high)) /* high part */
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a DOM_SID structure.
+********************************************************************/
+bool smb_io_dom_sid(const char *desc, DOM_SID *sid, prs_struct *ps, int depth)
+{
+	int i;
+
+	if (sid == NULL)
+		return false;
+	depth++;
+
+	if(!prs_uint8 ("sid_rev_num", ps, depth, &sid->sid_rev_num))
+		return false;
+
+	if(!prs_uint8 ("num_auths  ", ps, depth, &sid->num_auths))
+		return false;
+
+	for (i = 0; i < 6; i++)
+	{
+		fstring tmp;
+		snprintf(tmp, sizeof(tmp) - 1, "id_auth[%d] ", i);
+		if(!prs_uint8 (tmp, ps, depth, &sid->id_auth[i]))
+			return false;
+	}
+
+	/* oops! XXXX should really issue a warning here... */
+	if (sid->num_auths > MAXSUBAUTHS)
+		sid->num_auths = MAXSUBAUTHS;
+
+	if(!prs_uint32s(false, "sub_auths ", ps, depth, sid->sub_auths, sid->num_auths))
+		return false;
+
+	return true;
+}
+
+/* End of stuff from rpc_parse/parse_misc.c */
+
+/* From lib/util_sid.c */
+
+/*****************************************************************
+ Calculates size of a sid.
+*****************************************************************/  
+size_t sid_size(const DOM_SID *sid)
+{
+	if (sid == NULL)
+		return 0;
+
+	return sid->num_auths * sizeof(uint32) + 8;
+}
+
+
+/*****************************************************************
+ Compare the auth portion of two sids.
+*****************************************************************/  
+int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+	int i;
+
+	if (sid1 == sid2)
+		return 0;
+	if (!sid1)
+		return -1;
+	if (!sid2)
+		return 1;
+
+	if (sid1->sid_rev_num != sid2->sid_rev_num)
+		return sid1->sid_rev_num - sid2->sid_rev_num;
+
+	for (i = 0; i < 6; i++)
+		if (sid1->id_auth[i] != sid2->id_auth[i])
+			return sid1->id_auth[i] - sid2->id_auth[i];
+
+	return 0;
+}
+
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/  
+int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+	int i;
+
+	if (sid1 == sid2)
+		return 0;
+	if (!sid1)
+		return -1;
+	if (!sid2)
+		return 1;
+
+	/* Compare most likely different rids, first: i.e start at end */
+	if (sid1->num_auths != sid2->num_auths)
+		return sid1->num_auths - sid2->num_auths;
+
+	for (i = sid1->num_auths-1; i >= 0; --i)
+		if (sid1->sub_auths[i] != sid2->sub_auths[i])
+			return sid1->sub_auths[i] - sid2->sub_auths[i];
+
+	return sid_compare_auth(sid1, sid2);
+}
+
+
+/*****************************************************************
+ Compare two sids.
+*****************************************************************/  
+bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2)
+{
+	return sid_compare(sid1, sid2) == 0;
+}
+
+/* End of stuff from lib/util_sid.c */
+
+/* From lib/secace.c */
+
+/*******************************************************************
+ Check if ACE has OBJECT type.
+********************************************************************/
+
+bool sec_ace_object(uint8 type)
+{
+	if (type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT ||
+            type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT ||
+            type == SEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT ||
+            type == SEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) {
+		return true;
+	}
+	return false;
+}
+
+/* End of stuff from lib/secace.c */
+
+/* From rpc_parse/parse_sec.c */
+
+/*******************************************************************
+ Reads or writes a SEC_ACCESS structure.
+********************************************************************/
+bool sec_io_access(const char *desc, SEC_ACCESS *t, prs_struct *ps, int depth)
+{
+	if (t == NULL)
+		return false;
+
+	depth++;
+	
+	if(!prs_uint32("mask", ps, depth, &t->mask))
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a SEC_ACE structure.
+********************************************************************/
+bool sec_io_ace(const char *desc, SEC_ACE *psa, prs_struct *ps, int depth)
+{
+	uint32 old_offset;
+	uint32 offset_ace_size;
+
+	if (psa == NULL)
+		return false;
+
+	depth++;
+	
+	old_offset = ps->data_offset;
+
+	if(!prs_uint8("type ", ps, depth, &psa->type))
+		return false;
+
+	if(!prs_uint8("flags", ps, depth, &psa->flags))
+		return false;
+
+	if(!prs_uint16_pre("size ", ps, depth, &psa->size, &offset_ace_size))
+		return false;
+
+	if(!sec_io_access("info ", &psa->info, ps, depth))
+		return false;
+
+	/* check whether object access is present */
+	if (!sec_ace_object(psa->type)) {
+		if (!smb_io_dom_sid("trustee  ", &psa->trustee , ps, depth))
+			return false;
+	} else {
+		if (!prs_uint32("obj_flags", ps, depth, &psa->obj_flags))
+			return false;
+
+		if (psa->obj_flags & SEC_ACE_OBJECT_PRESENT)
+			if (!smb_io_uuid("obj_guid", &psa->obj_guid, ps,depth))
+				return false;
+
+		if (psa->obj_flags & SEC_ACE_OBJECT_INHERITED_PRESENT)
+			if (!smb_io_uuid("inh_guid", &psa->inh_guid, ps,depth))
+				return false;
+
+		if(!smb_io_dom_sid("trustee  ", &psa->trustee , ps, depth))
+			return false;
+	}
+
+	/* Theorectically an ACE can have a size greater than the
+	   sum of its components. When marshalling, pad with extra null bytes up to the
+	   correct size. */
+
+	if (!ps->io && (psa->size > ps->data_offset - old_offset)) {
+		uint32 extra_len = psa->size - (ps->data_offset - old_offset);
+		uint32 i;
+		uint8 c = 0;
+
+		for (i = 0; i < extra_len; i++) {
+			if (!prs_uint8("ace extra space", ps, depth, &c))
+				return false;
+		}
+	}
+
+	if(!prs_uint16_post("size ", ps, depth, &psa->size, offset_ace_size, old_offset))
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a SEC_ACL structure.  
+
+ First of the xx_io_xx functions that allocates its data structures
+ for you as it reads them.
+********************************************************************/
+bool sec_io_acl(const char *desc, SEC_ACL **ppsa, prs_struct *ps, int depth)
+{
+	unsigned int i;
+	uint32 old_offset;
+	uint32 offset_acl_size;
+	SEC_ACL *psa;
+
+	/*
+	 * Note that the size is always a multiple of 4 bytes due to the
+	 * nature of the data structure.  Therefore the prs_align() calls
+	 * have been removed as they through us off when doing two-layer
+	 * marshalling such as in the printing code (RPC_BUFFER).  --jerry
+	 */
+
+	if (ppsa == NULL)
+		return false;
+
+	psa = *ppsa;
+
+	if(ps->io && psa == NULL) {
+		/*
+		 * This is a read and we must allocate the stuct to read into.
+		 */
+	  if((psa = (SEC_ACL*)zalloc(sizeof(SEC_ACL))) == NULL)
+			return false;
+		*ppsa = psa;
+	}
+
+	depth++;
+	
+	old_offset = ps->data_offset;
+
+	if(!prs_uint16("revision", ps, depth, &psa->revision))
+		return false;
+
+	if(!prs_uint16_pre("size     ", ps, depth, &psa->size, &offset_acl_size))
+		return false;
+
+	if(!prs_uint32("num_aces ", ps, depth, &psa->num_aces))
+		return false;
+
+	if (ps->io) {
+		/*
+		 * Even if the num_aces is zero, allocate memory as there's a difference
+		 * between a non-present DACL (allow all access) and a DACL with no ACE's
+		 * (allow no access).
+		 */
+	  if((psa->ace = (SEC_ACE*)zcalloc(sizeof(SEC_ACE), psa->num_aces+1)) == NULL)
+			return false;
+	}
+
+	for (i = 0; i < psa->num_aces; i++) {
+		fstring tmp;
+		snprintf(tmp, sizeof(tmp)-1, "ace_list[%02d]: ", i);
+		if(!sec_io_ace(tmp, &psa->ace[i], ps, depth))
+			return false;
+	}
+
+	/* Theorectically an ACL can have a size greater than the
+	   sum of its components. When marshalling, pad with extra null bytes up to the
+	   correct size. */
+
+	if (!ps->io && (psa->size > ps->data_offset - old_offset)) {
+		uint32 extra_len = psa->size - (ps->data_offset - old_offset);
+		uint8 c = 0;
+
+		for (i = 0; i < extra_len; i++) {
+			if (!prs_uint8("acl extra space", ps, depth, &c))
+				return false;
+		}
+	}
+
+	if(!prs_uint16_post("size     ", ps, depth, &psa->size, offset_acl_size, old_offset))
+		return false;
+
+	return true;
+}
+
+
+/*******************************************************************
+ Reads or writes a SEC_DESC structure.
+ If reading and the *ppsd = NULL, allocates the structure.
+********************************************************************/
+bool sec_io_desc(const char *desc, SEC_DESC **ppsd, prs_struct *ps, int depth)
+{
+	uint32 old_offset;
+	uint32 max_offset = 0; /* after we're done, move offset to end */
+	uint32 tmp_offset = 0;
+
+	SEC_DESC *psd;
+
+	if (ppsd == NULL)
+		return false;
+
+	psd = *ppsd;
+
+	if (psd == NULL) {
+		if(ps->io) {
+		  if((psd = (SEC_DESC*)zalloc(sizeof(SEC_DESC))) == NULL)
+				return false;
+			*ppsd = psd;
+		} else {
+			/* Marshalling - just ignore. */
+			return true;
+		}
+	}
+
+	depth++;
+
+	/* start of security descriptor stored for back-calc offset purposes */
+	old_offset = ps->data_offset;
+
+	if(!prs_uint16("revision ", ps, depth, &psd->revision))
+		return false;
+
+	if(!prs_uint16("type     ", ps, depth, &psd->type))
+		return false;
+
+	if (!ps->io) {
+		uint32 offset = SEC_DESC_HEADER_SIZE;
+
+		/*
+		 * Work out the offsets here, as we write it out.
+		 */
+
+		if (psd->sacl != NULL) {
+			psd->off_sacl = offset;
+			offset += psd->sacl->size;
+		} else {
+			psd->off_sacl = 0;
+		}
+
+		if (psd->dacl != NULL) {
+			psd->off_dacl = offset;
+			offset += psd->dacl->size;
+		} else {
+			psd->off_dacl = 0;
+		}
+
+		if (psd->owner_sid != NULL) {
+			psd->off_owner_sid = offset;
+			offset += sid_size(psd->owner_sid);
+		} else {
+			psd->off_owner_sid = 0;
+		}
+
+		if (psd->grp_sid != NULL) {
+			psd->off_grp_sid = offset;
+			offset += sid_size(psd->grp_sid);
+		} else {
+			psd->off_grp_sid = 0;
+		}
+	}
+
+	if(!prs_uint32("off_owner_sid", ps, depth, &psd->off_owner_sid))
+		return false;
+
+	if(!prs_uint32("off_grp_sid  ", ps, depth, &psd->off_grp_sid))
+		return false;
+
+	if(!prs_uint32("off_sacl     ", ps, depth, &psd->off_sacl))
+		return false;
+
+	if(!prs_uint32("off_dacl     ", ps, depth, &psd->off_dacl))
+		return false;
+
+	max_offset = MAX(max_offset, ps->data_offset);
+
+	if (psd->off_owner_sid != 0) {
+
+		tmp_offset = ps->data_offset;
+		if(!prs_set_offset(ps, old_offset + psd->off_owner_sid))
+			return false;
+
+		if (ps->io) {
+			/* reading */
+		  if((psd->owner_sid = (DOM_SID*)zalloc(sizeof(DOM_SID))) == NULL)
+				return false;
+		}
+
+		if(!smb_io_dom_sid("owner_sid ", psd->owner_sid , ps, depth))
+			return false;
+
+		max_offset = MAX(max_offset, ps->data_offset);
+
+		if (!prs_set_offset(ps,tmp_offset))
+			return false;
+	}
+
+	if (psd->off_grp_sid != 0) {
+
+		tmp_offset = ps->data_offset;
+		if(!prs_set_offset(ps, old_offset + psd->off_grp_sid))
+			return false;
+
+		if (ps->io) {
+			/* reading */
+			if((psd->grp_sid = (DOM_SID*)zalloc(sizeof(DOM_SID))) == NULL)
+				return false;
+		}
+
+		if(!smb_io_dom_sid("grp_sid", psd->grp_sid, ps, depth))
+			return false;
+			
+		max_offset = MAX(max_offset, ps->data_offset);
+
+		if (!prs_set_offset(ps,tmp_offset))
+			return false;
+	}
+
+	if ((psd->type & SEC_DESC_SACL_PRESENT) && psd->off_sacl) {
+		tmp_offset = ps->data_offset;
+		if(!prs_set_offset(ps, old_offset + psd->off_sacl))
+			return false;
+		if(!sec_io_acl("sacl", &psd->sacl, ps, depth))
+			return false;
+		max_offset = MAX(max_offset, ps->data_offset);
+		if (!prs_set_offset(ps,tmp_offset))
+			return false;
+	}
+
+	if ((psd->type & SEC_DESC_DACL_PRESENT) && psd->off_dacl != 0) {
+		tmp_offset = ps->data_offset;
+		if(!prs_set_offset(ps, old_offset + psd->off_dacl))
+			return false;
+		if(!sec_io_acl("dacl", &psd->dacl, ps, depth))
+			return false;
+		max_offset = MAX(max_offset, ps->data_offset);
+		if (!prs_set_offset(ps,tmp_offset))
+			return false;
+	}
+
+	if(!prs_set_offset(ps, max_offset))
+		return false;
+
+	return true;
+}
+
+/* End of stuff from rpc_parse/parse_sec.c */
+
+/* From lib/secace.c */
+
+/*******************************************************************
+ Compares two SEC_ACE structures
+********************************************************************/
+bool sec_ace_equal(SEC_ACE *s1, SEC_ACE *s2)
+{
+	/* Trivial case */
+
+	if (!s1 && !s2) return true;
+
+	/* Check top level stuff */
+
+	if (s1->type != s2->type || s1->flags != s2->flags ||
+	    s1->info.mask != s2->info.mask) {
+		return false;
+	}
+
+	/* Check SID */
+
+	if (!sid_equal(&s1->trustee, &s2->trustee)) {
+		return false;
+	}
+
+	return true;
+}
+
+/* End of stuff from lib/secace.c */
+
+/* From lib/secacl.c */
+
+/*******************************************************************
+ Compares two SEC_ACL structures
+********************************************************************/
+
+bool sec_acl_equal(SEC_ACL *s1, SEC_ACL *s2)
+{
+	unsigned int i, j;
+
+	/* Trivial cases */
+
+	if (!s1 && !s2) return true;
+	if (!s1 || !s2) return false;
+
+	/* Check top level stuff */
+
+	if (s1->revision != s2->revision) {
+		return false;
+	}
+
+	if (s1->num_aces != s2->num_aces) {
+		return false;
+	}
+
+	/* The ACEs could be in any order so check each ACE in s1 against 
+	   each ACE in s2. */
+
+	for (i = 0; i < s1->num_aces; i++) {
+		bool found = false;
+
+		for (j = 0; j < s2->num_aces; j++) {
+			if (sec_ace_equal(&s1->ace[i], &s2->ace[j])) {
+				found = true;
+				break;
+			}
+		}
+
+		if (!found) return false;
+	}
+
+	return true;
+}
+
+/* End of stuff from lib/secacl.c */
+
+/* From lib/secdesc.c */
+
+/*******************************************************************
+ Compares two SEC_DESC structures
+********************************************************************/
+bool sec_desc_equal(SEC_DESC *s1, SEC_DESC *s2)
+{
+	/* Trivial case */
+
+	if (!s1 && !s2) {
+		goto done;
+	}
+
+	/* Check top level stuff */
+
+	if (s1->revision != s2->revision) {
+		return false;
+	}
+
+	if (s1->type!= s2->type) {
+		return false;
+	}
+
+	/* Check owner and group */
+
+	if (!sid_equal(s1->owner_sid, s2->owner_sid)) {
+		return false;
+	}
+
+	if (!sid_equal(s1->grp_sid, s2->grp_sid)) {
+		return false;
+	}
+
+	/* Check ACLs present in one but not the other */
+
+	if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) ||
+	    (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) 
+	{
+		return false;
+	}
+
+	/* Sigh - we have to do it the hard way by iterating over all
+	   the ACEs in the ACLs */
+
+	if (!sec_acl_equal(s1->dacl, s2->dacl) ||
+	    !sec_acl_equal(s1->sacl, s2->sacl)) 
+	{
+		return false;
+	}
+
+ done:
+	return true;
+}
+
+/* End of stuff from lib/secdesc.c */
Index: releases/0.2.1/lib/void_stack.c
===================================================================
--- releases/0.2.1/lib/void_stack.c	(revision 57)
+++ releases/0.2.1/lib/void_stack.c	(revision 57)
@@ -0,0 +1,181 @@
+/*
+ * This is a really simple implementation of a stack which stores chunks
+ * of memory of any type.  It still needs work to eliminate memory
+ * leaks. 
+ *
+ * Copyright (C) 2005 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$
+ */
+
+#include "../include/void_stack.h"
+
+void_stack* void_stack_new(unsigned short max_size)
+{
+  void_stack* ret_val = (void_stack*)malloc(sizeof(void_stack));
+
+  if (ret_val != NULL)
+  {
+    memset(ret_val, 0, sizeof(*ret_val));
+    ret_val->elements = (void**)malloc(max_size*sizeof(void*));
+    if (ret_val->elements == NULL)
+    {
+      free(ret_val);
+      ret_val = NULL;
+    }
+    else
+    {
+      memset(ret_val->elements, 0, max_size*sizeof(void*));
+    
+      ret_val->max_size = max_size;
+      ret_val->top = 0;
+    }
+  }
+
+  return ret_val;
+}
+
+
+void_stack* void_stack_copy(const void_stack* v)
+{
+  unsigned int i;
+  void_stack* ret_val;
+  if(v == NULL)
+    return NULL;
+
+  ret_val = void_stack_new(v->max_size);
+  if(ret_val == NULL)
+    return NULL;
+
+  for(i = 0; i < v->top; i++)
+    ret_val->elements[i] = v->elements[i];
+  ret_val->top = v->top;
+
+  return ret_val;
+}
+
+
+void_stack* void_stack_copy_reverse(const void_stack* v)
+{
+  unsigned int i;
+  void_stack* ret_val;
+  if(v == NULL)
+    return NULL;
+
+  ret_val = void_stack_new(v->max_size);
+  if(ret_val == NULL)
+    return NULL;
+
+  for(i = 0; i < v->top; i++)
+    ret_val->elements[i] = v->elements[v->top-i-1];
+  ret_val->top = v->top;
+
+  return ret_val;
+}
+
+
+void void_stack_destroy(void_stack* stack)
+{
+  free(stack->elements);
+  free(stack);
+}
+
+
+void void_stack_destroy_deep(void_stack* stack)
+{
+  unsigned short i;
+  for(i=0; i < stack->top; i++)
+    free(stack->elements[i]);
+  free(stack->elements);
+  free(stack);
+}
+
+
+unsigned short void_stack_size(void_stack* stack)
+{
+  return stack->top;
+}
+
+
+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;
+}
+
+
+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;
+  }
+  else
+    ret_val = NULL;
+
+  return ret_val;
+}
+
+
+const void* void_stack_cur(void_stack* stack)
+{
+  void* ret_val = NULL;
+
+  if(stack->top > 0)
+    ret_val = stack->elements[stack->top-1];
+  else
+    ret_val = NULL;
+
+  return ret_val;
+}
+
+
+void_stack_iterator* void_stack_iterator_new(void_stack* stack)
+{
+  void_stack_iterator* ret_val = NULL;
+  
+  ret_val = (void_stack_iterator*)malloc(sizeof(void_stack_iterator));
+  if (ret_val != NULL)
+  {
+    ret_val->stack = stack;
+    ret_val->cur = 0;
+  }
+
+  return ret_val;
+}
+
+
+void void_stack_iterator_destroy(void_stack_iterator* iter)
+{
+  free(iter);
+}
+
+
+const void* void_stack_iterator_next(void_stack_iterator* iter)
+{
+  if(iter->cur < iter->stack->top)
+    return iter->stack->elements[iter->cur++];
+  else
+    return NULL;
+}
Index: releases/0.2.1/src/Makefile
===================================================================
--- releases/0.2.1/src/Makefile	(revision 57)
+++ releases/0.2.1/src/Makefile	(revision 57)
@@ -0,0 +1,21 @@
+# $Id$
+
+################################################################################
+
+REGLOOKUP=$(BUILD_BIN)/reglookup
+LIBS=$(wildcard ../lib/*.o)
+FILES=$(REGLOOKUP)
+
+all: $(FILES)
+
+$(REGLOOKUP): reglookup.o $(LIBS)
+	$(CC) $(CFLAGS) $(OPTS) -o $@ reglookup.o $(LIBS)
+
+reglookup.o: reglookup.c
+	$(CC) $(CFLAGS) $(OPTS) -c -o $@ reglookup.c
+
+install:
+	cp -r $(BUILD_BIN)/* $(BIN_PREFIX)
+
+clean:
+	rm -f *.o
Index: releases/0.2.1/src/reglookup.c
===================================================================
--- releases/0.2.1/src/reglookup.c	(revision 57)
+++ releases/0.2.1/src/reglookup.c	(revision 57)
@@ -0,0 +1,788 @@
+/*
+ * A utility to read a Windows NT/2K/XP/2K3 registry file, using 
+ * Gerald Carter''s regfio interface.
+ *
+ * Copyright (C) 2005 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 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$
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include "../include/regfio.h"
+#include "../include/void_stack.h"
+
+/* Globals, influenced by command line parameters */
+bool print_verbose = false;
+bool print_security = false;
+bool print_header = true;
+bool path_filter_enabled = false;
+bool type_filter_enabled = false;
+char* path_filter = NULL;
+int type_filter;
+char* registry_file = NULL;
+
+/* Other globals */
+const char* special_chars = ",\"\\";
+
+void bailOut(int code, char* message)
+{
+  fprintf(stderr, message);
+  exit(code);
+}
+
+
+/* Returns a newly malloc()ed string which contains original buffer,
+ * except for non-printable or special characters are quoted in hex
+ * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
+ * character.  A null terminator is added, as only ascii, not binary, 
+ * is returned.
+ */
+static char* quote_buffer(const unsigned char* str, 
+			  unsigned int len, const char* special)
+{
+  unsigned int i;
+  unsigned int num_written=0;
+  unsigned int out_len = sizeof(char)*len+1;
+  char* ret_val = malloc(out_len);
+
+  if(ret_val == NULL)
+    return NULL;
+
+  for(i=0; i<len; i++)
+  {
+    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
+    {
+      out_len += 3;
+      /* XXX: may not be the most efficient way of getting enough memory. */
+      ret_val = realloc(ret_val, out_len);
+      if(ret_val == NULL)
+	break;
+      num_written += snprintf(ret_val+num_written, (out_len)-num_written,
+			      "\\x%.2X", str[i]);
+    }
+    else
+      ret_val[num_written++] = str[i];
+  }
+  ret_val[num_written] = '\0';
+
+  return ret_val;
+}
+
+
+/* Returns a newly malloc()ed string which contains original string, 
+ * except for non-printable or special characters are quoted in hex
+ * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
+ * character.
+ */
+static char* quote_string(const char* str, const char* special)
+{
+  unsigned int len;
+
+  if(str == NULL)
+    return NULL;
+
+  len = strlen(str);
+  return quote_buffer((const unsigned char*)str, len, special);
+}
+
+
+/*
+ * Convert from UniCode to Ascii ... Does not take into account other lang
+ * Restrict by ascii_max if > 0
+ */
+static int uni_to_ascii(unsigned char *uni, unsigned char *ascii, 
+			int ascii_max, int uni_max)
+{
+  int i = 0; 
+
+  while (i < ascii_max && (uni[i*2] || uni[i*2+1]))
+  {
+    if (uni_max > 0 && (i*2) >= uni_max) break;
+    ascii[i] = uni[i*2];
+    i++;
+  }
+  ascii[i] = '\0';
+
+  return i;
+}
+
+
+/*
+ * Convert a data value to a string for display
+ */
+static unsigned char* data_to_ascii(unsigned char *datap, int len, int type)
+{
+  unsigned char *asciip;
+  unsigned int i;
+  unsigned short num_nulls;
+  unsigned char* ascii;
+  unsigned char* cur_str;
+  unsigned char* cur_ascii;
+  char* cur_quoted;
+  unsigned int cur_str_len;
+  unsigned int ascii_max, cur_str_max;
+  unsigned int str_rem, cur_str_rem, alen;
+
+  switch (type) 
+  {
+  case REG_SZ:
+    ascii_max = sizeof(char)*len;
+    ascii = malloc(ascii_max+4);
+    if(ascii == NULL)
+      return NULL;
+    
+    /* XXX: This has to be fixed. It has to be UNICODE */
+    uni_to_ascii(datap, ascii, len, ascii_max);
+    cur_quoted = quote_string((char*)ascii, special_chars);
+    free(ascii);
+    return (unsigned char*)cur_quoted;
+    break;
+
+  case REG_EXPAND_SZ:
+    ascii_max = sizeof(char)*len;
+    ascii = malloc(ascii_max+2);
+    if(ascii == NULL)
+      return NULL;
+
+    uni_to_ascii(datap, ascii, len, ascii_max);
+    cur_quoted = quote_string((char*)ascii, special_chars);
+    free(ascii);
+    return (unsigned char*)cur_quoted;
+    break;
+
+  case REG_DWORD:
+    ascii_max = sizeof(char)*10;
+    ascii = malloc(ascii_max+1);
+    if(ascii == NULL)
+      return NULL;
+
+    if (*(int *)datap == 0)
+      snprintf((char*)ascii, ascii_max, "0");
+    else
+      snprintf((char*)ascii, ascii_max, "0x%x", *(int *)datap);
+    return ascii;
+    break;
+
+  /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
+   *      redone with fewer malloc calls and better string concatenation. 
+   */
+  case REG_MULTI_SZ:
+    ascii_max = sizeof(char)*len*4;
+    cur_str_max = sizeof(char)*len+1;
+    cur_str = malloc(cur_str_max);
+    cur_ascii = malloc(cur_str_max);
+    ascii = malloc(ascii_max+4);
+    if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
+      return NULL;
+
+    /* Reads until it reaches 4 consecutive NULLs, 
+     * which is two nulls in unicode, or until it reaches len, or until we
+     * run out of buffer.  The latter should never happen, but we shouldn't
+     * trust our file to have the right lengths/delimiters.
+     */
+    asciip = ascii;
+    num_nulls = 0;
+    str_rem = ascii_max;
+    cur_str_rem = cur_str_max;
+    cur_str_len = 0;
+
+    for(i=0; (i < len) && str_rem > 0; i++)
+    {
+      *(cur_str+cur_str_len) = *(datap+i);
+      if(*(cur_str+cur_str_len) == 0)
+	num_nulls++;
+      else
+	num_nulls = 0;
+      cur_str_len++;
+
+      if(num_nulls == 2)
+      {
+	uni_to_ascii(cur_str, cur_ascii, cur_str_max, 0);
+	cur_quoted = quote_string((char*)cur_ascii, ",|\"\\");
+	alen = snprintf((char*)asciip, str_rem, "%s", cur_quoted);
+	asciip += alen;
+	str_rem -= alen;
+	free(cur_quoted);
+
+	if(*(datap+i+1) == 0 && *(datap+i+2) == 0)
+	  break;
+	else
+	{
+	  alen = snprintf((char*)asciip, str_rem, "%c", '|');
+	  asciip += alen;
+	  str_rem -= alen;
+	  memset(cur_str, 0, cur_str_max);
+	  cur_str_len = 0;
+	  num_nulls = 0;
+	  /* To eliminate leading nulls in subsequent strings. */
+	  i++;
+	}
+      }
+    }
+    *asciip = 0;
+    free(cur_str);
+    free(cur_ascii);
+    return ascii;
+    break;
+
+  /* XXX: Dont know what to do with these yet, just print as binary... */
+  case REG_RESOURCE_LIST:
+  case REG_FULL_RESOURCE_DESCRIPTOR:
+  case REG_RESOURCE_REQUIREMENTS_LIST:
+
+  case REG_BINARY:
+    return (unsigned char*)quote_buffer(datap, len, special_chars);
+    break;
+
+  default:
+    return NULL;
+    break;
+  } 
+
+  return NULL;
+}
+
+
+void_stack* path2Stack(const char* s)
+{
+  void_stack* ret_val;
+  void_stack* rev_ret = void_stack_new(1024);
+  const char* cur = s;
+  char* next = NULL;
+  char* copy;
+
+  if (rev_ret == NULL)
+    return NULL;
+  if (s == NULL)
+    return rev_ret;
+  
+  while((next = strchr(cur, '/')) != NULL)
+  {
+    if ((next-cur) > 0)
+    {
+      copy = (char*)malloc((next-cur+1)*sizeof(char));
+      if(copy == NULL)
+	bailOut(2, "ERROR: Memory allocation problem.\n");
+	  
+      memcpy(copy, cur, next-cur);
+      copy[next-cur] = '\0';
+      void_stack_push(rev_ret, copy);
+    }
+    cur = next+1;
+  }
+  if(strlen(cur) > 0)
+  {
+    copy = strdup(cur);
+    void_stack_push(rev_ret, copy);
+  }
+
+  ret_val = void_stack_copy_reverse(rev_ret);
+  void_stack_destroy(rev_ret);
+
+  return ret_val;
+}
+
+
+char* stack2Path(void_stack* nk_stack)
+{
+  const REGF_NK_REC* cur;
+  uint32 buf_left = 127;
+  uint32 buf_len = buf_left+1;
+  uint32 name_len = 0;
+  uint32 grow_amt;
+  char* buf; 
+  char* new_buf;
+  void_stack_iterator* iter;
+  
+  buf = (char*)malloc((buf_len)*sizeof(char));
+  if (buf == NULL)
+    return NULL;
+  buf[0] = '\0';
+
+  iter = void_stack_iterator_new(nk_stack);
+  if (iter == NULL)
+  {
+    free(buf);
+    return NULL;
+  }
+
+  /* skip root element */
+  cur = void_stack_iterator_next(iter);
+
+  while((cur = void_stack_iterator_next(iter)) != NULL)
+  {
+    buf[buf_len-buf_left-1] = '/';
+    buf_left -= 1;
+    name_len = strlen(cur->keyname);
+    if(name_len+1 > buf_left)
+    {
+      grow_amt = (uint32)(buf_len/2);
+      buf_len += name_len+1+grow_amt-buf_left;
+      if((new_buf = realloc(buf, buf_len)) == NULL)
+      {
+	free(buf);
+	free(iter);
+	return NULL;
+      }
+      buf = new_buf;
+      buf_left = grow_amt + name_len + 1;
+    }
+    strncpy(buf+(buf_len-buf_left-1), cur->keyname, name_len);
+    buf_left -= name_len;
+    buf[buf_len-buf_left-1] = '\0';
+  }
+
+  return buf;
+}
+
+
+void printValue(REGF_VK_REC* vk, char* prefix)
+{
+  uint32 size;
+  uint8 tmp_buf[4];
+  unsigned char* quoted_value;
+  char* quoted_prefix;
+  char* quoted_name;
+
+  /* Thanks Microsoft for making this process so straight-forward!!! */
+  size = (vk->data_size & ~VK_DATA_IN_OFFSET);
+  if(vk->data_size & VK_DATA_IN_OFFSET)
+  {
+    tmp_buf[0] = (uint8)((vk->data_off >> 3) & 0xFF);
+    tmp_buf[1] = (uint8)((vk->data_off >> 2) & 0xFF);
+    tmp_buf[2] = (uint8)((vk->data_off >> 1) & 0xFF);
+    tmp_buf[3] = (uint8)(vk->data_off & 0xFF);
+    if(size > 4)
+      size = 4;
+    quoted_value = data_to_ascii(tmp_buf, 4, vk->type);
+  }
+  else
+  {
+    /* XXX: This is a safety hack.  No data fields have yet been found
+     * larger, but length limits are probably better got from fields
+     * in the registry itself, within reason.
+     */
+    if(size > 16384)
+    {
+      fprintf(stderr, "WARNING: key size %d larger than "
+	      "16384, truncating...\n", size);
+      size = 16384;
+    }
+    quoted_value = data_to_ascii(vk->data, vk->data_size, vk->type);
+  }
+  
+  /* XXX: Sometimes value names can be NULL in registry.  Need to
+   *      figure out why and when, and generate the appropriate output
+   *      for that condition.
+   */
+  quoted_prefix = quote_string(prefix, special_chars);
+  quoted_name = quote_string(vk->valuename, special_chars);
+  
+  if(print_security)
+    printf("%s/%s,%s,%s,,,,,\n", quoted_prefix, quoted_name,
+	   regfio_type_val2str(vk->type), quoted_value);
+  else
+    printf("%s/%s,%s,%s,\n", quoted_prefix, quoted_name,
+	   regfio_type_val2str(vk->type), quoted_value);
+  
+  if(quoted_value != NULL)
+    free(quoted_value);
+  if(quoted_prefix != NULL)
+    free(quoted_prefix);
+  if(quoted_name != NULL)
+    free(quoted_name);
+}
+
+
+void printValueList(REGF_NK_REC* nk, char* prefix)
+{
+  uint32 i;
+  
+  for(i=0; i < nk->num_values; i++)
+    if(!type_filter_enabled || (nk->values[i].type == type_filter))
+      printValue(&nk->values[i], prefix);
+}
+
+
+void printKey(REGF_NK_REC* k, char* full_path)
+{
+  static char empty_str[1] = "";
+  char* owner = NULL;
+  char* group = NULL;
+  char* sacl = NULL;
+  char* dacl = NULL;
+  char mtime[20];
+  time_t tmp_time[1];
+  struct tm* tmp_time_s = NULL;
+
+  *tmp_time = nt_time_to_unix(&k->mtime);
+  tmp_time_s = gmtime(tmp_time);
+  strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+
+  if(print_security)
+  {
+    owner = regfio_get_owner(k->sec_desc->sec_desc);
+    group = regfio_get_group(k->sec_desc->sec_desc);
+    sacl = regfio_get_sacl(k->sec_desc->sec_desc);
+    dacl = regfio_get_dacl(k->sec_desc->sec_desc);
+    if(owner == NULL)
+      owner = empty_str;
+    if(group == NULL)
+      group = empty_str;
+    if(sacl == NULL)
+      sacl = empty_str;
+    if(dacl == NULL)
+      dacl = empty_str;
+
+    printf("%s,KEY,,%s,%s,%s,%s,%s\n", full_path, mtime, 
+	   owner, group, sacl, dacl);
+
+    if(owner != empty_str)
+      free(owner);
+    if(group != empty_str)
+      free(group);
+    if(sacl != empty_str)
+      free(sacl);
+    if(dacl != empty_str)
+      free(dacl);
+  }
+  else
+    printf("%s,KEY,,%s\n", full_path, mtime);
+}
+
+
+/* XXX: this function is awful.  Needs to be re-designed. */
+void printKeyTree(REGF_FILE* f, void_stack* nk_stack, char* prefix)
+{
+  REGF_NK_REC* cur = NULL;
+  REGF_NK_REC* sub = NULL;
+  char* path = NULL;
+  char* val_path = NULL;
+  int key_type = regfio_type_str2val("KEY");
+  
+  if((cur = (REGF_NK_REC*)void_stack_cur(nk_stack)) != NULL)
+  {
+    cur->subkey_index = 0;
+    path = stack2Path(nk_stack);
+
+    if(print_verbose)
+    {
+      if(prefix[0] == '\0')
+	fprintf(stderr, "VERBOSE: Printing key tree under path: /\n");
+      else
+	fprintf(stderr, "VERBOSE: Printing key tree under path: %s\n",
+		prefix);
+    }
+
+    val_path = (char*)malloc(strlen(prefix)+strlen(path)+1);
+    sprintf(val_path, "%s%s", prefix, path);
+    if(val_path[0] == '\0')
+    {
+      val_path[0] = '/';
+      val_path[1] = '\0';
+    }
+
+    if(!type_filter_enabled || (key_type == type_filter))
+      printKey(cur, val_path);
+    if(!type_filter_enabled || (key_type != type_filter))
+      printValueList(cur, val_path);
+    if(val_path != NULL)
+      free(val_path);
+    while((cur = (REGF_NK_REC*)void_stack_cur(nk_stack)) != NULL)
+    {
+      if((sub = regfio_fetch_subkey(f, cur)) != NULL)
+      {
+	sub->subkey_index = 0;
+	void_stack_push(nk_stack, sub);
+	path = stack2Path(nk_stack);
+	if(path != NULL)
+	{
+	  val_path = (char*)malloc(strlen(prefix)+strlen(path)+1);
+	  sprintf(val_path, "%s%s", prefix, path);
+	  if(!type_filter_enabled || (key_type == type_filter))
+	    printKey(sub, val_path);
+	  if(!type_filter_enabled || (key_type != type_filter))
+	    printValueList(sub, val_path);
+	  if(val_path != NULL)
+	    free(val_path);
+	}
+      }
+      else
+      {
+	cur = void_stack_pop(nk_stack);
+	/* XXX: This is just a shallow free.  Need to write deep free
+	 * routines to replace the Samba code for this. 
+	 */ 
+	if(cur != NULL)
+	  free(cur);
+      }
+    }
+  }
+  if(print_verbose)
+    fprintf(stderr, "VERBOSE: Finished printing key tree.\n");
+}
+
+
+/*
+ * Returns 0 if path was found.
+ * Returns 1 if path was not found.
+ * Returns less than 0 on other error.
+ */
+int retrievePath(REGF_FILE* f, void_stack* nk_stack,
+		 void_stack* path_stack)
+{
+  REGF_NK_REC* sub = NULL; 
+  REGF_NK_REC* cur = NULL;
+  void_stack* sub_nk_stack;
+  char* prefix;
+  uint32 prefix_len;
+  char* cur_str = NULL;
+  char* path = NULL;
+  bool found_cur = true;
+  uint32 i;
+  uint16 path_depth;
+  if(path_stack == NULL)
+    return -1;
+
+  path_depth = void_stack_size(path_stack);
+  if(path_depth < 1)
+    return -2;
+
+  if(void_stack_size(nk_stack) < 1)
+    return -3;
+  cur = (REGF_NK_REC*)void_stack_cur(nk_stack);
+
+  if(print_verbose)
+    fprintf(stderr, "VERBOSE: Beginning retrieval of specified path: %s\n", 
+	    path_filter);
+
+  while(void_stack_size(path_stack) > 1)
+  {
+    /* Search key records only */
+    cur_str = (char*)void_stack_pop(path_stack);
+
+    found_cur = false;
+    while(!found_cur &&
+	  (sub = regfio_fetch_subkey(f, cur)) != NULL)
+    {
+      if(strcasecmp(sub->keyname, cur_str) == 0)
+      {
+	cur = sub;
+	void_stack_push(nk_stack, sub);
+	found_cur = true;
+      }
+    }
+    if(print_verbose && !found_cur)
+      fprintf(stderr, "VERBOSE: Could not find KEY '%s' in specified path.\n", 
+	      cur_str);
+
+    free(cur_str);
+    if(!found_cur)
+      return 1;
+  }
+
+  /* Last round, search value and key records */
+  cur_str = (char*)void_stack_pop(path_stack);
+
+  if(print_verbose)
+    fprintf(stderr, "VERBOSE: Searching values for last component"
+	            " of specified path.\n");
+
+  for(i=0; (i < cur->num_values); i++)
+  {
+    /* XXX: Not sure when/why this can be NULL, but it's happened. */
+    if(sub->values[i].valuename != NULL 
+       && strcasecmp(sub->values[i].valuename, cur_str) == 0)
+    {
+      path = stack2Path(nk_stack);
+
+      if(print_verbose)
+	fprintf(stderr, "VERBOSE: Found final path element as value.\n");
+
+      if(!type_filter_enabled || (sub->values[i].type == type_filter))
+        printValue(&sub->values[i], path);
+      if(path != NULL)
+	free(path);
+
+      return 0;
+    }
+  }
+
+  if(print_verbose)
+    fprintf(stderr, "VERBOSE: Searching keys for last component"
+	            " of specified path.\n");
+
+  while((sub = regfio_fetch_subkey(f, cur)) != NULL)
+  {
+    if(strcasecmp(sub->keyname, cur_str) == 0)
+    {
+      sub_nk_stack = void_stack_new(1024);
+      void_stack_push(sub_nk_stack, sub);
+      prefix = stack2Path(nk_stack);
+      prefix_len = strlen(prefix);
+      prefix = realloc(prefix, prefix_len+strlen(sub->keyname)+2);
+      if(prefix == NULL)
+	return -1;
+      strcat(prefix, "/");
+      strcat(prefix, sub->keyname);
+
+      if(print_verbose)
+	fprintf(stderr, "VERBOSE: Found final path element as key.\n");
+
+      printKeyTree(f, sub_nk_stack, prefix);
+
+      return 0;
+    }
+  }
+
+  if(print_verbose)
+    fprintf(stderr, "VERBOSE: Could not find last element of path.\n");
+
+  return 1;
+}
+
+
+static void usage(void)
+{
+  fprintf(stderr, "Usage: readreg [-v] [-s]"
+	  " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
+	  " <REGISTRY_FILE>\n");
+  /* XXX: replace version string with Subversion property? */
+  fprintf(stderr, "Version: 0.2.1\n");
+  fprintf(stderr, "Options:\n");
+  fprintf(stderr, "\t-v\t sets verbose mode.\n");
+  fprintf(stderr, "\t-h\t enables header row. (default)\n");
+  fprintf(stderr, "\t-H\t disables header row.\n");
+  fprintf(stderr, "\t-s\t enables security descriptor output.\n");
+  fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n");
+  fprintf(stderr, "\t-p\t restrict output to elements below this path.\n");
+  fprintf(stderr, "\t-t\t restrict results to this specific data type.\n");
+  fprintf(stderr, "\n");
+}
+
+
+int main(int argc, char** argv)
+{
+  void_stack* nk_stack;
+  void_stack* path_stack;
+  REGF_FILE* f;
+  REGF_NK_REC* root;
+  int retr_path_ret;
+  uint32 argi, arge;
+
+  /* Process command line arguments */
+  if(argc < 2)
+  {
+    usage();
+    bailOut(1, "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(1, "ERROR: '-p' option requires parameter.\n");
+      }
+      if((path_filter = strdup(argv[argi])) == NULL)
+	bailOut(2, "ERROR: Memory allocation problem.\n");
+
+      path_filter_enabled = true;
+    }
+    else if (strcmp("-t", argv[argi]) == 0)
+    {
+      if(++argi >= arge)
+      {
+	usage();
+	bailOut(1, "ERROR: '-t' option requires parameter.\n");
+      }
+      if((type_filter = regfio_type_str2val(argv[argi])) == 0)
+      {
+	fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]);
+	bailOut(1, "");
+      }
+
+      type_filter_enabled = true;
+    }
+    else if (strcmp("-h", argv[argi]) == 0)
+      print_header = true;
+    else if (strcmp("-H", argv[argi]) == 0)
+      print_header = false;
+    else if (strcmp("-s", argv[argi]) == 0)
+      print_security = true;
+    else if (strcmp("-S", argv[argi]) == 0)
+      print_security = false;
+    else if (strcmp("-v", argv[argi]) == 0)
+      print_verbose = true;
+    else
+    {
+      usage();
+      fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
+      bailOut(1, "");
+    }
+  }
+  if((registry_file = strdup(argv[argi])) == NULL)
+    bailOut(2, "ERROR: Memory allocation problem.\n");
+
+  f = regfio_open(registry_file);
+  if(f == NULL)
+  {
+    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
+    bailOut(3, "");
+  }
+
+  root = regfio_rootkey(f);
+  nk_stack = void_stack_new(1024);
+
+  if(void_stack_push(nk_stack, root))
+  {
+    if(print_header)
+    {
+      if(print_security)
+	printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL\n");
+      else
+	printf("PATH,TYPE,VALUE,MTIME\n");
+    }
+
+    path_stack = path2Stack(path_filter);
+    if(void_stack_size(path_stack) < 1)
+      printKeyTree(f, nk_stack, "");
+    else
+    {
+      retr_path_ret = retrievePath(f, nk_stack, path_stack);
+      if(retr_path_ret == 1)
+	fprintf(stderr, "WARNING: specified path not found.\n");
+      else if(retr_path_ret != 0)
+	bailOut(4, "ERROR:\n");
+    }
+  }
+  else
+    bailOut(2, "ERROR: Memory allocation problem.\n");
+
+  void_stack_destroy_deep(nk_stack);
+  regfio_close(f);
+
+  return 0;
+}
