Index: ADME.wiki
===================================================================
--- /README.wiki	(revision 296)
+++ 	(revision )
@@ -1,28 +1,0 @@
-<h2>What is this?</h2>
-<p>
-The RegLookup project is devoted to direct analysis of Windows NT-based registry files. RegLookup is released under the GNU GPL, and is implemented in ANSI C. RegLookup provides command line tools, a C API, and a Python module for accessing registry data structures. The project has a focus on providing tools for digital forensic examiners (though is useful for many purposes), and includes algorithms for retrieving deleted data structures from registry hives.
-</p>
-
-<h2>Where do I learn more?</h2>
-<p>
-See the <a href="http://projects.sentinelchicken.org/reglookup/">main project site</a>.  You'll find official releases there and more documentation.
-</p>
-
-<h2> What if I have trouble? </h2>
-<p>
-Ask for help on the <a href="http://projects.sentinelchicken.org/reglookup/mailing-list">development mailing list</a> or <a href="https://code.blindspotsecurity.com/trac/reglookup/newticket">submit a ticket</a>.
-</p>
-
-<h2> Is this project maintained anymore? </h2>
-<p>
-RegLookup is largely feature-complete for what we wanted to accomplish.  So the releases are infrequent (typically only when bugs crop up), but the project is still maintained.
-</p>
-
-<h2> How do I contribute? </h2>
-<p>
-We love contributions.  To submit a patch, please check out the latest revision with:
-</p>
-<pre>svn co https://code.blindspotsecurity.com/dav/reglookup/</pre>
-<p>
-Then apply your changes and run "<code>svn diff</code>".  Save the resulting diff and attach it to a ticket.
-</p>
Index: onstruct
===================================================================
--- /SConstruct	(revision 296)
+++ 	(revision )
@@ -1,180 +1,0 @@
-import sys
-import os
-
-cflags = '-std=gnu99 -pedantic -Wall -D_FILE_OFFSET_BITS=64 -DREGFI_WIN32'
-
-libiconv_path='.export/win32/libiconv/'
-libpthreads_path='.export/win32/libpthreads/'
-libpthread_name='pthreadGC2'
-libtalloc_path='.export/win32/libtalloc/'
-
-latest_release='1.0.1'
-source_targets=('src-trunk', 'src-0.99.0', 'src-1.0.0', 'src-1.0.1')
-win32_targets=('win32-trunk', 'win32-0.99.0', 'win32-1.0.0', 'win32-1.0.1')
-all_targets = source_targets+win32_targets
-
-
-def parse_target(target):
-    chunks = target.split('-')
-    if len(chunks) != 2:
-        return None
-    return chunks
-
-def version2input(version):
-    if version == 'trunk':
-        return 'trunk/'
-    else:
-        return 'releases/%s/' % version
-
-
-export_cmds='''
-rm -rf .export
-svn export --depth files svn+ssh://%(user)s@sentinelchicken.org/home/projects/subversion/reglookup .export
-svn export svn+ssh://%(user)s@sentinelchicken.org/home/projects/subversion/reglookup/win32 .export/win32
-svn export svn+ssh://%(user)s@sentinelchicken.org/home/projects/subversion/reglookup/%(path)s .export/%(path)s
-'''
-
-version_cmds='''
-echo 'REGFI_VERSION="%(version)s"' > .export/%(path)s/regfi_version.py
-'''
-
-svnversion_cmds='''
-svn info svn+ssh://%(user)s@sentinelchicken.org/home/projects/subversion/reglookup\
-  | grep "Last Changed Rev:" | cut -d' ' -f 4 \
-  | sed 's/^/REGFI_VERSION="%(latest_release)s./' | sed 's/$/"/' > .export/%(path)s/regfi_version.py
-'''
-
-cleanup_cmds='''
-rm -rf .export
-'''
-
-source_cmds='''
-mv %s .export/%s
-cd .export/%s && scons doc
-cd .export && tar cf %s.tar %s && gzip -9 %s.tar
-mv .export/%s.tar.gz .
-'''+cleanup_cmds
-
-win32_cmds='''
-mkdir -p .release/%(base)s/python/pyregfi
-cp %(path)s/src/*.exe .release/%(base)s
-cp %(path)s/LICENSE .release/%(base)s
-
-cp %(path)s/pyregfi-distutils.py .release/%(base)s/setup.py
-cp %(path)s/python/pyregfi/*.py .release/%(base)s/python/pyregfi
-
-cp -r -L .export/win32/doc .release/%(base)s
-cp .export/win32/libiconv/bin/*.dll .export/win32/libpthreads/bin/*.dll\
-     .export/win32/libtalloc/bin/*.dll %(path)s/lib/*.dll .release/%(base)s
-
-cd .release && zip -r %(base)s.zip %(base)s
-mv .release/%(base)s.zip . && rm -rf .release
-'''+cleanup_cmds
-
-
-def generate_cmds(source, target, env, for_signature):
-    ret_val = ''
-    input_prefix = str(source[0])+'/'
-
-    for t in target:
-        ttype,version = parse_target(str(t))
-        t_base = 'reglookup-%s-%s' % (ttype, version)
-
-        if ttype == 'src':
-            ret_val += source_cmds % (input_prefix, t_base, t_base, t_base,
-                                      t_base, t_base, t_base)
-        elif ttype == 'win32':
-            env['platform']='cygwin'
-            env['CC']='i586-mingw32msvc-cc'
-            env['AR']='i586-mingw32msvc-ar'
-            env['RANLIB']='i586-mingw32msvc-ranlib'
-            
-            env['CFLAGS']="""%s -DREGFI_VERSION='"%s"'""" % (cflags, version)
-            env['CPPPATH']=[input_prefix+'include', 
-                            libiconv_path+'include',
-                            libpthreads_path+'include',
-                            libtalloc_path+'include']
-            env['LIBPATH']=[input_prefix+'lib',
-                            libiconv_path+'lib',
-                            libpthreads_path+'lib',
-                            libtalloc_path+'lib']
-            env['LIBS']=['m', libpthread_name, 'iconv', 'regfi', 'talloc']
-            
-            # Third-party dependencies
-            extra_obj=['%s/lib/lib%s.a' % (libpthreads_path, libpthread_name),
-                       libiconv_path+'/lib/libiconv.dll.a',
-                       libtalloc_path+'/lib/libtalloc.dll.a']
-
-            # Build libregfi.dll
-            #   Core regfi source
-            lib_src = [input_prefix+'lib/regfi.c',
-                       input_prefix+'lib/winsec.c',
-                       input_prefix+'lib/range_list.c',
-                       input_prefix+'lib/lru_cache.c',
-                       input_prefix+'lib/void_stack.c']
-            regfi_o = env.Object(lib_src)
-
-            regfi_obj = []
-            for s in lib_src:
-                regfi_obj.append(s[0:-1]+'o')
-
-            # XXX: Several options here may not be necessary.  
-            #      Need to investigate why stdcall interfaces don't seem to be 
-            #      working on Windows.
-            env.Command(input_prefix+'lib/libregfi.o', regfi_o+extra_obj,
-                        'i586-mingw32msvc-dlltool --export-all-symbols'
-                        +' --add-stdcall-alias  --dllname libregfi.dll -e $TARGET'
-                        +' -l %slib/libregfi.dll.a %s' 
-                        % (input_prefix, ' '.join(regfi_obj)))
-
-            env.Command(input_prefix+'lib/libregfi.dll',
-                        input_prefix+'lib/libregfi.o',
-                        'i586-mingw32msvc-gcc ' + cflags 
-                        + ' --shared -Wl,--out-implib -add-stdcall-alias -o $TARGET $SOURCE %s'
-                        % ' '.join(regfi_obj+extra_obj))
-
-            # Executables
-            reglookup = env.Program(input_prefix+'src/reglookup.exe',
-                                    [input_prefix+'src/reglookup.c']+extra_obj)
-            reglookup_recover = env.Program(input_prefix+'src/reglookup-recover.exe',
-                                            [input_prefix+'src/reglookup-recover.c']+extra_obj)
-
-            params = {'path':input_prefix, 'base':t_base}
-            ret_val += win32_cmds % params
-
-    return ret_val
-
-
-release_builder = Builder(generator = generate_cmds,
-                          suffix = '',
-                          src_suffix = '',
-                          prefix='')
-
-env = Environment()
-env['ENV']['SSH_AGENT_PID'] = os.environ['SSH_AGENT_PID']
-env['ENV']['SSH_AUTH_SOCK'] = os.environ['SSH_AUTH_SOCK']
-env['BUILDERS']['Release'] = release_builder
-
-if len(COMMAND_LINE_TARGETS) == 0:
-    print('Acceptable targets: %s' % repr(all_targets))
-
-for target in COMMAND_LINE_TARGETS:
-    if target not in all_targets:
-        print('ERROR: cannot build "%s".  Acceptable targets: %s'
-              % (target, repr(all_targets)))
-        sys.exit(1)
-    AlwaysBuild(target)
-    ttype,version = parse_target(target)
-
-    params = {'user':os.environ['USER'], 
-              'path':version2input(version), 
-              'version':version,
-              'latest_release':latest_release}
-    env.Execute(export_cmds % params)
-    if version == 'trunk':
-        print env.Execute(svnversion_cmds % params)
-    else:
-        env.Execute(version_cmds % params)
-    env.Release(target, Dir('.export/'+params['path']))
-
-Default(None)
Index: /src/Makefile
===================================================================
--- /src/Makefile	(revision 1)
+++ /src/Makefile	(revision 1)
@@ -0,0 +1,9 @@
+OPTS=-ggdb -std=gnu89 -pedantic -Wall
+
+all: reglookup
+
+reglookup: reglookup.c
+	gcc $(OPTS) -o reglookup reglookup.c
+
+clean:
+	rm -f reglookup
Index: /src/reglookup.c
===================================================================
--- /src/reglookup.c	(revision 1)
+++ /src/reglookup.c	(revision 1)
@@ -0,0 +1,2417 @@
+/*
+ * $Id: $
+ *
+ * This code was taken from Richard Sharpe''s editreg utility, in the 
+ * Samba CVS tree.  It has since been simplified and turned into a
+ * strictly read-only utility.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
+ */
+ 
+/*************************************************************************
+                                                    
+ A utility to edit a Windows NT/2K etc registry file.
+                                     
+ Many of the ideas in here come from other people and software. 
+ I first looked in Wine in misc/registry.c and was also influenced by
+ http://www.wednesday.demon.co.uk/dosreg.html
+
+ Which seems to contain comments from someone else. I reproduce them here
+ incase the site above disappears. It actually comes from 
+ http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt. 
+
+ The goal here is to read the registry into memory, manipulate it, and then
+ write it out if it was changed by any actions of the user.
+
+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.
+
+*************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <strings.h>
+#include <string.h>
+#include <fcntl.h>
+
+#define False 0
+#define True 1
+#define REG_KEY_LIST_SIZE 10
+
+/*
+ * Structures for dealing with the on-disk format of the registry
+ */
+
+#define IVAL(buf) ((unsigned int) \
+                   (unsigned int)*((unsigned char *)(buf)+3)<<24| \
+                   (unsigned int)*((unsigned char *)(buf)+2)<<16| \
+                   (unsigned int)*((unsigned char *)(buf)+1)<<8| \
+                   (unsigned int)*((unsigned char *)(buf)+0)) 
+
+#define SVAL(buf) ((unsigned short) \
+                   (unsigned short)*((unsigned char *)(buf)+1)<<8| \
+                   (unsigned short)*((unsigned char *)(buf)+0)) 
+
+#define CVAL(buf) ((unsigned char)*((unsigned char *)(buf)))
+
+#define SIVAL(buf, val) \
+            ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
+             (((unsigned char *)(buf))[1])=(unsigned char)(((val)>>8)&0xFF),\
+             (((unsigned char *)(buf))[2])=(unsigned char)(((val)>>16)&0xFF),\
+             (((unsigned char *)(buf))[3])=(unsigned char)((val)>>24))
+
+#define SSVAL(buf, val) \
+  ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\
+   (((unsigned char *)(buf))[1])=(unsigned char)((val)>>8))
+
+static int verbose = 0;
+static int print_security = 0;
+static int full_print = 0;
+static const char *def_owner_sid_str = NULL;
+
+/* 
+ * These definitions are for the in-memory registry structure.
+ * It is a tree structure that mimics what you see with tools like regedit
+ */
+
+/*
+ * DateTime struct for Windows
+ */
+
+typedef struct date_time_s {
+  unsigned int low, high;
+} NTTIME;
+
+/*
+ * Definition of a Key. It has a name, classname, date/time last modified,
+ * sub-keys, values, and a security descriptor
+ */
+
+#define REG_ROOT_KEY 1
+#define REG_SUB_KEY  2
+#define REG_SYM_LINK 3
+
+typedef struct key_sec_desc_s KEY_SEC_DESC;
+
+typedef struct reg_key_s {
+  char *name;         /* Name of the key                    */
+  char *class_name;
+  int type;           /* One of REG_ROOT_KEY or REG_SUB_KEY */
+  NTTIME last_mod; /* Time last modified                 */
+  struct reg_key_s *owner;
+  struct key_list_s *sub_keys;
+  struct val_list_s *values;
+  KEY_SEC_DESC *security;
+  unsigned int offset;  /* Offset of the record in the file */
+} REG_KEY;
+
+/*
+ * The KEY_LIST struct lists sub-keys.
+ */
+
+typedef struct key_list_s {
+  int key_count;
+  int max_keys;
+  REG_KEY *keys[1];
+} KEY_LIST;
+
+typedef struct val_key_s {
+  char *name;
+  int has_name;
+  int data_type;
+  int data_len;
+  void *data_blk;    /* Might want a separate block */
+} VAL_KEY;
+
+typedef struct val_list_s {
+  int val_count;
+  int max_vals;
+  VAL_KEY *vals[1];
+} VAL_LIST;
+
+#ifndef MAXSUBAUTHS
+#define MAXSUBAUTHS 15
+#endif
+
+typedef struct sid_s {
+  unsigned char ver, auths;
+  unsigned char auth[6];
+  unsigned int sub_auths[MAXSUBAUTHS];
+} sid_t;
+
+typedef struct ace_struct_s {
+  unsigned char type, flags;
+  unsigned int perms;   /* Perhaps a better def is in order */
+  sid_t *trustee;
+} ACE; 
+
+typedef struct acl_struct_s {
+  unsigned short rev, refcnt;
+  unsigned short num_aces;
+  ACE *aces[1];
+} ACL;
+
+typedef struct sec_desc_s {
+  unsigned int rev, type;
+  sid_t *owner, *group;
+  ACL *sacl, *dacl;
+} SEC_DESC;
+
+#define SEC_DESC_NON 0
+#define SEC_DESC_RES 1
+#define SEC_DESC_OCU 2
+#define SEC_DESC_NBK 3
+typedef struct sk_struct SK_HDR;
+struct key_sec_desc_s {
+  struct key_sec_desc_s *prev, *next;
+  int ref_cnt;
+  int state;
+  int offset;
+  SK_HDR *sk_hdr;     /* This means we must keep the registry in memory */
+  SEC_DESC *sec_desc;
+}; 
+
+/* 
+ * All of the structures below actually have a four-byte length before them
+ * which always seems to be negative. The following macro retrieves that
+ * size as an integer
+ */
+
+#define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1))
+
+typedef unsigned int DWORD;
+typedef unsigned short WORD;
+
+#define REG_REGF_ID 0x66676572
+
+typedef struct regf_block {
+  DWORD REGF_ID;     /* regf */
+  DWORD uk1;
+  DWORD uk2;
+  DWORD tim1, tim2;
+  DWORD uk3;             /* 1 */
+  DWORD uk4;             /* 3 */
+  DWORD uk5;             /* 0 */
+  DWORD uk6;             /* 1 */
+  DWORD first_key;       /* offset */
+  unsigned int dblk_size;
+  DWORD uk7[116];        /* 1 */
+  DWORD chksum;
+} REGF_HDR;
+
+typedef struct hbin_sub_struct {
+  DWORD dblocksize;
+  char data[1];
+} HBIN_SUB_HDR;
+
+#define REG_HBIN_ID 0x6E696268
+
+typedef struct hbin_struct {
+  DWORD HBIN_ID; /* hbin */
+  DWORD off_from_first;
+  DWORD off_to_next;
+  DWORD uk1;
+  DWORD uk2;
+  DWORD uk3;
+  DWORD uk4;
+  DWORD blk_size;
+  HBIN_SUB_HDR hbin_sub_hdr;
+} HBIN_HDR;
+
+#define REG_NK_ID 0x6B6E
+
+typedef struct nk_struct {
+  WORD NK_ID;
+  WORD type;
+  DWORD t1, t2;
+  DWORD uk1;
+  DWORD own_off;
+  DWORD subk_num;
+  DWORD uk2;
+  DWORD lf_off;
+  DWORD uk3;
+  DWORD val_cnt;
+  DWORD val_off;
+  DWORD sk_off;
+  DWORD clsnam_off;
+  DWORD unk4[4];
+  DWORD unk5;
+  WORD nam_len;
+  WORD clsnam_len;
+  char key_nam[1];  /* Actual length determined by nam_len */
+} NK_HDR;
+
+#define REG_SK_ID 0x6B73
+
+struct sk_struct {
+  WORD SK_ID;
+  WORD uk1;
+  DWORD prev_off;
+  DWORD next_off;
+  DWORD ref_cnt;
+  DWORD rec_size;
+  char sec_desc[1];
+};
+
+typedef struct ace_struct {
+    unsigned char type;
+    unsigned char flags;
+    unsigned short length;
+    unsigned int perms;
+    sid_t trustee;
+} REG_ACE;
+
+typedef struct acl_struct {
+  WORD rev;
+  WORD size;
+  DWORD num_aces;
+  REG_ACE *aces;   /* One or more ACEs */
+} REG_ACL;
+
+typedef struct sec_desc_rec {
+  WORD rev;
+  WORD type;
+  DWORD owner_off;
+  DWORD group_off;
+  DWORD sacl_off;
+  DWORD dacl_off;
+} REG_SEC_DESC;
+
+typedef struct hash_struct {
+  DWORD nk_off;
+  char hash[4];
+} HASH_REC;
+
+#define REG_LF_ID 0x666C
+
+typedef struct lf_struct {
+  WORD LF_ID;
+  WORD key_count;
+  struct hash_struct hr[1];  /* Array of hash records, depending on key_count */
+} LF_HDR;
+
+typedef DWORD VL_TYPE[1];  /* Value list is an array of vk rec offsets */
+
+#define REG_VK_ID 0x6B76
+
+typedef struct vk_struct {
+  WORD VK_ID;
+  WORD nam_len;
+  DWORD dat_len;    /* If top-bit set, offset contains the data */
+  DWORD dat_off;   
+  DWORD dat_type;
+  WORD flag;        /* =1, has name, else no name (=Default). */
+  WORD unk1;
+  char dat_name[1]; /* Name starts here ... */
+} VK_HDR;
+
+#define REG_TYPE_DELETE    -1
+#define REG_TYPE_NONE      0
+#define REG_TYPE_REGSZ     1
+#define REG_TYPE_EXPANDSZ  2
+#define REG_TYPE_BIN       3  
+#define REG_TYPE_DWORD     4
+#define REG_TYPE_MULTISZ   7
+
+typedef struct _val_str { 
+  unsigned int val;
+  const char * str;
+} VAL_STR;
+
+/* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */
+typedef struct sk_map_s {
+  int sk_off;
+  KEY_SEC_DESC *key_sec_desc;
+} SK_MAP;
+
+/*
+ * This structure keeps track of the output format of the registry
+ */
+#define REG_OUTBLK_HDR 1
+#define REG_OUTBLK_HBIN 2
+
+typedef struct hbin_blk_s {
+  int type, size;
+  struct hbin_blk_s *next;
+  char *data;                /* The data block                */
+  unsigned int file_offset;  /* Offset in file                */
+  unsigned int free_space;   /* Amount of free space in block */
+  unsigned int fsp_off;      /* Start of free space in block  */
+  int complete, stored;
+} HBIN_BLK;
+
+/*
+ * This structure keeps all the registry stuff in one place
+ */
+typedef struct regf_struct_s {
+  int reg_type;
+  char *regfile_name, *outfile_name;
+  int fd;
+  struct stat sbuf;
+  char *base;
+  int modified;
+  NTTIME last_mod_time;
+  REG_KEY *root;  /* Root of the tree for this file */
+  int sk_count, sk_map_size;
+  SK_MAP *sk_map;
+  const char *owner_sid_str;
+  SEC_DESC *def_sec_desc;
+  /*
+   * These next pointers point to the blocks used to contain the 
+   * keys when we are preparing to write them to a file
+   */
+  HBIN_BLK *blk_head, *blk_tail, *free_space;
+} REGF;
+
+
+/* Function prototypes */
+
+static int nt_val_list_iterator(REGF *regf,  REG_KEY *key_tree, int bf, 
+                                char *path, int terminal, 
+                                const char* filter_prefix);
+static int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf,
+			   const char *path, const char* filter_prefix);
+static REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key);
+static int print_key(const char *path, char *name, char *class_name, int root,
+                     int terminal, int vals, char* newline);
+static int print_val(const char *path, char *val_name, int val_type, 
+                     int data_len, void *data_blk, int terminal, int first, 
+                     int last);
+
+static
+int print_sec(SEC_DESC *sec_desc);
+
+unsigned int str_is_prefix(const char* p, const char* s)
+{
+  const char* cp;
+  const char* cs;
+  
+  cs = s;
+  for(cp=p; (*cp) != '\0'; cp++)
+  {
+    if((*cp)!=(*cs))
+      return 0;
+    cs++;
+  }
+
+  return 1;
+}
+
+
+/*
+ * Iterate over the keys, depth first, calling a function for each key
+ * and indicating if it is terminal or non-terminal and if it has values.
+ *
+ * In addition, for each value in the list, call a value list function
+ */
+
+static
+int nt_val_list_iterator(REGF *regf,  REG_KEY *key_tree, int bf, char *path,
+			 int terminal, const char* filter_prefix)
+{
+  int i;
+  VAL_LIST* val_list = key_tree->values;
+
+  if (str_is_prefix(filter_prefix, path))
+  {  
+    for (i=0; i<val_list->val_count; i++) 
+    {
+      /*XXX: print_key() is doing nothing right now, can probably be removed. */
+      if (!print_key(path, key_tree->name,
+		     key_tree->class_name,
+		     (key_tree->type == REG_ROOT_KEY),
+		     (key_tree->sub_keys == NULL),
+		     (key_tree->values?(key_tree->values->val_count):0),
+		     "\n") ||
+	  !print_val(path, val_list->vals[i]->name,val_list->vals[i]->data_type,
+		     val_list->vals[i]->data_len, val_list->vals[i]->data_blk,
+		     terminal,
+		     (i == 0),
+		     (i == val_list->val_count)))
+      { return 0; }
+    }
+  }
+
+  return 1;
+}
+
+static
+int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf, 
+			 const char *path, const char* filter_prefix)
+{
+  int i;
+
+  if (!key_list) 
+    return 1;
+
+  for (i=0; i < key_list->key_count; i++) 
+  {
+    if (!nt_key_iterator(regf, key_list->keys[i], bf, path, filter_prefix)) 
+      return 0;
+  }
+  return 1;
+}
+
+static
+int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, 
+		    const char *path, const char* filter_prefix)
+{
+  int path_len = strlen(path);
+  char *new_path;
+
+  if (!regf || !key_tree)
+    return -1;
+
+  /* List the key first, then the values, then the sub-keys */
+  /*printf("filter_prefix: %s, path: %s\n", filter_prefix, path);*/
+  if (str_is_prefix(filter_prefix, path))
+  {
+    /*XXX: print_key() is doing nothing right now, can probably be removed. */
+    if (!print_key(path, key_tree->name,
+		   key_tree->class_name,
+		   (key_tree->type == REG_ROOT_KEY),
+		   (key_tree->sub_keys == NULL),
+		   (key_tree->values?(key_tree->values->val_count):0),
+		   "\n"))
+    { return 0; }
+
+    /*
+     * If we have a security print routine, call it
+     * If the security print routine returns false, stop.
+     */
+    if (key_tree->security && !print_sec(key_tree->security->sec_desc))
+      return 0;
+  }
+
+  new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1);
+  if (!new_path) 
+    return 0; /* Errors? */
+  new_path[0] = '\0';
+  strcat(new_path, path);
+  strcat(new_path, key_tree->name);
+  strcat(new_path, "\\");
+
+  /*
+   * Now, iterate through the values in the val_list 
+   */
+  if (key_tree->values &&
+      !nt_val_list_iterator(regf, key_tree, bf, new_path, 
+			    (key_tree->values!=NULL), filter_prefix))
+  {
+    free(new_path);
+    return 0;
+  }
+
+  /* 
+   * Now, iterate through the keys in the key list
+   */
+  if (key_tree->sub_keys && 
+      !nt_key_list_iterator(regf, key_tree->sub_keys, bf, 
+                            new_path, filter_prefix)) 
+  {
+    free(new_path);
+    return 0;
+  } 
+
+  free(new_path);
+  return 1;
+}
+
+
+/*
+ * Find key by name in a list ...
+ * Take the first component and search for that in the list
+ */
+static
+REG_KEY *nt_find_key_in_list_by_name(KEY_LIST *list, char *key)
+{
+  int i;
+  REG_KEY *res = NULL;
+
+  if (!list || !key || !*key) return NULL;
+
+  for (i = 0; i < list->key_count; i++)
+    if ((res = nt_find_key_by_name(list->keys[i], key)))
+      return res;
+  
+  return NULL;
+}
+
+
+/* 
+ * Find key by name in a tree ... We will assume absolute names here, but we
+ * need the root of the tree ...
+ */
+static REG_KEY* nt_find_key_by_name(REG_KEY* tree, char* key)
+{
+  char* lname = NULL;
+  char* c1;
+  char* c2;
+  REG_KEY* tmp;
+
+  if (!tree || !key || !*key) 
+    return NULL;
+
+  lname = strdup(key);
+  if (!lname) 
+    return NULL;
+
+  /*
+   * Make sure that the first component is correct ...
+   */
+  c1 = lname;
+  c2 = strchr(c1, '\\');
+  if (c2) 
+  { /* Split here ... */
+    *c2 = 0;
+    c2++;
+  }
+  
+  if (strcmp(c1, tree->name) != 0) 
+  {  
+    if (lname)
+      free(lname);
+    return NULL;
+  }
+  
+  if (c2) 
+  {
+    tmp = nt_find_key_in_list_by_name(tree->sub_keys, c2);
+    free(lname);
+    return tmp;
+  }
+  else 
+  {
+    if (lname) 
+      free(lname);
+    return tree;
+  }
+
+  return NULL;
+}
+
+/* Make, delete keys */
+static
+int nt_delete_val_key(VAL_KEY *val_key)
+{
+
+  if (val_key) {
+    if (val_key->name) free(val_key->name);
+    if (val_key->data_blk) free(val_key->data_blk);
+    free(val_key);
+  };
+  return 1;
+}
+
+
+/* 
+ * Add a key to the tree ... We walk down the components matching until
+ * we don't find any. There must be a match on the first component ...
+ * We return the key structure for the final component as that is 
+ * often where we want to add values ...
+ */
+
+/*
+ * Convert a string of the form S-1-5-x[-y-z-r] to a SID
+ */
+/* MIGHT COME IN HANDY LATER.
+static
+int sid_string_to_sid(sid_t **sid, const char *sid_str)
+{
+  int i = 0;
+  unsigned int auth;
+  const char *lstr;
+
+  *sid = (sid_t *)malloc(sizeof(sid_t));
+  if (!*sid) return 0;
+
+  memset(*sid, 0, sizeof(sid_t));
+
+  if (strncmp(sid_str, "S-1-5", 5)) {
+    fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str);
+    return 0;
+  }
+
+//We only allow strings of form S-1-5...
+
+  (*sid)->ver = 1;
+  (*sid)->auth[5] = 5;
+
+  lstr = sid_str + 5;
+
+  while (1) 
+  {
+    if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) 
+    {
+      if (i < 1) 
+      {
+	fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i);
+	return 0;
+      }
+      (*sid)->auths=i;
+      return 1;
+    }
+
+    (*sid)->sub_auths[i] = auth;
+    i++;
+    lstr = strchr(lstr + 1, '-'); 
+  }
+
+  return 1;
+}
+*/
+
+
+/*
+ * We will implement inheritence that is based on what the parent's SEC_DESC
+ * says, but the Owner and Group SIDs can be overwridden from the command line
+ * and additional ACEs can be applied from the command line etc.
+ */
+static
+KEY_SEC_DESC *nt_inherit_security(REG_KEY *key)
+{
+
+  if (!key) return NULL;
+  return key->security;
+}
+
+/*
+ * Add a sub-key 
+ */
+static
+REG_KEY *nt_add_reg_key_list(REGF *regf, REG_KEY *key, char * name, int create)
+{
+  int i;
+  REG_KEY *ret = NULL, *tmp = NULL;
+  KEY_LIST *list;
+  char *lname, *c1, *c2;
+
+  if (!key || !name || !*name) return NULL;
+  
+  list = key->sub_keys;
+  if (!list) { /* Create an empty list */
+
+    list = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (REG_KEY_LIST_SIZE - 1) * sizeof(REG_KEY *));
+    list->key_count = 0;
+    list->max_keys = REG_KEY_LIST_SIZE;
+
+  }
+
+  lname = strdup(name);
+  if (!lname) return NULL;
+
+  c1 = lname;
+  c2 = strchr(c1, '\\');
+  if (c2) { /* Split here ... */
+    *c2 = 0;
+    c2++;
+  }
+
+  for (i = 0; i < list->key_count; i++) {
+    if (strcmp(list->keys[i]->name, c1) == 0) {
+      ret = nt_add_reg_key_list(regf, list->keys[i], c2, create);
+      free(lname);
+      return ret;
+    }
+  }
+
+  /*
+   * If we reach here we could not find the the first component
+   * so create it ...
+   */
+
+  if (list->key_count < list->max_keys){
+    list->key_count++;
+  }
+  else { /* Create more space in the list ... */
+    if (!(list = (KEY_LIST *)realloc(list, sizeof(KEY_LIST) + 
+				     (list->max_keys + REG_KEY_LIST_SIZE - 1) 
+				     * sizeof(REG_KEY *))))
+      goto error;
+
+    list->max_keys += REG_KEY_LIST_SIZE;
+    list->key_count++;
+  }
+
+  /*
+   * add the new key at the new slot 
+   * FIXME: Sort the list someday
+   */
+
+  /*
+   * We want to create the key, and then do the rest
+   */
+
+  tmp = (REG_KEY *)malloc(sizeof(REG_KEY)); 
+
+  memset(tmp, 0, sizeof(REG_KEY));
+
+  tmp->name = strdup(c1);
+  if (!tmp->name) goto error;
+  tmp->owner = key;
+  tmp->type = REG_SUB_KEY;
+  /*
+   * Next, pull security from the parent, but override with
+   * anything passed in on the command line
+   */
+  tmp->security = nt_inherit_security(key);
+
+  list->keys[list->key_count - 1] = tmp;
+
+  if (c2) {
+    ret = nt_add_reg_key_list(regf, key, c2, True);
+  }
+
+  if (lname) free(lname);
+
+  return ret;
+
+ error:
+  if (tmp) free(tmp);
+  if (lname) free(lname);
+  return NULL;
+}
+
+
+/*
+ * Load and unload a registry file.
+ *
+ * Load, loads it into memory as a tree, while unload sealizes/flattens it
+ */
+
+/*
+ * Get the starting record for NT Registry file 
+ */
+
+/* 
+ * Where we keep all the regf stuff for one registry.
+ * This is the structure that we use to tie the in memory tree etc 
+ * together. By keeping separate structs, we can operate on different
+ * registries at the same time.
+ * Currently, the SK_MAP is an array of mapping structure.
+ * Since we only need this on input and output, we fill in the structure
+ * as we go on input. On output, we know how many SK items we have, so
+ * we can allocate the structure as we need to.
+ * If you add stuff here that is dynamically allocated, add the 
+ * appropriate free statements below.
+ */
+
+#define REGF_REGTYPE_NONE 0
+#define REGF_REGTYPE_NT   1
+#define REGF_REGTYPE_W9X  2
+
+#define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \
+                              (r)->last_mod_time.high = (t2);
+
+#define REGF_HDR_BLKSIZ 0x1000 
+
+#define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4) 
+#define LOCN(base, f) ((base) + OFF(f))
+
+const VAL_STR reg_type_names[] = {
+   { REG_TYPE_REGSZ,    "REG_SZ" },
+   { REG_TYPE_EXPANDSZ, "REG_EXPAND_SZ" },
+   { REG_TYPE_BIN,      "REG_BIN" },
+   { REG_TYPE_DWORD,    "REG_DWORD" },
+   { REG_TYPE_MULTISZ,  "REG_MULTI_SZ" },
+   { 0, NULL },
+};
+
+static
+const char *val_to_str(unsigned int val, const VAL_STR *val_array)
+{
+  int i = 0;
+
+  if (!val_array) return NULL;
+
+  while (val_array[i].val && val_array[i].str) {
+
+    if (val_array[i].val == val) return val_array[i].str;
+    i++;
+
+  }
+
+  return NULL;
+
+}
+
+/*
+ * 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 int ascii_max;
+  int str_rem;
+
+  switch (type) 
+  {
+  case REG_TYPE_REGSZ:
+    if (verbose)
+      fprintf(stderr, "Len: %d\n", len);
+
+    ascii_max = sizeof(char)*len;
+    ascii = malloc(ascii_max+4);
+    if(ascii == NULL)
+      return NULL;
+
+    /* FIXME. This has to be fixed. It has to be UNICODE */ 
+    uni_to_ascii(datap, ascii, len, ascii_max);
+    return ascii;
+    break;
+
+  case REG_TYPE_EXPANDSZ:
+    ascii_max = sizeof(char)*len;
+    ascii = malloc(ascii_max+2);
+    if(ascii == NULL)
+      return NULL;
+
+    uni_to_ascii(datap, ascii, len, ascii_max);
+    return ascii;
+    break;
+
+  case REG_TYPE_BIN:
+    ascii_max = sizeof(char)*len*3;
+    ascii = malloc(ascii_max+4);
+    if(ascii == NULL)
+      return NULL;
+
+    asciip = ascii;
+    for (i=0; (i<len)&&(i+1)*3<ascii_max; i++) { 
+      int str_rem = ascii_max - ((int)asciip - (int)ascii);
+      asciip += snprintf((char*)asciip, str_rem, "%02x", 
+			 *(unsigned char *)(datap+i));
+      if (i < len && str_rem > 0)
+	*asciip = ' '; asciip++;	
+    }
+    *asciip = '\0';
+    return ascii;
+    break;
+
+  case REG_TYPE_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;
+
+  case REG_TYPE_MULTISZ:
+    ascii_max = sizeof(char)*len*4;
+    ascii = malloc(ascii_max+4);
+    if(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;
+    for(i=0; (i < len) && (num_nulls < 4) && str_rem > 0; i++)
+    {
+      asciip += snprintf((char*)asciip, str_rem, "%02x ", 
+			 *(unsigned char *)(datap+i));
+      str_rem -= 3;
+      if(*(datap+i) == 0)
+	num_nulls++;
+      else
+	num_nulls = 0;
+    }
+    *asciip = '\0';
+    return ascii;
+    break;
+
+  default:
+    return NULL;
+    break;
+  } 
+
+  return NULL;
+}
+
+static
+REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent);
+
+static
+int nt_set_regf_input_file(REGF *regf, char *filename)
+{
+  return ((regf->regfile_name = strdup(filename)) != NULL); 
+}
+
+
+/* Create a regf structure and init it */
+
+static
+REGF *nt_create_regf(void)
+{
+  REGF *tmp = (REGF *)malloc(sizeof(REGF));
+  if (!tmp) return tmp;
+  memset(tmp, 0, sizeof(REGF));
+  tmp->owner_sid_str = def_owner_sid_str;
+  return tmp;
+} 
+
+
+/* Get the header of the registry. Return a pointer to the structure 
+ * If the mmap'd area has not been allocated, then mmap the input file
+ */
+static
+REGF_HDR *nt_get_regf_hdr(REGF *regf)
+{
+  if (!regf)
+    return NULL; /* What about errors */
+
+  if (!regf->regfile_name)
+    return NULL; /* What about errors */
+
+  if (!regf->base) { /* Try to mmap etc the file */
+
+    if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) {
+      return NULL; /* What about errors? */
+    }
+
+    if (fstat(regf->fd, &regf->sbuf) < 0) {
+      return NULL;
+    }
+
+    regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0);
+
+    if ((int)regf->base == 1) {
+      fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name,
+	      strerror(errno));
+      return NULL;
+    }
+  }
+
+  /* 
+   * At this point, regf->base != NULL, and we should be able to read the 
+   * header 
+   */
+
+  assert(regf->base != NULL);
+
+  return (REGF_HDR *)regf->base;
+}
+
+/*
+ * Validate a regf header
+ * For now, do nothing, but we should check the checksum
+ */
+static
+int valid_regf_hdr(REGF_HDR *regf_hdr)
+{
+  if (!regf_hdr) return 0;
+
+  return 1;
+}
+
+/*
+ * Process an SK header ...
+ * Every time we see a new one, add it to the map. Otherwise, just look it up.
+ * We will do a simple linear search for the moment, since many KEYs have the 
+ * same security descriptor. 
+ * We allocate the map in increments of 10 entries.
+ */
+
+/*
+ * Create a new entry in the map, and increase the size of the map if needed
+ */
+static
+SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off)
+{
+ if (!regf->sk_map) { /* Allocate a block of 10 */
+    regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10);
+    if (!regf->sk_map) {
+      free(tmp);
+      return NULL;
+    }
+    regf->sk_map_size = 10;
+    regf->sk_count = 1;
+    (regf->sk_map)[0].sk_off = sk_off;
+    (regf->sk_map)[0].key_sec_desc = tmp;
+  }
+  else { /* Simply allocate a new slot, unless we have to expand the list */ 
+    int ndx = regf->sk_count;
+    if (regf->sk_count >= regf->sk_map_size) {
+      regf->sk_map = (SK_MAP *)realloc(regf->sk_map, 
+				       (regf->sk_map_size + 10)*sizeof(SK_MAP));
+      if (!regf->sk_map) {
+	free(tmp);
+	return NULL;
+      }
+      /*
+       * ndx already points at the first entry of the new block
+       */
+      regf->sk_map_size += 10;
+    }
+    (regf->sk_map)[ndx].sk_off = sk_off;
+    (regf->sk_map)[ndx].key_sec_desc = tmp;
+    regf->sk_count++;
+  }
+ return regf->sk_map;
+}
+
+/*
+ * Search for a KEY_SEC_DESC in the sk_map, but don't create one if not
+ * found
+ */
+static
+KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off)
+{
+  int i;
+
+  if (!sk_map) return NULL;
+
+  for (i = 0; i < count; i++) {
+
+    if (sk_map[i].sk_off == sk_off)
+      return sk_map[i].key_sec_desc;
+
+  }
+
+  return NULL;
+
+}
+
+/*
+ * Allocate a KEY_SEC_DESC if we can't find one in the map
+ */
+static
+KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off)
+{
+  KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off);
+
+  if (tmp) {
+    return tmp;
+  }
+  else { /* Allocate a new one */
+    tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+    if (!tmp) {
+      return NULL;
+    }
+    memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */
+    tmp->state = SEC_DESC_RES;
+    if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+      return NULL;
+    }
+    return tmp;
+  }
+}
+
+/*
+ * Allocate storage and duplicate a SID 
+ * We could allocate the SID to be only the size needed, but I am too lazy. 
+ */
+static
+sid_t *dup_sid(sid_t *sid)
+{
+  sid_t *tmp = (sid_t *)malloc(sizeof(sid_t));
+  int i;
+  
+  if (!tmp) return NULL;
+  tmp->ver = sid->ver;
+  tmp->auths = sid->auths;
+  for (i=0; i<6; i++) {
+    tmp->auth[i] = sid->auth[i];
+  }
+  for (i=0; i<tmp->auths&&i<MAXSUBAUTHS; i++) {
+    tmp->sub_auths[i] = sid->sub_auths[i];
+  }
+  return tmp;
+}
+
+/*
+ * Allocate space for an ACE and duplicate the registry encoded one passed in
+ */
+static
+ACE *dup_ace(REG_ACE *ace)
+{
+  ACE *tmp = NULL; 
+
+  tmp = (ACE *)malloc(sizeof(ACE));
+
+  if (!tmp) return NULL;
+
+  tmp->type = CVAL(&ace->type);
+  tmp->flags = CVAL(&ace->flags);
+  tmp->perms = IVAL(&ace->perms);
+  tmp->trustee = dup_sid(&ace->trustee);
+  return tmp;
+}
+
+/*
+ * Allocate space for an ACL and duplicate the registry encoded one passed in 
+ */
+static
+ACL *dup_acl(REG_ACL *acl)
+{
+  ACL *tmp = NULL;
+  REG_ACE* ace;
+  int i, num_aces;
+
+  num_aces = IVAL(&acl->num_aces);
+
+  tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *));
+  if (!tmp) return NULL;
+
+  tmp->num_aces = num_aces;
+  tmp->refcnt = 1;
+  tmp->rev = SVAL(&acl->rev);
+  if (verbose) fprintf(stdout, "ACL: refcnt: %u, rev: %u\n", tmp->refcnt, 
+		       tmp->rev);
+  ace = (REG_ACE *)&acl->aces;
+  for (i=0; i<num_aces; i++) {
+    tmp->aces[i] = dup_ace(ace);
+    ace = (REG_ACE *)((char *)ace + SVAL(&ace->length));
+    /* XXX: FIXME, should handle malloc errors */
+  }
+
+  return tmp;
+}
+
+static
+SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc)
+{
+  SEC_DESC *tmp = NULL;
+  
+  tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC));
+
+  if (!tmp) {
+    return NULL;
+  }
+  
+  tmp->rev = SVAL(&sec_desc->rev);
+  tmp->type = SVAL(&sec_desc->type);
+  if (verbose) fprintf(stdout, "SEC_DESC Rev: %0X, Type: %0X\n", 
+		       tmp->rev, tmp->type);
+  if (verbose) fprintf(stdout, "SEC_DESC Owner Off: %0X\n",
+		       IVAL(&sec_desc->owner_off));
+  if (verbose) fprintf(stdout, "SEC_DESC Group Off: %0X\n",
+		       IVAL(&sec_desc->group_off));
+  if (verbose) fprintf(stdout, "SEC_DESC DACL Off: %0X\n",
+		       IVAL(&sec_desc->dacl_off));
+  tmp->owner = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->owner_off)));
+  if (!tmp->owner) {
+    free(tmp);
+    return NULL;
+  }
+  tmp->group = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->group_off)));
+  if (!tmp->group) {
+    free(tmp);
+    return NULL;
+  }
+
+  /* Now pick up the SACL and DACL */
+
+  if (sec_desc->sacl_off)
+    tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off)));
+  else
+    tmp->sacl = NULL;
+
+  if (sec_desc->dacl_off)
+    tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off)));
+  else
+    tmp->dacl = NULL;
+
+  return tmp;
+}
+
+static
+KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size)
+{
+  KEY_SEC_DESC *tmp = NULL;
+  int sk_next_off, sk_prev_off, sk_size;
+  REG_SEC_DESC *sec_desc;
+
+  if (!sk_hdr) return NULL;
+
+  if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) {
+    fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr,
+	    regf->regfile_name);
+    return NULL;
+  }
+
+  if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) {
+    fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n",
+	    -size, sk_size, regf->regfile_name);
+    return NULL;
+  }
+
+  /* 
+   * Now, we need to look up the SK Record in the map, and return it
+   * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can
+   * use that
+   */
+
+  if (regf->sk_map &&
+      ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL)
+      && (tmp->state == SEC_DESC_OCU)) {
+    tmp->ref_cnt++;
+    return tmp;
+  }
+
+  /* Here, we have an item in the map that has been reserved, or tmp==NULL. */
+
+  assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON));
+
+  /*
+   * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the
+   * new KEY_SEC_DESC to the mapping structure, since the offset supplied is 
+   * the actual offset of structure. The same offset will be used by
+   * all future references to this structure
+   * We could put all this unpleasantness in a function.
+   */
+
+  if (!tmp) {
+    tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC));
+    if (!tmp) return NULL;
+    memset(tmp, 0, sizeof(KEY_SEC_DESC));
+    
+    /*
+     * Allocate an entry in the SK_MAP ...
+     * We don't need to free tmp, because that is done for us if the
+     * sm_map entry can't be expanded when we need more space in the map.
+     */
+    
+    if (!alloc_sk_map_entry(regf, tmp, sk_off)) {
+      return NULL;
+    }
+  }
+
+  tmp->ref_cnt++;
+  tmp->state = SEC_DESC_OCU;
+
+  /*
+   * Now, process the actual sec desc and plug the values in
+   */
+
+  sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0];
+  tmp->sec_desc = process_sec_desc(regf, sec_desc);
+
+  /*
+   * Now forward and back links. Here we allocate an entry in the sk_map
+   * if it does not exist, and mark it reserved
+   */
+
+  sk_prev_off = IVAL(&sk_hdr->prev_off);
+  tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off);
+  assert(tmp->prev != NULL);
+  sk_next_off = IVAL(&sk_hdr->next_off);
+  tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off);
+  assert(tmp->next != NULL);
+
+  return tmp;
+}
+
+/*
+ * Process a VK header and return a value
+ */
+static
+VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size)
+{
+  char val_name[1024];
+  int nam_len, dat_len, flag, dat_type, dat_off, vk_id;
+  const char *val_type;
+  VAL_KEY *tmp = NULL; 
+
+  if (!vk_hdr) return NULL;
+
+  if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) {
+    fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n",
+	    vk_id, (int)vk_hdr, regf->regfile_name);
+    return NULL;
+  }
+
+  nam_len = SVAL(&vk_hdr->nam_len);
+  val_name[nam_len] = '\0';
+  flag = SVAL(&vk_hdr->flag);
+  dat_type = IVAL(&vk_hdr->dat_type);
+  dat_len = IVAL(&vk_hdr->dat_len);  /* If top bit, offset contains data */
+  dat_off = IVAL(&vk_hdr->dat_off);
+
+  tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY));
+  if (!tmp) {
+    goto error;
+  }
+  memset(tmp, 0, sizeof(VAL_KEY));
+  tmp->has_name = flag;
+  tmp->data_type = dat_type;
+
+  if (flag & 0x01) {
+    strncpy(val_name, vk_hdr->dat_name, nam_len);
+    tmp->name = strdup(val_name);
+    if (!tmp->name) {
+      goto error;
+    }
+  }
+  else
+    strncpy(val_name, "<No Name>", 10);
+
+  /*
+   * Allocate space and copy the data as a BLOB
+   */
+
+  if (dat_len) {
+    
+    char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF);
+    
+    if (!dtmp) {
+      goto error;
+    }
+
+    tmp->data_blk = dtmp;
+
+    if ((dat_len&0x80000000) == 0) 
+    { /* The data is pointed to by the offset */
+      char *dat_ptr = LOCN(regf->base, dat_off);
+      /* XXX: replace with memcpy */
+      bcopy(dat_ptr, dtmp, dat_len);
+    }
+    else { /* The data is in the offset or type */
+      /*
+       * FIXME.
+       * Some registry files seem to have wierd fields. If top bit is set,
+       * but len is 0, the type seems to be the value ...
+       * Not sure how to handle this last type for the moment ...
+       */
+      dat_len = dat_len & 0x7FFFFFFF;
+      /* XXX: replace with memcpy */
+      bcopy(&dat_off, dtmp, dat_len);
+    }
+
+    tmp->data_len = dat_len;
+  }
+
+  val_type = val_to_str(dat_type, reg_type_names);
+
+  /*
+   * We need to save the data area as well
+   */
+  if (verbose) 
+    fprintf(stdout, "  %s : %s : \n", val_name, val_type);
+
+  return tmp;
+
+ error:
+  if (tmp) nt_delete_val_key(tmp);
+  return NULL;
+
+}
+
+/*
+ * Process a VL Header and return a list of values
+ */
+static
+VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size)
+{
+  int i, vk_off;
+  VK_HDR *vk_hdr;
+  VAL_LIST *tmp = NULL;
+
+  if (!vl) return NULL;
+
+  if (-size < (count+1)*sizeof(int)){
+    fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size);
+    return NULL;
+  }
+
+  tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *));
+  if (!tmp) {
+    goto error;
+  }
+
+  for (i=0; i<count; i++) {
+    vk_off = IVAL(&vl[i]);
+    vk_hdr = (VK_HDR *)LOCN(regf->base, vk_off);
+    tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr));
+    if (!tmp->vals[i]){
+      goto error;
+    }
+  }
+
+  tmp->val_count = count;
+  tmp->max_vals = count;
+
+  return tmp;
+
+ error:
+  /* XXX: FIXME, free the partially allocated structure */
+  return NULL;
+} 
+
+/*
+ * Process an LF Header and return a list of sub-keys
+ */
+static
+KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size, REG_KEY *parent)
+{
+  int count, i, nk_off;
+  unsigned int lf_id;
+  KEY_LIST *tmp;
+
+  if (!lf_hdr) return NULL;
+
+  if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) {
+    fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n",
+	    lf_id, (int)lf_hdr, regf->regfile_name);
+    return NULL;
+  }
+
+  assert(size < 0);
+
+  count = SVAL(&lf_hdr->key_count);
+  if (verbose) 
+    fprintf(stdout, "Key Count: %u\n", count);
+  if (count <= 0) return NULL;
+
+  /* Now, we should allocate a KEY_LIST struct and fill it in ... */
+
+  tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *));
+  if (!tmp) {
+    goto error;
+  }
+
+  tmp->key_count = count;
+  tmp->max_keys = count;
+
+  for (i=0; i<count; i++) {
+    NK_HDR *nk_hdr;
+
+    nk_off = IVAL(&lf_hdr->hr[i].nk_off);
+    if (verbose) 
+      fprintf(stdout, "NK Offset: %0X\n", nk_off);
+    nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off);
+    tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr), parent);
+    if (!tmp->keys[i]) {
+      goto error;
+    }
+  }
+
+  return tmp;
+
+ error:
+  /*if (tmp) nt_delete_key_list(tmp, False);*/
+  return NULL;
+}
+
+
+/*
+ * This routine is passed an NK_HDR pointer and retrieves the entire tree
+ * from there down. It returns a REG_KEY *.
+ */
+static
+REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent)
+{
+  REG_KEY *tmp = NULL, *own;
+  int name_len, clsname_len, lf_off, val_off, val_count, sk_off, own_off;
+  unsigned int nk_id;
+  LF_HDR *lf_hdr;
+  VL_TYPE *vl;
+  SK_HDR *sk_hdr;
+  char key_name[1024];
+  unsigned char cls_name[1024];
+
+  if (!nk_hdr) return NULL;
+
+  if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) {
+    fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n", 
+            nk_id, (int)nk_hdr, regf->regfile_name);
+    return NULL;
+  }
+
+  assert(size < 0);
+
+  name_len = SVAL(&nk_hdr->nam_len);
+  clsname_len = SVAL(&nk_hdr->clsnam_len);
+
+  /*
+   * The value of -size should be ge 
+   * (sizeof(NK_HDR) - 1 + name_len)
+   * The -1 accounts for the fact that we included the first byte of 
+   * the name in the structure. clsname_len is the length of the thing 
+   * pointed to by clsnam_off
+   */
+
+  if (-size < (sizeof(NK_HDR) - 1 + name_len)) {
+    fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr);
+    fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n",
+            sizeof(NK_HDR), name_len, clsname_len);
+    /*return NULL;*/
+  }
+
+  if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n", 
+                       name_len, clsname_len);
+
+  /* Fish out the key name and process the LF list */
+
+  assert(name_len < sizeof(key_name));
+
+  /* Allocate the key struct now */
+  tmp = (REG_KEY *)malloc(sizeof(REG_KEY));
+  if (!tmp) return tmp;
+  memset(tmp, 0, sizeof(REG_KEY));
+
+  tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY);
+  
+  strncpy(key_name, nk_hdr->key_nam, name_len);
+  key_name[name_len] = '\0';
+
+  if (verbose) fprintf(stdout, "Key name: %s\n", key_name);
+
+  tmp->name = strdup(key_name);
+  if (!tmp->name) {
+    goto error;
+  }
+
+  /*
+   * Fish out the class name, it is in UNICODE, while the key name is 
+   * ASCII :-)
+   */
+
+  if (clsname_len) 
+  { /* Just print in Ascii for now */
+    unsigned char *clsnamep;
+    unsigned int clsnam_off;
+
+    clsnam_off = IVAL(&nk_hdr->clsnam_off);
+    clsnamep = (unsigned char*)LOCN(regf->base, clsnam_off);
+    if (verbose) fprintf(stdout, "Class Name Offset: %0X\n", clsnam_off);
+ 
+    memset(cls_name, 0, clsname_len);
+    uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len);
+    
+    /*
+     * I am keeping class name as an ascii string for the moment.
+     * That means it needs to be converted on output.
+     * It will also piss off people who need Unicode/UTF-8 strings. Sorry. 
+     * XXX: FIXME
+     */
+    tmp->class_name = strdup((char*)cls_name);
+    if (!tmp->class_name) {
+      goto error;
+    }
+
+    if (verbose) fprintf(stdout, "  Class Name: %s\n", cls_name);
+
+  }
+
+  /*
+   * Process the owner offset ...
+   */
+  own_off = IVAL(&nk_hdr->own_off);
+  own = (REG_KEY *)LOCN(regf->base, own_off);
+  if (verbose) 
+    fprintf(stdout, "Owner Offset: %0X\n", own_off);
+
+  if (verbose) 
+    fprintf(stdout, "  Owner locn: %0X, Our locn: %0X\n", 
+		       (unsigned int)own, (unsigned int)nk_hdr);
+
+  /* 
+   * We should verify that the owner field is correct ...
+   * for now, we don't worry ...
+   */
+  tmp->owner = parent;
+
+  /*
+   * If there are any values, process them here
+   */
+
+  val_count = IVAL(&nk_hdr->val_cnt);
+  if (verbose) 
+    fprintf(stdout, "Val Count: %d\n", val_count);
+  if (val_count) 
+  {
+    val_off = IVAL(&nk_hdr->val_off);
+    vl = (VL_TYPE *)LOCN(regf->base, val_off);
+    if (verbose) 
+      fprintf(stdout, "Val List Offset: %0X\n", val_off);
+
+    tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl));
+    if (!tmp->values) {
+      goto error;
+    }
+
+  }
+
+  /* 
+   * Also handle the SK header ...
+   */
+
+  sk_off = IVAL(&nk_hdr->sk_off);
+  sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off);
+  if (verbose) 
+    fprintf(stdout, "SK Offset: %0X\n", sk_off);
+
+  if (sk_off != -1) {
+
+    tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr));
+
+  } 
+
+  lf_off = IVAL(&nk_hdr->lf_off);
+  if (verbose) 
+    fprintf(stdout, "SubKey list offset: %0X\n", lf_off);
+
+  /*
+   * No more subkeys if lf_off == -1
+   */
+  if (lf_off != -1) 
+  {
+    lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off);
+    
+    tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr), tmp);
+    if (!tmp->sub_keys)
+      goto error;
+  }
+
+  return tmp;
+
+ error:
+  /*if (tmp) nt_delete_reg_key(tmp, False);*/
+  return NULL;
+}
+
+static
+int nt_load_registry(REGF *regf)
+{
+  REGF_HDR *regf_hdr;
+  unsigned int regf_id, hbin_id;
+  HBIN_HDR *hbin_hdr;
+  NK_HDR *first_key;
+
+  /* Get the header */
+
+  if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) {
+    return -1;
+  }
+
+  /* Now process that header and start to read the rest in */
+
+  if ((regf_id = IVAL(&regf_hdr->REGF_ID)) != REG_REGF_ID) {
+    fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n",
+	    regf_id, regf->regfile_name);
+    return -1;
+  }
+
+  /*
+   * Validate the header ...
+   */
+  if (!valid_regf_hdr(regf_hdr)) {
+    fprintf(stderr, "Registry file header does not validate: %s\n",
+	    regf->regfile_name);
+    return -1;
+  }
+
+  /* Update the last mod date, and then go get the first NK record and on */
+
+  TTTONTTIME(regf, IVAL(&regf_hdr->tim1), IVAL(&regf_hdr->tim2));
+
+  /* 
+   * The hbin hdr seems to be just uninteresting garbage. Check that
+   * it is there, but that is all.
+   */
+
+  hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ);
+
+  if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) {
+    fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n", 
+	    hbin_id, regf->regfile_name);
+    return -1;
+  } 
+
+  /*
+   * Get a pointer to the first key from the hreg_hdr
+   */
+
+  if (verbose) 
+    fprintf(stdout, "First Key: %0X\n", IVAL(&regf_hdr->first_key));
+
+  first_key = (NK_HDR *)LOCN(regf->base, IVAL(&regf_hdr->first_key));
+  if (verbose) fprintf(stdout, "First Key Offset: %0X\n", 
+		       IVAL(&regf_hdr->first_key));
+
+  if (verbose) fprintf(stdout, "Data Block Size: %d\n",
+		       IVAL(&regf_hdr->dblk_size));
+
+  if (verbose) fprintf(stdout, "Offset to next hbin block: %0X\n",
+		       IVAL(&hbin_hdr->off_to_next));
+
+  if (verbose) fprintf(stdout, "HBIN block size: %0X\n",
+		       IVAL(&hbin_hdr->blk_size));
+
+  /*
+   * Now, get the registry tree by processing that NK recursively
+   */
+
+  regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key), NULL);
+
+  assert(regf->root != NULL);
+
+  /*
+   * Unmap the registry file, as we might want to read in another
+   * tree etc.
+   */
+
+  if (regf->base) munmap(regf->base, regf->sbuf.st_size);
+  regf->base = NULL;
+  close(regf->fd);    /* Ignore the error :-) */
+
+  return 1;
+}
+
+
+/*
+ * Routines to parse a REGEDIT4 file
+ * 
+ * The file consists of:
+ * 
+ * REGEDIT4
+ * \[[-]key-path\]\n
+ * <value-spec>*
+ *
+ * Format:
+ * [cmd:]name=type:value
+ *
+ * cmd = a|d|c|add|delete|change|as|ds|cs
+ *
+ * There can be more than one key-path and value-spec.
+ *
+ * Since we want to support more than one type of file format, we
+ * construct a command-file structure that keeps info about the command file
+ */
+
+#define FMT_UNREC -1
+#define FMT_REGEDIT4 0
+#define FMT_EDITREG1_1 1
+
+#define FMT_STRING_REGEDIT4 "REGEDIT4"
+#define FMT_STRING_EDITREG1_0 "EDITREG1.0"
+
+#define CMD_NONE     0
+#define CMD_ADD_KEY  1
+#define CMD_DEL_KEY  2
+
+#define CMD_KEY 1
+#define CMD_VAL 2
+
+typedef struct val_spec_list {
+  struct val_spec_list *next;
+  char *name;
+  int type;
+  char *val;    /* Kept as a char string, really? */
+} VAL_SPEC_LIST;
+
+typedef struct command_s {
+  int cmd;
+  char *key;
+  int val_count;
+  VAL_SPEC_LIST *val_spec_list, *val_spec_last;
+} CMD;
+
+typedef struct cmd_line {
+  int len, line_len;
+  char *line;
+} CMD_LINE;
+
+
+
+#define INIT_ALLOC 10 
+
+
+/* prints a key */
+static
+int print_key(const char *path, char *name, char *class_name, int root, 
+	      int terminal, int vals, char* newline)
+{
+  if (full_print)
+    fprintf(stdout, "%s%s\\%s", path, name, newline);
+
+  return 1;
+}
+
+/*
+ * Sec Desc print functions 
+ */
+static
+void print_type(unsigned char type)
+{
+  switch (type) {
+  case 0x00:
+    fprintf(stdout, "    ALLOW");
+    break;
+  case 0x01:
+    fprintf(stdout, "     DENY");
+    break;
+  case 0x02:
+    fprintf(stdout, "    AUDIT");
+    break;
+  case 0x03:
+    fprintf(stdout, "    ALARM");
+    break;
+  case 0x04:
+    fprintf(stdout, "ALLOW CPD");
+    break;
+  case 0x05:
+    fprintf(stdout, "OBJ ALLOW");
+    break;
+  case 0x06:
+    fprintf(stdout, " OBJ DENY");
+    break;
+  default:
+    fprintf(stdout, "  UNKNOWN");
+    break;
+  }
+}
+
+static
+void print_flags(unsigned char flags)
+{
+  char flg_output[21];
+  int some = 0;
+
+  flg_output[0] = 0;
+  if (!flags) {
+    fprintf(stdout, "         ");
+    return;
+  }
+  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");
+  }
+  fprintf(stdout, " %s", flg_output);
+}
+
+static
+void print_perms(int perms)
+{
+  fprintf(stdout, " %8X", perms);
+}
+
+static
+void print_sid(sid_t *sid)
+{
+  int i, comps = sid->auths;
+  fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]);
+
+  for (i = 0; i < comps; i++) 
+    fprintf(stdout, "-%u", sid->sub_auths[i]);
+
+  /*fprintf(stdout, "\n");*/
+}
+
+static
+void print_acl(ACL *acl, const char *prefix)
+{
+  int i;
+
+  for (i = 0; i < acl->num_aces; i++) {
+    fprintf(stdout, ";;%s", prefix);
+    print_type(acl->aces[i]->type);
+    print_flags(acl->aces[i]->flags);
+    print_perms(acl->aces[i]->perms);
+    fprintf(stdout, " ");
+    print_sid(acl->aces[i]->trustee);
+  }
+}
+
+static
+int print_sec(SEC_DESC *sec_desc)
+{
+  if (!print_security) return 1;
+  fprintf(stdout, ";;  SECURITY\n");
+  fprintf(stdout, ";;   Owner: ");
+  print_sid(sec_desc->owner);
+  fprintf(stdout, ";;   Group: ");
+  print_sid(sec_desc->group);
+  if (sec_desc->sacl) {
+    fprintf(stdout, ";;    SACL:\n");
+    print_acl(sec_desc->sacl, " ");
+  }
+  if (sec_desc->dacl) {
+    fprintf(stdout, ";;    DACL:\n");
+    print_acl(sec_desc->dacl, " ");
+  }
+  return 1;
+}
+
+/*
+ * Value print function here ...
+ */
+static
+int print_val(const char *path, char *val_name, int val_type, int data_len, 
+	      void *data_blk, int terminal, int first, int last)
+{
+  unsigned char* data_asc;
+  
+  if(!val_name)
+    val_name = "<No Name>";
+
+  fprintf(stdout, "%s", path);
+  data_asc = data_to_ascii((unsigned char *)data_blk, data_len, val_type);
+  fprintf(stdout, "%s:%s=%s\n", val_name, val_to_str(val_type, reg_type_names),
+	  data_asc);
+  
+  free(data_asc);
+  return 1;
+}
+
+static
+void usage(void)
+{
+  fprintf(stderr, "Usage: readreg [-f<filterprefix>] [-v] [-p] [-k] [-s]"
+	  "<registryfile>\n");
+  fprintf(stderr, "Version: 0.1\n\n");
+  fprintf(stderr, "\n\t-v\t sets verbose mode");
+  fprintf(stderr, "\n\t-f\t a simple prefix filter.");
+  fprintf(stderr, "\n\t-p\t prints the registry");
+  fprintf(stderr, "\n\t-s\t prints security descriptors");
+  fprintf(stderr, "\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+  REGF *regf;
+  extern char *optarg;
+  extern int optind;
+  int opt; 
+  int regf_opt = 1;
+  char* filter_prefix = "";
+
+  if (argc < 2)
+  {
+    usage();
+    exit(1);
+  }
+  
+  /* 
+   * Now, process the arguments
+   */
+
+  while ((opt = getopt(argc, argv, "svkf:o:c:")) != EOF)
+  {
+    switch (opt)
+    {
+    case 'f':
+      /*full_print = 1;*/
+      filter_prefix = strdup(optarg);
+      regf_opt++;
+      break;
+
+    case 's':
+      print_security++;
+      full_print++;
+      regf_opt++;
+      break;
+
+    case 'v':
+      verbose++;
+      regf_opt++;
+      break;
+
+    case 'k':
+      regf_opt++;
+      break;
+
+    default:
+      usage();
+      exit(1);
+      break;
+    }
+  }
+
+  /*
+   * We only want to complain about the lack of a default owner SID if
+   * we need one. This approximates that need 
+   */
+  if (!def_owner_sid_str) {
+    def_owner_sid_str = "S-1-5-21-1-2-3-4";
+    if (verbose)
+      fprintf(stderr, "Warning, default owner SID not set. Setting to %s\n",
+	      def_owner_sid_str);
+  }
+
+  if ((regf = nt_create_regf()) == NULL) 
+  {
+    fprintf(stderr, "Could not create registry object: %s\n", strerror(errno));
+    exit(2);
+  }
+
+  if (regf_opt < argc) 
+  { /* We have a registry file */
+    if (!nt_set_regf_input_file(regf, argv[regf_opt])) 
+    {
+      fprintf(stderr, "Could not set name of registry file: %s, %s\n", 
+	      argv[regf_opt], strerror(errno));
+      exit(3);
+    }
+
+    /* Now, open it, and bring it into memory :-) */
+    if (nt_load_registry(regf) < 0) 
+    {
+      fprintf(stderr, "Could not load registry: %s\n", argv[1]);
+      exit(4);
+    }
+  }
+
+  /*
+   * At this point, we should have a registry in memory and should be able
+   * to iterate over it.
+   */
+  nt_key_iterator(regf, regf->root, 0, "", filter_prefix);
+
+  /* XXX: debugging; remove me. */
+  printf("%d,%d,%d\n", 
+	 str_is_prefix("foo", "foobar"),
+	 str_is_prefix("", "xyz"),
+	 str_is_prefix("foo", "foo"));
+
+  return 0;
+}
Index: st/Makefile
===================================================================
--- /test/Makefile	(revision 296)
+++ 	(revision )
@@ -1,25 +1,0 @@
-# $Id: Makefile 143 2009-02-13 03:24:27Z tim $
-
-################################################################################
-CC=gcc
-OPTS=-std=gnu99 -pedantic -Wall -ggdb
-INC:=-I../trunk/include -I../trunk/src -I/usr/local/include 
-LIB=-L/usr/local/lib -lm -lpthread -ltalloc
-
-REGFI_THREADTEST=regfi-threadtest
-OBJ=$(wildcard ../trunk/lib/*.o)
-FILES=$(REGFI_THREADTEST)
-
-all: deps $(FILES)
-
-deps:
-	cd ../trunk && scons libregfi
-
-$(REGFI_THREADTEST): regfi-threadtest.o
-	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ regfi-threadtest.o $(OBJ) $(EXTRA_OBJ)
-
-regfi-threadtest.o: regfi-threadtest.c
-	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ regfi-threadtest.c
-
-clean:
-	rm -f *.o $(FILES)
Index: st/diff-reglookup
===================================================================
--- /test/diff-reglookup	(revision 296)
+++ 	(revision )
@@ -1,49 +1,0 @@
-#!/bin/sh
-
-mkdir -p tmp
-TMP_DIR=tmp
-OUTPUT_DIR=tmp
-
-OLD=reglookup
-NEW=../trunk/build/bin/reglookup
-
-
-compareOneFile()
-{
-  echo "Comparing output on $BNAME, test case: $CASE" 1>&2
-
-  echo "  $OLD $OPTS $F" 1>&2
-  $OLD $OPTS $F > $TMP_DIR/old.csv
-
-  echo "  $NEW $OPTS $F" 1>&2
-  $NEW $OPTS $F > $TMP_DIR/new.csv
-
-  diff -ud $TMP_DIR/old.csv $TMP_DIR/new.csv > $OUTPUT_DIR/$CASE-${BNAME}.diff  
-  SIZE=`stat -c '%s' $OUTPUT_DIR/$CASE-${BNAME}.diff`
-  if [ "x$SIZE" = "x0" ]; then
-    rm $OUTPUT_DIR/$CASE-${BNAME}.diff
-  fi
-}
-
-for F in hives/*; do
-{
-  BNAME=`basename $F`
-
-  CASE=basic
-  OPTS=''
-  compareOneFile
-
-  CASE=security
-  OPTS='-s'
-  compareOneFile
-
-  CASE=type
-  OPTS='-t MULTI_SZ'
-  compareOneFile
-
-  CASE=security+type
-  OPTS='-s -t KEY'
-  compareOneFile
-} done
-
-rm $TMP_DIR/old.csv $TMP_DIR/new.csv
Index: st/diff-reglookup-recover
===================================================================
--- /test/diff-reglookup-recover	(revision 296)
+++ 	(revision )
@@ -1,42 +1,0 @@
-#!/bin/sh
-
-mkdir -p tmp
-TMP_DIR=tmp
-OUTPUT_DIR=tmp
-
-OLD=reglookup-recover
-NEW=../trunk/build/bin/reglookup-recover
-
-
-compareOneFile()
-{
-  echo "Comparing output on $BNAME, test case: $CASE" 1>&2
-
-  echo "  $OLD $OPTS $F" 1>&2
-  $OLD $OPTS $F > $TMP_DIR/old.csv
-
-  echo "  $NEW $OPTS $F" 1>&2
-  $NEW $OPTS $F > $TMP_DIR/new.csv
-
-  diff -ud $TMP_DIR/old.csv $TMP_DIR/new.csv > $OUTPUT_DIR/$CASE-${BNAME}.diff  
-  SIZE=`stat -c '%s' $OUTPUT_DIR/$CASE-${BNAME}.diff`
-  if [ "x$SIZE" = "x0" ]; then
-    rm $OUTPUT_DIR/$CASE-${BNAME}.diff
-  fi
-}
-
-for F in hives/*; do
-{
-
-  BNAME=`basename $F`
-
-  CASE=basic
-  OPTS=''
-  compareOneFile
-
-  CASE=raw
-  OPTS='-r'
-  compareOneFile
-} done
-
-rm $TMP_DIR/old.csv $TMP_DIR/new.csv
Index: st/pyregfi-smoketest.py
===================================================================
--- /test/pyregfi-smoketest.py	(revision 296)
+++ 	(revision )
@@ -1,339 +1,0 @@
-#!/usr/bin/env python3
-
-import sys
-import gc
-import io
-import time
-import threading
-import pyregfi
-
-
-
-pyregfi.setLogMask((pyregfi.LOG_TYPES.INFO, pyregfi.LOG_TYPES.WARN, pyregfi.LOG_TYPES.ERROR))
-
-# Uses the HiveIterator to walk all keys
-# Gathers various (meaningless) statistics to exercise simple attribute access
-# and to hopefully smoke out any bugs that can be identified by changing stats
-def iterTallyNames(hive, fh):
-    key_count = 0
-    key_lens = 0
-    key_rawlens = 0
-    value_count = 0
-    value_lens = 0
-    value_rawlens = 0
-
-    for k in hive:
-        key_count += 1
-        if k.name != None:
-            key_lens += len(k.name)
-        if k.name_raw != None:
-            key_rawlens += len(k.name_raw)
-
-        for v in k.values:
-            value_count += 1
-            if v.name != None:
-                value_lens += len(v.name)
-            if v.name_raw != None:
-                value_rawlens += len(v.name_raw)
-
-    print("  Counts: keys=%d, values=%d" % (key_count, value_count))
-    print("  Total name length: keys=%d, values=%d" % (key_lens, value_lens))
-    print("  Total raw name lengths: keys=%d, values=%d" % (key_rawlens, value_rawlens))
-
-
-# walks up parents to obtain path, rather than using downward links like iterator
-def getCurrentPath(key):
-    if key == None:
-        return ''
-    path = []
-    p = key
-    while p != None:
-        path.append(p.name)
-        p = p.get_parent()
-    path.reverse()
-    del path[0]
-
-    return path
-
-# For each key in the hive, this traverses the parent links up to the root, 
-# recording the path as it goes, and then uses the subtree/descend method
-# to find the same key again, verifying it is the same.  This test is currently
-# very slow because no key caching is used.
-def iterParentWalk(hive, fh):
-    i = 1
-    for k in hive:
-        path = getCurrentPath(k)
-        try:
-            hive_iter = hive.subtree(path)
-            if hive_iter.current_key() != k:
-                print("WARNING: k != current_key for path '%s'." % path)
-            else:
-                i += 1
-        except Exception as e:
-            print("WARNING: Could not descend to path '%s'.\nError:\n %s\n%s" % (path,e.args,e))
-    print("   Successfully tested paths on %d keys." % i)
-
-
-# Uses the HiveIterator to walk all keys
-# Gathers various (meaningless) statistics about data/data_raw attributes
-def iterTallyData(hive, fh):
-    data_stat = 0.0
-    dataraw_stat = 0.0
-    
-    for k in hive:
-        for v in k.values:
-            d = v.fetch_data()
-            if d == None:
-                data_stat += 0.1
-            elif hasattr(d, "__len__"):
-                data_stat += len(d)
-            else:
-                data_stat += d/2.0**64
-
-            d = v.fetch_raw_data()
-            if d == None:
-                dataraw_stat += 0.1
-            else:
-                dataraw_stat += len(d)
-
-    print("  Data stat: %f" % data_stat)
-    print("  Raw data stat: %f" % dataraw_stat)
-
-
-recurseKey_stat = 0.0
-recurseValue_stat = 0.0
-def checkValues(key):
-    global recurseKey_stat
-    global recurseValue_stat
-    recurseKey_stat += (key.mtime - key.max_bytes_subkeyname) * key.flags
-    for v in key.values:
-        recurseValue_stat += (v.data_off - v.data_size) / (1.0 + v.flags) + v.data_in_offset
-        try:
-            value = key.values[v.name]
-            if v != value:
-                print("WARNING: iterator value '%s' does not match dictionary value '%s'." 
-                      % (v.name, value.name))
-        except Exception as e:
-                print("WARNING: iterator value name '%s' is not retrievable via value list\n"
-                      % (v.name,))
-        
-
-def recurseTree(cur, operation):
-    for k in cur.subkeys:
-        key = cur.subkeys[k.name]
-        if k != key:
-            print("WARNING: iterator subkey '%s' does not match dictionary subkey '%s'." 
-                  % (k.name, key.name))
-        del key
-        operation(k)
-        recurseTree(k, operation)
-
-# Retrieves all keys by recursion, rather than the iterator, and validates
-# list dictionary access.  Also builds nonsensical statistics as an excuse
-# to access various base structure attributes.
-def recurseKeyTally(hive, fh):
-    checkValues(hive.root)
-    recurseTree(hive.root, checkValues)
-    print("  Key stat: %f" % recurseKey_stat)
-    print("  Value stat: %f" % recurseValue_stat)
-
-
-# Iterates hive gathering stats about security and classname records
-def iterFetchRelated(hive, fh):
-    security_stat = 0.0
-    classname_stat = 0.0
-    modified_stat = 0.0
-
-    for k in hive:
-        cn = k.fetch_classname()
-        if cn == None:
-            classname_stat += 0.000001
-        elif type(cn) == bytearray:
-            classname_stat += len(cn)/2**32
-        else:
-            classname_stat += len(cn)
-
-        modified_stat += k.modified
-        
-    print("  Security stat: %f" % security_stat)
-    print("  Classname stat: %f" % classname_stat)
-    print("  Modified stat: %f" % modified_stat)
-
-
-
-def iterIterWalk(hive, fh):
-    sk_stat = 0.0
-    v_stat = 0.0
-    iter = pyregfi.HiveIterator(hive)
-    for k in iter:
-        path = iter.current_path()
-        try:
-            hive_iter = hive.subtree(path[1:])
-            sk = hive_iter.first_subkey()
-            while sk != None:
-                ssk = hive_iter.find_subkey(sk.name)
-                if ssk != None:
-                    if ssk.name != None:
-                        sk_stat += len(ssk.name)
-                else:
-                    print("WARNING: ssk was None")
-                sk = hive_iter.next_subkey()
-
-            v = hive_iter.first_value()
-            while v != None:
-                vv = hive_iter.find_value(v.name)
-                if vv != None:
-                    if vv.name != None:
-                        v_stat += len(vv.name)
-                else:
-                    print("WARNING: vv was None")
-                v = hive_iter.next_value()
-
-        except Exception as e:
-            print("WARNING: Could not descend to path '%s'.\nError:\n %s\n%s" % (path[1:],e.args,e))
-    print("   Subkey stat: %f" % sk_stat)
-    print("   Value stat: %f" % v_stat)
-
-
-def iterCallbackIO(hive, fh):
-    fh.seek(0)
-    new_fh = io.BytesIO(fh.read())
-    new_hive = pyregfi.Hive(new_fh)
-    stat = 0
-    for k in new_hive:
-        stat += 1
-    print("  %d keys found" % stat)
-
-
-def threadIterMain(iter):
-    x = 0
-    try:
-        for k in iter:
-            #x += len(k.name) + len(k.subkeys)
-            pass
-    except Exception as e:
-        print("%s dying young: %s" % (threading.current_thread().name, repr(e)))
-        # Exceptions are thrown on iteration because python state gets out of 
-        # whack.  That's fine, because we're really just interested in finding
-        # segfaults.  People should not use iterators without locks, but it 
-        # should at least not segfault on them.
-        pass
-    print("%s finished" % threading.current_thread().name)
-
-def iterMultithread(hive, fh):
-    num_threads = 10
-    iter = pyregfi.HiveIterator(hive)
-    threads = []
-    for t in range(0,num_threads):
-        threads.append(threading.Thread(target=threadIterMain, args=(iter,)))
-    for t in threads:
-        t.start()
-    for t in threads:
-        t.join()
-
-
-def loopSecurity(hive, fh):
-    cur = hive.root.fetch_security()
-    first = cur
-    stat = 0
-    while True:
-        stat += len(cur.descriptor.owner)
-        stat += len(cur.descriptor.group)
-        if cur.descriptor.sacl:
-            stat += len(cur.descriptor.sacl)
-        if cur.descriptor.dacl:
-            stat += len(cur.descriptor.dacl)
-        
-        nxt = cur.next_security()
-        if first == nxt:
-            break
-        cur = nxt
-    print("  Security stat: %d" % stat)
-
-
-def iterSecurity(hive, fh):
-    stat = 0
-    for k in hive:
-        security = k.fetch_security()
-        stat += security.ref_count
-        stat += len(security.descriptor.owner)
-        stat += len(security.descriptor.group)
-        if security.descriptor.sacl:
-            for ace in security.descriptor.sacl:
-                stat += ace.flags
-                if ace.object:
-                    stat += ace.object.int
-                if ace.inherited_object:
-                    stat += ace.inherited_object.int
-
-        if security.descriptor.dacl:
-            for ace in security.descriptor.dacl:
-                stat += ace.flags
-                if ace.object:
-                    stat += ace.object.int
-                if ace.inherited_object:
-                    stat += ace.inherited_object.int
-    print("  Security stat: %d" % stat)
-
-
-tests = {
-    "iterTallyNames":iterTallyNames,
-    "iterParentWalk":iterParentWalk,
-    "iterTallyData":iterTallyData,
-    "recurseKeyTally":recurseKeyTally,
-    "iterFetchRelated":iterFetchRelated,
-    "iterIterWalk":iterIterWalk,
-    "iterCallbackIO":iterCallbackIO,
-    "iterMultithread":iterMultithread,
-    "loopSecurity":loopSecurity,
-    "iterSecurity":iterSecurity,
-    }
-
-def usage():
-    sys.stderr.write("USAGE: pyregfi-smoketest.py { test1[,test2[,...]] | * } hive1 [hive2 ...]\n")
-    sys.stderr.write("\tAvailable tests:\n")
-    for t in tests.keys():
-        sys.stderr.write("\t\t%s\n" % t)
-
-
-if len(sys.argv) < 3:
-    usage()
-    sys.exit(1)
-
-if sys.argv[1] == '*':
-    selected_tests = tests.keys()
-else:
-    selected_tests = sys.argv[1].split(',')
-    for st in selected_tests:
-        if st not in tests:
-            usage()
-            sys.stderr.write("ERROR: %s not a valid test type\n\n" % st)
-            sys.exit(1)
-
-files = []
-for f in sys.argv[2:]:
-    files.append((f, open(f,"rb")))
-
-
-start_time = time.time()
-for hname,fh in files:
-    #hive = pyregfi.Hive(fh)
-    hive = pyregfi.openHive(hname)
-    for tname in selected_tests:
-        t = tests[tname]
-        teststart = time.time()
-        tstr = "'%s' on '%s'" % (tname,hname)
-        print("##BEGIN %s:" % tstr)
-        t(hive, fh)
-        print("##END %s; runtime=%f; messages:" % (tstr, time.time() - teststart))
-        print(pyregfi.getLogMessages())
-        print
-        sys.stdout.flush()
-    fh.close()
-
-hive = None
-files = None
-tests = None
-gc.collect()
-print("### Tests Completed, runtime: %f ###" % (time.time() -  start_time))
-#print(gc.garbage)
Index: st/regfi-threadtest.c
===================================================================
--- /test/regfi-threadtest.c	(revision 296)
+++ 	(revision )
@@ -1,239 +1,0 @@
-/*
- * A program to stress test regfi under multithreaded use.
- *
- * Copyright (C) 2005-2011 Timothy D. Morgan
- * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 3 of the License.
- * 
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
- *
- * $Id: $
- */
-
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <strings.h>
-#include <time.h>
-#include <pthread.h>
-#include "regfi.h"
-#include "void_stack.h"
-
-/* Globals, influenced by command line parameters */
-bool print_verbose = false;
-const char* registry_file = NULL;
-
-/* Other globals */
-REGFI_FILE* f;
-
-
-/* XXX: A hack to share some functions with reglookup-recover.c.
- *      Should move these into a proper library at some point.
- */
-#include "common.c"
-
-static bool keysEqual(const REGFI_NK* x, const REGFI_NK* y)
-{
-  return (x != NULL && y != NULL && x->offset == y->offset);
-}
-
-void traverseValueList(REGFI_ITERATOR* iter)
-{
-  const REGFI_VK* value;
-  bool ret;
-
-  for(ret=regfi_iterator_first_value(iter); 
-      ret; 
-      ret=regfi_iterator_next_value(iter))
-  {
-    value = regfi_iterator_cur_value(iter);
-    printMsgs(iter->f);
-    regfi_free_record(iter->f, value);
-  }
-}
-
-
-void traverseKeyTree(REGFI_ITERATOR* iter)
-{
-  const REGFI_NK* root = NULL;
-  const REGFI_NK* cur = NULL;
-  const REGFI_NK* sub = NULL;
-  const REGFI_SK* sk;
-  bool print_this = true;
-
-  root = regfi_iterator_cur_key(iter);
-  regfi_reference_record(iter->f, root);
-  cur = root;
-  regfi_iterator_first_subkey(iter);
-  sub = regfi_iterator_cur_subkey(iter);
-  printMsgs(iter->f);
-
-  if(root == NULL)
-    bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: root cannot be NULL.\n");
-  
-  do
-  {
-    if(print_this)
-      traverseValueList(iter);
-    
-    if(sub == NULL)
-    {
-      if(!keysEqual(cur,root))
-      {
-	/* We're done with this sub-tree, going up and hitting other branches. */
-        regfi_free_record(iter->f, cur);
-        cur = NULL;
-	if(!regfi_iterator_up(iter))
-	{
-	  printMsgs(iter->f);
-	  bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator upward.\n");
-	}
-
-	cur = regfi_iterator_cur_key(iter);
-	/*	fprintf(stderr, "%s\n", cur->keyname);*/
-	printMsgs(iter->f);
-	if(cur == NULL)
-	  bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: unexpected NULL for key.\n");
-	sk = regfi_fetch_sk(iter->f, cur);
-	printMsgs(iter->f);
-        regfi_free_record(iter->f, sk);
-
-	regfi_iterator_next_subkey(iter);
-	sub = regfi_iterator_cur_subkey(iter);
-      }
-      print_this = false;
-    }
-    else
-    { /* We have unexplored sub-keys.  
-       * Let's move down and print this first sub-tree out. 
-       */
-      regfi_free_record(iter->f, cur);
-      cur = NULL;
-      if(!regfi_iterator_down(iter))
-      {
-	printMsgs(iter->f);
-	bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator downward.\n");
-      }
-
-      cur = regfi_iterator_cur_key(iter);
-      printMsgs(iter->f);
-      regfi_free_record(iter->f, sub);
-
-      regfi_iterator_first_subkey(iter);
-      sub = regfi_iterator_cur_subkey(iter);
-      printMsgs(iter->f);
-      print_this = true;
-    }
-    printMsgs(iter->f);
-  } while(!(keysEqual(cur,root) && (sub == NULL)));
-  if(cur != NULL)
-    regfi_free_record(iter->f, cur);
-  regfi_free_record(iter->f, root);
-
-  if(print_verbose)
-    fprintf(stderr, "INFO: Finished printing key tree.\n");
-}
-
-
-int num_iterations;
-void* threadLoop(void* file)
-{
-  REGFI_ITERATOR* iter;
-  int i;
-
-  regfi_log_set_mask(REGFI_LOG_INFO|REGFI_LOG_WARN|REGFI_LOG_ERROR);
-
-  iter = regfi_iterator_new((REGFI_FILE*)f);
-  if(iter == NULL)
-  {
-    printMsgs(f);
-    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Couldn't create registry iterator.\n");
-  }
-
-  for(i=0; i< num_iterations; i++)
-  {
-    traverseKeyTree(iter);
-    regfi_iterator_to_root(iter);
-  }
-
-  regfi_iterator_free(iter);
-  fprintf(stderr, "INFO: thread finished\n");
-
-  return NULL;
-}
-
-
-static void usage(void)
-{
-  fprintf(stderr, "Usage: regfi-threadtest <REGISTRY_FILE>\n");
-  fprintf(stderr, "\n");
-}
-
-
-int main(int argc, char** argv)
-{
-  int fd, tret, i;
-  uint32_t argi, arge, num_threads;
-  pthread_t* threads;
-
-  num_threads = 10;
-  num_iterations = 2;
-
-  /* Process command line arguments */
-  if(argc < 2)
-  {
-    usage();
-    bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n");
-  }
-  
-  arge = argc-1;
-  for(argi = 1; argi < arge; argi++)
-  {
-    usage();
-    fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]);
-    bailOut(REGLOOKUP_EXIT_USAGE, "");
-  }
-  registry_file = argv[argi];
-
-  regfi_log_set_mask(REGFI_LOG_INFO|REGFI_LOG_WARN|REGFI_LOG_ERROR);
-
-  fd = openHive(registry_file);
-  if(fd < 0)
-  {
-    fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
-    bailOut(REGLOOKUP_EXIT_NOINPUT, "");
-  }
-
-  f = regfi_alloc(fd, REGFI_ENCODING_ASCII);
-  if(f == NULL)
-  {
-    close(fd);
-    bailOut(REGLOOKUP_EXIT_NOINPUT, "ERROR: Failed to create REGFI_FILE structure.\n");
-  }
-
-  threads = malloc(sizeof(pthread_t)*num_threads);
-  for(i=0; i<num_threads; i++)
-  {
-    tret = pthread_create(threads+i, NULL, threadLoop, (void*) f);
-  }
-  
-  for(i=0; i<num_threads; i++)
-    pthread_join(threads[i], NULL);
-
-  free(threads);
-  regfi_free(f);
-  close(fd);
-
-  return 0;
-}
