[Forensics-changes] [SCM] debian-forensics/reglookup branch, upstream, updated. upstream/0.12.0-1-ge565885

Elías Alejandro ealmdz at gmail.com
Sat Oct 8 21:11:23 UTC 2011


The following commit has been merged in the upstream branch:
commit e565885c4076c1428c14ba99bb12d67918cb880b
Author: Elías Alejandro <ealmdz at gmail.com>
Date:   Sat Oct 8 15:28:08 2011 -0500

    Imported Upstream version 1.0.1

diff --git a/.sconsign.dblite b/.sconsign.dblite
new file mode 100644
index 0000000..ca20cd0
Binary files /dev/null and b/.sconsign.dblite differ
diff --git a/INSTALL b/INSTALL
index 468ffc5..d3bef5a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -4,11 +4,13 @@ RegLookup Installation
 Prerequisites
 -------------
 
-This package doesn't require much for installation.  Just what typically
-comes with any free operating system.  Be sure you have:
+RegLookup and its associated libraries have the following build dependencies:
 
- - GNU Make
+ - SCons (package "scons" in most popular distributions or from http://www.scons.org/)
  - GCC
+ - talloc 2.x (under Debian, "libtalloc2" and "libtalloc-dev")
+ - Python 2 (2.6+) or Python 3  (Python is required for SCons anyway)
+ - Doxygen (optional, only needed to build developer documentation)
 
 Note that iconv support is required, as specified in IEEE Std 1003.1 
 (POSIX.1-2001).  Some platforms still do not contain support for this 
@@ -19,36 +21,15 @@ natively, in which case you may need to install libiconv from:
 Survival Commands
 -----------------
 
-make
+scons
 # and as root
-make install
-
-If GNU Make isn't your default, you may need to run `gmake' instead.
+scons install
 
 
 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.
+By default, most RegLookup files are installed in the appropriate
+directories under /usr/local.  In order to override this behavior, set
+the PREFIX environment variable to the desired path.  For example:
 
-Primitive build features have been added to support cross-compiling to
-Windows binaries using MinGW.  For more information on this, see: 
-  doc/mingw-build.txt
+$ PREFIX=/home/myuser/reglookup scons install
diff --git a/Makefile b/Makefile
deleted file mode 100644
index a19a61f..0000000
--- a/Makefile
+++ /dev/null
@@ -1,76 +0,0 @@
-# $Id: Makefile 159 2009-12-06 20:09:01Z tim $
-
-# Installation prefixes.  Change to install elsewhere.
-
-PREFIX=/usr/local
-BIN_PREFIX=$(PREFIX)/bin
-DOC_PREFIX=$(PREFIX)/share/doc/reglookup
-MAN_PREFIX=$(PREFIX)/man
-
-################################################################################
-
-CC=gcc
-OPTS=-std=gnu99 -pedantic -Wall -ggdb
-#OPTS=-std=gnu99 -pedantic -Wall
-INC:=-I$(PWD)/include -I/usr/local/include 
-LIB=-L/usr/local/lib -lm
-BIN_EXT=
-EXTRA_OBJ=
-
-UNAME := $(shell uname)
-ifneq ($(UNAME),Linux) 	
-  LIB:=$(LIB) -liconv
-endif
-
-
-################################################################################
-# MinGW cross-compiling build settings
-ifdef BUILD_MINGW
-
-## These may need to be changed
-CC=i586-mingw32msvc-cc
-LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib
-
-## These probably do not need to be changed
-BIN_EXT=.exe
-INC:=$(INC) -I$(LIBICONV_PATH)/include
-EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a
-
-endif
-################################################################################
-
-
-BUILD=$(CURDIR)/build
-BUILD_BIN=$(BUILD)/bin
-BUILD_DOC=$(BUILD)/doc
-
-BUILD_TREE=$(BUILD_BIN) $(BUILD_DOC)
-SUB_DIRS=lib src doc bin
-
-FILES=$(REGLOOKUP)
-.PHONY: $(SUB_DIRS) clean
-export
-
-
-all: $(BUILD_TREE) $(SUB_DIRS)
-
-#XXX: This should be more generalized.
-install: all
-	mkdir -p $(BIN_PREFIX)
-	mkdir -p $(DOC_PREFIX)
-	mkdir -p $(MAN_PREFIX)/man1
-	$(MAKE) -C bin install
-	$(MAKE) -C src install
-	$(MAKE) -C doc install
-
-
-$(SUB_DIRS):
-	$(MAKE) -C $@
-
-$(BUILD_TREE):
-	mkdir -p $@
-
-clean:
-	$(MAKE) -C src clean
-	$(MAKE) -C lib clean
-	rm -rf $(BUILD)/*
diff --git a/SConstruct b/SConstruct
new file mode 100644
index 0000000..3222a2d
--- /dev/null
+++ b/SConstruct
@@ -0,0 +1,97 @@
+import sys
+import os
+sys.dont_write_bytecode = True
+from regfi_version import REGFI_VERSION
+
+cflags = '-std=gnu99 -pedantic -Wall -D_FILE_OFFSET_BITS=64 -fvisibility=hidden'
+cflags += ' -DREGFI_VERSION=\'"%s"\'' % REGFI_VERSION
+cflags += ' -ggdb'
+
+lib_src = ['lib/regfi.c',
+           'lib/winsec.c',
+           'lib/range_list.c',
+           'lib/lru_cache.c',
+           'lib/void_stack.c']
+
+cc=os.environ.get('CC', 'gcc')
+env = Environment(ENV=os.environ,
+                  CC=cc,
+                  CFLAGS=cflags,
+                  CPPPATH=['include', '/usr/local/include'],
+                  LIBPATH=['lib', '/usr/local/lib'],
+                  LIBS=['m', 'pthread', 'regfi', 'talloc'])
+
+# Libraries
+libregfi_static = env.Library(lib_src)
+libregfi = env.SharedLibrary(lib_src, LIBS=['m','pthread', 'talloc'])
+
+
+# Executables
+reglookup = env.Program(['src/reglookup.c'])
+reglookup_recover = env.Program(['src/reglookup-recover.c'])
+
+
+# Documentation
+#  This only needs to be run during the release/packaging process
+man_fixup = "|sed 's/.SH DESCRIPTION/\\n.SH DESCRIPTION/'"
+man_builder = Builder(action='docbook2x-man --to-stdout $SOURCE'
+                      + man_fixup + '| gzip -9 > $TARGET',
+                      suffix = '.gz',
+                      src_suffix = '.docbook')
+env['BUILDERS']['ManPage'] = man_builder
+
+man_reglookup = env.ManPage('doc/reglookup.1.docbook')
+man_reglookup_recover = env.ManPage('doc/reglookup-recover.1.docbook')
+man_reglookup_timeline = env.ManPage('doc/reglookup-timeline.1.docbook')
+
+# Installation
+prefix='/usr/local/'
+if 'PREFIX' in os.environ:
+   prefix = os.environ['PREFIX']+'/'
+
+install_items = [prefix+'bin',
+                 prefix+'lib', 
+                 prefix+'include/regfi',
+                 prefix+'man']
+
+env.Install(prefix+'bin', [reglookup, reglookup_recover, 'bin/reglookup-timeline'])
+libinstall = env.Install(prefix+'lib', [libregfi, libregfi_static])
+env.Install(prefix+'include/regfi', Glob('include/*.h'))
+env.Install(prefix+'man/man1', [man_reglookup, man_reglookup_recover,
+                                man_reglookup_timeline])
+env.AddPostAction(libinstall, 'ldconfig')
+
+if sys.version_info[0] == 2:
+   install_items.append('pyregfi2-install.log')
+   env.Command('pyregfi2-install.log', ['python/pyregfi/__init__.py', 
+                                        'python/pyregfi/structures.py', 
+                                        'python/pyregfi/winsec.py'],
+               "python pyregfi-distutils.py install | tee pyregfi2-install.log")
+
+python_path = os.popen('which python3').read()
+if python_path != '':
+   install_items.append('pyregfi3-install.log')
+   env.Command('pyregfi3-install.log', ['python/pyregfi/__init__.py', 
+                                        'python/pyregfi/structures.py', 
+                                        'python/pyregfi/winsec.py'], 
+               "python3 pyregfi-distutils.py install | tee pyregfi3-install.log")
+
+# API documentation
+regfi_doc = env.Command('doc/devel/regfi/index.html', 
+                        Glob('lib/*.c')+Glob('include/*.h')+['doc/devel/Doxyfile.regfi'],
+                        'doxygen doc/devel/Doxyfile.regfi')
+pyregfi_doc = env.Command('doc/devel/pyregfi/index.html', 
+                          Glob('python/pyregfi/*.py')+['doc/devel/Doxyfile.pyregfi', regfi_doc],
+                          'doxygen doc/devel/Doxyfile.pyregfi')
+
+
+# User Friendly Targets
+env.Alias('libregfi', libregfi)
+env.Alias('reglookup', reglookup)
+env.Alias('reglookup-recover', reglookup_recover)
+env.Alias('bin', [reglookup_recover, reglookup])
+env.Alias('doc', [man_reglookup,man_reglookup_recover,man_reglookup_timeline])
+env.Alias('doc-devel', [regfi_doc, pyregfi_doc])
+env.Alias('install', install_items)
+
+Default('bin', libregfi)
diff --git a/bin/Makefile b/bin/Makefile
deleted file mode 100644
index 96420a7..0000000
--- a/bin/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-# $Id: Makefile 90 2007-03-28 19:22:38Z tim $
-
-################################################################################
-
-TIMELINE=$(BUILD_BIN)/reglookup-timeline
-FILES=$(TIMELINE)
-
-all: $(FILES)
-
-$(TIMELINE):
-	cp reglookup-timeline $@
-
-install:
-	install -m 0755 $(FILES) $(BIN_PREFIX)
-
-clean:
-	rm -f *~
diff --git a/doc/Makefile b/doc/Makefile
deleted file mode 100644
index 39ef109..0000000
--- a/doc/Makefile
+++ /dev/null
@@ -1,35 +0,0 @@
-# $Id: Makefile 155 2009-06-03 23:45:58Z tim $
-
-BUILD_FILES=$(BUILD_DOC)/man/man1/reglookup.1.gz\
-	$(BUILD_DOC)/man/man1/reglookup-timeline.1.gz\
-	$(BUILD_DOC)/man/man1/reglookup-recover.1.gz
-
-default: $(BUILD_FILES)
-
-$(BUILD_DOC)/man/man1:
-	mkdir -p $(BUILD_DOC)/man/man1
-
-$(BUILD_DOC)/man/man1/reglookup.1.gz: man/man1/reglookup.1.gz $(BUILD_DOC)/man/man1
-	cp man/man1/reglookup.1.gz $@
-
-$(BUILD_DOC)/man/man1/reglookup-timeline.1.gz: man/man1/reglookup-timeline.1.gz $(BUILD_DOC)/man/man1
-	cp man/man1/reglookup-timeline.1.gz $@
-
-$(BUILD_DOC)/man/man1/reglookup-recover.1.gz: man/man1/reglookup-recover.1.gz $(BUILD_DOC)/man/man1
-	cp man/man1/reglookup-recover.1.gz $@
-
-install:
-	cp -r $(BUILD_DOC)/* $(DOC_PREFIX)
-	ln -sf $(DOC_PREFIX)/man/man1/*  $(MAN_PREFIX)/man1
-
-
-#XXX: Used during release only
-release:
-	#XXX: The sed filter is a temporary hack to work around a bug in docbook2x-man
-	#     See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=516165
-	docbook2x-man --to-stdout reglookup.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup.1
-	docbook2x-man --to-stdout reglookup-timeline.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-timeline.1
-	docbook2x-man --to-stdout reglookup-recover.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-recover.1
-	cd man/man1 && gzip -9 -f reglookup.1
-	cd man/man1 && gzip -9 -f reglookup-timeline.1
-	cd man/man1 && gzip -9 -f reglookup-recover.1
diff --git a/doc/devel/Doxyfile.pyregfi b/doc/devel/Doxyfile.pyregfi
new file mode 100644
index 0000000..e22446b
--- /dev/null
+++ b/doc/devel/Doxyfile.pyregfi
@@ -0,0 +1,1570 @@
+# Doxyfile 1.6.2-20100208
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = pyregfi
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc/devel/pyregfi
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = YES
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set
+# FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = YES
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort
+# the (brief and detailed) documentation of class members so that constructors
+# and destructors are listed first. If set to NO (the default) the
+# constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief
+# docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if
+# SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = python/pyregfi
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.py
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = talloc*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = .
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for
+# more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+#  plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted
+# before the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML
+# help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be 
+# implemented using a PHP enabled web server instead of at the web client using
+# Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server based approach is 
+# that it scales better to large projects and allows full text search. The 
+# disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code 
+# with syntax highlighting in the LaTeX output. Note that which sources are
+# shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = doc/devel/regfi/regfi.tags=../regfi
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = NO
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = NO
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/doc/devel/Doxyfile.regfi b/doc/devel/Doxyfile.regfi
new file mode 100644
index 0000000..78efa53
--- /dev/null
+++ b/doc/devel/Doxyfile.regfi
@@ -0,0 +1,1570 @@
+# Doxyfile 1.6.2-20100208
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = regfi
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc/devel/regfi
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set
+# FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = YES
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = YES
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES   = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS       = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort
+# the (brief and detailed) documentation of class members so that constructors
+# and destructors are listed first. If set to NO (the default) the
+# constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief
+# docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if
+# SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE            =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT                  = lib include
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.c *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = talloc*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = .
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        =
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for
+# more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE               =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+#  will be generated, which together with the HTML files, form an Eclipse help
+#  plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted
+# before the help appears.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES       = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML
+# help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be 
+# implemented using a PHP enabled web server instead of at the web client using
+# Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server based approach is 
+# that it scales better to large projects and allows full text search. The 
+# disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code 
+# with syntax highlighting in the LaTeX output. Note that which sources are
+# shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE      = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA             =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD                =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+ 
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = doc/devel/regfi/regfi.tags
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = NO
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS           =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP            = YES
diff --git a/doc/devel/README b/doc/devel/README
index 2960276..e0040ec 100644
--- a/doc/devel/README
+++ b/doc/devel/README
@@ -1,4 +1,5 @@
-Developer's documentation previously stored here has been removed from the
-main software distribution trunk.  It is still available in the Subversion
-repository, however, and may be obtained here:
-  http://code.google.com/p/reglookup/source/browse/
+This directory contains technical information on the registry format,
+RegLookup design goals and programmer's API usage information. For
+information on how to use the RegLookup tools, refer to the doc
+directory and man pages included in the main release distribution or
+Subversion trunk directory.
diff --git a/doc/devel/TODO b/doc/devel/TODO
new file mode 100644
index 0000000..176545b
--- /dev/null
+++ b/doc/devel/TODO
@@ -0,0 +1,62 @@
+$Id: TODO 262 2011-06-17 17:51:31Z tim $
+
+If you are interested in contributing to this project, here's a few
+things you could look into:
+
+ - Currently there is no way on the command line to search for exotic 
+   paths/types.  For instance, if reglookup encounters an unknown VK
+   type, it just prints it out in Hex.  However, if you wanted to search
+   specifically for that type, there is no way to do it.  Similarly, it
+   isn't possible to specify certain binary or weird characters in
+   paths.  Reglookup should take the user path and unquote each path
+   component using the \xQQ syntax prior to searching.
+
+ - It might be nice to have a way to filter results by security 
+   descriptor information.  Maybe by MTIME as well.
+
+ - reglookup-timeline needs to be replaced with something cross-platform.  
+   Perhaps a python script that provides MTIME range filtering capabilities.
+
+ - Need to integrate much of reglookup-recover's algorithms into regfi 
+   and then expose them from the bottom-up to provide building blocks 
+   through regfi and pyregfi.  This should be addressed along with code 
+   to support handling of partial/fragmented registry hives.
+
+ - Testing, testing, and more testing.  reglookup needs to be more
+   heavily tested on all recent Windows platforms.  A regression test
+   suite would be nice too.  Some thoughts on this include a script
+   which randomly fuzzes an existing registry file, and tries to detect
+   crashes of reglookup when parsing it.  Another test script might
+   randomly truncate an existing registry file, which will help improve
+   reglookup's parsing on fragmentary files.
+
+ - Unicode support still needs improvement.  While parsing strings seems
+   to be decent, UTF-8 output would be nice.
+
+ - Continue to improve regfi/pyregfi APIs as needed.  winsec library needs more
+   flexibility and documentation.
+
+ - Consider adding regfi wrappers for other high-level languages (perl? ruby?).
+
+ - Documentation.  The security descriptor output format needs to be
+   documented.  Also, function contracts should be added to the
+   lower-level functions of regfi.c. Continue adding
+
+ - Consider switching from libiconv to Joachim Metz's libuna for
+   increased portability and easier builds.
+
+ - Grep through the source for 'XXX', and you'll find more.
+
+ - Consider integrating packaging rules for debian/other platforms into trunk.
+
+ - Investigate why file descriptors can't be directly used in Windows
+
+
+1.0 RELEASE
+===========
+
+Testing
+  Full diffs
+  regfi and pyregfi threading
+  valgrind in multiple scenarios for reglookup, reglookup-recover
+  double check man pages
diff --git a/doc/mingw-build.txt b/doc/devel/mingw-build-OBSOLETE.txt
similarity index 100%
rename from doc/mingw-build.txt
rename to doc/devel/mingw-build-OBSOLETE.txt
diff --git a/doc/devel/references.txt b/doc/devel/references.txt
new file mode 100644
index 0000000..f63a0b2
--- /dev/null
+++ b/doc/devel/references.txt
@@ -0,0 +1,34 @@
+- The Windows NT Registry File Format
+  (A work in progress to support this tool.)
+  http://sentinelchicken.com/research/registry_format/
+
+- Recovering Deleted Data From the Windows Registry
+  (The research that is implemented as a PoC in reglookup-recover.)
+  http://sentinelchicken.com/research/registry_recovery/
+
+- Petter Nordahl-Hagen.  Windows NT registry file format description.
+  (The file 'winntreg.txt' included in this distribution is derived from this.)
+  http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt
+
+- Nigel Williams.  Much of the same information as provided in 'winntreg.txt',
+  but with some code:
+  http://www.wednesday.demon.co.uk/dosreg.html
+
+- Some useful information on how Windows reads from and writes to registry
+  hives:
+  http://www.microsoft.com/technet/archive/winntas/tips/winntmag/inreg.mspx
+
+- Registry key, value, and depth limits:
+  http://msdn2.microsoft.com/en-us/library/ms724872.aspx
+
+- Misc references for windows registry permissions and ownership:
+  http://msdn2.microsoft.com/en-gb/library/ms724878.aspx
+  http://technet2.microsoft.com/WindowsServer/en/library/86cf2457-4f17-43f8-a2ab-7f4e2e5659091033.mspx?mfr=true
+  http://msdn2.microsoft.com/en-gb/library/aa374892.aspx
+
+- ACL/ACE flags information
+  http://support.microsoft.com/kb/220167
+  http://msdn2.microsoft.com/en-us/library/aa772242.aspx
+
+- Info on SAM hive, syskey, and hash extraction (with tools bkhive and samdump2):
+  http://www.studenti.unina.it/~ncuomo/syskey/
diff --git a/doc/devel/winntreg.txt b/doc/devel/winntreg.txt
new file mode 100644
index 0000000..31bbdf1
--- /dev/null
+++ b/doc/devel/winntreg.txt
@@ -0,0 +1,272 @@
+The windows NT registry has 2 different blocks, where one can occur many
+times...
+
+the "regf"-Block
+================
+ 
+"regf" is obviosly the abbreviation for "Registry file". "regf" is the
+signature of the header-block which is always 4kb in size, although only
+the first 64 bytes seem to be used and a checksum is calculated over
+the first 0x200 bytes only!
+
+Offset            Size      Contents
+0x00000000      D-Word      ID: ASCII-"regf" = 0x66676572
+0x00000004      D-Word      ???? //see struct REGF
+0x00000008      D-Word      ???? Always the same value as at 0x00000004
+0x0000000C      Q-Word      last modify date in WinNT date-format
+0x00000014      D-Word      1
+0x00000018      D-Word      3
+0x0000001C      D-Word      0
+0x00000020      D-Word      1
+0x00000024      D-Word      Offset of 1st key record
+0x00000028      D-Word      Size of the data-blocks (Filesize-4kb)
+0x0000002C      D-Word      1
+0x000001FC      D-Word      Sum of all D-Words from 0x00000000 to
+                            0x000001FB  //XOR of all words. Nigel
+
+I have analyzed more registry files (from multiple machines running
+NT 4.0 german version) and could not find an explanation for the values
+marked with ???? the rest of the first 4kb page is not important...
+
+the "hbin"-Block
+================
+I dont know what "hbin" stands for, but this block is always a multiple
+of 4kb in size.
+
+Inside these hbin-blocks the different records are placed. The memory-
+management looks like a C-compiler heap management to me...
+
+hbin-Header
+===========
+Offset      Size      Contents
+0x0000      D-Word      ID: ASCII-"hbin" = 0x6E696268
+0x0004      D-Word      Offset from the 1st hbin-Block
+0x0008      D-Word      Offset to the next hbin-Block
+0x001C      D-Word      Block-size
+
+The values in 0x0008 and 0x001C should be the same, so I dont know
+if they are correct or swapped...
+
+From offset 0x0020 inside a hbin-block data is stored with the following
+format:
+
+Offset      Size      Contents
+0x0000      D-Word      Data-block size    //this size must be a
+multiple of 8. Nigel
+0x0004      ????      Data
+ 
+If the size field is negative (bit 31 set), the corresponding block
+is free and has a size of -blocksize!
+
+That does not seem to be true. All block lengths seem to be negative! 
+(Richard Sharpe) 
+
+The data is stored as one record per block. Block size is a multiple
+of 4 and the last block reaches the next hbin-block, leaving no room.
+
+(That also seems incorrect, in that the block size if a multiple of 8.
+That is, the block, including the 4 byte header, is always a multiple of
+8 bytes. Richard Sharpe.)
+
+Records in the hbin-blocks
+==========================
+
+nk-Record
+
+      The nk-record can be treated as a kombination of tree-record and
+      key-record of the win 95 registry.
+
+lf-Record
+
+      The lf-record is the counterpart to the RGKN-record (the
+      hash-function)
+
+vk-Record
+
+      The vk-record consists information to a single value.
+
+sk-Record
+
+      sk (? Security Key ?) is the ACL of the registry.
+
+Value-Lists
+
+      The value-lists contain information about which values are inside a
+      sub-key and dont have a header.
+
+Datas
+
+      The datas of the registry are (like the value-list) stored without a
+      header.
+
+All offset-values are relative to the first hbin-block and point to the
+block-size field of the record-entry. to get the file offset, you have to add
+the header size (4kb) and the size field (4 bytes)...
+
+the nk-Record
+=============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"nk" = 0x6B6E
+0x0002      Word      for the root-key: 0x2C, otherwise 0x20  //key symbolic links 0x10. Nigel
+0x0004      Q-Word      write-date/time in windows nt notation
+0x000C      D-Word      UNKNOWN // added by TDM
+0x0010      D-Word      Offset of Owner/Parent key
+0x0014      D-Word      number of sub-Keys
+0x0018      D-Word      UNKNOWN // added by TDM
+0x001C      D-Word      Offset of the sub-key lf-Records
+0x0020      D-Word      UNKNOWN // added by TDM
+0x0024      D-Word      number of values
+0x0028      D-Word      Offset of the Value-List
+0x002C      D-Word      Offset of the sk-Record
+
+0x0030      D-Word      Offset of the Class-Name //see NK structure for the use of these fields. Nigel
+0x0044      D-Word      Unused (data-trash)  //some kind of run time index. Does not appear to be important. Nigel
+0x0048      Word      name-length
+0x004A      Word      class-name length
+0x004C      ????      key-name
+
+the Value-List
+==============
+Offset      Size      Contents
+0x0000      D-Word      Offset 1st Value
+0x0004      D-Word      Offset 2nd Value
+0x????      D-Word      Offset nth Value
+
+To determine the number of values, you have to look at the owner-nk-record!
+
+Der vk-Record
+=============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"vk" = 0x6B76
+0x0002      Word      name length
+0x0004      D-Word      length of the data   //if top bit is set when offset contains data. Nigel
+0x0008      D-Word      Offset of Data
+0x000C      D-Word      Type of value
+0x0010      Word      Flag
+0x0012      Word      Unused (data-trash)
+0x0014      ????      Name
+
+If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default)
+
+If the data-size is lower 5, the data-offset value is used to store the data itself!
+
+The data-types
+==============
+Wert      Beteutung
+0x0001      RegSZ:             character string (in UNICODE!)
+0x0002      ExpandSZ:   string with "%var%" expanding (UNICODE!)
+0x0003      RegBin:           raw-binary value
+0x0004      RegDWord:   Dword
+0x0007      RegMultiSZ:      multiple strings, seperated with 0
+                  (UNICODE!)
+
+The "lf"/"lh"/"ri"-record (hash list header)
+===============
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"lf" = 0x666C  (or "lh" or "ri")
+0x0002      Word      number of keys
+0x0004      ????      Hash-Records
+
+Hash-Record
+===========
+Offset      Size      Contents
+0x0000      D-Word      Offset of corresponding "nk"-Record
+0x0004      D-Word      ASCII: the first 4 characters of the key-name, padded with 0-s. Case sensitive! 
+			(the hash value may be computed differently for the various header types 
+			 "lf"/"lh"/"ri"/etc)
+Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the 
+key-name you have to change the hash-value too!
+
+//These hashrecords must be sorted low to high within the lf record. Nigel.
+
+The "sk"-block
+==============
+(due to the complexity of the SAM-info, not clear jet)
+(This is just a self-relative security descriptor in the data. R Sharpe.) 
+
+
+Offset      Size      Contents
+0x0000      Word      ID: ASCII-"sk" = 0x6B73
+0x0002      Word      Unused
+0x0004      D-Word      Offset of previous "sk"-Record
+0x0008      D-Word      Offset of next "sk"-Record
+0x000C      D-Word      usage-counter
+0x0010      D-Word      Size of "sk"-record in bytes
+????                                             //standard self
+relative security desciptor. Nigel
+????  ????      Security and auditing settings...
+????
+
+The usage counter counts the number of references to this
+"sk"-record. You can use one "sk"-record for the entire registry!
+
+Windows nt date/time format
+===========================
+The time-format is a 64-bit integer which is incremented every
+0,0000001 seconds by 1 (I dont know how accurate it really is!)
+It starts with 0 at the 1st of january 1601 0:00! All values are
+stored in GMT time! The time-zone is important to get the real
+time!
+
+Common values for win95 and win-nt
+==================================
+Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF).
+If a value has no name (length=0, flag(bit 0)=0), it is treated as the
+"Default" entry...
+If a value has no data (length=0), it is displayed as empty.
+
+simplyfied win-3.?? registry:
+=============================
+
++-----------+
+| next rec. |---+                      +----->+------------+
+| first sub |   |                      |      | Usage cnt. |
+| name      |   |  +-->+------------+  |      | length     |
+| value     |   |  |   | next rec.  |  |      | text       |------->+-------+
++-----------+   |  |   | name rec.  |--+      +------------+        | xxxxx |
+   +------------+  |   | value rec. |-------->+------------+        +-------+
+   v               |   +------------+         | Usage cnt. |
++-----------+      |                          | length     |
+| next rec. |      |                          | text       |------->+-------+
+| first sub |------+                          +------------+        | xxxxx |
+| name      |                                                       +-------+
+| value     |
++-----------+    
+
+Greatly simplyfied structure of the nt-registry:
+================================================
+   
++---------------------------------------------------------------+
+|                                                               |
+v                                                               |
++---------+     +---------->+-----------+  +----->+---------+   |
+| "nk"    |     |           | lf-rec.   |  |      | nk-rec. |   |
+| ID      |     |           | # of keys |  |      | parent  |---+
+| Date    |     |           | 1st key   |--+      | ....    |
+| parent  |     |           +-----------+         +---------+
+| suk-keys|-----+
+| values  |--------------------->+----------+
+| SK-rec. |---------------+      | 1. value |--> +----------+
+| class   |--+            |      +----------+    | vk-rec.  |
++---------+  |            |                      | ....     |
+             v            |                      | data     |--> +-------+
+      +------------+      |                      +----------+    | xxxxx |
+      | Class name |      |                                      +-------+
+      +------------+      |
+                          v
+          +---------+    +---------+
+   +----->| next sk |--->| Next sk |--+
+   |  +---| prev sk |<---| prev sk |  |
+   |  |   | ....    |    | ...     |  |
+   |  |   +---------+    +---------+  |
+   |  |                    ^          |
+   |  |                    |          |
+   |  +--------------------+          |
+   +----------------------------------+
+
+---------------------------------------------------------------------------
+
+Hope this helps....  (Although it was *fun* for me to uncover this things,
+                  it took me several sleepless nights ;)
+
+            B.D.
diff --git a/doc/man/man1/reglookup-recover.1.gz b/doc/man/man1/reglookup-recover.1.gz
deleted file mode 100644
index 82e8ad0..0000000
Binary files a/doc/man/man1/reglookup-recover.1.gz and /dev/null differ
diff --git a/doc/man/man1/reglookup-timeline.1.gz b/doc/man/man1/reglookup-timeline.1.gz
deleted file mode 100644
index ddbb3df..0000000
Binary files a/doc/man/man1/reglookup-timeline.1.gz and /dev/null differ
diff --git a/doc/man/man1/reglookup.1.gz b/doc/man/man1/reglookup.1.gz
deleted file mode 100644
index 872fb55..0000000
Binary files a/doc/man/man1/reglookup.1.gz and /dev/null differ
diff --git a/doc/reglookup-recover.1.docbook b/doc/reglookup-recover.1.docbook
index 95f47f5..672409c 100644
--- a/doc/reglookup-recover.1.docbook
+++ b/doc/reglookup-recover.1.docbook
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <refentry id='reglookup-recover.1'>
-  <!--  $Id: reglookup-recover.1.docbook 138 2009-02-08 19:53:48Z tim $ -->
+  <!--  $Id: reglookup-recover.1.docbook 264 2011-06-20 01:13:35Z tim $ -->
   <refmeta>
     <refentrytitle>reglookup</refentrytitle>
     <manvolnum>1</manvolnum>
@@ -199,6 +199,8 @@
     <para>
       For more information on registry format details and the recovery 
       algorithm, see:
+    </para>
+    <para>
         http://sentinelchicken.com/research/registry_format/
         http://sentinelchicken.com/research/registry_recovery/
     </para>
diff --git a/doc/reglookup-recover.1.gz b/doc/reglookup-recover.1.gz
new file mode 100644
index 0000000..f72c0cb
Binary files /dev/null and b/doc/reglookup-recover.1.gz differ
diff --git a/doc/reglookup-timeline.1.gz b/doc/reglookup-timeline.1.gz
new file mode 100644
index 0000000..0bdecee
Binary files /dev/null and b/doc/reglookup-timeline.1.gz differ
diff --git a/doc/reglookup.1.docbook b/doc/reglookup.1.docbook
index 7bf3a83..c1480eb 100644
--- a/doc/reglookup.1.docbook
+++ b/doc/reglookup.1.docbook
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <refentry id='reglookup.1'>
-  <!--  $Id: reglookup.1.docbook 176 2010-03-09 03:10:10Z tim $ -->
+  <!--  $Id: reglookup.1.docbook 264 2011-06-20 01:13:35Z tim $ -->
   <refmeta>
     <refentrytitle>reglookup</refentrytitle>
     <manvolnum>1</manvolnum>
@@ -16,7 +16,7 @@
     <para>
       <command>
 	reglookup [options] <replaceable>registry-file</replaceable>
-      </command> 
+      </command>
     </para>
   </refsect1>
 
@@ -67,7 +67,7 @@
 	    and
 	    <command>
 	      KEY
-	    </command>
+	    </command>.
 	  </para>
         </listitem>
       </varlistentry>
@@ -185,7 +185,7 @@
       and writes them to stdout.  The format is designed to simplify parsing 
       algorithms of other tools by quoting CSV special characters using a 
       common hexadecimal format.  Specifically, special characters or non-ascii 
-      bytes are converted to "\xQQ" where QQ is the hexadecimal value for 
+      bytes are converted to "%XX" where XX is the hexadecimal value for 
       the byte.
     </para>
     <para>
@@ -325,11 +325,6 @@
       this field is very poor.
     </para>
     <para>
-      Backslashes are currently considered special characters, to make 
-      parsing easier for automated tools.  However, this causes paths 
-      to be difficult to read by mere mortals.
-    </para>
-    <para>
       For more information on registry format details, see:
         http://sentinelchicken.com/research/registry_format/
     </para>
diff --git a/doc/reglookup.1.gz b/doc/reglookup.1.gz
new file mode 100644
index 0000000..e7496fb
Binary files /dev/null and b/doc/reglookup.1.gz differ
diff --git a/include/compat.h b/include/compat.h
new file mode 100644
index 0000000..576dce1
--- /dev/null
+++ b/include/compat.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010-2011 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: regfi.h 252 2011-05-08 17:33:49Z tim $
+ */
+
+#ifndef _COMPAT_H
+#define _COMPAT_H
+
+/* GCC-specific macro for library exports */
+#ifdef _EXPORT
+#undef _EXPORT
+#endif
+#ifdef REGFI_WIN32
+#define _EXPORT() __declspec(dllexport)
+#else
+#define _EXPORT() __attribute__((visibility("default")))
+#endif
+
+#ifndef EOVERFLOW
+# define EOVERFLOW E2BIG
+#endif
+
+#endif /*_COMPAT_H*/
diff --git a/include/lru_cache.h b/include/lru_cache.h
index b718bca..6dfe467 100644
--- a/include/lru_cache.h
+++ b/include/lru_cache.h
@@ -14,7 +14,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: lru_cache.h 169 2010-03-03 19:24:58Z tim $
+ * $Id: lru_cache.h 253 2011-06-13 02:27:42Z tim $
  */
 
 /**
@@ -34,7 +34,10 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
-#include "talloc.h"
+#include <talloc.h>
+
+#include "compat.h"
+
 
 struct lru_cache_element;
 typedef struct lru_cache_element lru_cache_element; 
@@ -67,12 +70,14 @@ typedef struct _lru_cache
 /**
  * XXX: finish documenting.
  */
+_EXPORT()
 lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret);
 
 
 /**
  * XXX: finish documenting.
  */
+_EXPORT()
 lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, 
 				uint32_t secret, bool talloc_data);
 
@@ -80,12 +85,14 @@ lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
 /**
  * XXX: finish documenting.
  */
+_EXPORT()
 void lru_cache_destroy(lru_cache* ht);
 
 
 /**
  * XXX: finish documenting.
  */
+_EXPORT()
 bool lru_cache_update(lru_cache* ht, const void* index, 
 		      uint32_t index_len, void* data);
 
@@ -95,6 +102,7 @@ bool lru_cache_update(lru_cache* ht, const void* index,
  * @return A pointer to data previously stored at index.
  *         If no data was found at index, NULL is returned.
  */
+_EXPORT()
 void* lru_cache_find(lru_cache* ht, const void* index, 
 		     uint32_t index_len);
 
@@ -106,6 +114,7 @@ void* lru_cache_find(lru_cache* ht, const void* index,
  * @return A pointer to data that was there previously or NULL if no entry is
  *         at index.
  */
+_EXPORT()
 bool lru_cache_remove(lru_cache* ht, const void* index, 
 		      uint32_t index_len);
 
diff --git a/include/range_list.h b/include/range_list.h
index 65c4643..356c08e 100644
--- a/include/range_list.h
+++ b/include/range_list.h
@@ -14,7 +14,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: range_list.h 169 2010-03-03 19:24:58Z tim $
+ * $Id: range_list.h 253 2011-06-13 02:27:42Z tim $
  */
 
 /**
@@ -35,7 +35,9 @@
 #include <stdint.h>
 #include <string.h>
 #include <math.h>
-#include "talloc.h"
+#include <talloc.h>
+
+#include "compat.h"
 
 typedef struct _range_list_element
 {
@@ -58,6 +60,7 @@ typedef struct _range_list
  *
  * @return A newly allocated range_list, or NULL if an error occurred.
  */
+_EXPORT()
 range_list* range_list_new();
 
 
@@ -68,6 +71,7 @@ range_list* range_list_new();
  *
  * @param rl the range_list to be free()d.
  */
+_EXPORT()
 void range_list_free(range_list* rl);
 
 
@@ -77,6 +81,7 @@ void range_list_free(range_list* rl);
  *
  * @return The number of elements currently in the list.
  */
+_EXPORT()
 uint32_t range_list_size(const range_list* rl);
 
 
@@ -96,6 +101,7 @@ uint32_t range_list_size(const range_list* rl);
  * or if the submitted range overlaps with an existing element.  Other
  * errors may also be possible.
  */
+_EXPORT()
 bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data);
 
 
@@ -108,6 +114,7 @@ bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data
  *
  * @return true if the element was successfully removed, false otherwise.
  */
+_EXPORT()
 bool range_list_remove(range_list* rl, uint32_t index);
 
 
@@ -119,6 +126,7 @@ bool range_list_remove(range_list* rl, uint32_t index);
  * @return The element for a given index, or NULL if the element is not 
  *         available.
  */
+_EXPORT()
 const range_list_element* range_list_get(const range_list* rl, uint32_t index);
 
 
@@ -129,6 +137,7 @@ const range_list_element* range_list_get(const range_list* rl, uint32_t index);
  *
  * @return A matching element index or a negative value if none could be found.
  */
+_EXPORT()
 int32_t range_list_find(const range_list* rl, uint32_t offset);
 
 
@@ -143,6 +152,7 @@ int32_t range_list_find(const range_list* rl, uint32_t offset);
  *  NOTE: May also return NULL if an element matched but the data
  *        element was never set.
  */
+_EXPORT()
 void* range_list_find_data(const range_list* rl, uint32_t offset);
 
 
@@ -164,6 +174,7 @@ void* range_list_find_data(const range_list* rl, uint32_t offset);
  *
  * @return true if the element was successfully split, false otherwise.
  */
+_EXPORT()
 bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset);
 
 
@@ -176,6 +187,7 @@ bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset);
  *
  * @return true if the specified range exists and is complete, false otherwise.
  */
+_EXPORT()
 bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length);
 
 #endif
diff --git a/include/regfi.h b/include/regfi.h
index 2f3ae08..962ad22 100644
--- a/include/regfi.h
+++ b/include/regfi.h
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2005-2011 Timothy D. Morgan
+ * Copyright (C) 2010 Michael Cohen
  * Copyright (C) 2005 Gerald (Jerry) Carter
  *
  * This program is free software; you can redistribute it and/or modify
@@ -15,7 +16,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: regfi.h 172 2010-03-08 03:04:34Z tim $
+ * $Id: regfi.h 263 2011-06-18 00:06:51Z tim $
  */
 
 /**
@@ -47,7 +48,7 @@
  * @li @ref regfiParseLayer
  *
  * Most users will find that a combination of the Base Layer and the Iterator Layer 
- * will be sufficient for accessing registry hive files.  Those who are wiling
+ * will be sufficient for accessing registry hive files.  Those who are willing
  * to dive deep into registry data structures, for instance to recover deleted
  * data structures or to research Windows registry behavior in detail, will 
  * find the Parse Layer to be quite useful.
@@ -68,59 +69,68 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <iconv.h>
+#include <pthread.h>
+#include <talloc.h>
 
+/* regfi headers */
+#include "compat.h"
 #include "byteorder.h"
-#include "talloc.h"
 #include "winsec.h"
 #include "void_stack.h"
 #include "range_list.h"
 #include "lru_cache.h"
 
 /******************************************************************************/
+/* Constants for use while interacting with the library                       */
+/******************************************************************************/
 
 /* regfi library error message types */
-#define REGFI_MSG_INFO  0x0001
-#define REGFI_MSG_WARN  0x0004
-#define REGFI_MSG_ERROR 0x0010
+#define REGFI_LOG_INFO  0x0001
+#define REGFI_LOG_WARN  0x0004
+#define REGFI_LOG_ERROR 0x0010
+#define REGFI_DEFAULT_LOG_MASK REGFI_LOG_ERROR|REGFI_LOG_WARN
 
-typedef uint8_t REGFI_ENCODING;
 /* regfi library supported character encodings */
-#define REGFI_ENCODING_ASCII   0
-#define REGFI_ENCODING_UTF8    1
-#define REGFI_ENCODING_DEFAULT REGFI_ENCODING_ASCII
 /* UTF16LE is not supported for output */
-#define REGFI_ENCODING_UTF16LE 2
-
-#define REGFI_NUM_ENCODINGS    3
-
-/* Windows is lame */
-#ifdef O_BINARY
-#define REGFI_OPEN_FLAGS O_RDONLY|O_BINARY
-#else
-#define REGFI_OPEN_FLAGS O_RDONLY
-#endif
+typedef enum {
+  REGFI_ENCODING_DEFAULT  = 0,
+  REGFI_ENCODING_ASCII =   0,
+  REGFI_ENCODING_UTF8  =  1,
+  REGFI_ENCODING_UTF16LE = 2,
+  REGFI_NUM_ENCODINGS  =  3
+} REGFI_ENCODING;
 
 /* Registry data types */
-#define REG_NONE                       0
-#define REG_SZ		               1
-#define REG_EXPAND_SZ                  2
-#define REG_BINARY 	               3
-#define REG_DWORD	               4
-#define REG_DWORD_LE	               4  /* DWORD, little endian */
-#define REG_DWORD_BE	               5  /* DWORD, big endian */
-#define REG_LINK                       6
-#define REG_MULTI_SZ  	               7
-#define REG_RESOURCE_LIST              8
-#define REG_FULL_RESOURCE_DESCRIPTOR   9
-#define REG_RESOURCE_REQUIREMENTS_LIST 10
-#define REG_QWORD                      11 /* 64-bit little endian */
+typedef enum {
+  REG_NONE                   =    0,
+  REG_SZ		     =    1,
+  REG_EXPAND_SZ              =    2,
+  REG_BINARY 	             =    3,
+  REG_DWORD	             =    4,
+  REG_DWORD_LE	             =    4 , /* DWORD, little endian */
+  REG_DWORD_BE	             =    5 , /* DWORD, big endian */
+  REG_LINK                   =    6,
+  REG_MULTI_SZ  	     =    7,
+  REG_RESOURCE_LIST          =    8,
+  REG_FULL_RESOURCE_DESCRIPTOR=   9,
+  REG_RESOURCE_REQUIREMENTS_LIST= 10,
+  REG_QWORD                     = 11, /* 64-bit little endian */
 /* XXX: Has MS defined a REG_QWORD_BE? */
 /* Not a real type in the registry */
-#define REG_KEY                    0x7FFFFFFF
-
+  REG_KEY                 =   0x7FFFFFFF
+} REGFI_DATA_TYPE;
 #define REGFI_OFFSET_NONE          0xffffffff
 
 
+
+/******************************************************************************/
+/* Various resource limits and related constants                              */
+/******************************************************************************/
+
+/* Flags determining how many records to cache internally */
+#define REGFI_CACHE_SK_MAX         64
+#define REGFI_CACHE_NK_MAX         1024
+
 /* This maximum depth is described here:
  * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
  */
@@ -135,6 +145,13 @@ typedef uint8_t REGFI_ENCODING;
 #define REGFI_MAX_SUBKEY_DEPTH     255
 
 
+/******************************************************************************/
+/* Symbols for internal use                                                   */
+/******************************************************************************/
+
+/* Global thread-local storage key */
+pthread_key_t regfi_log_key;
+
 /* Header sizes and magic number lengths for various records */
 #define REGFI_HBIN_ALLOC           0x1000 /* Minimum allocation unit for HBINs */
 #define REGFI_REGF_SIZE            0x1000 /* "regf" header block size */
@@ -160,13 +177,12 @@ typedef uint8_t REGFI_ENCODING;
  *      perhaps conservatively implement a check.
  */
  /* Minimum time is Jan 1, 1990 00:00:00 */
-#define REGFI_MTIME_MIN_HIGH       0x01B41E6D
-#define REGFI_MTIME_MIN_LOW        0x26F98000
+#define REGFI_MTIME_MIN            0x01B41E6D00000000L
+
  /* Maximum time is Jan 1, 2290 00:00:00
   * (We hope no one is using Windows by then...) 
   */
-#define REGFI_MTIME_MAX_HIGH       0x03047543
-#define REGFI_MTIME_MAX_LOW        0xC80A4000
+#define REGFI_MTIME_MAX            0x0304754300000000L
 
 
 /* Flags for the vk records */
@@ -233,17 +249,32 @@ typedef uint8_t REGFI_ENCODING;
                                     | REGFI_NK_FLAG_UNKNOWN3)
 
 
+#ifndef CHAR_BIT
 #define CHAR_BIT 8
+#endif
+
 #define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \
 		    : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
 #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
-#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+#define REGFI_TIME_FIXUP (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
+
 
-typedef struct _regfi_nttime
+
+/******************************************************************************/
+/* Structures                                                                 */
+/******************************************************************************/
+
+typedef uint64_t REGFI_NTTIME;
+
+typedef struct _regfi_log
 {
-  uint32_t low;
-  uint32_t high;
-} REGFI_NTTIME;
+  /* Error/warning/info messages returned by lower layer functions */
+  char* messages;
+
+  /* Mask for error message types that will be stored. */
+  uint16_t msg_mask;
+
+} REGFI_LOG;
 
 
 /** HBIN block information
@@ -297,16 +328,16 @@ typedef struct _regfi_subkey_list
   uint32_t cell_size;
   
   /* Number of immediate children */
-  uint32_t num_children;  
+  uint32_t num_children;
 
-  /* Total number of keys referenced by this list and it's children */
-  uint32_t num_keys;      
+  /* Total number of keys referenced by this list and its children */
+  uint32_t num_keys;
 
   REGFI_SUBKEY_LIST_ELEM* elements;
   uint8_t magic[REGFI_CELL_MAGIC_SIZE];
 
   /* Set if the magic indicates this subkey list points to child subkey lists */
-  bool recursive_type;  
+  bool recursive_type;
 } REGFI_SUBKEY_LIST;
 
 
@@ -316,6 +347,11 @@ typedef uint32_t REGFI_VALUE_LIST_ELEM;
  */
 typedef struct _regfi_value_list
 {
+  /* Real offset of this record's cell in the file */
+  uint32_t offset;
+
+  uint32_t cell_size;
+
   /* Actual number of values referenced by this list.  
    * May differ from parent key's num_values if there were parsing errors. 
    */
@@ -330,6 +366,9 @@ typedef struct _regfi_value_list
  */
 typedef struct _regfi_classname
 {
+  /** Real offset of this record's cell in the file */
+  uint32_t offset;
+
   /** As converted to requested REGFI_ENCODING */
   char* interpreted;
 
@@ -352,8 +391,13 @@ typedef struct _regfi_classname
  */
 typedef struct _regfi_data
 {
+  /* XXX: this isn't populated yet. Should set it to start of data cell
+   *      or big data cell. 
+   */
+  uint32_t offset;
+
   /** Data type of this data, as indicated by the referencing VK record. */
-  uint32_t type;
+  REGFI_DATA_TYPE type;
 
   /** Length of the raw data. */
   uint32_t size;
@@ -361,7 +405,9 @@ typedef struct _regfi_data
   /** This is always present, representing the raw data cell contents. */
   uint8_t* raw;
 
-  /** Represents the length of the interpreted value. Meaning is type-specific. */
+  /** Represents the length of the interpreted value. Meaning is type-specific. 
+   *  Will be 0 if interpretation failed for any reason.
+   */
   uint32_t interpreted_size;
 
   /** These items represent interpreted versions of the REGFI_DATA::raw field.
@@ -454,7 +500,7 @@ typedef struct _regfi_data
 /** Value structure
  * @ingroup regfiBase
  */
-typedef struct
+typedef struct _regfi_vk
 {
   /** Real offset of this record's cell in the file */
   uint32_t offset;	
@@ -462,23 +508,20 @@ typedef struct
   /** ((start_offset - end_offset) & 0xfffffff8) */
   uint32_t cell_size;
 
-  /* XXX: deprecated */
-  REGFI_DATA* data;
-
   /** The name of this value converted to desired REGFI_ENCODING.  
    *
    * This conversion typically occurs automatically through REGFI_ITERATOR
    * settings.  String is NUL terminated.
    */
-  char*    valuename;
+  char* name;
 
   /** The raw value name
    *
    * Length of the buffer is stored in name_length.
    */
-  uint8_t* valuename_raw;
+  uint8_t* name_raw;
 
-  /** Length of valuename_raw */
+  /** Length of name_raw */
   uint16_t name_length;
 
   /** Offset from beginning of this hbin block */
@@ -494,7 +537,7 @@ typedef struct
   uint32_t data_off;
 
   /** Value's data type */
-  uint32_t type;
+  REGFI_DATA_TYPE type;
 
   /** VK record's magic number (should be "vk") */
   uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
@@ -510,16 +553,20 @@ typedef struct
    * This information is derived from the high bit of the raw data size field.
    */
   bool     data_in_offset;
-} REGFI_VK_REC;
+
+  /* XXX: deprecated */
+  REGFI_DATA* data;
+
+} REGFI_VK;
 
 
 /* Key Security */
-struct _regfi_sk_rec;
+struct _regfi_sk;
 
 /** Security structure
  * @ingroup regfiBase
  */
-typedef struct _regfi_sk_rec 
+typedef struct _regfi_sk 
 {
   /** Real file offset of this record */
   uint32_t offset;
@@ -550,13 +597,13 @@ typedef struct _regfi_sk_rec
 
   /** The magic number for this record (should be "sk") */
   uint8_t  magic[REGFI_CELL_MAGIC_SIZE];
-} REGFI_SK_REC;
+} REGFI_SK;
 
 
 /** Key structure
  * @ingroup regfiBase
  */
-typedef struct
+typedef struct _regfi_nk
 {
   /** Real offset of this record's cell in the file */
   uint32_t offset;
@@ -588,7 +635,7 @@ typedef struct
   /** Key's last modification time */
   REGFI_NTTIME mtime;
 
-  /** Length of keyname_raw */
+  /** Length of name_raw */
   uint16_t name_length;
 
   /** Length of referenced classname */
@@ -599,18 +646,18 @@ typedef struct
    * This conversion typically occurs automatically through REGFI_ITERATOR
    * settings.  String is NUL terminated.
    */
-  char* keyname;
+  char* name;
 
   /** The raw key name
    *
    * Length of the buffer is stored in name_length.
    */
-  uint8_t* keyname_raw;
+  uint8_t* name_raw;
 
-  /** Virutal offset of parent key */
+  /** Virtual offset of parent key */
   uint32_t parent_off;
 
-  /** Virutal offset of classname key */
+  /** Virtual offset of classname key */
   uint32_t classname_off;
   
   /* XXX: max subkey name * 2 */
@@ -619,7 +666,7 @@ typedef struct
   /* XXX: max subkey classname length (as if) */
   uint32_t max_bytes_subkeyclassname;
 
-  /* XXX: max valuename * 2 */
+  /* XXX: max value name * 2 */
   uint32_t max_bytes_valuename;
 
   /* XXX: max value data size */
@@ -645,8 +692,18 @@ typedef struct
 
   /** Virtual offset of SK record */
   uint32_t sk_off;
-} REGFI_NK_REC;
+} REGFI_NK;
+
+
+typedef struct _regfi_raw_file
+{
+  int64_t  (* seek)(); /* (REGFI_RAW_FILE* self, uint64_t offset, int whence) */
+  ssize_t  (* read)(); /* (REGFI_RAW_FILE* self, void* buf, size_t count) */
 
+  uint64_t cur_off;
+  uint64_t size;
+  void*    state;
+} REGFI_RAW_FILE;
 
 
 /** Registry hive file data structure
@@ -654,39 +711,18 @@ typedef struct
  * This essential structure stores run-time information about a single open
  * registry hive as well as file header (REGF block) data.  This structure 
  * also stores a list of warnings and error messages generated while parsing
- * the registry hive.  These can be tuned using @ref regfi_set_message_mask.  
- * Messages may be retrieved using @ref regfi_get_messages.
+ * the registry hive.  These can be tuned using @ref regfi_log_set_mask.  
+ * Messages may be retrieved using @ref regfi_log_get_str.
  *
  * @note If the message mask is set to record any messages, dependent code 
- *       must use @ref regfi_get_messages periodically to clear the message
+ *       must use @ref regfi_log_get_str periodically to clear the message
  *       queue. Otherwise, this structure will grow in size over time as 
  *       messages queue up.
  *
  * @ingroup regfiBase
  */ 
-typedef struct 
+typedef struct _regfi_file
 {
-  /* Run-time information */
-  /************************/
-  /* file descriptor */
-  int fd;
-
-  /* For sanity checking (not part of the registry header) */
-  uint32_t file_length;
-
-  /* Metadata about hbins */
-  range_list* hbins;
-
-  /* SK record cached since they're repeatedly reused */
-  lru_cache* sk_cache;
-
-  /* Error/warning/info messages returned by lower layer functions */
-  char* last_message;
-
-  /* Mask for error message types that will be stored. */
-  uint16_t msg_mask;
-
-
   /* Data parsed from file header */
   /********************************/
   uint8_t  magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */
@@ -737,9 +773,68 @@ typedef struct
    * or doing research. */
   uint8_t reserved2[REGFI_REGF_RESERVED2_SIZE];
 
+
+  /* Run-time information */
+  /************************/
+  /* For sanity checking (not part of the registry header) */
+  uint32_t file_length;
+
+  /** The encoding that all strings are converted to during interpretation.
+   */
+  REGFI_ENCODING string_encoding;
+
+  /* Functions for accessing the file */
+  REGFI_RAW_FILE* cb;
+
+  /* Mutex for all cb access.  This is done to prevent one thread from moving
+   * the file offset while another thread is in the middle of a multi-read
+   * parsing transaction */
+  pthread_mutex_t cb_lock;
+
+  /* Metadata about hbins */
+  range_list* hbins;
+
+  /* Multiple read access allowed, write access is exclusive */
+  pthread_rwlock_t hbins_lock;
+
+  /* Small number of SK records cached */
+  lru_cache* sk_cache;
+
+  /* Need exclusive access for LRUs, since lookups make changes */
+  pthread_mutex_t sk_lock;
+
+  /* Limited number of keys cached */
+  lru_cache* nk_cache;
+
+  /* Need exclusive access for LRUs, since lookups make changes */
+  pthread_mutex_t nk_lock;
+
+  /* Needed to protect various talloc calls */
+  pthread_mutex_t mem_lock;
+
 } REGFI_FILE;
 
 
+typedef struct _regfi_iter_position
+{
+  /* key offset */
+  uint32_t offset;
+
+  /* Index of the current subkey */
+  uint32_t cur_subkey;
+
+  /* Index of the current value */
+  uint32_t cur_value;
+
+  /* The number of subkeys of this key */
+  uint32_t num_subkeys;
+
+  /* The number of values of this key */
+  uint32_t num_values;
+
+} REGFI_ITER_POSITION;
+
+
 /** Registry hive iterator
  * @ingroup regfiIteratorLayer
  */
@@ -751,31 +846,10 @@ typedef struct _regfi_iterator
   /** All current parent keys and associated iterator positions */
   void_stack* key_positions;
 
-  /** The current key */
-  REGFI_NK_REC* cur_key;
-
-  /** The encoding that all strings are converted to as set during iterator
-   *  creation.
-   */
-  REGFI_ENCODING string_encoding;
-
-  /** Index of the current subkey */
-  uint32_t cur_subkey;
-
-  /** Index of the current value */
-  uint32_t cur_value;
+  REGFI_ITER_POSITION* cur;
 } REGFI_ITERATOR;
 
 
-typedef struct _regfi_iter_position
-{
-  REGFI_NK_REC* nk;
-  uint32_t cur_subkey;
-  /* We could store a cur_value here as well, but didn't see 
-   * the use in it right now.
-   */
-} REGFI_ITER_POSITION;
-
 
 /** General purpose buffer with stored length
  * @ingroup regfiBottomLayer
@@ -797,17 +871,16 @@ typedef struct _regfi_buffer
  */
 /******************************************************************************/
 
-/** Attempts to open a registry hive and allocate related data structures.
- * 
- * @param filename A string containing the relative or absolute path of the
- *               registry hive to be opened.
+
+
+/** Returns the current regfi library version
  *
- * @return A reference to a newly allocated REGFI_FILE structure, 
- *         if successful;  NULL on error.
+ * @return A string indicating the version.
  *
  * @ingroup regfiBase
  */
-REGFI_FILE*           regfi_open(const char* filename);
+_EXPORT()
+const char* regfi_version();
 
 
 /** Parses file headers of an already open registry hive file and 
@@ -815,24 +888,46 @@ REGFI_FILE*           regfi_open(const char* filename);
  *
  * @param fd A file descriptor of an already open file.  Must be seekable.
  *
+ * @param output_encoding Character encoding that strings should be returned in.
+ *                        Only supply the REGFI_ENCODING_* constants, as others 
+ *                        will be rejected.
+ *                        The following values are currently accepted:
+ *                        REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
+ *                        REGFI_ENCODING_ASCII
+ *                        REGFI_ENCODING_UTF8
+ *
  * @return A reference to a newly allocated REGFI_FILE structure, if successful;
- *         NULL on error.
+ *         NULL on error.  Use regfi_free to free the returned REGFI_FILE.
  *
  * @ingroup regfiBase
  */
-REGFI_FILE*           regfi_alloc(int fd);
+_EXPORT()
+REGFI_FILE* regfi_alloc(int fd, REGFI_ENCODING output_encoding);
 
 
-/** Closes and frees an open registry hive.
+/** Parses file headers returned by supplied callback functions.
+ *
+ * This interface is useful if you have a registry hive in memory
+ * or have some other reason to emulate a real file.
+ *
+ * @param file_cb A structure defining the callback functions needed to access the file. 
  *
- * @param file The registry structure to close.
+ * @param output_encoding Character encoding that strings should be returned in.
+ *                        Only supply the REGFI_ENCODING_* constants, as others 
+ *                        will be rejected.
+ *                        The following values are currently accepted:
+ *                        REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
+ *                        REGFI_ENCODING_ASCII
+ *                        REGFI_ENCODING_UTF8
  *
- * @return 0 on success, -1 on failure with errno set.  
- *         errno codes are similar to those of close(2).
+ * @return A reference to a newly allocated REGFI_FILE structure, if successful;
+ *         NULL on error.  Use regfi_free to free the returned REGFI_FILE.
  *
  * @ingroup regfiBase
  */
-int                   regfi_close(REGFI_FILE* file);
+_EXPORT()
+REGFI_FILE* regfi_alloc_cb(REGFI_RAW_FILE* file_cb,
+			   REGFI_ENCODING output_encoding);
 
 
 /** Frees a hive's data structures without closing the underlying file.
@@ -841,67 +936,299 @@ int                   regfi_close(REGFI_FILE* file);
  *
  * @ingroup regfiBase
  */
-void                  regfi_free(REGFI_FILE* file);
+_EXPORT()
+void regfi_free(REGFI_FILE* file);
 
 
 /** Get errors, warnings, and/or verbose information relating to processing of
  *  the given registry file.
  *
- * @param file the structure for the registry file
- *
  * @return A newly allocated char* which must be free()d by the caller.
  *
  * @ingroup regfiBase
  */
-char*                 regfi_get_messages(REGFI_FILE* file);
+_EXPORT()
+char* regfi_log_get_str();
 
 
-/** Set the verbosity level of errors and warnings generated by the library
- *  (as accessible via regfi_get_messages).
- *
- * This may be called at any time and will take effect immediately.
- *
- * @param file   the structure for the registry file
+/** Set the verbosity level of messages generated by the library for the 
+ *  current thread.
  *
- * @param mask   an integer representing the types of messages desired.
+ * @param mask   An integer representing the types of messages desired.
  *               Acceptable values are created through bitwise ORs of 
- *               REGFI_MSG_* values.  For instance, if only errors and
+ *               REGFI_LOG_* values.  For instance, if only errors and
  *               informational messages were desired (but not warnings),
- *               then one would specify: REGFI_MSG_ERROR|REGFI_MSG_INFO
- *               New REGFI_FILE structures are created with:
- *                REGFI_MSG_ERROR|REGFI_MSG_WARN
- *               Note that error and warning messages will continue to
- *               accumulate in memory if they are not fetched using
- *               regfi_get_messages and then freed by the caller.
- *               To disable error messages entirely, supply 0, which
- *               will prevent message accumulation.  
+ *               then one would specify: REGFI_LOG_ERROR|REGFI_LOG_INFO
+ *               By default the message mask is: REGFI_LOG_ERROR|REGFI_LOG_WARN.
+ *
+ * @return       true on success and false on failure.  Failure occurs if 
+ *               underlying pthread functions fail.  errno is set in this case.
+ *
+ * Message masks are set in a thread-specific way.  If one were to set a message
+ * mask in one thread and then spawn a new thread, then the new thread will have
+ * it's message mask reset to the default.  This function may be called at any 
+ * time and will take effect immediately for the current thread.
+ *
+ * @note When a non-zero message mask is set, messages will
+ *       accumulate in memory without limit if they are not fetched using
+ *       @ref regfi_log_get_str and subsequently freed by the caller.  It is
+ *       recommended that messsages be fetched after each regfi API call in
+ *       order to provide the most context.
  *
  * @ingroup regfiBase
  */
-void                  regfi_set_message_mask(REGFI_FILE* file, uint16_t mask);
+_EXPORT()
+bool regfi_log_set_mask(uint16_t mask);
 
 
-/* Dispose of previously parsed records */
+/** Fetches a hive's root key.
+ *
+ * @return Returns the root key or NULL on failure.  Key must be freed using
+ *         @ref regfi_free_record.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_NK*       regfi_get_rootkey(REGFI_FILE* file);
+
 
-/** Frees a key structure previously returned by one of the API functions
+/** Frees a record previously returned by one of the API functions.
  *
- * XXX: finish documenting
+ * @param file The file from which the record originated.  
+ *             (This is needed for memory management reasons.)
+ * 
+ * @param record Any of the following record types: REGFI_NK, REGFI_VK, 
+ *        REGFI_SK, REGFI_DATA, and REGFI_CLASSNAME records.
+ *
+ * @note The "const" in the record data type is a bit misleading and is there just for
+ * convenience.  Since records returned previously must not be modified by users
+ * of the API due to internal caching, these are returned as const, so this
+ * function is const to make passing those records back easy.
  *
  * @ingroup regfiBase
  */
-void                  regfi_free_key(REGFI_NK_REC* nk);
+_EXPORT()
+void regfi_free_record(REGFI_FILE* file, const void* record);
 
 
-/** Frees a value structure previously returned by one of the API functions
+/** Increments reference count on record
  *
- * XXX: finish documenting
+ * Adds an extra internal reference to specified record, making it necessary to
+ * call regfi_free_record on it an additional time before it is freed.  This is
+ * useful in cases where multiple threads/structures need access to a shared record,
+ * without requiring them to be in sync with when it is freed.
+ *
+ * @param file The file from which the record originated.  
+ *             (This is needed for memory management reasons.)
+ * 
+ * @param record Any of the following record types: REGFI_NK, REGFI_VK, 
+ *        REGFI_SK, REGFI_DATA, and REGFI_CLASSNAME records.
+ *
+ * @return Updated record pointer on success, NULL otherwise
+ *
+ * @note Be sure to use the returned record for further access to the structure
+ *       instead of the previous version of the pointer.  E.g.:
+ *       @code 
+ *       myKey = (const REGFI_NK*)regfi_reference_record(myFile, myKey);
+ *       @endcode
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const void* regfi_reference_record(REGFI_FILE* file, const void* record);
+
+
+/** Retrieves number of subkeys referenced by this key.
+ *
+ * Number of subkeyss in key structure and subkey list structure could differ,
+ * so this provides a standard/sane way of determining the number.
+ *
+ * @param key  the key whose number of subkeys is desired
+ *
+ * @return Returns the number of subkeys referenced by this key.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+uint32_t regfi_fetch_num_subkeys(const REGFI_NK* key);
+
+
+/** Retrieves number of values referenced by this key.
+ *
+ * Number of values in key structure and value list structure could differ,
+ * so this provides a standard/sane way of determining the number.
+ *
+ * @param key  the key whose number of values is desired
+ *
+ * @return Returns the number of values referenced by this key.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+uint32_t regfi_fetch_num_values(const REGFI_NK* key);
+
+
+/** Retrieves classname for a given key.
+ *
+ * @param file the file from which key is derived
+ * @param key the key whose classname is desired
+ *
+ * @return Returns a newly allocated classname structure, or NULL on failure.
+ *         Classname structures must be freed with @ref regfi_free_record.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_CLASSNAME* regfi_fetch_classname(REGFI_FILE* file, 
+					     const REGFI_NK* key);
+
+
+/** Returns the SK (security) record referenced by the supplied key.
+ *
+ * @param file the file from which key is derived
+ * @param key  the key whose SK record is desired
+ * 
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_SK* regfi_fetch_sk(REGFI_FILE* file, const REGFI_NK* key);
+
+
+/** Returns the next SK (security) record referenced by the supplied SK record
+ *
+ * @param file the file from which sk is derived
+ * @param sk   the SK record whose next sibling SK record is desired
+ * 
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @note
+ * SK records are included in a circular, doubly-linked list.
+ * To iterate over all SK records, be sure to check for the repetition of
+ * the SK record you started with to determine when all have been traversed.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_SK* regfi_next_sk(REGFI_FILE* file, const REGFI_SK* sk);
+
+
+/** Returns the previous SK (security) record referenced by the supplied SK record
+ *
+ * @param file the file from which sk is derived
+ * @param sk   the SK record whose previous sibling SK record is desired
+ * 
+ * @return A read-only SK structure, or NULL on failure.
+ *
+ * @note
+ * SK records are included in a circular, doubly-linked list.
+ * To iterate over all SK records, be sure to check for the repetition of
+ * the SK record you started with to determine when all have been traversed.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_SK* regfi_prev_sk(REGFI_FILE* file, const REGFI_SK* sk);
+
+
+/** Retrieves data for a given value.
+ *
+ * @param file the file from which value is derived
+ * @param value the value whose data is desired
+ *
+ * @return Returns a newly allocated data structure, or NULL on failure.
+ *         Data structures must be freed with @ref regfi_free_record.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_DATA* regfi_fetch_data(REGFI_FILE* file,
+				   const REGFI_VK* value);
+
+
+/** Locates a specific subkey of a given key.
+ *
+ * @param file  the file from which key is derived
+ * @param key   the key whose subkey is desired
+ * @param name  name of the desired subkey (case-insensitive)
+ * @param index a return value: the index of the desired subkey.
+ *              undefined on error
+ *
+ * @return true if the subkey is found, false if an error occurred or if the
+ *         specified name could not be found. If an error occurs, messages
+ *         will be written explaining the issue. (See regfi_log_get_str.)
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+bool regfi_find_subkey(REGFI_FILE* file, const REGFI_NK* key, 
+		       const char* name, uint32_t* index);
+
+
+/** Locates a specific value of a given key.
+ *
+ * @param file  the file from which key is derived
+ * @param key   the key whose value is desired
+ * @param name  name of the desired value (case-insensitive)
+ * @param index a return value: the index of the desired value.  
+ *              undefined on error
+ *
+ * @return true if the value is found, false if an error occurred or if the
+ *         specified name could not be found. If an error occurs, messages
+ *         will be written explaining the issue. (See regfi_log_get_str.)
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+bool regfi_find_value(REGFI_FILE* file, const REGFI_NK* key,
+		      const char* name, uint32_t* index);
+
+
+/** Retrieves a specific subkey of a given key.
+ *
+ * @param file  the file from which key is derived
+ * @param key   the key whose subkey is desired
+ * @param index the index of the desired subkey
+ *
+ * @return the requested subkey or NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_NK* regfi_get_subkey(REGFI_FILE* file, const REGFI_NK* key, 
+				 uint32_t index);
+
+
+/** Retrieves a specific value of a given key.
+ *
+ * @param file  the file from which key is derived
+ * @param key   the key whose value is desired
+ * @param index the index of the desired value
+ *
+ * @return the requested value or NULL on error.
  *
  * @ingroup regfiBase
  */
-void                  regfi_free_value(REGFI_VK_REC* vk);
+_EXPORT()
+const REGFI_VK* regfi_get_value(REGFI_FILE* file, const REGFI_NK* key, 
+				uint32_t index);
 
 
 
+/** Uses a key's parent_off reference to retrieve it's parent.
+ *
+ * @param file  the file from which key is derived
+ * @param key   the key whose parent is desired
+ *
+ * @return the requested subkey or NULL on error.
+ *
+ * @ingroup regfiBase
+ */
+_EXPORT()
+const REGFI_NK* regfi_get_parentkey(REGFI_FILE* file, const REGFI_NK* key);
+
+
 /******************************************************************************/
 /** 
  * @defgroup regfiIteratorLayer Iterator Layer: Primary regfi Library Interface
@@ -916,21 +1243,13 @@ void                  regfi_free_value(REGFI_VK_REC* vk);
  *
  * @param file The opened registry file the iterator should be created for.
  *
- * @param output_encoding Character encoding that strings should be returned in.
- *                        Only supply the REGFI_ENCODING_* constants, as others 
- *                        will be rejected.
- *                        The following values are currently accepted:
- *                        REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII)
- *                        REGFI_ENCODING_ASCII
- *                        REGFI_ENCODING_UTF8
- *
  * @return A newly allocated REGFI_ITERATOR. 
  *         Must be free()d with regfi_iterator_free.
  *
  * @ingroup regfiIteratorLayer
  */
-REGFI_ITERATOR*       regfi_iterator_new(REGFI_FILE* file,
-					 REGFI_ENCODING output_encoding);
+_EXPORT()
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file);
 
 
 /** Frees a registry file iterator previously created by regfi_iterator_new.
@@ -941,7 +1260,8 @@ REGFI_ITERATOR*       regfi_iterator_new(REGFI_FILE* file,
  *
  * @ingroup regfiIteratorLayer
  */
-void                  regfi_iterator_free(REGFI_ITERATOR* i);
+_EXPORT()
+void regfi_iterator_free(REGFI_ITERATOR* i);
 
 
 /** Traverse deeper into the registry tree at the current subkey.
@@ -957,7 +1277,8 @@ void                  regfi_iterator_free(REGFI_ITERATOR* i);
  *
  * @ingroup regfiIteratorLayer
  */
-bool                  regfi_iterator_down(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_down(REGFI_ITERATOR* i);
 
 
 /** Traverse up to the current key's parent key.
@@ -969,7 +1290,8 @@ bool                  regfi_iterator_down(REGFI_ITERATOR* i);
  *
  * @ingroup regfiIteratorLayer
  */
-bool                  regfi_iterator_up(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_up(REGFI_ITERATOR* i);
 
 
 /** Traverse up to the root key of the hive.
@@ -980,7 +1302,8 @@ bool                  regfi_iterator_up(REGFI_ITERATOR* i);
  *
  * @ingroup regfiIteratorLayer
  */
-bool                  regfi_iterator_to_root(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_to_root(REGFI_ITERATOR* i);
 
 
 /** Traverse down multiple levels in the registry hive.
@@ -1001,7 +1324,8 @@ bool                  regfi_iterator_to_root(REGFI_ITERATOR* i);
  *
  * @ingroup regfiIteratorLayer
  */
-bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path);
+_EXPORT()
+bool regfi_iterator_descend(REGFI_ITERATOR* i, const char** path);
 
 
 /** Returns the currently referenced key.
@@ -1009,36 +1333,25 @@ bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path);
  * @param i the iterator
  *
  * @return A read-only key structure for the current key, or NULL on failure.
+ *         Data structures must be freed with @ref regfi_free_record.
  *
  * @ingroup regfiIteratorLayer
  */
-const REGFI_NK_REC*   regfi_iterator_cur_key(REGFI_ITERATOR* i);
-
-
-/** Returns the SK (security) record referenced by the current key.
- *
- * @param i the iterator
- *
- * @return A read-only SK structure, or NULL on failure.
- *
- * @ingroup regfiIteratorLayer
- */
-const REGFI_SK_REC*   regfi_iterator_cur_sk(REGFI_ITERATOR* i);
+_EXPORT()
+const REGFI_NK* regfi_iterator_cur_key(REGFI_ITERATOR* i);
 
 
 /** Sets the internal subkey index to the first subkey referenced by the current
- *  key and returns that key.
+ *  key.
  *
  * @param i the iterator
  *
- * @return A newly allocated key structure for the newly referenced first 
- *         subkey, or NULL on failure.  Failure may be due to a lack of any
- *         subkeys or other errors.  Newly allocated keys must be freed with
- *         regfi_free_key.
+ * @return True if the current key has any subkeys, false otherwise.
  *
  * @ingroup regfiIteratorLayer
  */
-REGFI_NK_REC*         regfi_iterator_first_subkey(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_first_subkey(REGFI_ITERATOR* i);
 
 
 /** Returns the currently indexed subkey.
@@ -1047,30 +1360,30 @@ REGFI_NK_REC*         regfi_iterator_first_subkey(REGFI_ITERATOR* i);
  *
  * @return A newly allocated key structure for the currently referenced subkey,
  *         or NULL on failure.  Newly allocated keys must be freed with 
- *         regfi_free_key.
+ *         @ref regfi_free_record.
  *
  * @ingroup regfiIteratorLayer
  */
-REGFI_NK_REC*         regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
+_EXPORT()
+const REGFI_NK* regfi_iterator_cur_subkey(REGFI_ITERATOR* i);
 
 
-/** Increments the internal subkey index to the next key in the subkey-list and
- *  returns the subkey for that index.
+/** Increments the internal subkey index to the next key in the subkey-list.
  *
  * @param i the iterator
  *
- * @return A newly allocated key structure for the next subkey or NULL on
- *         failure.  Newly allocated keys must be freed with regfi_free_key.
+ * @return True if another subkey should exist, false otherwise.
  *
  * @ingroup regfiIteratorLayer
  */
-REGFI_NK_REC*         regfi_iterator_next_subkey(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_next_subkey(REGFI_ITERATOR* i);
 
 
 /** Searches for a subkey with a given name under the current key.
  *
- * @param i           the iterator
- * @param subkey_name subkey name to search for
+ * @param i     the iterator
+ * @param name  subkey name to search for
  *
  * @return True if such a subkey was found, false otherwise.  If a subkey is
  *         found, the current subkey index is set to that subkey.  Otherwise,
@@ -1078,22 +1391,21 @@ REGFI_NK_REC*         regfi_iterator_next_subkey(REGFI_ITERATOR* i);
  *
  * @ingroup regfiIteratorLayer
  */
-bool                  regfi_iterator_find_subkey(REGFI_ITERATOR* i, 
-						 const char* subkey_name);
+_EXPORT()
+bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* name);
+
 
 /** Sets the internal value index to the first value referenced by the current
- *  key and returns that value.
+ *  key.
  *
  * @param i the iterator
  *
- * @return  A newly allocated value structure for the newly referenced first
- *          value, or NULL on failure.  Failure may be due to a lack of any
- *          values or other errors.  Newly allocated keys must be freed with
- *          regfi_free_value.
+ * @return True if the current key has any values, false otherwise.
  *
  * @ingroup regfiIteratorLayer
  */
-REGFI_VK_REC*         regfi_iterator_first_value(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_first_value(REGFI_ITERATOR* i);
 
 
 /** Returns the currently indexed value.
@@ -1102,30 +1414,30 @@ REGFI_VK_REC*         regfi_iterator_first_value(REGFI_ITERATOR* i);
  *
  * @return A newly allocated value structure for the currently referenced value,
  *         or NULL on failure.  Newly allocated values must be freed with 
- *         regfi_free_value.
+ *         @ref regfi_free_record.
  *
  * @ingroup regfiIteratorLayer
  */
-REGFI_VK_REC*         regfi_iterator_cur_value(REGFI_ITERATOR* i);
+_EXPORT()
+const REGFI_VK* regfi_iterator_cur_value(REGFI_ITERATOR* i);
 
 
-/** Increments the internal value index to the next value in the value-list and
- *  returns the value for that index.
+/** Increments the internal value index to the next value in the value-list.
  *
  * @param i the iterator
  *
- * @return  A newly allocated key structure for the next value or NULL on 
- *          failure.  Newly allocated keys must be freed with regfi_free_value.
+ * @return True if another value should exist, false otherwise.
  *
  * @ingroup regfiIteratorLayer
  */
-REGFI_VK_REC*         regfi_iterator_next_value(REGFI_ITERATOR* i);
+_EXPORT()
+bool regfi_iterator_next_value(REGFI_ITERATOR* i);
 
 
 /** Searches for a value with a given name under the current key.
  *
- * @param i          the iterator
- * @param value_name value name to search for
+ * @param i     the iterator
+ * @param name  value name to search for
  *
  * @return True if such a value was found, false otherwise.  If a value is 
  *         found, the current value index is set to that value.  Otherwise,
@@ -1133,36 +1445,27 @@ REGFI_VK_REC*         regfi_iterator_next_value(REGFI_ITERATOR* i);
  *
  * @ingroup regfiIteratorLayer
  */
-bool                  regfi_iterator_find_value(REGFI_ITERATOR* i, 
-						const char* value_name);
-
-/** Retrieves classname for a given key.
- *
- * @param i   the iterator
- * @param key the key whose classname is desired
- *
- * @return Returns a newly allocated classname structure, or NULL on failure.
- *         Classname structures must be freed with regfi_free_classname.
- *
- * @ingroup regfiIteratorLayer
- */
-REGFI_CLASSNAME*      regfi_iterator_fetch_classname(REGFI_ITERATOR* i, 
-						     const REGFI_NK_REC* key);
+_EXPORT()
+bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* name);
 
 
-/** Retrieves data for a given value.
+/** Returns the current key and all parent keys as a list of NK records
  *
  * @param i     the iterator
- * @param value the value whose data is desired
  *
- * @return Returns a newly allocated data structure, or NULL on failure.
- *         Data structures must be freed with regfi_free_data.
+ * @return An array of NK record pointers terminated by a NULL pointer.  
+ *         This array may be passed directly to regfi_free_record to free
+ *         the entire array.
+ *
+ * @note In order to use an element of the array independently from the 
+ *       array (that is, to hold a pointer to an individual NK record while 
+ *       freeing the remaining array), callers must first use 
+ *       regfi_reference_record on the elements to be kept.
  *
  * @ingroup regfiIteratorLayer
  */
-REGFI_DATA*           regfi_iterator_fetch_data(REGFI_ITERATOR* i, 
-						const REGFI_VK_REC* value);
-
+_EXPORT()
+const REGFI_NK** regfi_iterator_ancestry(REGFI_ITERATOR* i);
 
 
 /******************************************************************************/
@@ -1171,15 +1474,15 @@ REGFI_DATA*           regfi_iterator_fetch_data(REGFI_ITERATOR* i,
  */
 /******************************************************************************/
 
-/** Loads a key at a given file offset along with associated data structures.
+/** Loads a key and associated data structures given a file offset.
  *
  * XXX: finish documenting
  *
  * @ingroup regfiGlueLayer
  */
-REGFI_NK_REC*         regfi_load_key(REGFI_FILE* file, uint32_t offset, 
-				     REGFI_ENCODING output_encoding, 
-				     bool strict);
+_EXPORT()
+REGFI_NK* regfi_load_key(REGFI_FILE* file, uint32_t offset, 
+                         bool strict);
 
 
 /** Loads a value at a given file offset alng with associated data structures.
@@ -1188,9 +1491,9 @@ REGFI_NK_REC*         regfi_load_key(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiGlueLayer
  */
-REGFI_VK_REC*         regfi_load_value(REGFI_FILE* file, uint32_t offset, 
-				       REGFI_ENCODING output_encoding, 
-				       bool strict);
+_EXPORT()
+REGFI_VK* regfi_load_value(REGFI_FILE* file, uint32_t offset, 
+                           bool strict);
 
 
 /** Loads a logical subkey list in its entirety which may span multiple records.
@@ -1199,9 +1502,10 @@ REGFI_VK_REC*         regfi_load_value(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiGlueLayer
  */
-REGFI_SUBKEY_LIST*    regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
-					    uint32_t num_keys, uint32_t max_size,
-					    bool strict);
+_EXPORT()
+REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
+					 uint32_t num_keys, uint32_t max_size,
+					 bool strict);
 
 
 /** Loads a valuelist.
@@ -1210,9 +1514,10 @@ REGFI_SUBKEY_LIST*    regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiGlueLayer
  */
-REGFI_VALUE_LIST*     regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, 
-					   uint32_t num_values, uint32_t max_size,
-					   bool strict);
+_EXPORT()
+REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, 
+				       uint32_t num_values, uint32_t max_size,
+				       bool strict);
 
 
 /** Loads a data record which may be contained in the virtual offset, in a
@@ -1222,9 +1527,10 @@ REGFI_VALUE_LIST*     regfi_load_valuelist(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiGlueLayer
  */
-REGFI_BUFFER          regfi_load_data(REGFI_FILE* file, uint32_t voffset,
-				      uint32_t length, bool data_in_offset,
-				      bool strict);
+_EXPORT()
+REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
+			     uint32_t length, bool data_in_offset,
+			     bool strict);
 
 
 /** Loads the data associated with a big data record at the specified offset.
@@ -1233,10 +1539,11 @@ REGFI_BUFFER          regfi_load_data(REGFI_FILE* file, uint32_t voffset,
  *
  * @ingroup regfiGlueLayer
  */
-REGFI_BUFFER          regfi_load_big_data(REGFI_FILE* file, uint32_t offset, 
-					  uint32_t data_length,uint32_t cell_length,
-					  range_list* used_ranges,
-					  bool strict);
+_EXPORT()
+REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, uint32_t offset, 
+				 uint32_t data_length,uint32_t cell_length,
+				 range_list* used_ranges,
+				 bool strict);
 
 
 /** Given raw data, attempts to interpret the data based on a specified registry
@@ -1246,28 +1553,11 @@ REGFI_BUFFER          regfi_load_big_data(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiGlueLayer
  */
-bool                  regfi_interpret_data(REGFI_FILE* file, 
-					   REGFI_ENCODING string_encoding,
-					   uint32_t type, REGFI_DATA* data);
+_EXPORT()
+bool regfi_interpret_data(REGFI_FILE* file, 
+			  uint32_t type, REGFI_DATA* data);
 
 
-/** Frees the memory associated with a REGFI_CLASSNAME data structure.
- *
- * XXX: finish documenting
- *
- * @ingroup regfiGlueLayer
- */
-void                  regfi_free_classname(REGFI_CLASSNAME* classname);
-
-
-/** Frees the memory associated with a REGFI_DATA data structure.
- *
- * XXX: finish documenting
- *
- * @ingroup regfiGlueLayer
- */
-void                  regfi_free_data(REGFI_DATA* data);
-
 
 /* These are cached so return values don't need to be freed. */
 
@@ -1277,8 +1567,11 @@ void                  regfi_free_data(REGFI_DATA* data);
  *
  * @ingroup regfiGlueLayer
  */
-const REGFI_SK_REC*   regfi_load_sk(REGFI_FILE* file, uint32_t offset,
-				    bool strict);
+_EXPORT()
+const REGFI_SK* regfi_load_sk(REGFI_FILE* file, uint32_t offset,
+                              bool strict);
+
+
 
 
 /** Retrieves the HBIN data structure stored at the specified offset.
@@ -1287,7 +1580,8 @@ const REGFI_SK_REC*   regfi_load_sk(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiGlueLayer
  */
-const REGFI_HBIN*     regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset);
+_EXPORT()
+const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset);
 
 
 
@@ -1297,9 +1591,12 @@ const REGFI_HBIN*     regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset);
  */
 /******************************************************************************/
 
-REGFI_FILE*           regfi_parse_regf(int fd, bool strict);
-REGFI_HBIN*           regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, 
-				       bool strict);
+_EXPORT()
+REGFI_FILE* regfi_parse_regf(REGFI_RAW_FILE* file_cb, bool strict);
+
+_EXPORT()
+REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, 
+			     bool strict);
 
 
 /** Parses an NK record at the specified offset
@@ -1314,8 +1611,9 @@ REGFI_HBIN*           regfi_parse_hbin(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiParseLayer
  */
-REGFI_NK_REC*         regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
-				     uint32_t max_size, bool strict);
+_EXPORT()
+REGFI_NK* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
+			     uint32_t max_size, bool strict);
 
 
 /** Parses a single cell containing a subkey-list record.
@@ -1324,8 +1622,9 @@ REGFI_NK_REC*         regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiParseLayer
  */
-REGFI_SUBKEY_LIST*    regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
-					     uint32_t max_size, bool strict);
+_EXPORT()
+REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
+					  uint32_t max_size, bool strict);
 
 
 /** Parses a VK (value) record at the specified offset
@@ -1334,8 +1633,9 @@ REGFI_SUBKEY_LIST*    regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiParseLayer
  */
-REGFI_VK_REC*         regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
-				     uint32_t max_size, bool strict);
+_EXPORT()
+REGFI_VK* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
+			     uint32_t max_size, bool strict);
 
 
 /** Parses an SK (security) record at the specified offset
@@ -1344,8 +1644,9 @@ REGFI_VK_REC*         regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiParseLayer
  */
-REGFI_SK_REC*         regfi_parse_sk(REGFI_FILE* file, uint32_t offset, 
-				     uint32_t max_size, bool strict);
+_EXPORT()
+REGFI_SK* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, 
+			     uint32_t max_size, bool strict);
 
 
 /** Retrieves information on all cells in the registry hive which are 
@@ -1357,7 +1658,8 @@ REGFI_SK_REC*         regfi_parse_sk(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiParseLayer
  */
-range_list*           regfi_parse_unalloc_cells(REGFI_FILE* file);
+_EXPORT()
+range_list* regfi_parse_unalloc_cells(REGFI_FILE* file);
 
 
 /** Helper function to parse a cell
@@ -1366,9 +1668,10 @@ range_list*           regfi_parse_unalloc_cells(REGFI_FILE* file);
  *
  * @ingroup regfiParseLayer
  */
-bool                  regfi_parse_cell(int fd, uint32_t offset, 
-				       uint8_t* hdr, uint32_t hdr_len,
-				       uint32_t* cell_length, bool* unalloc);
+_EXPORT()
+bool regfi_parse_cell(REGFI_RAW_FILE* file_cb, uint32_t offset,
+		      uint8_t* hdr, uint32_t hdr_len,
+		      uint32_t* cell_length, bool* unalloc);
 
 
 /** Parses a classname cell
@@ -1377,9 +1680,10 @@ bool                  regfi_parse_cell(int fd, uint32_t offset,
  *
  * @ingroup regfiParseLayer
  */
-uint8_t*                regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
-					    uint16_t* name_length, 
-					    uint32_t max_size, bool strict);
+_EXPORT()
+uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
+			       uint16_t* name_length, 
+			       uint32_t max_size, bool strict);
 
 
 /** Parses a single-cell data record
@@ -1388,8 +1692,9 @@ uint8_t*                regfi_parse_classname(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiParseLayer
  */
-REGFI_BUFFER          regfi_parse_data(REGFI_FILE* file, uint32_t offset,
-				       uint32_t length, bool strict);
+_EXPORT()
+REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
+			      uint32_t length, bool strict);
 
 
 /** Parses a "little data" record which is stored entirely within the 
@@ -1399,24 +1704,37 @@ REGFI_BUFFER          regfi_parse_data(REGFI_FILE* file, uint32_t offset,
  *
  * @ingroup regfiParseLayer
  */
-REGFI_BUFFER          regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset, 
-					      uint32_t length, bool strict);
+_EXPORT()
+REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset, 
+				     uint32_t length, bool strict);
 
 
 /******************************************************************************/
-/*    Private Functions                                                       */
+/*    Private (and undocumented) Functions                                    */
 /******************************************************************************/
-REGFI_NK_REC*         regfi_rootkey(REGFI_FILE* file, 
-				    REGFI_ENCODING output_encoding);
-void                  regfi_subkeylist_free(REGFI_SUBKEY_LIST* list);
-uint32_t              regfi_read(int fd, uint8_t* buf, uint32_t* length);
-
+int64_t               regfi_raw_seek(REGFI_RAW_FILE* self, 
+				     uint64_t offset, int whence);
+ssize_t               regfi_raw_read(REGFI_RAW_FILE* self, 
+				     void* buf, size_t count);
+_EXPORT()
+uint64_t              regfi_seek(REGFI_RAW_FILE* file_cb, 
+				 uint64_t offset, int whence);
+_EXPORT()
+uint32_t              regfi_read(REGFI_RAW_FILE* file_cb, 
+				 uint8_t* buf, uint32_t* length);
+
+_EXPORT()
 const char*           regfi_type_val2str(unsigned int val);
+_EXPORT()
 int                   regfi_type_str2val(const char* str);
 
+_EXPORT()
 char*                 regfi_get_sacl(WINSEC_DESC* sec_desc);
+_EXPORT()
 char*                 regfi_get_dacl(WINSEC_DESC* sec_desc);
+_EXPORT()
 char*                 regfi_get_owner(WINSEC_DESC* sec_desc);
+_EXPORT()
 char*                 regfi_get_group(WINSEC_DESC* sec_desc);
 
 REGFI_SUBKEY_LIST*    regfi_merge_subkeylists(uint16_t num_lists, 
@@ -1427,24 +1745,29 @@ REGFI_SUBKEY_LIST*    regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offse
 						uint8_t depth_left);
 void                  regfi_add_message(REGFI_FILE* file, uint16_t msg_type, 
 					const char* fmt, ...);
-REGFI_NK_REC*         regfi_copy_nk(const REGFI_NK_REC* nk);
-REGFI_VK_REC*         regfi_copy_vk(const REGFI_VK_REC* vk);
+REGFI_NK*             regfi_copy_nk(const REGFI_NK* nk);
+REGFI_VK*             regfi_copy_vk(const REGFI_VK* vk);
+_EXPORT()
 int32_t               regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset);
-int32_t               regfi_conv_charset(const char* input_charset, 
-					 const char* output_charset,
-					 uint8_t* input, char* output, 
-					 uint32_t input_len, uint32_t output_max);
+REGFI_BUFFER          regfi_conv_charset(const char* input_charset, const char* output_charset,
+                                         uint8_t* input, uint32_t input_len);
+_EXPORT()
 REGFI_DATA*           regfi_buffer_to_data(REGFI_BUFFER raw_data);
 
 /* XXX: move to base API and document */
-void                  regfi_unix2nt_time(REGFI_NTTIME* nt, time_t t);
-time_t                regfi_nt2unix_time(const REGFI_NTTIME* nt);
+_EXPORT()
+REGFI_NTTIME          regfi_unix2nt_time(time_t t);
+_EXPORT()
+double                regfi_nt2unix_time(REGFI_NTTIME nt);
+
 
+_EXPORT()
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK* nk, bool strict);
+_EXPORT()
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK* vk, bool strict);
 
-void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk, 
-			     REGFI_ENCODING output_encoding, bool strict);
-void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk, 
-			       REGFI_ENCODING output_encoding, bool strict);
+_EXPORT()
+void regfi_init();
 
 
 #endif	/* _REGFI_H */
diff --git a/include/talloc.h b/include/talloc.h
deleted file mode 100644
index d7a08a6..0000000
--- a/include/talloc.h
+++ /dev/null
@@ -1,169 +0,0 @@
-/* 
-   Unix SMB/CIFS implementation.
-   Samba temporary memory allocation functions
-
-   Copyright (C) Andrew Tridgell 2004-2005
-   Copyright (C) Stefan Metzmacher 2006
-   Copyright (C) Timothy D. Morgan 2009 
-   
-     ** NOTE! The following LGPL license applies to the talloc library.
-     ** This does NOT imply that all of reglookup is released under the LGPL
-   
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 3 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-/* $Id: talloc.h 168 2010-03-03 00:08:42Z tim $ */
-
-#ifndef _TALLOC_H_
-#define _TALLOC_H_
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdbool.h>
-
-#ifndef MAX
-#define MAX(a,b) ((a)>(b)?(a):(b))
-#endif
-
-#ifndef MIN
-#define MIN(a,b) ((a)<(b)?(a):(b))
-#endif
-
-/*
-  this uses a little trick to allow __LINE__ to be stringified
-*/
-#ifndef __location__
-#define __TALLOC_STRING_LINE1__(s)    #s
-#define __TALLOC_STRING_LINE2__(s)   __TALLOC_STRING_LINE1__(s)
-#define __TALLOC_STRING_LINE3__  __TALLOC_STRING_LINE2__(__LINE__)
-#define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__
-#endif
-
-#ifndef TALLOC_DEPRECATED
-#define TALLOC_DEPRECATED 0
-#endif
-
-#ifndef PRINTF_ATTRIBUTE
-#if (__GNUC__ >= 3)
-/** Use gcc attribute to check printf fns.  a1 is the 1-based index of
- * the parameter containing the format, and a2 the index of the first
- * argument. Note that some gcc 2.x versions don't handle this
- * properly **/
-#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2)))
-#else
-#define PRINTF_ATTRIBUTE(a1, a2)
-#endif
-#endif
-
-#define talloc_set_destructor(ptr, function) \
-	_talloc_set_destructor((ptr), (int (*)(void *))(function))
-#define _TALLOC_TYPEOF(ptr) void *
-#define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr))
-
-#define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr))
-#define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr))
-
-/* useful macros for creating type checked pointers */
-#define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type)
-#define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__)
-#define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr)))
-
-#define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__)
-
-#define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type)
-#define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__)
-
-#define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type)
-#define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type)
-#define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__)
-#define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count)
-
-#define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type)
-#define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__)
-
-#define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__)
-
-#define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type)
-#define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type)
-
-#define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type)
-
-/* The following definitions come from talloc.c  */
-void *_talloc(const void *context, size_t size);
-void *talloc_pool(const void *context, size_t size);
-void _talloc_set_destructor(const void *ptr, int (*destructor)(void *));
-int talloc_increase_ref_count(const void *ptr);
-size_t talloc_reference_count(const void *ptr);
-void *_talloc_reference(const void *context, const void *ptr);
-int talloc_unlink(const void *context, void *ptr);
-const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-void talloc_set_name_const(const void *ptr, const char *name);
-void *talloc_named(const void *context, size_t size, 
-		   const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
-void *talloc_named_const(const void *context, size_t size, const char *name);
-const char *talloc_get_name(const void *ptr);
-void *talloc_check_name(const void *ptr, const char *name);
-void *talloc_parent(const void *ptr);
-const char *talloc_parent_name(const void *ptr);
-void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2);
-int talloc_free(void *ptr);
-void talloc_free_children(void *ptr);
-void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name);
-void *_talloc_steal(const void *new_ctx, const void *ptr);
-void *_talloc_move(const void *new_ctx, const void *pptr);
-size_t talloc_total_size(const void *ptr);
-size_t talloc_total_blocks(const void *ptr);
-void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
-			    void (*callback)(const void *ptr,
-			  		     int depth, int max_depth,
-					     int is_ref,
-					     void *private_data),
-			    void *private_data);
-void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f);
-void talloc_report_full(const void *ptr, FILE *f);
-void talloc_report(const void *ptr, FILE *f);
-void talloc_enable_null_tracking(void);
-void talloc_disable_null_tracking(void);
-void talloc_enable_leak_report(void);
-void talloc_enable_leak_report_full(void);
-void *_talloc_zero(const void *ctx, size_t size, const char *name);
-void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name);
-void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name);
-void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name);
-void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name);
-void *talloc_realloc_fn(const void *context, void *ptr, size_t size);
-void *talloc_autofree_context(void);
-size_t talloc_get_size(const void *ctx);
-void *talloc_find_parent_byname(const void *ctx, const char *name);
-void talloc_show_parents(const void *context, FILE *file);
-int talloc_is_parent(const void *context, const void *ptr);
-
-char *talloc_strdup(const void *t, const char *p);
-char *talloc_strdup_append(char *s, const char *a);
-char *talloc_strdup_append_buffer(char *s, const char *a);
-
-char *talloc_strndup(const void *t, const char *p, size_t n);
-char *talloc_strndup_append(char *s, const char *a, size_t n);
-char *talloc_strndup_append_buffer(char *s, const char *a, size_t n);
-
-char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-
-char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3);
-
-#endif
diff --git a/include/void_stack.h b/include/void_stack.h
index dc1c048..eb17766 100644
--- a/include/void_stack.h
+++ b/include/void_stack.h
@@ -14,7 +14,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: void_stack.h 169 2010-03-03 19:24:58Z tim $
+ * $Id: void_stack.h 253 2011-06-13 02:27:42Z tim $
  */
 
 /** 
@@ -31,7 +31,9 @@
 #include <stdlib.h>
 #include <stdbool.h>
 #include <string.h>
-#include "talloc.h"
+#include <talloc.h>
+
+#include "compat.h"
 
 /** XXX: document this. */
 typedef struct _void_stack
@@ -58,6 +60,7 @@ typedef struct _void_stack_iterator
  * @return a pointer to the newly allocated void_stack, 
  *         or NULL if an error occurred.
  */
+_EXPORT()
 void_stack* void_stack_new(unsigned short max_size);
 
 
@@ -67,6 +70,7 @@ void_stack* void_stack_new(unsigned short max_size);
  *
  * @return a pointer to the duplicate void_stack, or NULL if an error occurred.
  */
+_EXPORT()
 void_stack* void_stack_copy(const void_stack* v);
 
 
@@ -77,6 +81,7 @@ void_stack* void_stack_copy(const void_stack* v);
  * @return a pointer to the duplicate void_stack 
  *         (which will be in reverse order), or NULL if an error occurred.
  */
+_EXPORT()
 void_stack* void_stack_copy_reverse(const void_stack* v);
 
 
@@ -85,6 +90,7 @@ void_stack* void_stack_copy_reverse(const void_stack* v);
  *
  * @param stack the stack to be free()d.
  */
+_EXPORT()
 void void_stack_free(void_stack* stack);
 
 
@@ -97,6 +103,7 @@ void void_stack_free(void_stack* stack);
  *
  * @param stack the stack to be free()d.
  */
+_EXPORT()
 void void_stack_free_deep(void_stack* stack);
 
 
@@ -106,6 +113,7 @@ void void_stack_free_deep(void_stack* stack);
  *
  * @return the number of elements currently on the stack.
  */
+_EXPORT()
 unsigned short void_stack_size(const void_stack* stack);
 
 
@@ -116,6 +124,7 @@ unsigned short void_stack_size(const void_stack* stack);
  * @return a pointer to the popped stack element, or NULL if no elements exist
  *         on the stack.
  */
+_EXPORT()
 void* void_stack_pop(void_stack* stack);
 
 
@@ -126,6 +135,7 @@ void* void_stack_pop(void_stack* stack);
  *
  * @return true if the element was successfully added, false otherwise.
  */
+_EXPORT()
 bool void_stack_push(void_stack* stack, void* e);
 
 
@@ -136,6 +146,7 @@ bool void_stack_push(void_stack* stack, void* e);
  * @return a pointer to the current element on the top of the stack, or NULL if
  *         no elements exist in the stack.
  */
+_EXPORT()
 const void* void_stack_cur(const void_stack* stack);
 
 
@@ -145,6 +156,7 @@ const void* void_stack_cur(const void_stack* stack);
  *
  * @return a new void_stack_iterator, or NULL if an error occurred.
  */
+_EXPORT()
 void_stack_iterator* void_stack_iterator_new(const void_stack* stack);
 
 
@@ -154,6 +166,7 @@ void_stack_iterator* void_stack_iterator_new(const void_stack* stack);
  *
  * @param iter the void_stack_iterator to be free()d.
  */
+_EXPORT()
 void void_stack_iterator_free(void_stack_iterator* iter);
 
 
@@ -165,6 +178,7 @@ void void_stack_iterator_free(void_stack_iterator* iter);
  *
  * @return a pointer to the next element.
  */
+_EXPORT()
 const void* void_stack_iterator_next(void_stack_iterator* iter);
 
 
diff --git a/include/winsec.h b/include/winsec.h
index 6c7de49..9df4a30 100644
--- a/include/winsec.h
+++ b/include/winsec.h
@@ -1,5 +1,5 @@
 /* 
- * Copyright (C) 2005,2009-2010 Timothy D. Morgan
+ * Copyright (C) 2005,2009-2011 Timothy D. Morgan
  * Copyright (C) 1992-2005 Samba development team 
  * 
  * This program is free software; you can redistribute it and/or modify
@@ -15,7 +15,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: winsec.h 169 2010-03-03 19:24:58Z tim $
+ * $Id: winsec.h 261 2011-06-17 00:55:49Z tim $
  */
 
 /**
@@ -43,8 +43,9 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <talloc.h>
 
-#include "talloc.h"
+#include "compat.h"
 #include "byteorder.h"
 
 
@@ -216,6 +217,7 @@ typedef struct _winsec_desc
  *
  * XXX: finish documenting
  */
+_EXPORT()
 WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
 
 
@@ -223,12 +225,14 @@ WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len);
  *
  * XXX: finish documenting
  */
+_EXPORT()
 void winsec_free_descriptor(WINSEC_DESC* desc);
 
 /**
  *
  * XXX: finish documenting
  */
+_EXPORT()
 WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
 			       const uint8_t* buf, uint32_t buf_len);
 
@@ -236,6 +240,7 @@ WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
  *
  * XXX: finish documenting
  */
+_EXPORT()
 WINSEC_ACL* winsec_parse_acl(void* talloc_ctx, 
 			     const uint8_t* buf, uint32_t buf_len);
 
@@ -243,6 +248,7 @@ WINSEC_ACL* winsec_parse_acl(void* talloc_ctx,
  *
  * XXX: finish documenting
  */
+_EXPORT()
 WINSEC_ACE* winsec_parse_ace(void* talloc_ctx, 
 			     const uint8_t* buf, uint32_t buf_len);
 
@@ -250,6 +256,7 @@ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
  *
  * XXX: finish documenting
  */
+_EXPORT()
 WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx, 
 				     const uint8_t* buf, uint32_t buf_len);
 
@@ -257,6 +264,7 @@ WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx,
  *
  * XXX: finish documenting
  */
+_EXPORT()
 WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx, 
 			       const uint8_t* buf, uint32_t buf_len);
 
@@ -265,48 +273,63 @@ WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx,
  *
  * XXX: finish documenting
  */
+_EXPORT()
 size_t winsec_sid_size(const WINSEC_DOM_SID* sid);
 
 /**
  *
  * XXX: finish documenting
  */
+_EXPORT()
 int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
 
 /**
  *
  * XXX: finish documenting
  */
+_EXPORT()
 int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
 
 /**
  *
  * XXX: finish documenting
  */
+_EXPORT()
 bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2);
 
 /**
  *
  * XXX: finish documenting
  */
+_EXPORT()
+char* winsec_sid2str(const WINSEC_DOM_SID* sid);
+
+/**
+ *
+ * XXX: finish documenting
+ */
+_EXPORT()
 bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2);
 
 /**
  *
  * XXX: finish documenting
  */
+_EXPORT()
 bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2);
 
 /**
  *
  * XXX: finish documenting
  */
+_EXPORT()
 bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2);
 
 /**
  *
  * XXX: finish documenting
  */
+_EXPORT()
 bool winsec_ace_object(uint8_t type);
 
 #endif /* _WINSEC_H */
diff --git a/lib/lru_cache.c b/lib/lru_cache.c
index dfa9111..67c1fce 100644
--- a/lib/lru_cache.c
+++ b/lib/lru_cache.c
@@ -14,7 +14,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: lru_cache.c 169 2010-03-03 19:24:58Z tim $
+ * $Id: lru_cache.c 252 2011-05-08 17:33:49Z tim $
  */
 
 /**
@@ -118,13 +118,15 @@ lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
 
   if(max_keys == 0)
     ret_val->num_buckets = 1024;
+  else if(max_keys == 1)
+    ret_val->num_buckets = 1;    
   else
   {
     ret_val->num_buckets = max_keys/lru_cache_floor_log2(max_keys);
     if(ret_val->num_buckets < 1)
       ret_val->num_buckets = 1;
   }
-
+  
   ret_val->table = talloc_array(ret_val, 
 				lru_cache_element*, ret_val->num_buckets);
   if(ret_val->table == NULL)
@@ -148,7 +150,7 @@ lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys,
 void lru_cache_destroy(lru_cache* ht)
 {
   ht->secret = 0;
-  talloc_free(ht);
+  talloc_unlink(NULL, ht);
 }
 
 
@@ -176,7 +178,7 @@ bool lru_cache_update(lru_cache* ht, const void* index,
      * so remove it from the list for now.
      */
     if(ht->talloc_data)
-      talloc_free(e->data);
+      talloc_unlink(e, e->data);
 
     if(e->newer == NULL)
       ht->newest = e->older;
@@ -223,7 +225,7 @@ bool lru_cache_update(lru_cache* ht, const void* index,
       e->next = NULL;
 
       if(ht->talloc_data)
-	talloc_free(e->data);
+	talloc_unlink(e, e->data);
 
       tmp_index = talloc_realloc_size(e, e->index, index_len);
       if(tmp_index == NULL)
@@ -254,13 +256,12 @@ bool lru_cache_update(lru_cache* ht, const void* index,
     memcpy(e->index, index, index_len);
     e->index_len = index_len;
 
-    /* Insert at beginning of chain, in a vaguely LRU style */
     e->next = ht->table[hash];
     ht->table[hash] = e;
   }
-  e->data = data;
   if(ht->talloc_data)
-    talloc_steal(e, e->data);
+    data = talloc_reference(e, data);
+  e->data = data;
 
   /* Finally, let's insert the element to the newest position in the LRU list.*/
   if(ht->newest != NULL)
diff --git a/lib/regfi.c b/lib/regfi.c
index d292dfe..aced069 100644
--- a/lib/regfi.c
+++ b/lib/regfi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2005-2011 Timothy D. Morgan
  * Copyright (C) 2005 Gerald (Jerry) Carter
  *
  * This program is free software; you can redistribute it and/or modify
@@ -15,7 +15,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: regfi.c 173 2010-03-08 03:39:09Z tim $
+ * $Id: regfi.c 274 2011-09-28 00:29:25Z tim $
  */
 
 /** 
@@ -33,6 +33,11 @@
 
 #include "regfi.h"
 
+/* Library version can be overridden at build time */
+#ifndef REGFI_VERSION
+#define REGFI_VERSION "trunk"
+#endif
+
 
 /* Registry types mapping */
 const unsigned int regfi_num_reg_types = 12;
@@ -41,73 +46,161 @@ static const char* regfi_type_names[] =
    "MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"};
 
 const char* regfi_encoding_names[] =
-  {"US-ASCII//TRANSLIT", "UTF-8//TRANSLIT", "UTF-16LE//TRANSLIT"};
+  {"US-ASCII", "UTF-8", "UTF-16LE"};
+
+
+/* Ensures regfi_init runs only once */
+static pthread_once_t regfi_init_once = PTHREAD_ONCE_INIT;
+
+
+/******************************************************************************
+ ******************************************************************************/
+const char* regfi_version()
+{
+  return REGFI_VERSION;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_log_free(void* ptr)
+{
+  REGFI_LOG* log_info = (REGFI_LOG*)ptr;
+  
+  if(log_info->messages != NULL)
+    free(log_info->messages);
+
+  talloc_free(log_info);
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_init()
+{
+  int err;
+  if((err = pthread_key_create(&regfi_log_key, regfi_log_free)) != 0)
+    fprintf(stderr, "ERROR: key_create: %s\n", strerror(err));
+  errno = err;
+}
 
 
 /******************************************************************************
  ******************************************************************************/
-void regfi_add_message(REGFI_FILE* file, uint16_t msg_type, const char* fmt, ...)
+REGFI_LOG* regfi_log_new()
 {
-  /* XXX: This function is not particularly efficient,
-   *      but then it is mostly used during errors. 
+  int err;
+  REGFI_LOG* log_info = talloc(NULL, REGFI_LOG);
+  if(log_info == NULL)
+    return NULL;
+
+  log_info->msg_mask = REGFI_DEFAULT_LOG_MASK;
+  log_info->messages = NULL;
+
+  pthread_once(&regfi_init_once, regfi_init);
+
+  if((err = pthread_setspecific(regfi_log_key, log_info)) != 0)
+  {
+    fprintf(stderr, "ERROR: setspecific: %s\n", strerror(err));
+    goto fail;
+  }
+
+  return log_info;
+
+ fail:
+  talloc_free(log_info);
+  errno = err;
+  return NULL;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+void regfi_log_add(uint16_t msg_type, const char* fmt, ...)
+{
+  /* XXX: Switch internal storage over to a linked list or stack.
+   *      Then add a regfi_log_get function that returns the list in some
+   *      convenient, user-friendly data structure.  regfi_log_get_str should
+   *      stick around and will simply smush the list into a big string when 
+   *      it's called, rather than having messages smushed when they're first
+   *      written to the log.
    */
   uint32_t buf_size, buf_used;
   char* new_msg;
+  REGFI_LOG* log_info;
   va_list args;
 
-  if((file->msg_mask & msg_type) != 0)
-  {
-    if(file->last_message == NULL)
-      buf_used = 0;
-    else
-      buf_used = strlen(file->last_message);
-    
-    buf_size = buf_used+strlen(fmt)+160;
-    new_msg = realloc(file->last_message, buf_size);
-    if(new_msg == NULL)
-      /* XXX: should we report this? */
-      return;
+  log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
+  if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
+    return;
 
-    switch (msg_type)
-    {
-    case REGFI_MSG_INFO:
-      strcpy(new_msg+buf_used, "INFO: ");
-      buf_used += 6;
-      break;
-    case REGFI_MSG_WARN:
-      strcpy(new_msg+buf_used, "WARN: ");
-      buf_used += 6;
-      break;
-    case REGFI_MSG_ERROR:
-      strcpy(new_msg+buf_used, "ERROR: ");
-      buf_used += 7;
-      break;
-    }
+  if((log_info->msg_mask & msg_type) == 0)
+    return;
 
-    va_start(args, fmt);
-    vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args);
-    va_end(args);
-    strncat(new_msg, "\n", buf_size-1);
-    
-    file->last_message = new_msg;
+  if(log_info->messages == NULL)
+    buf_used = 0;
+  else
+    buf_used = strlen(log_info->messages);
+  
+  buf_size = buf_used+strlen(fmt)+160;
+  new_msg = realloc(log_info->messages, buf_size);
+  if(new_msg == NULL)
+    /* XXX: should we report this? */
+    return;
+  
+  switch (msg_type)
+  {
+  case REGFI_LOG_INFO:
+    strcpy(new_msg+buf_used, "INFO: ");
+    buf_used += 6;
+    break;
+  case REGFI_LOG_WARN:
+    strcpy(new_msg+buf_used, "WARN: ");
+    buf_used += 6;
+    break;
+  case REGFI_LOG_ERROR:
+    strcpy(new_msg+buf_used, "ERROR: ");
+    buf_used += 7;
+    break;
   }
+  
+  va_start(args, fmt);
+  vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args);
+  va_end(args);
+  strncat(new_msg, "\n", buf_size-1);
+  
+  log_info->messages = new_msg;
 }
 
 
 /******************************************************************************
  ******************************************************************************/
-char* regfi_get_messages(REGFI_FILE* file)
+char* regfi_log_get_str()
 {
-  char* ret_val = file->last_message;
-  file->last_message = NULL;
+  char* ret_val;
+  REGFI_LOG* log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
+  if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
+    return NULL;
+  
+  ret_val = log_info->messages;
+  log_info->messages = NULL;
 
   return ret_val;
 }
 
 
-void regfi_set_message_mask(REGFI_FILE* file, uint16_t mask)
+/******************************************************************************
+ ******************************************************************************/
+bool regfi_log_set_mask(uint16_t msg_mask)
 {
-  file->msg_mask = mask;
+  REGFI_LOG* log_info = (REGFI_LOG*)pthread_getspecific(regfi_log_key);
+  if(log_info == NULL && (log_info = regfi_log_new()) == NULL)
+  {
+      return false;
+  }
+
+  log_info->msg_mask = msg_mask;
+  return true;
 }
 
 
@@ -309,28 +402,6 @@ char* regfi_ace_perms2str(uint32_t perms)
 }
 
 
-char* regfi_sid2str(WINSEC_DOM_SID* sid)
-{
-  uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
-  uint32_t left = size;
-  uint8_t comps = sid->num_auths;
-  char* ret_val = malloc(size);
-  
-  if(ret_val == NULL)
-    return NULL;
-
-  if(comps > WINSEC_MAX_SUBAUTHS)
-    comps = WINSEC_MAX_SUBAUTHS;
-
-  left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
-
-  for (i = 0; i < comps; i++) 
-    left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
-
-  return ret_val;
-}
-
-
 char* regfi_get_acl(WINSEC_ACL* acl)
 {
   uint32_t i, extra, size = 0;
@@ -346,7 +417,7 @@ char* regfi_get_acl(WINSEC_ACL* acl)
 
   for (i = 0; i < acl->num_aces && !failed; i++)
   {
-    sid_str = regfi_sid2str(acl->aces[i]->trustee);
+    sid_str = winsec_sid2str(acl->aces[i]->trustee);
     type_str = regfi_ace_type2str(acl->aces[i]->type);
     perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask);
     flags_str = regfi_ace_flags2str(acl->aces[i]->flags);
@@ -411,20 +482,116 @@ char* regfi_get_dacl(WINSEC_DESC *sec_desc)
 
 char* regfi_get_owner(WINSEC_DESC *sec_desc)
 {
-  return regfi_sid2str(sec_desc->owner_sid);
+  return winsec_sid2str(sec_desc->owner_sid);
 }
 
 
 char* regfi_get_group(WINSEC_DESC *sec_desc)
 {
-  return regfi_sid2str(sec_desc->grp_sid);
+  return winsec_sid2str(sec_desc->grp_sid);
+}
+
+
+bool regfi_read_lock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
+{
+  int lock_ret = pthread_rwlock_rdlock(lock);
+  if(lock_ret != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Error obtaining read lock in"
+		      "%s due to: %s\n", context, strerror(lock_ret));
+    return false;
+  }
+
+  return true;
+}
+
+
+bool regfi_write_lock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
+{
+  int lock_ret = pthread_rwlock_wrlock(lock);
+  if(lock_ret != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Error obtaining write lock in"
+		      "%s due to: %s\n", context, strerror(lock_ret));
+    return false;
+  }
+
+  return true;
+}
+
+
+bool regfi_rw_unlock(REGFI_FILE* file, pthread_rwlock_t* lock, const char* context)
+{
+  int lock_ret = pthread_rwlock_unlock(lock);
+  if(lock_ret != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Error releasing lock in"
+		      "%s due to: %s\n", context, strerror(lock_ret));
+    return false;
+  }
+
+  return true;
+}
+
+
+bool regfi_lock(REGFI_FILE* file, pthread_mutex_t* lock, const char* context)
+{
+  int lock_ret = pthread_mutex_lock(lock);
+  if(lock_ret != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Error obtaining mutex lock in"
+		      "%s due to: %s\n", context, strerror(lock_ret));
+    return false;
+  }
+
+  return true;
+}
+
+
+bool regfi_unlock(REGFI_FILE* file, pthread_mutex_t* lock, const char* context)
+{
+  int lock_ret = pthread_mutex_unlock(lock);
+  if(lock_ret != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Error releasing mutex lock in"
+		      "%s due to: %s\n", context, strerror(lock_ret));
+    return false;
+  }
+
+  return true;
+}
+
+
+int64_t regfi_raw_seek(REGFI_RAW_FILE* self, uint64_t offset, int whence)
+{
+  if(sizeof(off_t) == 4 && offset > 2147483647)
+  {
+    errno = EOVERFLOW;
+    return -1;
+  }
+  return lseek(*(int*)self->state, offset, whence);
+}
+
+ssize_t regfi_raw_read(REGFI_RAW_FILE* self, void* buf, size_t count)
+{
+  return read(*(int*)self->state, buf, count);
+}
+
+
+/*****************************************************************************
+ * Convenience function to wrap up the ugly callback stuff
+ *****************************************************************************/
+uint64_t regfi_seek(REGFI_RAW_FILE* file_cb, uint64_t offset, int whence)
+{
+  return file_cb->seek(file_cb, offset, whence);
 }
 
 
 /*****************************************************************************
  * This function is just like read(2), except that it continues to
  * re-try reading from the file descriptor if EINTR or EAGAIN is received.  
- * regfi_read will attempt to read length bytes from fd and write them to buf.
+ * regfi_read will attempt to read length bytes from the file and write them to
+ * buf.
  *
  * On success, 0 is returned.  Upon failure, an errno code is returned.
  *
@@ -432,14 +599,16 @@ char* regfi_get_group(WINSEC_DESC *sec_desc)
  * parameter by reference.  If both the return value and length parameter are 
  * returned as 0, then EOF was encountered immediately
  *****************************************************************************/
-uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length)
+uint32_t regfi_read(REGFI_RAW_FILE* file_cb, uint8_t* buf, uint32_t* length)
 {
   uint32_t rsize = 0;
   uint32_t rret = 0;
 
   do
   {
-    rret = read(fd, buf + rsize, *length - rsize);
+    rret = file_cb->read(file_cb, 
+                         buf + rsize, 
+                         *length - rsize);
     if(rret > 0)
       rsize += rret;
   }while(*length - rsize > 0 
@@ -456,18 +625,18 @@ uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length)
 /*****************************************************************************
  *
  *****************************************************************************/
-bool regfi_parse_cell(int fd, uint32_t offset, uint8_t* hdr, uint32_t hdr_len,
-		      uint32_t* cell_length, bool* unalloc)
+bool regfi_parse_cell(REGFI_RAW_FILE* file_cb, uint32_t offset, uint8_t* hdr, 
+		      uint32_t hdr_len, uint32_t* cell_length, bool* unalloc)
 {
   uint32_t length;
   int32_t raw_length;
   uint8_t tmp[4];
 
-  if(lseek(fd, offset, SEEK_SET) == -1)
+  if(regfi_seek(file_cb, offset, SEEK_SET) == -1)
     return false;
 
   length = 4;
-  if((regfi_read(fd, tmp, &length) != 0) || length != 4)
+  if((regfi_read(file_cb, tmp, &length) != 0) || length != 4)
     return false;
   raw_length = IVALS(tmp, 0);
 
@@ -488,7 +657,7 @@ bool regfi_parse_cell(int fd, uint32_t offset, uint8_t* hdr, uint32_t hdr_len,
   if(hdr_len > 0)
   {
     length = hdr_len;
-    if((regfi_read(fd, hdr, &length) != 0) || length != hdr_len)
+    if((regfi_read(file_cb, hdr, &length) != 0) || length != hdr_len)
       return false;
   }
 
@@ -552,7 +721,7 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
 				      REGFI_MAX_SUBKEY_DEPTH);
   if(ret_val == NULL)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Failed to load subkey list at"
+    regfi_log_add(REGFI_LOG_WARN, "Failed to load subkey list at"
 		      " offset 0x%.8X.", offset);
     return NULL;
   }
@@ -563,7 +732,7 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset,
      *  NK record, or the number in the subkey list.  Just emit a warning for
      *  now if they don't match.
      */
-    regfi_add_message(file, REGFI_MSG_WARN, "Number of subkeys listed in parent"
+    regfi_log_add(REGFI_LOG_WARN, "Number of subkeys listed in parent"
 		      " (%d) did not match number found in subkey list/tree (%d)"
 		      " while parsing subkey list/tree at offset 0x%.8X.", 
 		      num_keys, ret_val->num_keys, offset);
@@ -586,7 +755,7 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset,
 
   if(depth_left == 0)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Maximum depth reached"
+    regfi_log_add(REGFI_LOG_WARN, "Maximum depth reached"
 		      " while parsing subkey list/tree at offset 0x%.8X.", 
 		      offset);
     return NULL;
@@ -626,27 +795,30 @@ REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset,
 REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset, 
 					  uint32_t max_size, bool strict)
 {
-  REGFI_SUBKEY_LIST* ret_val;
+  REGFI_SUBKEY_LIST* ret_val = NULL;
   uint32_t i, cell_length, length, elem_size, read_len;
   uint8_t* elements = NULL;
   uint8_t buf[REGFI_SUBKEY_LIST_MIN_LEN];
   bool unalloc;
   bool recursive_type;
 
-  if(!regfi_parse_cell(file->fd, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN, 
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_subkeylist"))
+     goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN,
 		       &cell_length, &unalloc))
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while "
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while "
 		      "parsing subkey-list at offset 0x%.8X.", offset);
-    return NULL;
+    goto fail_locked;
   }
 
   if(cell_length > max_size)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Cell size longer than max_size"
+    regfi_log_add(REGFI_LOG_WARN, "Cell size longer than max_size"
 		      " while parsing subkey-list at offset 0x%.8X.", offset);
     if(strict)
-      return NULL;
+      goto fail_locked;
     cell_length = max_size & 0xFFFFFFF8;
   }
 
@@ -657,20 +829,22 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
     elem_size = sizeof(uint32_t);
   }
   else if(buf[0] == 'l' && buf[1] == 'i')
+  {
     elem_size = sizeof(uint32_t);
+  }
   else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h'))
     elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM);
   else
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Unknown magic number"
+    regfi_log_add(REGFI_LOG_ERROR, "Unknown magic number"
 		      " (0x%.2X, 0x%.2X) encountered while parsing"
 		      " subkey-list at offset 0x%.8X.", buf[0], buf[1], offset);
-    return NULL;
+    goto fail_locked;
   }
 
   ret_val = talloc(NULL, REGFI_SUBKEY_LIST);
   if(ret_val == NULL)
-    return NULL;
+    goto fail_locked;
 
   ret_val->offset = offset;
   ret_val->cell_size = cell_length;
@@ -685,26 +859,29 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
   length = elem_size*ret_val->num_children;
   if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t) < length)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Number of elements too large for"
+    regfi_log_add(REGFI_LOG_WARN, "Number of elements too large for"
 		      " cell while parsing subkey-list at offset 0x%.8X.", 
 		      offset);
     if(strict)
-      goto fail;
+      goto fail_locked;
     length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t);
   }
 
   ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, 
 				   ret_val->num_children);
   if(ret_val->elements == NULL)
-    goto fail;
+    goto fail_locked;
 
   elements = (uint8_t*)malloc(length);
   if(elements == NULL)
-    goto fail;
+    goto fail_locked;
 
   read_len = length;
-  if(regfi_read(file->fd, elements, &read_len) != 0 || read_len != length)
-    goto fail;
+  if(regfi_read(file->cb, elements, &read_len) != 0 || read_len!=length)
+    goto fail_locked;
+
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_subkeylist"))
+     goto fail;
 
   if(elem_size == sizeof(uint32_t))
   {
@@ -726,6 +903,8 @@ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset,
 
   return ret_val;
 
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_subkeylist");
  fail:
   if(elements != NULL)
     free(elements);
@@ -782,7 +961,7 @@ REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists,
   }
   
   for(i=0; i < num_lists; i++)
-    regfi_subkeylist_free(lists[i]);
+    talloc_free(lists[i]);
   free(lists);
 
   return ret_val;
@@ -792,33 +971,36 @@ REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists,
 /******************************************************************************
  *
  ******************************************************************************/
-REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, 
+REGFI_SK* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, 
 			     bool strict)
 {
-  REGFI_SK_REC* ret_val;
+  REGFI_SK* ret_val = NULL;
   uint8_t* sec_desc_buf = NULL;
   uint32_t cell_length, length;
   uint8_t sk_header[REGFI_SK_MIN_LENGTH];
   bool unalloc = false;
 
-  if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_sk"))
+     goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, sk_header, REGFI_SK_MIN_LENGTH,
 		       &cell_length, &unalloc))
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse SK record cell"
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse SK record cell"
 		      " at offset 0x%.8X.", offset);
-    return NULL;
+    goto fail_locked;
   }
    
   if(sk_header[0] != 's' || sk_header[1] != 'k')
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
+    regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch in parsing"
 		      " SK record at offset 0x%.8X.", offset);
-    return NULL;
+    goto fail_locked;
   }
 
-  ret_val = talloc(NULL, REGFI_SK_REC);
+  ret_val = talloc(NULL, REGFI_SK);
   if(ret_val == NULL)
-    return NULL;
+    goto fail_locked;
 
   ret_val->offset = offset;
   /* XXX: Is there a way to be more conservative (shorter) with 
@@ -831,9 +1013,9 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_siz
   if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
      || (strict && (ret_val->cell_size & 0x00000007) != 0))
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while"
+    regfi_log_add(REGFI_LOG_WARN, "Invalid cell size found while"
 		      " parsing SK record at offset 0x%.8X.", offset);
-    goto fail;
+    goto fail_locked;
   }
 
   ret_val->magic[0] = sk_header[0];
@@ -848,38 +1030,41 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_siz
   if((ret_val->prev_sk_off & 0x00000007) != 0
      || (ret_val->next_sk_off & 0x00000007) != 0)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "SK record's next/previous offsets"
+    regfi_log_add(REGFI_LOG_WARN, "SK record's next/previous offsets"
 		      " are not a multiple of 8 while parsing SK record at"
 		      " offset 0x%.8X.", offset);
-    goto fail;
+    goto fail_locked;
   }
 
   if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Security descriptor too large for"
+    regfi_log_add(REGFI_LOG_WARN, "Security descriptor too large for"
 		      " cell while parsing SK record at offset 0x%.8X.", 
 		      offset);
-    goto fail;
+    goto fail_locked;
   }
 
   sec_desc_buf = (uint8_t*)malloc(ret_val->desc_size);
   if(sec_desc_buf == NULL)
-    goto fail;
+    goto fail_locked;
 
   length = ret_val->desc_size;
-  if(regfi_read(file->fd, sec_desc_buf, &length) != 0 
+  if(regfi_read(file->cb, sec_desc_buf, &length) != 0 
      || length != ret_val->desc_size)
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read security"
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to read security"
 		      " descriptor while parsing SK record at offset 0x%.8X.",
 		      offset);
-    goto fail;
+    goto fail_locked;
   }
 
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_sk"))
+     goto fail;
+
   if(!(ret_val->sec_desc = winsec_parse_desc(ret_val, sec_desc_buf, 
 						   ret_val->desc_size)))
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to parse security"
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to parse security"
 		      " descriptor while parsing SK record at offset 0x%.8X.",
 		      offset);
     goto fail;
@@ -888,6 +1073,8 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_siz
   free(sec_desc_buf);
   return ret_val;
 
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_sk");
  fail:
   if(sec_desc_buf != NULL)
     free(sec_desc_buf);
@@ -899,58 +1086,63 @@ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_siz
 REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset, 
 					uint32_t num_values, bool strict)
 {
-  REGFI_VALUE_LIST* ret_val;
+  REGFI_VALUE_LIST* ret_val = NULL;
   uint32_t i, cell_length, length, read_len;
   bool unalloc;
 
-  if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_valuelist"))
+     goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc))
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read cell header"
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to read cell header"
 		      " while parsing value list at offset 0x%.8X.", offset);
-    return NULL;
+    goto fail_locked;
   }
 
   if((cell_length & 0x00000007) != 0)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Cell length not a multiple of 8"
+    regfi_log_add(REGFI_LOG_WARN, "Cell length not a multiple of 8"
 		      " while parsing value list at offset 0x%.8X.", offset);
     if(strict)
-      return NULL;
+      goto fail_locked;
     cell_length = cell_length & 0xFFFFFFF8;
   }
 
   if((num_values * sizeof(uint32_t)) > cell_length-sizeof(uint32_t))
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Too many values found"
+    regfi_log_add(REGFI_LOG_WARN, "Too many values found"
 		      " while parsing value list at offset 0x%.8X.", offset);
     if(strict)
-      return NULL;
+      goto fail_locked;
     num_values = cell_length/sizeof(uint32_t) - sizeof(uint32_t);
   }
 
   read_len = num_values*sizeof(uint32_t);
   ret_val = talloc(NULL, REGFI_VALUE_LIST);
   if(ret_val == NULL)
-    return NULL;
+    goto fail_locked;
 
   ret_val->elements = (REGFI_VALUE_LIST_ELEM*)talloc_size(ret_val, read_len);
   if(ret_val->elements == NULL)
-  {
-    talloc_free(ret_val);
-    return NULL;
-  }
+    goto fail_locked;
+
+  ret_val->offset = offset;
+  ret_val->cell_size = cell_length;
   ret_val->num_values = num_values;
 
   length = read_len;
-  if((regfi_read(file->fd, (uint8_t*)ret_val->elements, &length) != 0) 
+  if((regfi_read(file->cb, (uint8_t*)ret_val->elements, &length) != 0) 
      || length != read_len)
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read value pointers"
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to read value pointers"
 		      " while parsing value list at offset 0x%.8X.", offset);
-    talloc_free(ret_val);
-    return NULL;
+    goto fail_locked;
   }
   
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_valuelist"))
+     goto fail;
+
   for(i=0; i < num_values; i++)
   {
     /* Fix endianness */
@@ -964,21 +1156,25 @@ REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset,
       if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length)
 	 || ((ret_val->elements[i] & 0x00000007) != 0))
       {
-	regfi_add_message(file, REGFI_MSG_WARN, "Invalid value pointer"
+	regfi_log_add(REGFI_LOG_WARN, "Invalid value pointer"
 			  " (0x%.8X) found while parsing value list at offset"
 			  " 0x%.8X.", ret_val->elements[i], offset);
-	talloc_free(ret_val);
-	return NULL;
+	goto fail;
       }
     }
   }
 
   return ret_val;
-}
 
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_valuelist");
+ fail:
+  talloc_free(ret_val);
+  return NULL;
+}
 
-void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk, 
-			       REGFI_ENCODING output_encoding, bool strict)
+/* XXX: should give this boolean return type to indicate errors */
+void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK* vk, bool strict)
 {
   /* XXX: Registry value names are supposedly limited to 16383 characters 
    *      according to:
@@ -988,38 +1184,36 @@ void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk,
    *      Also, it may be useful to use this information to limit false positives
    *      when recovering deleted VK records.
    */
-  int32_t tmp_size;
+  REGFI_BUFFER tmp_buf;
   REGFI_ENCODING from_encoding = (vk->flags & REGFI_VK_FLAG_ASCIINAME)
     ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
 
-  if(from_encoding == output_encoding)
+  if(vk->name_length == 0)
+    return;
+
+  if(from_encoding == file->string_encoding)
   {
-    vk->valuename_raw = talloc_realloc(vk, vk->valuename_raw,
-					    uint8_t, vk->name_length+1);
-    vk->valuename_raw[vk->name_length] = '\0';
-    vk->valuename = (char*)vk->valuename_raw;
+    vk->name_raw[vk->name_length] = '\0';
+    vk->name = (char*)vk->name_raw;
   }
   else
   {
-    vk->valuename = talloc_array(vk, char, vk->name_length+1);
-    if(vk->valuename == NULL)
+    tmp_buf = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+                                 regfi_encoding_int2str(file->string_encoding),
+                                 vk->name_raw, vk->name_length);
+    if(tmp_buf.buf == NULL)
     {
-      regfi_free_value(vk);
-      return;
+      regfi_log_add(REGFI_LOG_WARN, "Error occurred while converting"
+                    " value name to encoding %s.  VK offset: 0x%.8X."
+                    "  Error message: %s",
+                    regfi_encoding_int2str(file->string_encoding),
+                    vk->offset, strerror(errno));
+      vk->name = NULL;
     }
-
-    tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
-				  regfi_encoding_int2str(output_encoding),
-				  vk->valuename_raw, vk->valuename,
-				  vk->name_length, vk->name_length+1);
-    if(tmp_size < 0)
+    else
     {
-      regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting"
-			" valuename to encoding %s.  Error message: %s",
-			regfi_encoding_int2str(output_encoding), 
-			strerror(-tmp_size));
-      talloc_free(vk->valuename);
-      vk->valuename = NULL;
+      vk->name = (char*)tmp_buf.buf;
+      talloc_reparent(NULL, vk, vk->name);
     }
   }
 }
@@ -1027,10 +1221,9 @@ void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk,
 
 /******************************************************************************
  ******************************************************************************/
-REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset, 
-			       REGFI_ENCODING output_encoding, bool strict)
+REGFI_VK* regfi_load_value(REGFI_FILE* file, uint32_t offset, bool strict)
 {
-  REGFI_VK_REC* ret_val = NULL;
+  REGFI_VK* ret_val = NULL;
   int32_t max_size;
 
   max_size = regfi_calc_maxsize(file, offset);
@@ -1041,7 +1234,7 @@ REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset,
   if(ret_val == NULL)
     return NULL;
 
-  regfi_interpret_valuename(file, ret_val, output_encoding, strict);
+  regfi_interpret_valuename(file, ret_val, strict);
 
   return ret_val;
 }
@@ -1058,7 +1251,7 @@ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset,
 
   if((num_values+1) * sizeof(uint32_t) > max_size)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Number of values indicated by"
+    regfi_log_add(REGFI_LOG_WARN, "Number of values indicated by"
 		      " parent key (%d) would cause cell to straddle HBIN"
 		      " boundary while loading value list at offset"
 		      " 0x%.8X.", num_values, offset);
@@ -1073,8 +1266,8 @@ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset,
 }
 
 
-void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk, 
-			     REGFI_ENCODING output_encoding, bool strict)
+/* XXX: should give this boolean return type to indicate errors */
+void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK* nk, bool strict)
 {
   /* XXX: Registry key names are supposedly limited to 255 characters according to:
    *      http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx
@@ -1083,37 +1276,36 @@ void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk,
    *      Also, it may be useful to use this information to limit false positives
    *      when recovering deleted NK records.
    */
-  int32_t tmp_size;
+  REGFI_BUFFER tmp_buf;
   REGFI_ENCODING from_encoding = (nk->flags & REGFI_NK_FLAG_ASCIINAME) 
     ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE;
-  
-  if(from_encoding == output_encoding)
+
+  if(nk->name_length == 0)
+    return;  
+
+  if(from_encoding == file->string_encoding)
   {
-    nk->keyname_raw = talloc_realloc(nk, nk->keyname_raw, uint8_t, nk->name_length+1);
-    nk->keyname_raw[nk->name_length] = '\0';
-    nk->keyname = (char*)nk->keyname_raw;
+    nk->name_raw[nk->name_length] = '\0';
+    nk->name = (char*)nk->name_raw;
   }
   else
   {
-    nk->keyname = talloc_array(nk, char, nk->name_length+1);
-    if(nk->keyname == NULL)
+    tmp_buf = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
+                                 regfi_encoding_int2str(file->string_encoding),
+                                 nk->name_raw, nk->name_length);
+    if(tmp_buf.buf == NULL)
     {
-      regfi_free_key(nk);
-      return;
+      regfi_log_add(REGFI_LOG_WARN, "Error occurred while converting"
+                    " key name to encoding %s.  NK offset: 0x%.8X."
+                    "  Error message: %s",
+                    regfi_encoding_int2str(file->string_encoding), 
+                    nk->offset, strerror(errno));
+      nk->name = NULL;
     }
-
-    tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding),
-				  regfi_encoding_int2str(output_encoding),
-				  nk->keyname_raw, nk->keyname,
-				  nk->name_length, nk->name_length+1);
-    if(tmp_size < 0)
+    else
     {
-      regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting"
-			" keyname to encoding %s.  Error message: %s",
-			regfi_encoding_int2str(output_encoding), 
-			strerror(-tmp_size));
-      talloc_free(nk->keyname);
-      nk->keyname = NULL;
+      nk->name = (char*)tmp_buf.buf;
+      talloc_reparent(NULL, nk, nk->name);
     }
   }
 }
@@ -1122,13 +1314,30 @@ void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk,
 /******************************************************************************
  *
  ******************************************************************************/
-REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset, 
-			     REGFI_ENCODING output_encoding, bool strict)
+REGFI_NK* regfi_load_key(REGFI_FILE* file, uint32_t offset, bool strict)
 {
-  REGFI_NK_REC* nk;
+  REGFI_NK* nk;
   uint32_t off;
   int32_t max_size;
 
+  if(file->nk_cache != NULL)
+  {
+    /* First, check to see if we have this key in our cache */
+    if(!regfi_lock(file, &file->mem_lock, "regfi_load_nk"))
+      return NULL;
+    regfi_lock(file, &file->nk_lock, "regfi_load_nk");
+    
+    nk = (REGFI_NK*)lru_cache_find(file->nk_cache, &offset, 4);
+    if(nk != NULL)
+      nk = talloc_reference(NULL, nk);
+
+    regfi_unlock(file, &file->nk_lock, "regfi_load_nk");
+    regfi_unlock(file, &file->mem_lock, "regfi_load_nk");
+    if(nk != NULL)
+      return nk;
+  }
+
+  /* Not cached currently, proceed with loading it */
   max_size = regfi_calc_maxsize(file, offset);
   if (max_size < 0) 
     return NULL;
@@ -1136,12 +1345,12 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
   /* get the initial nk record */
   if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL)
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at"
-		      " offset 0x%.8X.", offset);
+    regfi_log_add(REGFI_LOG_ERROR, "Could not load NK record at"
+		  " offset 0x%.8X.", offset);
     return NULL;
   }
 
-  regfi_interpret_keyname(file, nk, output_encoding, strict);
+  regfi_interpret_keyname(file, nk, strict);
 
   /* get value list */
   if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) 
@@ -1152,12 +1361,11 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
     {
       if(strict)
       {
-	regfi_free_key(nk);
+	talloc_free(nk);
 	return NULL;
       }
       else
 	nk->values = NULL;
-
     }
     else
     {
@@ -1165,15 +1373,15 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
 					max_size, true);
       if(nk->values == NULL)
       {
-	regfi_add_message(file, REGFI_MSG_WARN, "Could not load value list"
-			  " for NK record at offset 0x%.8X.", offset);
+	regfi_log_add(REGFI_LOG_WARN, "Could not load value list"
+		      " for NK record at offset 0x%.8X.", offset);
 	if(strict)
 	{
-	  regfi_free_key(nk);
+	  talloc_free(nk);
 	  return NULL;
 	}
       }
-      talloc_steal(nk, nk->values);
+      talloc_reparent(NULL, nk, nk->values);
     }
   }
 
@@ -1186,7 +1394,7 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
     {
       if(strict)
       {
-	regfi_free_key(nk);
+	talloc_free(nk);
 	return NULL;
       }
       else
@@ -1199,52 +1407,82 @@ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset,
 
       if(nk->subkeys == NULL)
       {
-	regfi_add_message(file, REGFI_MSG_WARN, "Could not load subkey list"
-			  " while parsing NK record at offset 0x%.8X.", offset);
+	regfi_log_add(REGFI_LOG_WARN, "Could not load subkey list"
+		      " while parsing NK record at offset 0x%.8X.", offset);
 	nk->num_subkeys = 0;
       }
-      talloc_steal(nk, nk->subkeys);
+      talloc_reparent(NULL, nk, nk->subkeys);
     }
   }
 
+  if(file->nk_cache != NULL)
+  {
+    /* All is well, so let us cache this key for later */
+    if(!regfi_lock(file, &file->mem_lock, "regfi_load_nk"))
+      return NULL;
+    regfi_lock(file, &file->nk_lock, "regfi_load_nk");
+    
+    lru_cache_update(file->nk_cache, &offset, 4, nk);
+    
+    regfi_unlock(file, &file->nk_lock, "regfi_load_nk");
+    regfi_unlock(file, &file->mem_lock, "regfi_load_nk");
+  }
+
   return nk;
 }
 
 
 /******************************************************************************
  ******************************************************************************/
-const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict)
+const REGFI_SK* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict)
 {
-  REGFI_SK_REC* ret_val = NULL;
+  REGFI_SK* ret_val = NULL;
   int32_t max_size;
   void* failure_ptr = NULL;
   
+  max_size = regfi_calc_maxsize(file, offset);
+  if(max_size < 0)
+    return NULL;
+
+  if(file->sk_cache == NULL)
+    return regfi_parse_sk(file, offset, max_size, strict);
+
+  if(!regfi_lock(file, &file->mem_lock, "regfi_load_sk"))
+    return NULL;
+  regfi_lock(file, &file->sk_lock, "regfi_load_sk");
+
   /* First look if we have already parsed it */
-  ret_val = (REGFI_SK_REC*)lru_cache_find(file->sk_cache, &offset, 4);
+  ret_val = (REGFI_SK*)lru_cache_find(file->sk_cache, &offset, 4);
 
   /* Bail out if we have previously cached a parse failure at this offset. */
   if(ret_val == (void*)REGFI_OFFSET_NONE)
-    return NULL;
+  {
+    ret_val = NULL;
+    goto unlock;
+  }
 
   if(ret_val == NULL)
   {
-    max_size = regfi_calc_maxsize(file, offset);
-    if(max_size < 0)
-      return NULL;
-
     ret_val = regfi_parse_sk(file, offset, max_size, strict);
     if(ret_val == NULL)
     { /* Cache the parse failure and bail out. */
       failure_ptr = talloc(NULL, uint32_t);
       if(failure_ptr == NULL)
-	return NULL;
+	goto unlock;
+
       *(uint32_t*)failure_ptr = REGFI_OFFSET_NONE;
       lru_cache_update(file->sk_cache, &offset, 4, failure_ptr);
-      return NULL;
-    }
 
-    lru_cache_update(file->sk_cache, &offset, 4, ret_val);
+      /* Let the cache be the only owner of this */
+      talloc_unlink(NULL, failure_ptr);
+    }
   }
+  else
+    ret_val = talloc_reference(NULL, ret_val);
+
+ unlock:
+  regfi_unlock(file, &file->sk_lock, "regfi_load_sk");
+  regfi_unlock(file, &file->mem_lock, "regfi_load_sk");
 
   return ret_val;
 }
@@ -1253,10 +1491,9 @@ const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict
 
 /******************************************************************************
  ******************************************************************************/
-REGFI_NK_REC* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin, 
-				 REGFI_ENCODING output_encoding)
+REGFI_NK* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin)
 {
-  REGFI_NK_REC* nk = NULL;
+  REGFI_NK* nk = NULL;
   uint32_t cell_length;
   uint32_t cur_offset = hbin->file_off+REGFI_HBIN_HEADER_SIZE;
   uint32_t hbin_end = hbin->file_off+hbin->block_size;
@@ -1264,16 +1501,26 @@ REGFI_NK_REC* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin,
 
   while(cur_offset < hbin_end)
   {
-    if(!regfi_parse_cell(file->fd, cur_offset, NULL, 0, &cell_length, &unalloc))
-    {
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell at offset"
-			" 0x%.8X while searching for root key.", cur_offset);
+
+    if(!regfi_lock(file, &file->cb_lock, "regfi_find_root_nk"))
       return NULL;
+
+    if(!regfi_parse_cell(file->cb, cur_offset, NULL, 0, &cell_length, &unalloc))
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Could not parse cell at offset"
+		    " 0x%.8X while searching for root key.", cur_offset);
+      goto error_locked;
     }
-    
+
+    if(!regfi_unlock(file, &file->cb_lock, "regfi_find_root_nk"))
+      return NULL;
+
+    if(cell_length == 0)
+      break;
+
     if(!unalloc)
     {
-      nk = regfi_load_key(file, cur_offset, output_encoding, true);
+      nk = regfi_load_key(file, cur_offset, true);
       if(nk != NULL)
       {
 	if(nk->flags & REGFI_NK_FLAG_ROOT)
@@ -1285,67 +1532,141 @@ REGFI_NK_REC* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin,
   }
 
   return NULL;
+
+ error_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_find_root_nk");
+  return NULL;
 }
 
 
+
 /******************************************************************************
  ******************************************************************************/
-REGFI_FILE* regfi_open(const char* filename)
+REGFI_FILE* regfi_alloc(int fd, REGFI_ENCODING output_encoding)
 {
   REGFI_FILE* ret_val;
-  int fd;
-
-  /* open an existing file */
-  if ((fd = open(filename, REGFI_OPEN_FLAGS)) == -1)
-  {
-    /* fprintf(stderr, "regfi_open: failure to open %s (%s)\n", filename, strerror(errno));*/
+  REGFI_RAW_FILE* file_cb = talloc(NULL, REGFI_RAW_FILE);
+  if(file_cb == NULL) 
     return NULL;
-  }
-
-  ret_val = regfi_alloc(fd);
 
+  file_cb->state = (void*)talloc(file_cb, int);
+  if(file_cb->state == NULL)
+    goto fail;
+  *(int*)file_cb->state = fd;
+  
+  file_cb->cur_off = 0;
+  file_cb->size = 0;
+  file_cb->read = &regfi_raw_read;
+  file_cb->seek = &regfi_raw_seek;
+  
+  ret_val = regfi_alloc_cb(file_cb, output_encoding);
   if(ret_val == NULL)
-    close(fd);
+    goto fail;
 
+  /* In this case, we want file_cb to be freed when ret_val is */
+  talloc_reparent(NULL, ret_val, file_cb);
   return ret_val;
-}
-
+
+ fail:
+    talloc_free(file_cb);
+    return NULL;
+}
+
+
+/******************************************************************************
+ ******************************************************************************/
+static int regfi_free_cb(void* f)
+{
+  REGFI_FILE* file = (REGFI_FILE*)f;
+
+  pthread_mutex_destroy(&file->cb_lock);
+  pthread_rwlock_destroy(&file->hbins_lock);
+  pthread_mutex_destroy(&file->sk_lock);
+  pthread_mutex_destroy(&file->nk_lock);
+  pthread_mutex_destroy(&file->mem_lock);
+
+  return 0;
+}
+
 
 /******************************************************************************
  ******************************************************************************/
-REGFI_FILE* regfi_alloc(int fd)
+REGFI_FILE* regfi_alloc_cb(REGFI_RAW_FILE* file_cb, 
+			   REGFI_ENCODING output_encoding)
 {
-  struct stat sbuf;
   REGFI_FILE* rb;
   REGFI_HBIN* hbin = NULL;
-  uint32_t hbin_off, file_length, cache_secret;
+  uint32_t hbin_off, cache_secret;
+  int64_t file_length;
   bool rla;
 
-  /* Determine file length.  Must be at least big enough 
-   * for the header and one hbin. 
+  /* Determine file length.  Must be at least big enough for the header
+   * and one hbin.
    */
-  if (fstat(fd, &sbuf) == -1)
-    return NULL;
-  file_length = sbuf.st_size;
+  file_length = regfi_seek(file_cb, 0, SEEK_END);
   if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "File length (%d) too short to contain a"
+		  " header and at least one HBIN.", file_length);
+    return NULL;
+  }
+  regfi_seek(file_cb, 0, SEEK_SET);
+
+  if(output_encoding != REGFI_ENCODING_UTF8
+     && output_encoding != REGFI_ENCODING_ASCII)
+  { 
+    regfi_log_add(REGFI_LOG_ERROR, "Invalid output_encoding supplied"
+		  " in creation of regfi iterator.");
     return NULL;
+  }
 
   /* Read file header */
-  if ((rb = regfi_parse_regf(fd, true)) == NULL) 
+  if ((rb = regfi_parse_regf(file_cb, false)) == NULL)
   {
-    /* fprintf(stderr, "regfi_alloc: Failed to read initial REGF block\n"); */
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to read REGF block.");
     return NULL;
   }
-  rb->file_length = file_length;  
+  rb->file_length = file_length;
+  rb->cb = file_cb;
+  rb->string_encoding = output_encoding;
+
+  if(pthread_mutex_init(&rb->cb_lock, NULL) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create cb_lock mutex.");
+    goto fail;
+  }
+
+  if(pthread_rwlock_init(&rb->hbins_lock, NULL) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create hbins_lock rwlock.");
+    goto fail;
+  }
+
+  if(pthread_mutex_init(&rb->sk_lock, NULL) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create sk_lock mutex.");
+    goto fail;
+  }
+
+  if(pthread_mutex_init(&rb->nk_lock, NULL) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create nk_lock mutex.");
+    goto fail;
+  }
+
+  if(pthread_mutex_init(&rb->mem_lock, NULL) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create mem_lock mutex.");
+    goto fail;
+  }
 
   rb->hbins = range_list_new();
   if(rb->hbins == NULL)
   {
-    /* fprintf(stderr, "regfi_alloc: Failed to create HBIN list.\n"); */
-    talloc_free(rb);
-    return NULL;
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to create HBIN range_list.");
+    goto fail;
   }
-  talloc_steal(rb, rb->hbins);
+  talloc_reparent(NULL, rb, rb->hbins);
 
   rla = true;
   hbin_off = REGFI_REGF_SIZE;
@@ -1354,7 +1675,8 @@ REGFI_FILE* regfi_alloc(int fd)
   {
     rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin);
     if(rla)
-      talloc_steal(rb->hbins, hbin);
+      talloc_reparent(NULL, rb->hbins, hbin);
+
     hbin_off = hbin->file_off + hbin->block_size;
     hbin = regfi_parse_hbin(rb, hbin_off, true);
   }
@@ -1365,43 +1687,38 @@ REGFI_FILE* regfi_alloc(int fd)
    */
   cache_secret = 0x15DEAD05^time(NULL)^(getpid()<<16);
 
-  /* Cache an unlimited number of SK records.  Typically there are very few. */
-  rb->sk_cache = lru_cache_create_ctx(rb, 0, cache_secret, true);
+  rb->sk_cache = NULL;
+  if(REGFI_CACHE_SK_MAX > 0)
+    rb->sk_cache = lru_cache_create_ctx(rb, REGFI_CACHE_SK_MAX, 
+                                        cache_secret, true);
 
-  /* Default message mask */
-  rb->msg_mask = REGFI_MSG_ERROR|REGFI_MSG_WARN;
+  rb->nk_cache = NULL;
+  if(REGFI_CACHE_NK_MAX > 0)
+    rb->nk_cache = lru_cache_create_ctx(rb, REGFI_CACHE_NK_MAX, 
+                                        cache_secret, true);
 
   /* success */
+  talloc_set_destructor(rb, regfi_free_cb);
   return rb;
-}
-
-
-/******************************************************************************
- ******************************************************************************/
-int regfi_close(REGFI_FILE* file)
-{
-  int fd;
-
-  /* nothing to do if there is no open file */
-  if ((file == NULL) || (file->fd == -1))
-    return 0;
 
-  fd = file->fd;
-  file->fd = -1;
-
-  regfi_free(file);
-
-  return close(fd);
+ fail:
+  pthread_mutex_destroy(&rb->cb_lock);
+  pthread_rwlock_destroy(&rb->hbins_lock);
+  pthread_mutex_destroy(&rb->sk_lock);
+  pthread_mutex_destroy(&rb->nk_lock);
+  pthread_mutex_destroy(&rb->mem_lock);
+
+  range_list_free(rb->hbins);
+  talloc_free(rb);
+  return NULL;
 }
 
 
 /******************************************************************************
  ******************************************************************************/
-void regfi_free(REGFI_FILE *file)
+void regfi_free(REGFI_FILE* file)
 {
-  if(file->last_message != NULL)
-    free(file->last_message);
-
+  /* Callback handles cleanup side effects */
   talloc_free(file);
 }
 
@@ -1410,9 +1727,9 @@ void regfi_free(REGFI_FILE *file)
  * First checks the offset given by the file header, then checks the
  * rest of the file if that fails.
  ******************************************************************************/
-REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file, REGFI_ENCODING output_encoding)
+const REGFI_NK* regfi_get_rootkey(REGFI_FILE* file)
 {
-  REGFI_NK_REC* nk = NULL;
+  REGFI_NK* nk = NULL;
   REGFI_HBIN* hbin;
   uint32_t root_offset, i, num_hbins;
   
@@ -1420,81 +1737,126 @@ REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file, REGFI_ENCODING output_encoding)
     return NULL;
 
   root_offset = file->root_cell+REGFI_REGF_SIZE;
-  nk = regfi_load_key(file, root_offset, output_encoding, true);
+  nk = regfi_load_key(file, root_offset, true);
   if(nk != NULL)
   {
     if(nk->flags & REGFI_NK_FLAG_ROOT)
       return nk;
   }
 
-  regfi_add_message(file, REGFI_MSG_WARN, "File header indicated root key at"
-		    " location 0x%.8X, but no root key found."
-		    " Searching rest of file...", root_offset);
+  regfi_log_add(REGFI_LOG_WARN, "File header indicated root key at"
+		" location 0x%.8X, but no root key found."
+		" Searching rest of file...", root_offset);
   
   /* If the file header gives bad info, scan through the file one HBIN
    * block at a time looking for an NK record with a root key type.
    */
+  
+  if(!regfi_read_lock(file, &file->hbins_lock, "regfi_get_rootkey"))
+    return NULL;
+
   num_hbins = range_list_size(file->hbins);
   for(i=0; i < num_hbins && nk == NULL; i++)
   {
     hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data;
-    nk = regfi_find_root_nk(file, hbin, output_encoding);
+    nk = regfi_find_root_nk(file, hbin);
   }
 
+  if(!regfi_rw_unlock(file, &file->hbins_lock, "regfi_get_rootkey"))
+    return NULL;
+
   return nk;
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-void regfi_free_key(REGFI_NK_REC* nk)
+void regfi_free_record(REGFI_FILE* file, const void* record)
 {
-  regfi_subkeylist_free(nk->subkeys);
-  talloc_free(nk);
+  if(!regfi_lock(file, &file->mem_lock, "regfi_free_record"))
+    return;
+
+  talloc_unlink(NULL, (void*)record);
+
+  regfi_unlock(file, &file->mem_lock, "regfi_free_record");
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-void regfi_free_value(REGFI_VK_REC* vk)
+const void* regfi_reference_record(REGFI_FILE* file, const void* record)
 {
-  talloc_free(vk);
+  const void* ret_val = NULL;
+
+  if(!regfi_lock(file, &file->mem_lock, "regfi_reference_record"))
+    return ret_val;
+
+  ret_val = talloc_reference(NULL, record);
+
+  regfi_unlock(file, &file->mem_lock, "regfi_reference_record");
+  return ret_val;
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list)
+uint32_t regfi_fetch_num_subkeys(const REGFI_NK* key)
 {
-  if(list != NULL)
+  uint32_t num_in_list = 0;
+  if(key == NULL)
+    return 0;
+
+  if(key->subkeys != NULL)
+    num_in_list = key->subkeys->num_keys;
+
+  if(num_in_list != key->num_subkeys)
   {
-    talloc_free(list);
+    regfi_log_add(REGFI_LOG_INFO, "Key at offset 0x%.8X contains %d keys in its"
+		  " subkey list but reports %d should be available.", 
+		  key->offset, num_in_list, key->num_subkeys);
+    return (num_in_list < key->num_subkeys)?num_in_list:key->num_subkeys;
   }
+  
+  return num_in_list;
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file, 
-				   REGFI_ENCODING output_encoding)
+uint32_t regfi_fetch_num_values(const REGFI_NK* key)
 {
-  REGFI_NK_REC* root;
-  REGFI_ITERATOR* ret_val;
+  uint32_t num_in_list = 0;
+  if(key == NULL)
+    return 0;
 
-  if(output_encoding != REGFI_ENCODING_UTF8
-     && output_encoding != REGFI_ENCODING_ASCII)
-  { 
-    regfi_add_message(file, REGFI_MSG_ERROR, "Invalid output_encoding supplied"
-		      " in creation of regfi iterator.");
-    return NULL;
+  if(key->values != NULL)
+    num_in_list = key->values->num_values;
+
+  if(num_in_list != key->num_values)
+  {
+    regfi_log_add(REGFI_LOG_INFO, "Key at offset 0x%.8X contains %d values in"
+		  " its value list but reports %d should be available.",
+		  key->offset, num_in_list, key->num_values);
+    return (num_in_list < key->num_values)?num_in_list:key->num_values;
   }
+  
+  return num_in_list;
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file)
+{
+  REGFI_NK* root;
+  REGFI_ITERATOR* ret_val;
 
   ret_val = talloc(NULL, REGFI_ITERATOR);
   if(ret_val == NULL)
     return NULL;
-
-  root = regfi_rootkey(file, output_encoding);
-  if(root == NULL)
+  
+  ret_val->cur = talloc(ret_val, REGFI_ITER_POSITION);
+  if(ret_val->cur == NULL)
   {
     talloc_free(ret_val);
     return NULL;
@@ -1506,14 +1868,31 @@ REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file,
     talloc_free(ret_val);
     return NULL;
   }
-  talloc_steal(ret_val, ret_val->key_positions);
+  talloc_reparent(NULL, ret_val, ret_val->key_positions);
 
+  root = (REGFI_NK*)regfi_get_rootkey(file);
+  if(root == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  ret_val->cur->offset = root->offset;
+  if(root->subkeys_off == REGFI_OFFSET_NONE)
+    ret_val->cur->num_subkeys = 0;
+  else
+    ret_val->cur->num_subkeys = regfi_fetch_num_subkeys(root);
+  
+  if(root->values_off == REGFI_OFFSET_NONE)
+    ret_val->cur->num_values = 0;
+  else
+    ret_val->cur->num_values = regfi_fetch_num_values(root);
+
+  ret_val->cur->cur_subkey = 0;
+  ret_val->cur->cur_value = 0;
   ret_val->f = file;
-  ret_val->cur_key = root;
-  ret_val->cur_subkey = 0;
-  ret_val->cur_value = 0;
-  ret_val->string_encoding = output_encoding;
-    
+
+  regfi_free_record(ret_val->f, root);
   return ret_val;
 }
 
@@ -1522,44 +1901,50 @@ REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file,
  *****************************************************************************/
 void regfi_iterator_free(REGFI_ITERATOR* i)
 {
-  talloc_free(i);
+  talloc_unlink(NULL, i);
 }
 
 
-
 /******************************************************************************
  *****************************************************************************/
 /* XXX: some way of indicating reason for failure should be added. */
 bool regfi_iterator_down(REGFI_ITERATOR* i)
 {
-  REGFI_NK_REC* subkey;
-  REGFI_ITER_POSITION* pos;
-
-  pos = talloc(i->key_positions, REGFI_ITER_POSITION);
+  REGFI_NK* subkey;
+  REGFI_ITER_POSITION* pos = talloc(i, REGFI_ITER_POSITION);
   if(pos == NULL)
     return false;
 
-  subkey = (REGFI_NK_REC*)regfi_iterator_cur_subkey(i);
+  subkey = (REGFI_NK*)regfi_iterator_cur_subkey(i);
   if(subkey == NULL)
   {
     talloc_free(pos);
     return false;
   }
 
-  pos->nk = i->cur_key;
-  pos->cur_subkey = i->cur_subkey;
-  if(!void_stack_push(i->key_positions, pos))
+  if(!void_stack_push(i->key_positions, i->cur))
   {
     talloc_free(pos);
-    regfi_free_key(subkey);
+    regfi_free_record(i->f, subkey);
     return false;
   }
-  talloc_steal(i, subkey);
 
-  i->cur_key = subkey;
-  i->cur_subkey = 0;
-  i->cur_value = 0;
+  pos->offset = subkey->offset;
+  if(subkey->subkeys_off == REGFI_OFFSET_NONE)
+    pos->num_subkeys = 0;
+  else
+    pos->num_subkeys = regfi_fetch_num_subkeys(subkey);
 
+  if(subkey->values_off == REGFI_OFFSET_NONE)
+    pos->num_values = 0;
+  else
+    pos->num_values = regfi_fetch_num_values(subkey);
+
+  pos->cur_subkey = 0;
+  pos->cur_value = 0;
+  i->cur = pos;
+
+  regfi_free_record(i->f, subkey);
   return true;
 }
 
@@ -1574,12 +1959,14 @@ bool regfi_iterator_up(REGFI_ITERATOR* i)
   if(pos == NULL)
     return false;
 
-  regfi_free_key(i->cur_key);
-  i->cur_key = pos->nk;
-  i->cur_subkey = pos->cur_subkey;
-  i->cur_value = 0;
-  talloc_free(pos);
+  if(!regfi_lock(i->f, &i->f->mem_lock, "regfi_iterator_up"))
+    return false;
+  
+  talloc_unlink(i, i->cur);
+
+  regfi_unlock(i->f, &i->f->mem_lock, "regfi_iterator_up");
 
+  i->cur = pos;
   return true;
 }
 
@@ -1597,43 +1984,33 @@ bool regfi_iterator_to_root(REGFI_ITERATOR* i)
 
 /******************************************************************************
  *****************************************************************************/
-bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name)
+bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* name)
 {
-  REGFI_NK_REC* subkey;
-  bool found = false;
-  uint32_t old_subkey = i->cur_subkey;
-
-  if(subkey_name == NULL)
-    return false;
+  const REGFI_NK* cur_key;
+  uint32_t new_index;
+  bool ret_val = false;
 
-  /* XXX: this alloc/free of each sub key might be a bit excessive */
-  subkey = (REGFI_NK_REC*)regfi_iterator_first_subkey(i);
-  while((subkey != NULL) && (found == false))
+  cur_key = regfi_iterator_cur_key(i);
+  if(cur_key == NULL)
   {
-    if(subkey->keyname != NULL 
-       && strcasecmp(subkey->keyname, subkey_name) == 0)
-      found = true;
-    else
-    {
-      regfi_free_key(subkey);
-      subkey = (REGFI_NK_REC*)regfi_iterator_next_subkey(i);
-    }
+    regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in find_subkey.");
+    return ret_val;
   }
 
-  if(found == false)
+  if(regfi_find_subkey(i->f, cur_key, name, &new_index))
   {
-    i->cur_subkey = old_subkey;
-    return false;
+    i->cur->cur_subkey = new_index;
+    ret_val = true;
   }
 
-  regfi_free_key(subkey);
-  return true;
+  regfi_free_record(i->f, cur_key);
+  return ret_val;
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
+bool regfi_iterator_descend(REGFI_ITERATOR* i, const char** path)
 {
   uint32_t x;
   if(path == NULL)
@@ -1646,8 +2023,10 @@ bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
   { continue; }
 
   if(path[x] == NULL)
+  {
     return true;
-  
+  }
+
   /* XXX: is this the right number of times? */
   for(; x > 0; x--)
     regfi_iterator_up(i);
@@ -1658,178 +2037,228 @@ bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path)
 
 /******************************************************************************
  *****************************************************************************/
-const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i)
+const REGFI_NK* regfi_iterator_cur_key(REGFI_ITERATOR* i)
 {
-  return i->cur_key;
+  const REGFI_NK* ret_val = NULL;
+
+  ret_val = regfi_load_key(i->f, i->cur->offset, true);
+  return ret_val;
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i)
+const REGFI_SK* regfi_fetch_sk(REGFI_FILE* file, const REGFI_NK* key)
 {
-  if(i->cur_key == NULL || i->cur_key->sk_off == REGFI_OFFSET_NONE)
+  if(key == NULL || key->sk_off == REGFI_OFFSET_NONE)
     return NULL;
 
-  return regfi_load_sk(i->f, i->cur_key->sk_off + REGFI_REGF_SIZE, true);
+  return regfi_load_sk(file, key->sk_off + REGFI_REGF_SIZE, true);
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i)
+const REGFI_SK* regfi_next_sk(REGFI_FILE* file, const REGFI_SK* sk)
 {
-  i->cur_subkey = 0;
-  return regfi_iterator_cur_subkey(i);
+  if(sk == NULL || sk->next_sk_off == REGFI_OFFSET_NONE)
+    return NULL;
+
+  return regfi_load_sk(file, sk->next_sk_off + REGFI_REGF_SIZE, true);
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
+const REGFI_SK* regfi_prev_sk(REGFI_FILE* file, const REGFI_SK* sk)
 {
-  uint32_t nk_offset;
-
-  /* see if there is anything left to report */
-  if (!(i->cur_key) || (i->cur_key->subkeys_off==REGFI_OFFSET_NONE)
-      || (i->cur_subkey >= i->cur_key->num_subkeys))
+  if(sk == NULL || sk->prev_sk_off == REGFI_OFFSET_NONE)
     return NULL;
 
-  nk_offset = i->cur_key->subkeys->elements[i->cur_subkey].offset;
-
-  return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, i->string_encoding, 
-			true);
+  return regfi_load_sk(file, sk->prev_sk_off + REGFI_REGF_SIZE, true);
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-/* XXX: some way of indicating reason for failure should be added. */
-REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i)
+bool regfi_iterator_first_subkey(REGFI_ITERATOR* i)
 {
-  REGFI_NK_REC* subkey;
+  i->cur->cur_subkey = 0;
+  return (i->cur->cur_subkey < i->cur->num_subkeys);
+}
 
-  i->cur_subkey++;
-  subkey = regfi_iterator_cur_subkey(i);
 
-  if(subkey == NULL)
-    i->cur_subkey--;
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK* regfi_iterator_cur_subkey(REGFI_ITERATOR* i)
+{
+  const REGFI_NK* cur_key;
+  const REGFI_NK* ret_val;
+  
+  cur_key = regfi_iterator_cur_key(i);
+  if(cur_key == NULL)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in cur_subkey.");
+    return NULL;
+  }
 
-  return subkey;
+  ret_val = regfi_get_subkey(i->f, cur_key, i->cur->cur_subkey);
+
+  regfi_free_record(i->f, cur_key);
+  return ret_val;
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name)
+bool regfi_iterator_next_subkey(REGFI_ITERATOR* i)
 {
-  REGFI_VK_REC* cur;
-  bool found = false;
-  uint32_t old_value = i->cur_value;
+  i->cur->cur_subkey++;
+  return (i->cur->cur_subkey < i->cur->num_subkeys);
+}
 
-  /* XXX: cur->valuename can be NULL in the registry.  
-   *      Should we allow for a way to search for that? 
-   */
-  if(value_name == NULL)
-    return false;
 
-  cur = regfi_iterator_first_value(i);
-  while((cur != NULL) && (found == false))
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* name)
+{
+  const REGFI_NK* cur_key;
+  uint32_t new_index;
+  bool ret_val = false;
+
+  cur_key = regfi_iterator_cur_key(i);
+  if(cur_key == NULL)
   {
-    if((cur->valuename != NULL)
-       && (strcasecmp(cur->valuename, value_name) == 0))
-      found = true;
-    else
-    {
-      regfi_free_value(cur);
-      cur = regfi_iterator_next_value(i);
-    }
+    regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in find_value.");
+    return ret_val;
   }
-  
-  if(found == false)
+
+  if(regfi_find_value(i->f, cur_key, name, &new_index))
   {
-    i->cur_value = old_value;
-    return false;
+    i->cur->cur_value = new_index;
+    ret_val = true;
   }
 
-  regfi_free_value(cur);
-  return true;
+  regfi_free_record(i->f, cur_key);
+  return ret_val;
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i)
+bool regfi_iterator_first_value(REGFI_ITERATOR* i)
 {
-  i->cur_value = 0;
-  return regfi_iterator_cur_value(i);
+  i->cur->cur_value = 0;
+  return (i->cur->cur_value < i->cur->num_values);
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i)
+const REGFI_VK* regfi_iterator_cur_value(REGFI_ITERATOR* i)
 {
-  REGFI_VK_REC* ret_val = NULL;
-  uint32_t voffset;
+  const REGFI_NK* cur_key;
+  const REGFI_VK* ret_val = NULL;
 
-  if(i->cur_key->values != NULL && i->cur_key->values->elements != NULL)
+  cur_key = regfi_iterator_cur_key(i);
+  if(cur_key == NULL)
   {
-    if(i->cur_value < i->cur_key->values->num_values)
-    {
-      voffset = i->cur_key->values->elements[i->cur_value];
-      ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE, 
-				 i->string_encoding, true);
-    }
+    regfi_log_add(REGFI_LOG_ERROR, "Current key invalid in cur_value.");
+    return ret_val;
   }
 
+  ret_val = regfi_get_value(i->f, cur_key, i->cur->cur_value);
+  
+  regfi_free_record(i->f, cur_key);
   return ret_val;
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i)
+bool regfi_iterator_next_value(REGFI_ITERATOR* i)
+{
+  i->cur->cur_value++;
+  return (i->cur->cur_value < i->cur->num_values);
+}
+
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK** regfi_iterator_ancestry(REGFI_ITERATOR* i)
 {
-  REGFI_VK_REC* ret_val;
+  REGFI_NK** ret_val;
+  void_stack_iterator* iter;
+  const REGFI_ITER_POSITION* cur;
+  uint16_t k, num_keys;
 
-  i->cur_value++;
-  ret_val = regfi_iterator_cur_value(i);
+  num_keys = void_stack_size(i->key_positions)+1;
+  ret_val = talloc_array(NULL, REGFI_NK*, num_keys+1);
   if(ret_val == NULL)
-    i->cur_value--;
+    return NULL;
 
-  return ret_val;
+  iter = void_stack_iterator_new(i->key_positions);
+  if (iter == NULL)
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  k=0;
+  for(cur=void_stack_iterator_next(iter);
+      cur != NULL; cur=void_stack_iterator_next(iter))
+  { 
+    ret_val[k++] = regfi_load_key(i->f, cur->offset, true); 
+  }
+  ret_val[k] = regfi_load_key(i->f, i->cur->offset, true);
+  void_stack_iterator_free(iter);
+
+  if(!regfi_lock(i->f, &i->f->mem_lock, "regfi_iterator_ancestry"))
+  {
+    talloc_free(ret_val);
+    return NULL;
+  }
+
+  for(k=0; k<num_keys; k++)
+    talloc_reparent(NULL, ret_val, ret_val[k]);
+
+  regfi_unlock(i->f, &i->f->mem_lock, "regfi_iterator_ancestry");
+
+  ret_val[k] = NULL;
+  return (const REGFI_NK**)ret_val;
 }
 
 
 /******************************************************************************
  *****************************************************************************/
-REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i, 
-						const REGFI_NK_REC* key)
+const REGFI_CLASSNAME* regfi_fetch_classname(REGFI_FILE* file,
+					     const REGFI_NK* key)
 {
   REGFI_CLASSNAME* ret_val;
   uint8_t* raw;
-  char* interpreted;
+  REGFI_BUFFER tmp_buf;
   uint32_t offset;
-  int32_t conv_size, max_size;
+  int32_t max_size;
   uint16_t parse_length;
 
   if(key->classname_off == REGFI_OFFSET_NONE || key->classname_length == 0)
     return NULL;
 
   offset = key->classname_off + REGFI_REGF_SIZE;
-  max_size = regfi_calc_maxsize(i->f, offset);
+  max_size = regfi_calc_maxsize(file, offset);
   if(max_size <= 0)
     return NULL;
 
   parse_length = key->classname_length;
-  raw = regfi_parse_classname(i->f, offset, &parse_length, max_size, true);
+  raw = regfi_parse_classname(file, offset, &parse_length, max_size, true);
   
   if(raw == NULL)
   {
-    regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse class"
-		      " name at offset 0x%.8X for key record at offset 0x%.8X.",
-		      offset, key->offset);
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse class"
+		  " name at offset 0x%.8X for key record at offset 0x%.8X.",
+		  offset, key->offset);
     return NULL;
   }
 
@@ -1837,29 +2266,25 @@ REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i,
   if(ret_val == NULL)
     return NULL;
 
+  ret_val->offset = offset;
   ret_val->raw = raw;
   ret_val->size = parse_length;
-  talloc_steal(ret_val, raw);
-
-  interpreted = talloc_array(NULL, char, parse_length);
+  talloc_reparent(NULL, ret_val, raw);
 
-  conv_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
-				 regfi_encoding_int2str(i->string_encoding),
-				 raw, interpreted,
-				 parse_length, parse_length);
-  if(conv_size < 0)
+  tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+                               regfi_encoding_int2str(file->string_encoding),
+                               raw, parse_length);
+  if(tmp_buf.buf == NULL)
   {
-    regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred while"
-		      " converting classname to charset %s.  Error message: %s",
-		      i->string_encoding, strerror(-conv_size));
-    talloc_free(interpreted);
+    regfi_log_add(REGFI_LOG_WARN, "Error occurred while"
+		  " converting classname to charset %s.  Error message: %s",
+		  file->string_encoding, strerror(errno));
     ret_val->interpreted = NULL;
   }
   else
   {
-    interpreted = talloc_realloc(NULL, interpreted, char, conv_size);
-    ret_val->interpreted = interpreted;
-    talloc_steal(ret_val, interpreted);
+    ret_val->interpreted = (char*)tmp_buf.buf;
+    talloc_reparent(NULL, ret_val, tmp_buf.buf);
   }
 
   return ret_val;
@@ -1868,21 +2293,21 @@ REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i,
 
 /******************************************************************************
  *****************************************************************************/
-REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i, 
-				      const REGFI_VK_REC* value)
+const REGFI_DATA* regfi_fetch_data(REGFI_FILE* file, 
+				   const REGFI_VK* value)
 {
   REGFI_DATA* ret_val = NULL;
   REGFI_BUFFER raw_data;
 
   if(value->data_size != 0)
   {
-    raw_data = regfi_load_data(i->f, value->data_off, value->data_size,
-			      value->data_in_offset, true);
+    raw_data = regfi_load_data(file, value->data_off, value->data_size,
+			       value->data_in_offset, true);
     if(raw_data.buf == NULL)
     {
-      regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse data record"
-			" while parsing VK record at offset 0x%.8X.",
-			value->offset);
+      regfi_log_add(REGFI_LOG_WARN, "Could not parse data record"
+		    " while parsing VK record at offset 0x%.8X.",
+		    value->offset);
     }
     else
     {
@@ -1890,19 +2315,19 @@ REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i,
 
       if(ret_val == NULL)
       {
-	regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred in converting"
-			  " data buffer to data structure while interpreting "
-			  "data for VK record at offset 0x%.8X.",
-			  value->offset);
+	regfi_log_add(REGFI_LOG_WARN, "Error occurred in converting"
+		      " data buffer to data structure while interpreting "
+		      "data for VK record at offset 0x%.8X.",
+		      value->offset);
 	talloc_free(raw_data.buf);
 	return NULL;
       }
 
-      if(!regfi_interpret_data(i->f, i->string_encoding, value->type, ret_val))
+      if(!regfi_interpret_data(file, value->type, ret_val))
       {
-	regfi_add_message(i->f, REGFI_MSG_INFO, "Error occurred while"
-			  " interpreting data for VK record at offset 0x%.8X.",
-			  value->offset);
+	regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
+		      " interpreting data for VK record at offset 0x%.8X.",
+		      value->offset);
       }
     }
   }
@@ -1911,21 +2336,131 @@ REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i,
 }
 
 
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_find_subkey(REGFI_FILE* file, const REGFI_NK* key, 
+		       const char* name, uint32_t* index)
+{
+  const REGFI_NK* cur;
+  uint32_t i;
+  uint32_t num_subkeys = regfi_fetch_num_subkeys(key);
+  bool found = false;
+
+  /* XXX: should we allow "(default)" subkey names? 
+   *      Do realistically they exist?
+   */
+  if(name == NULL)
+    return false;
+
+  /* XXX: Should lazily build a hash table in memory to index where keys are when
+   *      there are a large number of subkeys.  Attach this to cached keys to
+   *      bound the extra amount of memory used. 
+   */
+  for(i=0; (i < num_subkeys) && (found == false); i++)
+  {
+    cur = regfi_get_subkey(file, key, i);
+    if(cur == NULL)
+      return false;
+
+    /* A NULL name signifies the "(default)" value for a key */
+    if(cur->name != NULL && (strcasecmp(cur->name, name) == 0))
+    {
+      found = true;
+      *index = i;
+    }
+
+    regfi_free_record(file, cur);
+  }
+
+  return found;
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool regfi_find_value(REGFI_FILE* file, const REGFI_NK* key, 
+		      const char* name, uint32_t* index)
+{
+  const REGFI_VK* cur;
+  uint32_t i;
+  uint32_t num_values = regfi_fetch_num_values(key);
+  bool found = false;
+
+  /* XXX: Should lazily build a hash table in memory to index where values are when
+   *      there are a large number of them.  Attach this to cached keys to
+   *      bound the extra amount of memory used. 
+   */
+  for(i=0; (i < num_values) && (found == false); i++)
+  {
+    cur = regfi_get_value(file, key, i);
+    if(cur == NULL)
+      return false;
+
+    /* A NULL name signifies the "(default)" value for a key */
+    if(((name == NULL) && (cur->name == NULL))
+       || ((name != NULL) && (cur->name != NULL) 
+           && (strcasecmp(cur->name, name) == 0)))
+    {
+      found = true;
+      *index = i;
+    }
+
+    regfi_free_record(file, cur);
+  }
+
+  return found;
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+const REGFI_NK* regfi_get_subkey(REGFI_FILE* file, const REGFI_NK* key, 
+				 uint32_t index)
+{
+  if(index < regfi_fetch_num_subkeys(key))
+  {
+    return regfi_load_key(file, 
+                          key->subkeys->elements[index].offset+REGFI_REGF_SIZE, 
+                          true);
+  }
+
+  return NULL;
+}
+
+
 /******************************************************************************
  *****************************************************************************/
-void regfi_free_classname(REGFI_CLASSNAME* classname)
+const REGFI_VK* regfi_get_value(REGFI_FILE* file, const REGFI_NK* key, 
+				uint32_t index)
 {
-  talloc_free(classname);
+  if(index < regfi_fetch_num_values(key))
+  {
+    return regfi_load_value(file, 
+			    key->values->elements[index]+REGFI_REGF_SIZE,
+                            true);
+  }
+
+  return NULL;  
 }
 
+
+
 /******************************************************************************
  *****************************************************************************/
-void regfi_free_data(REGFI_DATA* data)
+const REGFI_NK* regfi_get_parentkey(REGFI_FILE* file, const REGFI_NK* key)
 {
-  talloc_free(data);
+  if(key != NULL && key->parent_off != REGFI_OFFSET_NONE)
+    return regfi_load_key(file, 
+                          key->parent_off+REGFI_REGF_SIZE, true);
+
+  return NULL;
 }
 
 
+
 /******************************************************************************
  *****************************************************************************/
 REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
@@ -1939,7 +2474,7 @@ REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
   if(ret_val == NULL)
     return NULL;
   
-  talloc_steal(ret_val, raw_data.buf);
+  talloc_reparent(NULL, ret_val, raw_data.buf);
   ret_val->raw = raw_data.buf;
   ret_val->size = raw_data.len;
   ret_val->interpreted_size = 0;
@@ -1951,13 +2486,11 @@ REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data)
 
 /******************************************************************************
  *****************************************************************************/
-bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
-			  uint32_t type, REGFI_DATA* data)
+bool regfi_interpret_data(REGFI_FILE* file, uint32_t type, REGFI_DATA* data)
 {
+  REGFI_BUFFER tmp_buf;
   uint8_t** tmp_array;
-  uint8_t* tmp_str;
-  int32_t tmp_size;
-  uint32_t i, j, array_size;
+  uint32_t i, j;
 
   if(data == NULL)
     return false;
@@ -1968,33 +2501,23 @@ bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
   case REG_EXPAND_SZ:
   /* REG_LINK is a symbolic link, stored as a unicode string. */
   case REG_LINK:
-    tmp_str = talloc_array(NULL, uint8_t, data->size);
-    if(tmp_str == NULL)
+    tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+                                 regfi_encoding_int2str(file->string_encoding),
+                                 data->raw, data->size);
+    if(tmp_buf.buf == NULL)
     {
-      data->interpreted.string = NULL;
-      data->interpreted_size = 0;
-      return false;
-    }
-      
-    tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
-				  regfi_encoding_int2str(string_encoding),
-				  data->raw, (char*)tmp_str, 
-				  data->size, data->size);
-    if(tmp_size < 0)
-    {
-      regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while"
-			" converting data of type %d to %s.  Error message: %s",
-			type, string_encoding, strerror(-tmp_size));
-      talloc_free(tmp_str);
+      regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
+		    " converting data of type %d to string encoding %d."
+                    "  Error message: %s",
+		    type, file->string_encoding, strerror(errno));
       data->interpreted.string = NULL;
       data->interpreted_size = 0;
       return false;
     }
 
-    tmp_str = talloc_realloc(NULL, tmp_str, uint8_t, tmp_size);
-    data->interpreted.string = tmp_str;
-    data->interpreted_size = tmp_size;
-    talloc_steal(data, tmp_str);
+    data->interpreted.string = tmp_buf.buf;
+    data->interpreted_size = tmp_buf.len;
+    talloc_reparent(NULL, data, tmp_buf.buf);
     break;
 
   case REG_DWORD:
@@ -2032,55 +2555,46 @@ bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
     break;
     
   case REG_MULTI_SZ:
-    tmp_str = talloc_array(NULL, uint8_t, data->size);
-    if(tmp_str == NULL)
-    {
-      data->interpreted.multiple_string = NULL;
-      data->interpreted_size = 0;
-      return false;
-    }
-
     /* Attempt to convert entire string from UTF-16LE to output encoding,
      * then parse and quote fields individually.
      */
-    tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
-				  regfi_encoding_int2str(string_encoding),
-				  data->raw, (char*)tmp_str,
-				  data->size, data->size);
-    if(tmp_size < 0)
+    tmp_buf = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE),
+                                 regfi_encoding_int2str(file->string_encoding),
+                                 data->raw, data->size);
+    if(tmp_buf.buf == NULL)
     {
-      regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while"
-			" converting data of type %d to %s.  Error message: %s",
-			type, string_encoding, strerror(-tmp_size));
-      talloc_free(tmp_str);
+      regfi_log_add(REGFI_LOG_INFO, "Error occurred while"
+		    " converting data of type %d to string encoding %d."
+                    "  Error message: %s",
+		    type, file->string_encoding, strerror(errno));
       data->interpreted.multiple_string = NULL;
       data->interpreted_size = 0;
       return false;
     }
 
-    array_size = tmp_size+1;
-    tmp_array = talloc_array(NULL, uint8_t*, array_size);
+    tmp_array = talloc_array(NULL, uint8_t*, tmp_buf.len+1);
     if(tmp_array == NULL)
     {
-      talloc_free(tmp_str);
+      talloc_free(tmp_buf.buf);
       data->interpreted.string = NULL;
       data->interpreted_size = 0;
       return false;
     }
-    
-    tmp_array[0] = tmp_str;
-    for(i=0,j=1; i < tmp_size && j < array_size-1; i++)
+
+    tmp_array[0] = tmp_buf.buf;
+    for(i=0,j=1; i < tmp_buf.len && j < tmp_buf.len; i++)
     {
-      if(tmp_str[i] == '\0' && (i+1 < tmp_size))
-	tmp_array[j++] = tmp_str+i+1;
+      if(tmp_buf.buf[i] == '\0' && (i+1 < tmp_buf.len) 
+         && tmp_buf.buf[i+1] != '\0')
+	tmp_array[j++] = tmp_buf.buf+i+1;
     }
     tmp_array[j] = NULL;
     tmp_array = talloc_realloc(NULL, tmp_array, uint8_t*, j+1);
     data->interpreted.multiple_string = tmp_array;
     /* XXX: how meaningful is this?  should we store number of strings instead? */
-    data->interpreted_size = tmp_size;
-    talloc_steal(tmp_array, tmp_str);
-    talloc_steal(data, tmp_array);
+    data->interpreted_size = tmp_buf.len;
+    talloc_reparent(NULL, tmp_array, tmp_buf.buf);
+    talloc_reparent(NULL, data, tmp_array);
     break;
 
   /* XXX: Dont know how to interpret these yet, just treat as binary */
@@ -2121,40 +2635,82 @@ bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding,
 
 
 /******************************************************************************
- * Convert from UTF-16LE to specified character set. 
- * On error, returns a negative errno code.
+ * Convert string from input_charset to output_charset.
+ * On error, returns a NULL buf attribute and sets the errno.
  *****************************************************************************/
-int32_t regfi_conv_charset(const char* input_charset, const char* output_charset,
-			 uint8_t* input, char* output, 
-			 uint32_t input_len, uint32_t output_max)
+REGFI_BUFFER regfi_conv_charset(const char* input_charset, const char* output_charset,
+                                uint8_t* input, uint32_t input_len)
 {
   iconv_t conv_desc;
   char* inbuf = (char*)input;
-  char* outbuf = output;
-  size_t in_len = (size_t)input_len;
-  size_t out_len = (size_t)(output_max-1);
+  char* outbuf;
+  char* retbuf;
+  size_t allocated = (size_t)input_len;
+  size_t in_left = (size_t)input_len;
+  size_t out_left = (size_t)allocated-1;
+  REGFI_BUFFER ret_val;
   int ret;
 
+  ret_val.buf = NULL;
+  ret_val.len = 0;
+  retbuf = talloc_array(NULL, char, allocated);
+  outbuf = retbuf;
+  if(outbuf == NULL)
+  {
+    errno = ENOMEM;
+    return ret_val;
+  }
+
+  /* Set up conversion descriptor. */
   /* XXX: Consider creating a couple of conversion descriptors earlier,
    *      storing them on an iterator so they don't have to be recreated
    *      each time.
    */
-
-  /* Set up conversion descriptor. */
   conv_desc = iconv_open(output_charset, input_charset);
 
-  ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
+  ret = 0;
+  do
+  {
+    if(ret == -1)
+    {
+      retbuf = talloc_realloc(NULL, retbuf, char, allocated+(in_left*2));
+      if(retbuf == NULL)
+      {
+        errno = ENOMEM;
+        return ret_val;
+      }
+      outbuf = retbuf+(allocated-1-out_left);
+      out_left += in_left*2;
+      allocated += in_left*2;
+    }
+    ret = iconv(conv_desc, &inbuf, &in_left, &outbuf, &out_left);
+    
+  } while(ret == -1 && errno == E2BIG);
+  
   if(ret == -1)
   {
     iconv_close(conv_desc);
-    return -errno;
+    return ret_val;
   }
-  *outbuf = '\0';
 
-  iconv_close(conv_desc);  
-  return output_max-out_len-1;
-}
+  /* Save memory */
+  if(out_left > 0)
+  {
+    retbuf = talloc_realloc(NULL, retbuf, char, allocated-out_left);
+    if(retbuf == NULL)
+    {
+      errno = ENOMEM;
+      return ret_val;
+    }
+    allocated -= out_left;
+  }
+  retbuf[allocated-1] = '\0';
+  iconv_close(conv_desc);
 
+  ret_val.buf = (uint8_t*)retbuf;
+  ret_val.len = allocated-1;
+  return ret_val;
+}
 
 
 /*******************************************************************
@@ -2180,9 +2736,8 @@ static uint32_t regfi_compute_header_checksum(uint8_t* buffer)
 
 
 /*******************************************************************
- * XXX: Add way to return more detailed error information.
  *******************************************************************/
-REGFI_FILE* regfi_parse_regf(int fd, bool strict)
+REGFI_FILE* regfi_parse_regf(REGFI_RAW_FILE* file_cb, bool strict)
 {
   uint8_t file_header[REGFI_REGF_SIZE];
   uint32_t length;
@@ -2192,50 +2747,66 @@ REGFI_FILE* regfi_parse_regf(int fd, bool strict)
   if(ret_val == NULL)
     return NULL;
 
-  ret_val->fd = fd;
   ret_val->sk_cache = NULL;
-  ret_val->last_message = NULL;
   ret_val->hbins = NULL;
-  
+
   length = REGFI_REGF_SIZE;
-  if((regfi_read(fd, file_header, &length)) != 0 || length != REGFI_REGF_SIZE)
+  if((regfi_read(file_cb, file_header, &length)) != 0 
+     || length != REGFI_REGF_SIZE)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Read failed while parsing REGF structure.");
     goto fail;
-  
+  }
+
   ret_val->checksum = IVAL(file_header, 0x1FC);
   ret_val->computed_checksum = regfi_compute_header_checksum(file_header);
   if (strict && (ret_val->checksum != ret_val->computed_checksum))
-    goto fail;
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Stored header checksum (%.8X) did not equal"
+		  " computed checksum (%.8X).",
+		  ret_val->checksum, ret_val->computed_checksum);
+    if(strict)
+      goto fail;
+  }
 
   memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE);
   if(memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0)
   {
-    if(strict)
-      goto fail;
-    regfi_add_message(ret_val, REGFI_MSG_WARN, "Magic number mismatch "
-		      "(%.2X %.2X %.2X %.2X) while parsing hive header",
-		      ret_val->magic[0], ret_val->magic[1], 
-		      ret_val->magic[2], ret_val->magic[3]);
+    regfi_log_add(REGFI_LOG_ERROR, "Magic number mismatch "
+		  "(%.2X %.2X %.2X %.2X) while parsing hive header",
+		  ret_val->magic[0], ret_val->magic[1], 
+		  ret_val->magic[2], ret_val->magic[3]);
+    goto fail;
   }
+
   ret_val->sequence1 = IVAL(file_header, 0x4);
   ret_val->sequence2 = IVAL(file_header, 0x8);
-  ret_val->mtime.low = IVAL(file_header, 0xC);
-  ret_val->mtime.high = IVAL(file_header, 0x10);
+  ret_val->mtime = ((uint64_t)IVAL(file_header, 0x10)) << 32;
+  ret_val->mtime |= IVAL(file_header, 0xC);
   ret_val->major_version = IVAL(file_header, 0x14);
   ret_val->minor_version = IVAL(file_header, 0x18);
   ret_val->type = IVAL(file_header, 0x1C);
   ret_val->format = IVAL(file_header, 0x20);
   ret_val->root_cell = IVAL(file_header, 0x24);
   ret_val->last_block = IVAL(file_header, 0x28);
-
   ret_val->cluster = IVAL(file_header, 0x2C);
 
   memcpy(ret_val->file_name, file_header+0x30,  REGFI_REGF_NAME_SIZE);
 
-  /* XXX: Should we add a warning if these uuid parsers fail?  Can they? */
   ret_val->rm_id = winsec_parse_uuid(ret_val, file_header+0x70, 16);
+  if(ret_val->rm_id == NULL)
+    regfi_log_add(REGFI_LOG_WARN, "Hive header's rm_id failed to parse.");
+
   ret_val->log_id = winsec_parse_uuid(ret_val, file_header+0x80, 16);
+  if(ret_val->log_id == NULL)
+    regfi_log_add(REGFI_LOG_WARN, "Hive header's log_id failed to parse.");
+
   ret_val->flags = IVAL(file_header, 0x90);
+
   ret_val->tm_id = winsec_parse_uuid(ret_val, file_header+0x94, 16);
+  if(ret_val->tm_id == NULL)
+    regfi_log_add(REGFI_LOG_WARN, "Hive header's tm_id failed to parse.");
+
   ret_val->guid_signature = IVAL(file_header, 0xa4);
 
   memcpy(ret_val->reserved1, file_header+0xa8, REGFI_REGF_RESERVED1_SIZE);
@@ -2262,51 +2833,56 @@ REGFI_FILE* regfi_parse_regf(int fd, bool strict)
  ******************************************************************************/
 REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict)
 {
-  REGFI_HBIN *hbin;
+  REGFI_HBIN* hbin = NULL;
   uint8_t hbin_header[REGFI_HBIN_HEADER_SIZE];
   uint32_t length;
   
   if(offset >= file->file_length)
-    return NULL;
+    goto fail;
+  
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_hbin"))
+    goto fail;
 
-  if(lseek(file->fd, offset, SEEK_SET) == -1)
+  if(regfi_seek(file->cb, offset, SEEK_SET) == -1)
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
-		      " while parsing hbin at offset 0x%.8X.", offset);
-    return NULL;
+    regfi_log_add(REGFI_LOG_ERROR, "Seek failed"
+		  " while parsing hbin at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
   length = REGFI_HBIN_HEADER_SIZE;
-  if((regfi_read(file->fd, hbin_header, &length) != 0) 
+  if((regfi_read(file->cb, hbin_header, &length) != 0) 
      || length != REGFI_HBIN_HEADER_SIZE)
-    return NULL;
-
-  if(lseek(file->fd, offset, SEEK_SET) == -1)
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed"
-		      " while parsing hbin at offset 0x%.8X.", offset);
-    return NULL;
+    regfi_log_add(REGFI_LOG_ERROR, "Read failed"
+		  " while parsing hbin at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_hbin"))
+    goto fail;
+
   hbin = talloc(NULL, REGFI_HBIN);
   if(hbin == NULL)
-    return NULL;
+    goto fail;
   hbin->file_off = offset;
 
   memcpy(hbin->magic, hbin_header, 4);
   if(strict && (memcmp(hbin->magic, "hbin", 4) != 0))
   {
-    regfi_add_message(file, REGFI_MSG_INFO, "Magic number mismatch "
-		      "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
-		      " 0x%.8X.", hbin->magic[0], hbin->magic[1], 
-		      hbin->magic[2], hbin->magic[3], offset);
-    talloc_free(hbin);
-    return NULL;
+    /* This always seems to happen at the end of a file, so we make it an INFO
+     * message, rather than something more serious.
+     */
+    regfi_log_add(REGFI_LOG_INFO, "Magic number mismatch "
+		  "(%.2X %.2X %.2X %.2X) while parsing hbin at offset"
+		  " 0x%.8X.", hbin->magic[0], hbin->magic[1], 
+		  hbin->magic[2], hbin->magic[3], offset);
+    goto fail;
   }
 
   hbin->first_hbin_off = IVAL(hbin_header, 0x4);
   hbin->block_size = IVAL(hbin_header, 0x8);
-  /* this should be the same thing as hbin->block_size but just in case */
+  /* this should be the same thing as hbin->block_size, but just in case */
   hbin->next_block = IVAL(hbin_header, 0x1C);
 
 
@@ -2319,49 +2895,56 @@ REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict)
   if((offset + hbin->block_size > file->file_length)
      || (hbin->block_size & 0xFFFFF000) != hbin->block_size)
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "The hbin offset is not aligned"
-		      " or runs off the end of the file"
-		      " while parsing hbin at offset 0x%.8X.", offset);
-    talloc_free(hbin);
-    return NULL;
+    regfi_log_add(REGFI_LOG_ERROR, "The hbin offset is not aligned"
+		  " or runs off the end of the file"
+		  " while parsing hbin at offset 0x%.8X.", offset);
+    goto fail;
   }
 
   return hbin;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_hbin");
+ fail:
+  talloc_free(hbin);
+  return NULL;
 }
 
 
 /*******************************************************************
  *******************************************************************/
-REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset, 
-			     uint32_t max_size, bool strict)
+REGFI_NK* regfi_parse_nk(REGFI_FILE* file, uint32_t offset, 
+			 uint32_t max_size, bool strict)
 {
   uint8_t nk_header[REGFI_NK_MIN_LENGTH];
-  REGFI_NK_REC* ret_val;
+  REGFI_NK* ret_val;
   uint32_t length,cell_length;
   bool unalloc = false;
 
-  if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH,
-		       &cell_length, &unalloc))
+  ret_val = talloc(NULL, REGFI_NK);
+  if(ret_val == NULL)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
-		      " while parsing NK record at offset 0x%.8X.", offset);
-    return NULL;
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to allocate memory while"
+		  " parsing NK record at offset 0x%.8X.", offset);
+    goto fail;
   }
 
-  /* A bit of validation before bothering to allocate memory */
-  if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_nk"))
+    goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, nk_header, REGFI_NK_MIN_LENGTH,
+		       &cell_length, &unalloc))
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing"
-		      " NK record at offset 0x%.8X.", offset);
-    return NULL;
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
+		  " while parsing NK record at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
-  ret_val = talloc(NULL, REGFI_NK_REC);
-  if(ret_val == NULL)
+  if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k'))
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to allocate memory while"
-		      " parsing NK record at offset 0x%.8X.", offset);
-    return NULL;
+    regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch in parsing"
+		  " NK record at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
   ret_val->values = NULL;
@@ -2374,10 +2957,9 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
   if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) 
      || (strict && (ret_val->cell_size & 0x00000007) != 0))
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "A length check failed while"
-		      " parsing NK record at offset 0x%.8X.", offset);
-    talloc_free(ret_val);
-    return NULL;
+    regfi_log_add(REGFI_LOG_WARN, "A length check failed while"
+		  " parsing NK record at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
   ret_val->magic[0] = nk_header[0x0];
@@ -2386,23 +2968,21 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
   
   if((ret_val->flags & ~REGFI_NK_KNOWN_FLAGS) != 0)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Unknown key flags (0x%.4X) while"
-		      " parsing NK record at offset 0x%.8X.", 
-		      (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset);
+    regfi_log_add(REGFI_LOG_WARN, "Unknown key flags (0x%.4X) while"
+		  " parsing NK record at offset 0x%.8X.", 
+		  (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset);
   }
 
-  ret_val->mtime.low = IVAL(nk_header, 0x4);
-  ret_val->mtime.high = IVAL(nk_header, 0x8);
+  ret_val->mtime = ((uint64_t)IVAL(nk_header, 0x8)) << 32;
+  ret_val->mtime |= IVAL(nk_header, 0x4);
   /* If the key is unallocated and the MTIME is earlier than Jan 1, 1990
    * or later than Jan 1, 2290, we consider this a bad key.  This helps
    * weed out some false positives during deleted data recovery.
    */
   if(unalloc
-     && ((ret_val->mtime.high < REGFI_MTIME_MIN_HIGH 
-	  && ret_val->mtime.low < REGFI_MTIME_MIN_LOW)
-	 || (ret_val->mtime.high > REGFI_MTIME_MAX_HIGH 
-	     && ret_val->mtime.low > REGFI_MTIME_MAX_LOW)))
-    return NULL;
+     && (ret_val->mtime < REGFI_MTIME_MIN
+	 || ret_val->mtime > REGFI_MTIME_MAX))
+  { goto fail_locked; }
 
   ret_val->unknown1 = IVAL(nk_header, 0xC);
   ret_val->parent_off = IVAL(nk_header, 0x10);
@@ -2423,16 +3003,15 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
 
   ret_val->name_length = SVAL(nk_header, 0x48);
   ret_val->classname_length = SVAL(nk_header, 0x4A);
-  ret_val->keyname = NULL;
+  ret_val->name = NULL;
 
   if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size)
   {
     if(strict)
     {
-      regfi_add_message(file, REGFI_MSG_ERROR, "Contents too large for cell"
-			" while parsing NK record at offset 0x%.8X.", offset);
-      talloc_free(ret_val);
-      return NULL;
+      regfi_log_add(REGFI_LOG_ERROR, "Contents too large for cell"
+		    " while parsing NK record at offset 0x%.8X.", offset);
+      goto fail_locked;
     }
     else
       ret_val->name_length = ret_val->cell_size - REGFI_NK_MIN_LENGTH;
@@ -2449,128 +3028,147 @@ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset,
       ret_val->cell_size = length;
   }
 
-  ret_val->keyname_raw = talloc_array(ret_val, uint8_t, ret_val->name_length);
-  if(ret_val->keyname_raw == NULL)
-  {
-    talloc_free(ret_val);
-    return NULL;
-  }
+  /* +1 to length in case we decided to use this directly as a string later */
+  ret_val->name_raw = talloc_array(ret_val, uint8_t, ret_val->name_length+1);
+  if(ret_val->name_raw == NULL)
+    goto fail_locked;
 
   /* Don't need to seek, should be at the right offset */
   length = ret_val->name_length;
-  if((regfi_read(file->fd, (uint8_t*)ret_val->keyname_raw, &length) != 0)
+  if((regfi_read(file->cb, (uint8_t*)ret_val->name_raw, &length) != 0)
      || length != ret_val->name_length)
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read key name"
-		      " while parsing NK record at offset 0x%.8X.", offset);
-    talloc_free(ret_val);
-    return NULL;
+    regfi_log_add(REGFI_LOG_ERROR, "Failed to read key name"
+		  " while parsing NK record at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_nk"))
+    goto fail;
+
   return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_nk");
+ fail:
+  talloc_free(ret_val);
+  return NULL;
 }
 
 
 uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset, 
-			     uint16_t* name_length, uint32_t max_size, bool strict)
+			       uint16_t* name_length, uint32_t max_size, bool strict)
 {
   uint8_t* ret_val = NULL;
   uint32_t length;
   uint32_t cell_length;
   bool unalloc = false;
 
-  if(*name_length > 0 && offset != REGFI_OFFSET_NONE 
-     && (offset & 0x00000007) == 0)
-  {
-    if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc))
-    {
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
-			" while parsing class name at offset 0x%.8X.", offset);
-	return NULL;
-    }
-
-    if((cell_length & 0x0000007) != 0)
-    {
-      regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8"
-			" while parsing class name at offset 0x%.8X.", offset);
-      return NULL;
-    }
+  if(*name_length <= 0 || offset == REGFI_OFFSET_NONE  
+     || (offset & 0x00000007) != 0)
+  { goto fail; }
 
-    if(cell_length > max_size)
-    {
-      regfi_add_message(file, REGFI_MSG_WARN, "Cell stretches past hbin "
-			"boundary while parsing class name at offset 0x%.8X.",
-			offset);
-      if(strict)
-	return NULL;
-      cell_length = max_size;
-    }
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_classname"))
+    goto fail;
 
-    if((cell_length - 4) < *name_length)
-    {
-      regfi_add_message(file, REGFI_MSG_WARN, "Class name is larger than"
-			" cell_length while parsing class name at offset"
-			" 0x%.8X.", offset);
-      if(strict)
-	return NULL;
-      *name_length = cell_length - 4;
-    }
-    
-    ret_val = talloc_array(NULL, uint8_t, *name_length);
-    if(ret_val != NULL)
+  if(!regfi_parse_cell(file->cb, offset, NULL, 0, &cell_length, &unalloc))
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
+		  " while parsing class name at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+  
+  if((cell_length & 0x0000007) != 0)
+  {
+    regfi_log_add(REGFI_LOG_ERROR, "Cell length not a multiple of 8"
+		  " while parsing class name at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
+  
+  if(cell_length > max_size)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Cell stretches past hbin "
+		  "boundary while parsing class name at offset 0x%.8X.",
+		  offset);
+    if(strict)
+      goto fail_locked;
+    cell_length = max_size;
+  }
+  
+  if((cell_length - 4) < *name_length)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Class name is larger than"
+		  " cell_length while parsing class name at offset"
+		  " 0x%.8X.", offset);
+    if(strict)
+      goto fail_locked;
+    *name_length = cell_length - 4;
+  }
+  
+  ret_val = talloc_array(NULL, uint8_t, *name_length);
+  if(ret_val != NULL)
+  {
+    length = *name_length;
+    if((regfi_read(file->cb, ret_val, &length) != 0)
+       || length != *name_length)
     {
-      length = *name_length;
-      if((regfi_read(file->fd, ret_val, &length) != 0)
-	 || length != *name_length)
-      {
-	regfi_add_message(file, REGFI_MSG_ERROR, "Could not read class name"
-			  " while parsing class name at offset 0x%.8X.", offset);
-	talloc_free(ret_val);
-	return NULL;
-      }
+      regfi_log_add(REGFI_LOG_ERROR, "Could not read class name"
+		    " while parsing class name at offset 0x%.8X.", offset);
+      goto fail_locked;
     }
   }
 
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_classname"))
+    goto fail;
+
   return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_classname");
+ fail:
+  talloc_free(ret_val);
+  return NULL;
 }
 
 
 /******************************************************************************
 *******************************************************************************/
-REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
+REGFI_VK* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, 
 			     uint32_t max_size, bool strict)
 {
-  REGFI_VK_REC* ret_val;
+  REGFI_VK* ret_val;
   uint8_t vk_header[REGFI_VK_MIN_LENGTH];
   uint32_t raw_data_size, length, cell_length;
   bool unalloc = false;
 
-  if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH,
+  ret_val = talloc(NULL, REGFI_VK);
+  if(ret_val == NULL)
+    goto fail;
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_nk"))
+    goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, vk_header, REGFI_VK_MIN_LENGTH,
 		       &cell_length, &unalloc))
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header"
-		      " while parsing VK record at offset 0x%.8X.", offset);
-    return NULL;
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell header"
+		  " while parsing VK record at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
-  ret_val = talloc(NULL, REGFI_VK_REC);
-  if(ret_val == NULL)
-    return NULL;
-
   ret_val->offset = offset;
   ret_val->cell_size = cell_length;
-  ret_val->valuename = NULL;
-  ret_val->valuename_raw = NULL;
+  ret_val->name = NULL;
+  ret_val->name_raw = NULL;
   
   if(ret_val->cell_size > max_size)
     ret_val->cell_size = max_size & 0xFFFFFFF8;
   if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) 
      || (ret_val->cell_size & 0x00000007) != 0)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered"
-		      " while parsing VK record at offset 0x%.8X.", offset);
-    talloc_free(ret_val);
-    return NULL;
+    regfi_log_add(REGFI_LOG_WARN, "Invalid cell size encountered"
+		  " while parsing VK record at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
   ret_val->magic[0] = vk_header[0x0];
@@ -2581,10 +3179,9 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
      *      often have this (and the name length) overwritten with
      *      0xFFFF. 
      */
-    regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch"
-		      " while parsing VK record at offset 0x%.8X.", offset);
-    talloc_free(ret_val);
-    return NULL;
+    regfi_log_add(REGFI_LOG_WARN, "Magic number mismatch"
+		  " while parsing VK record at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
   ret_val->name_length = SVAL(vk_header, 0x2);
@@ -2603,14 +3200,11 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
   {
     if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size)
     {
-      regfi_add_message(file, REGFI_MSG_WARN, "Name too long for remaining cell"
-			" space while parsing VK record at offset 0x%.8X.",
-			offset);
+      regfi_log_add(REGFI_LOG_WARN, "Name too long for remaining cell"
+		    " space while parsing VK record at offset 0x%.8X.",
+		    offset);
       if(strict)
-      {
-	talloc_free(ret_val);
-	return NULL;
-      }
+	goto fail_locked;
       else
 	ret_val->name_length = ret_val->cell_size - REGFI_VK_MIN_LENGTH - 4;
     }
@@ -2620,26 +3214,26 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
     if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4)
       cell_length+=8;
 
-    ret_val->valuename_raw = talloc_array(ret_val, uint8_t, ret_val->name_length);
-    if(ret_val->valuename_raw == NULL)
-    {
-      talloc_free(ret_val);
-      return NULL;
-    }
+    /* +1 to length in case we decided to use this directly as a string later */
+    ret_val->name_raw = talloc_array(ret_val, uint8_t, ret_val->name_length+1);
+    if(ret_val->name_raw == NULL)
+      goto fail_locked;
 
     length = ret_val->name_length;
-    if((regfi_read(file->fd, (uint8_t*)ret_val->valuename_raw, &length) != 0)
+    if((regfi_read(file->cb, (uint8_t*)ret_val->name_raw, &length) != 0)
        || length != ret_val->name_length)
     {
-      regfi_add_message(file, REGFI_MSG_ERROR, "Could not read value name"
-			" while parsing VK record at offset 0x%.8X.", offset);
-      talloc_free(ret_val);
-      return NULL;
+      regfi_log_add(REGFI_LOG_ERROR, "Could not read value name"
+		    " while parsing VK record at offset 0x%.8X.", offset);
+      goto fail_locked;
     }
   }
   else
     cell_length = REGFI_VK_MIN_LENGTH + 4;
 
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_nk"))
+    goto fail;
+
   if(unalloc)
   {
     /* If cell_size is still greater, truncate. */
@@ -2648,6 +3242,12 @@ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset,
   }
 
   return ret_val;
+  
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_vk");
+ fail:
+  talloc_free(ret_val);
+  return NULL;
 }
 
 
@@ -2678,8 +3278,8 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
   /* XXX: add way to skip this check at user discression. */
   if(length > REGFI_VK_MAX_DATA_LENGTH)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Value data size %d larger than "
-		      "%d, truncating...", length, REGFI_VK_MAX_DATA_LENGTH);
+    regfi_log_add(REGFI_LOG_WARN, "Value data size %d larger than "
+		  "%d, truncating...", length, REGFI_VK_MAX_DATA_LENGTH);
     length = REGFI_VK_MAX_DATA_LENGTH;
   }
 
@@ -2691,32 +3291,38 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
     max_size = regfi_calc_maxsize(file, offset);
     if(max_size < 0)
     {
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data"
-			" at offset 0x%.8X.", offset);
+      regfi_log_add(REGFI_LOG_WARN, "Could not find HBIN for data"
+		    " at offset 0x%.8X.", offset);
       goto fail;
     }
     
-    if(!regfi_parse_cell(file->fd, offset, NULL, 0,
+    if(!regfi_lock(file, &file->cb_lock, "regfi_load_data"))
+      goto fail;
+
+    if(!regfi_parse_cell(file->cb, offset, NULL, 0,
 			 &cell_length, &unalloc))
     {
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
-			" parsing data record at offset 0x%.8X.", offset);
-      goto fail;
+      regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+		    " parsing data record at offset 0x%.8X.", offset);
+      goto fail_locked;
     }
 
+    if(!regfi_unlock(file, &file->cb_lock, "regfi_load_data"))
+      goto fail;
+
     if((cell_length & 0x00000007) != 0)
     {
-      regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8"
-			" while parsing data record at offset 0x%.8X.",
-			offset);
+      regfi_log_add(REGFI_LOG_WARN, "Cell length not multiple of 8"
+		    " while parsing data record at offset 0x%.8X.",
+		    offset);
       goto fail;
     }
 
     if(cell_length > max_size)
     {
-      regfi_add_message(file, REGFI_MSG_WARN, "Cell extends past HBIN boundary"
-			" while parsing data record at offset 0x%.8X.",
-			offset);
+      regfi_log_add(REGFI_LOG_WARN, "Cell extends past HBIN boundary"
+		    " while parsing data record at offset 0x%.8X.",
+		    offset);
       goto fail;
     }
 
@@ -2734,10 +3340,10 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
       }
       else
       {
-	regfi_add_message(file, REGFI_MSG_WARN, "Data length (0x%.8X) larger than"
-			  " remaining cell length (0x%.8X)"
-			  " while parsing data record at offset 0x%.8X.", 
-			  length, cell_length - 4, offset);
+	regfi_log_add(REGFI_LOG_WARN, "Data length (0x%.8X) larger than"
+		      " remaining cell length (0x%.8X)"
+		      " while parsing data record at offset 0x%.8X.", 
+		      length, cell_length - 4, offset);
 	if(strict)
 	  goto fail;
 	else
@@ -2750,6 +3356,8 @@ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset,
 
   return ret_val;
 
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_load_data");
  fail:
   ret_val.buf = NULL;
   ret_val.len = 0;
@@ -2769,28 +3377,40 @@ REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset,
   ret_val.buf = NULL;
   ret_val.len = 0;
   
-  if(lseek(file->fd, offset+4, SEEK_SET) == -1)
-  {
-    regfi_add_message(file, REGFI_MSG_WARN, "Could not seek while "
-		      "reading data at offset 0x%.8X.", offset);
-    return ret_val;
-  }
-
   if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL)
-    return ret_val;
+    goto fail;
   ret_val.len = length;
+
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_data"))
+    goto fail;
+
+  if(regfi_seek(file->cb, offset+4, SEEK_SET) == -1)
+  {
+    regfi_log_add(REGFI_LOG_WARN, "Could not seek while "
+		  "reading data at offset 0x%.8X.", offset);
+    goto fail_locked;
+  }
   
   read_length = length;
-  if((regfi_read(file->fd, ret_val.buf, &read_length) != 0)
+  if((regfi_read(file->cb, ret_val.buf, &read_length) != 0)
      || read_length != length)
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while"
-		      " parsing data record at offset 0x%.8X.", offset);
-    talloc_free(ret_val.buf);
-    ret_val.buf = NULL;
-    ret_val.buf = 0;
+    regfi_log_add(REGFI_LOG_ERROR, "Could not read data block while"
+		  " parsing data record at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_data"))
+    goto fail;
+
+  return ret_val;
+
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_data");
+ fail:
+  talloc_free(ret_val.buf);
+  ret_val.buf = NULL;
+  ret_val.buf = 0;
   return ret_val;
 }
 
@@ -2810,9 +3430,9 @@ REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset,
 
   if(length > 4)
   {
-    regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4"
-		      " while parsing data record. (voffset=0x%.8X, length=%d)",
-		      voffset, length);
+    regfi_log_add(REGFI_LOG_ERROR, "Data in offset but length > 4"
+		  " while parsing data record. (voffset=0x%.8X, length=%d)",
+		  voffset, length);
     return ret_val;
   }
 
@@ -2842,37 +3462,43 @@ REGFI_BUFFER regfi_parse_big_data_header(REGFI_FILE* file, uint32_t offset,
 
   if(REGFI_BIG_DATA_MIN_LENGTH > max_size)
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Big data header exceeded max_size "
-		      "while parsing big data header at offset 0x%.8X.",offset);
+    regfi_log_add(REGFI_LOG_WARN, "Big data header exceeded max_size "
+		  "while parsing big data header at offset 0x%.8X.",offset);
     goto fail;
   }
 
-  if(!regfi_parse_cell(file->fd, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_header"))
+    goto fail;
+
+
+  if(!regfi_parse_cell(file->cb, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH,
 		       &cell_length, &unalloc))
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
-		      " parsing big data header at offset 0x%.8X.", offset);
-    goto fail;
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+		  " parsing big data header at offset 0x%.8X.", offset);
+    goto fail_locked;
   }
 
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_header"))
+    goto fail;
+
   if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b'))
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number"
-		      " (0x%.2X, 0x%.2X) encountered while parsing"
-		      " big data header at offset 0x%.8X.", 
-		      ret_val.buf[0], ret_val.buf[1], offset);
+    regfi_log_add(REGFI_LOG_WARN, "Unknown magic number"
+		  " (0x%.2X, 0x%.2X) encountered while parsing"
+		  " big data header at offset 0x%.8X.", 
+		  ret_val.buf[0], ret_val.buf[1], offset);
     goto fail;
   }
 
   ret_val.len = REGFI_BIG_DATA_MIN_LENGTH;
   return ret_val;
 
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_header");
  fail:
-  if(ret_val.buf != NULL)
-  {
-    talloc_free(ret_val.buf);
-    ret_val.buf = NULL;
-  }
+  talloc_free(ret_val.buf);
+  ret_val.buf = NULL;
   ret_val.len = 0;
   return ret_val;
 }
@@ -2892,7 +3518,6 @@ uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
   bool unalloc;
 
   /* XXX: do something with unalloc? */
-
   max_size = regfi_calc_maxsize(file, offset);
   if((max_size < 0) || (num_chunks*sizeof(uint32_t) + 4 > max_size))
     return NULL;
@@ -2901,16 +3526,22 @@ uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
   if(ret_val == NULL)
     goto fail;
 
-  if(!regfi_parse_cell(file->fd, offset, (uint8_t*)ret_val,
+  if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_indirect"))
+    goto fail;
+
+  if(!regfi_parse_cell(file->cb, offset, (uint8_t*)ret_val,
 		       num_chunks*sizeof(uint32_t),
 		       &indirect_length, &unalloc))
   {
-    regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
-		      " parsing big data indirect record at offset 0x%.8X.", 
-		      offset);
-    goto fail;
+    regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+		  " parsing big data indirect record at offset 0x%.8X.", 
+		  offset);
+    goto fail_locked;
   }
 
+  if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_indirect"))
+    goto fail;
+
   /* Convert pointers to proper endianess, verify they are aligned. */
   for(i=0; i<num_chunks; i++)
   {
@@ -2921,9 +3552,10 @@ uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset,
   
   return ret_val;
 
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_indirect");
  fail:
-  if(ret_val != NULL)
-    talloc_free(ret_val);
+  talloc_free(ret_val);
   return NULL;
 }
 
@@ -2955,22 +3587,30 @@ range_list* regfi_parse_big_data_cells(REGFI_FILE* file, uint32_t* offsets,
   
   for(i=0; i<num_chunks; i++)
   {
+    if(!regfi_lock(file, &file->cb_lock, "regfi_parse_big_data_cells"))
+      goto fail;
+
     chunk_offset = offsets[i]+REGFI_REGF_SIZE;
-    if(!regfi_parse_cell(file->fd, chunk_offset, NULL, 0,
+    if(!regfi_parse_cell(file->cb, chunk_offset, NULL, 0,
 			 &cell_length, &unalloc))
     {
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while"
-			" parsing big data chunk at offset 0x%.8X.", 
-			chunk_offset);
-      goto fail;
+      regfi_log_add(REGFI_LOG_WARN, "Could not parse cell while"
+		    " parsing big data chunk at offset 0x%.8X.", 
+		    chunk_offset);
+      goto fail_locked;
     }
 
+    if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_cells"))
+      goto fail;
+
     if(!range_list_add(ret_val, chunk_offset, cell_length, NULL))
       goto fail;
   }
 
   return ret_val;
 
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_parse_big_data_cells");
  fail:
   if(ret_val != NULL)
     range_list_free(ret_val);
@@ -3046,9 +3686,9 @@ REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
     {
       if(i+1 != num_chunks)
       {
-	regfi_add_message(file, REGFI_MSG_WARN, "Left over chunks detected "
-			  "while constructing big data at offset 0x%.8X "
-			  "(chunk offset 0x%.8X).", offset, cell_info->offset);
+	regfi_log_add(REGFI_LOG_WARN, "Left over chunks detected "
+		      "while constructing big data at offset 0x%.8X "
+		      "(chunk offset 0x%.8X).", offset, cell_info->offset);
       }
       read_length = data_left;
     }
@@ -3058,30 +3698,36 @@ REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
 
     if(read_length > regfi_calc_maxsize(file, cell_info->offset))
     {
-      regfi_add_message(file, REGFI_MSG_WARN, "A chunk exceeded the maxsize "
-			"while constructing big data at offset 0x%.8X "
-			"(chunk offset 0x%.8X).", offset, cell_info->offset);
+      regfi_log_add(REGFI_LOG_WARN, "A chunk exceeded the maxsize "
+		    "while constructing big data at offset 0x%.8X "
+		    "(chunk offset 0x%.8X).", offset, cell_info->offset);
       goto fail;
     }
 
-    if(lseek(file->fd, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1)
-    {
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not seek to chunk while "
-			"constructing big data at offset 0x%.8X "
-			"(chunk offset 0x%.8X).", offset, cell_info->offset);
+    if(!regfi_lock(file, &file->cb_lock, "regfi_load_big_data"))
       goto fail;
+
+    if(regfi_seek(file->cb, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1)
+    {
+      regfi_log_add(REGFI_LOG_WARN, "Could not seek to chunk while "
+		    "constructing big data at offset 0x%.8X "
+		    "(chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail_locked;
     }
 
     tmp_len = read_length;
-    if(regfi_read(file->fd, ret_val.buf+(data_length-data_left), 
+    if(regfi_read(file->cb, ret_val.buf+(data_length-data_left), 
 		  &read_length) != 0 || (read_length != tmp_len))
     {
-      regfi_add_message(file, REGFI_MSG_WARN, "Could not read data chunk while"
-			" constructing big data at offset 0x%.8X"
-			" (chunk offset 0x%.8X).", offset, cell_info->offset);
-      goto fail;
+      regfi_log_add(REGFI_LOG_WARN, "Could not read data chunk while"
+		    " constructing big data at offset 0x%.8X"
+		    " (chunk offset 0x%.8X).", offset, cell_info->offset);
+      goto fail_locked;
     }
 
+    if(!regfi_unlock(file, &file->cb_lock, "regfi_load_big_data"))
+      goto fail;
+
     if(used_ranges != NULL)
       if(!range_list_add(used_ranges, cell_info->offset,cell_info->length,NULL))
 	goto fail;
@@ -3093,11 +3739,11 @@ REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file,
   ret_val.len = data_length-data_left;
   return ret_val;
 
+ fail_locked:
+  regfi_unlock(file, &file->cb_lock, "regfi_load_big_data");
  fail:
-  if(ret_val.buf != NULL)
-    talloc_free(ret_val.buf);
-  if(indirect_ptrs != NULL)
-    talloc_free(indirect_ptrs);
+  talloc_free(ret_val.buf);
+  talloc_free(indirect_ptrs);
   if(bd_cells != NULL)
     range_list_free(bd_cells);
   ret_val.buf = NULL;
@@ -3118,6 +3764,12 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
   if(ret_val == NULL)
     return NULL;
 
+  if(!regfi_read_lock(file, &file->hbins_lock, "regfi_parse_unalloc_cells"))
+  {
+    range_list_free(ret_val);
+    return NULL;
+  }
+
   num_hbins = range_list_size(file->hbins);
   for(i=0; i<num_hbins; i++)
   {
@@ -3129,15 +3781,24 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
     curr_off = REGFI_HBIN_HEADER_SIZE;
     while(curr_off < hbin->block_size)
     {
-      if(!regfi_parse_cell(file->fd, hbin->file_off+curr_off, NULL, 0,
+      if(!regfi_lock(file, &file->cb_lock, "regfi_parse_unalloc_cells"))
+	break;
+
+      if(!regfi_parse_cell(file->cb, hbin->file_off+curr_off, NULL, 0,
 			   &cell_len, &is_unalloc))
+      {
+	regfi_unlock(file, &file->cb_lock, "regfi_parse_unalloc_cells");
 	break;
-      
+      }
+
+      if(!regfi_unlock(file, &file->cb_lock, "regfi_parse_unalloc_cells"))
+	break;
+
       if((cell_len == 0) || ((cell_len & 0x00000007) != 0))
       {
-	regfi_add_message(file, REGFI_MSG_ERROR, "Bad cell length encountered"
-			  " while parsing unallocated cells at offset 0x%.8X.",
-			  hbin->file_off+curr_off);
+	regfi_log_add(REGFI_LOG_ERROR, "Bad cell length encountered"
+		      " while parsing unallocated cells at offset 0x%.8X.",
+		      hbin->file_off+curr_off);
 	break;
       }
 
@@ -3156,6 +3817,12 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
     }
   }
 
+  if(!regfi_rw_unlock(file, &file->hbins_lock, "regfi_parse_unalloc_cells"))
+  {
+    range_list_free(ret_val);
+    return NULL;
+  }
+
   return ret_val;
 }
 
@@ -3163,33 +3830,21 @@ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file)
 /* From lib/time.c */
 
 /****************************************************************************
- Put a 8 byte filetime from a time_t
+ Returns an 8 byte filetime from a time_t
  This takes real GMT as input and converts to kludge-GMT
 ****************************************************************************/
-void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t)
+REGFI_NTTIME regfi_unix2nt_time(time_t t)
 {
   double d;
-  
-  if (t==0) 
-  {
-    nt->low = 0;
-    nt->high = 0;
-    return;
-  }
+
+  if (t==0)
+    return 0L;
   
   if (t == TIME_T_MAX) 
-  {
-    nt->low = 0xffffffff;
-    nt->high = 0x7fffffff;
-    return;
-  }		
+    return 0x7fffffffffffffffL;
   
   if (t == -1) 
-  {
-    nt->low = 0xffffffff;
-    nt->high = 0xffffffff;
-    return;
-  }		
+    return 0xffffffffffffffffL;
   
   /* this converts GMT to kludge-GMT */
   /* XXX: This was removed due to difficult dependency requirements.  
@@ -3198,12 +3853,14 @@ void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t)
    */
   /* t -= TimeDiff(t) - get_serverzone(); */
   
-  d = (double)(t);
-  d += TIME_FIXUP_CONSTANT;
+  d = (double)(t) + REGFI_TIME_FIXUP;
   d *= 1.0e7;
-  
-  nt->high = (uint32_t)(d * (1.0/(4.0*(double)(1<<30))));
-  nt->low  = (uint32_t)(d - ((double)nt->high)*4.0*(double)(1<<30));
+  /*
+  nt->high = (uint32_t)(d * (1.0/c));
+  nt->low  = (uint32_t)(d - ((double)nt->high) * c);
+  */
+
+  return (REGFI_NTTIME) d;
 }
 
 
@@ -3218,32 +3875,17 @@ void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t)
  serverzone. This is NOT the same as GMT in some cases. This routine
  converts this to real GMT.
 ****************************************************************************/
-time_t regfi_nt2unix_time(const REGFI_NTTIME* nt)
+double regfi_nt2unix_time(REGFI_NTTIME nt)
 {
-  double d;
-  time_t ret;
-  /* The next two lines are a fix needed for the 
-     broken SCO compiler. JRA. */
-  time_t l_time_min = TIME_T_MIN;
-  time_t l_time_max = TIME_T_MAX;
+  double ret_val;
   
-  if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff))
-    return(0);
+  if (nt == 0 || nt == 0xffffffffffffffffL)
+    return 0;
   
-  d = ((double)nt->high)*4.0*(double)(1<<30);
-  d += (nt->low&0xFFF00000);
-  d *= 1.0e-7;
+  ret_val = (double)(nt) * 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);
+  ret_val -= REGFI_TIME_FIXUP;
   
   /* this takes us from kludge-GMT to real GMT */
   /* XXX: This was removed due to difficult dependency requirements.  
@@ -3255,7 +3897,7 @@ time_t regfi_nt2unix_time(const REGFI_NTTIME* nt)
     ret += LocTimeDiff(ret);
   */
 
-  return(ret);
+  return ret_val;
 }
 
 /* End of stuff from lib/time.c */
diff --git a/lib/talloc.c b/lib/talloc.c
deleted file mode 100644
index faa4378..0000000
--- a/lib/talloc.c
+++ /dev/null
@@ -1,1696 +0,0 @@
-/* 
-   Samba Unix SMB/CIFS implementation.
-
-   Samba trivial allocation library - new interface
-     inspired by http://swapped.cc/halloc/
-
-   NOTE: Please read talloc_guide.txt for full documentation
-
-   Copyright (C) Andrew Tridgell 2004
-   Copyright (C) Stefan Metzmacher 2006
-   Copyright (C) Timothy D. Morgan 2009 
- 
-     ** NOTE! The following LGPL license applies to the talloc library.
-     ** This does NOT imply that all of reglookup is released under the LGPL
-   
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 3 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, see <http://www.gnu.org/licenses/>.
-*/
-/* $Id: talloc.c 147 2009-02-22 19:31:52Z tim $ */
-
-#include "talloc.h"
-
-/* use this to force every realloc to change the pointer, to stress test
-   code that might not cope */
-#define ALWAYS_REALLOC 0
-
-
-#define MAX_TALLOC_SIZE 0x10000000
-#define TALLOC_MAGIC 0xe814ec70
-#define TALLOC_FLAG_FREE 0x01
-#define TALLOC_FLAG_LOOP 0x02
-#define TALLOC_FLAG_POOL 0x04		/* This is a talloc pool */
-#define TALLOC_FLAG_POOLMEM 0x08	/* This is allocated in a pool */
-#define TALLOC_MAGIC_REFERENCE ((const char *)1)
-
-/* by default we abort when given a bad pointer (such as when talloc_free() is called 
-   on a pointer that came from malloc() */
-#ifndef TALLOC_ABORT
-#define TALLOC_ABORT(reason) abort()
-#endif
-
-#ifndef discard_const_p
-#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
-# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
-#else
-# define discard_const_p(type, ptr) ((type *)(ptr))
-#endif
-#endif
-
-/* these macros gain us a few percent of speed on gcc */
-#if (__GNUC__ >= 3)
-/* the strange !! is to ensure that __builtin_expect() takes either 0 or 1
-   as its first argument */
-#define likely(x)   __builtin_expect(!!(x), 1)
-#define unlikely(x) __builtin_expect(!!(x), 0)
-#else
-#define likely(x) x
-#define unlikely(x) x
-#endif
-
-/* this null_context is only used if talloc_enable_leak_report() or
-   talloc_enable_leak_report_full() is called, otherwise it remains
-   NULL
-*/
-static void *null_context;
-static void *autofree_context;
-
-struct talloc_reference_handle {
-	struct talloc_reference_handle *next, *prev;
-	void *ptr;
-};
-
-typedef int (*talloc_destructor_t)(void *);
-
-struct talloc_chunk {
-	struct talloc_chunk *next, *prev;
-	struct talloc_chunk *parent, *child;
-	struct talloc_reference_handle *refs;
-	talloc_destructor_t destructor;
-	const char *name;
-	size_t size;
-	unsigned flags;
-
-	/*
-	 * "pool" has dual use:
-	 *
-	 * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool"
-	 * marks the end of the currently allocated area.
-	 *
-	 * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool"
-	 * is a pointer to the struct talloc_chunk of the pool that it was
-	 * allocated from. This way children can quickly find the pool to chew
-	 * from.
-	 */
-	void *pool;
-};
-
-/* 16 byte alignment seems to keep everyone happy */
-#define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15)
-#define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc))
-
-static void talloc_abort_double_free(void)
-{
-	TALLOC_ABORT("Bad talloc magic value - double free"); 
-}
-
-static void talloc_abort_unknown_value(void)
-{
-	TALLOC_ABORT("Bad talloc magic value - unknown value"); 
-}
-
-/* panic if we get a bad magic value */
-static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr)
-{
-	const char *pp = (const char *)ptr;
-	struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE);
-	if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { 
-		if (tc->flags & TALLOC_FLAG_FREE) {
-			talloc_abort_double_free();
-		} else {
-			talloc_abort_unknown_value();
-		}
-	}
-	return tc;
-}
-
-/* hook into the front of the list */
-#define _TLIST_ADD(list, p) \
-do { \
-        if (!(list)) { \
-		(list) = (p); \
-		(p)->next = (p)->prev = NULL; \
-	} else { \
-		(list)->prev = (p); \
-		(p)->next = (list); \
-		(p)->prev = NULL; \
-		(list) = (p); \
-	}\
-} while (0)
-
-/* remove an element from a list - element doesn't have to be in list. */
-#define _TLIST_REMOVE(list, p) \
-do { \
-	if ((p) == (list)) { \
-		(list) = (p)->next; \
-		if (list) (list)->prev = NULL; \
-	} else { \
-		if ((p)->prev) (p)->prev->next = (p)->next; \
-		if ((p)->next) (p)->next->prev = (p)->prev; \
-	} \
-	if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
-} while (0)
-
-
-/*
-  return the parent chunk of a pointer
-*/
-static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr)
-{
-	struct talloc_chunk *tc;
-
-	if (unlikely(ptr == NULL)) {
-		return NULL;
-	}
-
-	tc = talloc_chunk_from_ptr(ptr);
-	while (tc->prev) tc=tc->prev;
-
-	return tc->parent;
-}
-
-void *talloc_parent(const void *ptr)
-{
-	struct talloc_chunk *tc = talloc_parent_chunk(ptr);
-	return tc? TC_PTR_FROM_CHUNK(tc) : NULL;
-}
-
-/*
-  find parents name
-*/
-const char *talloc_parent_name(const void *ptr)
-{
-	struct talloc_chunk *tc = talloc_parent_chunk(ptr);
-	return tc? tc->name : NULL;
-}
-
-/*
-  A pool carries an in-pool object count count in the first 16 bytes.
-  bytes. This is done to support talloc_steal() to a parent outside of the
-  pool. The count includes the pool itself, so a talloc_free() on a pool will
-  only destroy the pool if the count has dropped to zero. A talloc_free() of a
-  pool member will reduce the count, and eventually also call free(3) on the
-  pool memory.
-
-  The object count is not put into "struct talloc_chunk" because it is only
-  relevant for talloc pools and the alignment to 16 bytes would increase the
-  memory footprint of each talloc chunk by those 16 bytes.
-*/
-
-#define TALLOC_POOL_HDR_SIZE 16
-
-static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc)
-{
-	return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk));
-}
-
-/*
-  Allocate from a pool
-*/
-
-static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent,
-					      size_t size)
-{
-	struct talloc_chunk *pool_ctx = NULL;
-	size_t space_left;
-	struct talloc_chunk *result;
-	size_t chunk_size;
-
-	if (parent == NULL) {
-		return NULL;
-	}
-
-	if (parent->flags & TALLOC_FLAG_POOL) {
-		pool_ctx = parent;
-	}
-	else if (parent->flags & TALLOC_FLAG_POOLMEM) {
-		pool_ctx = (struct talloc_chunk *)parent->pool;
-	}
-
-	if (pool_ctx == NULL) {
-		return NULL;
-	}
-
-	space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size)
-		- ((char *)pool_ctx->pool);
-
-	/*
-	 * Align size to 16 bytes
-	 */
-	chunk_size = ((size + 15) & ~15);
-
-	if (space_left < chunk_size) {
-		return NULL;
-	}
-
-	result = (struct talloc_chunk *)pool_ctx->pool;
-
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED)
-	VALGRIND_MAKE_MEM_UNDEFINED(result, size);
-#endif
-
-	pool_ctx->pool = (void *)((char *)result + chunk_size);
-
-	result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM;
-	result->pool = pool_ctx;
-
-	*talloc_pool_objectcount(pool_ctx) += 1;
-
-	return result;
-}
-
-/* 
-   Allocate a bit of memory as a child of an existing pointer
-*/
-static inline void *__talloc(const void *context, size_t size)
-{
-	struct talloc_chunk *tc = NULL;
-
-	if (unlikely(context == NULL)) {
-		context = null_context;
-	}
-
-	if (unlikely(size >= MAX_TALLOC_SIZE)) {
-		return NULL;
-	}
-
-	if (context != NULL) {
-		tc = talloc_alloc_pool(talloc_chunk_from_ptr(context),
-				       TC_HDR_SIZE+size);
-	}
-
-	if (tc == NULL) {
-		tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size);
-		if (unlikely(tc == NULL)) return NULL;
-		tc->flags = TALLOC_MAGIC;
-		tc->pool  = NULL;
-	}
-
-	tc->size = size;
-	tc->destructor = NULL;
-	tc->child = NULL;
-	tc->name = NULL;
-	tc->refs = NULL;
-
-	if (likely(context)) {
-		struct talloc_chunk *parent = talloc_chunk_from_ptr(context);
-
-		if (parent->child) {
-			parent->child->parent = NULL;
-			tc->next = parent->child;
-			tc->next->prev = tc;
-		} else {
-			tc->next = NULL;
-		}
-		tc->parent = parent;
-		tc->prev = NULL;
-		parent->child = tc;
-	} else {
-		tc->next = tc->prev = tc->parent = NULL;
-	}
-
-	return TC_PTR_FROM_CHUNK(tc);
-}
-
-/*
- * Create a talloc pool
- */
-
-void *talloc_pool(const void *context, size_t size)
-{
-	void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE);
-	struct talloc_chunk *tc;
-
-	if (unlikely(result == NULL)) {
-		return NULL;
-	}
-
-	tc = talloc_chunk_from_ptr(result);
-
-	tc->flags |= TALLOC_FLAG_POOL;
-	tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE;
-
-	*talloc_pool_objectcount(tc) = 1;
-
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
-	VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size);
-#endif
-
-	return result;
-}
-
-/*
-  setup a destructor to be called on free of a pointer
-  the destructor should return 0 on success, or -1 on failure.
-  if the destructor fails then the free is failed, and the memory can
-  be continued to be used
-*/
-void _talloc_set_destructor(const void *ptr, int (*destructor)(void *))
-{
-	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
-	tc->destructor = destructor;
-}
-
-/*
-  increase the reference count on a piece of memory. 
-*/
-int talloc_increase_ref_count(const void *ptr)
-{
-	if (unlikely(!talloc_reference(null_context, ptr))) {
-		return -1;
-	}
-	return 0;
-}
-
-/*
-  helper for talloc_reference()
-
-  this is referenced by a function pointer and should not be inline
-*/
-static int talloc_reference_destructor(struct talloc_reference_handle *handle)
-{
-	struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr);
-	_TLIST_REMOVE(ptr_tc->refs, handle);
-	return 0;
-}
-
-/*
-   more efficient way to add a name to a pointer - the name must point to a 
-   true string constant
-*/
-static inline void _talloc_set_name_const(const void *ptr, const char *name)
-{
-	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
-	tc->name = name;
-}
-
-/*
-  internal talloc_named_const()
-*/
-static inline void *_talloc_named_const(const void *context, size_t size, const char *name)
-{
-	void *ptr;
-
-	ptr = __talloc(context, size);
-	if (unlikely(ptr == NULL)) {
-		return NULL;
-	}
-
-	_talloc_set_name_const(ptr, name);
-
-	return ptr;
-}
-
-/*
-  make a secondary reference to a pointer, hanging off the given context.
-  the pointer remains valid until both the original caller and this given
-  context are freed.
-  
-  the major use for this is when two different structures need to reference the 
-  same underlying data, and you want to be able to free the two instances separately,
-  and in either order
-*/
-void *_talloc_reference(const void *context, const void *ptr)
-{
-	struct talloc_chunk *tc;
-	struct talloc_reference_handle *handle;
-	if (unlikely(ptr == NULL)) return NULL;
-
-	tc = talloc_chunk_from_ptr(ptr);
-	handle = (struct talloc_reference_handle *)_talloc_named_const(context,
-						   sizeof(struct talloc_reference_handle),
-						   TALLOC_MAGIC_REFERENCE);
-	if (unlikely(handle == NULL)) return NULL;
-
-	/* note that we hang the destructor off the handle, not the
-	   main context as that allows the caller to still setup their
-	   own destructor on the context if they want to */
-	talloc_set_destructor(handle, talloc_reference_destructor);
-	handle->ptr = discard_const_p(void, ptr);
-	_TLIST_ADD(tc->refs, handle);
-	return handle->ptr;
-}
-
-
-/* 
-   internal talloc_free call
-*/
-static inline int _talloc_free(void *ptr)
-{
-	struct talloc_chunk *tc;
-
-	if (unlikely(ptr == NULL)) {
-		return -1;
-	}
-
-	tc = talloc_chunk_from_ptr(ptr);
-
-	if (unlikely(tc->refs)) {
-		int is_child;
-		/* check this is a reference from a child or grantchild
-		 * back to it's parent or grantparent
-		 *
-		 * in that case we need to remove the reference and
-		 * call another instance of talloc_free() on the current
-		 * pointer.
-		 */
-		is_child = talloc_is_parent(tc->refs, ptr);
-		_talloc_free(tc->refs);
-		if (is_child) {
-			return _talloc_free(ptr);
-		}
-		return -1;
-	}
-
-	if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) {
-		/* we have a free loop - stop looping */
-		return 0;
-	}
-
-	if (unlikely(tc->destructor)) {
-		talloc_destructor_t d = tc->destructor;
-		if (d == (talloc_destructor_t)-1) {
-			return -1;
-		}
-		tc->destructor = (talloc_destructor_t)-1;
-		if (d(ptr) == -1) {
-			tc->destructor = d;
-			return -1;
-		}
-		tc->destructor = NULL;
-	}
-
-	if (tc->parent) {
-		_TLIST_REMOVE(tc->parent->child, tc);
-		if (tc->parent->child) {
-			tc->parent->child->parent = tc->parent;
-		}
-	} else {
-		if (tc->prev) tc->prev->next = tc->next;
-		if (tc->next) tc->next->prev = tc->prev;
-	}
-
-	tc->flags |= TALLOC_FLAG_LOOP;
-
-	while (tc->child) {
-		/* we need to work out who will own an abandoned child
-		   if it cannot be freed. In priority order, the first
-		   choice is owner of any remaining reference to this
-		   pointer, the second choice is our parent, and the
-		   final choice is the null context. */
-		void *child = TC_PTR_FROM_CHUNK(tc->child);
-		const void *new_parent = null_context;
-		if (unlikely(tc->child->refs)) {
-			struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
-			if (p) new_parent = TC_PTR_FROM_CHUNK(p);
-		}
-		if (unlikely(_talloc_free(child) == -1)) {
-			if (new_parent == null_context) {
-				struct talloc_chunk *p = talloc_parent_chunk(ptr);
-				if (p) new_parent = TC_PTR_FROM_CHUNK(p);
-			}
-			talloc_steal(new_parent, child);
-		}
-	}
-
-	tc->flags |= TALLOC_FLAG_FREE;
-
-	if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) {
-		struct talloc_chunk *pool;
-		unsigned int *pool_object_count;
-
-		pool = (tc->flags & TALLOC_FLAG_POOL)
-			? tc : (struct talloc_chunk *)tc->pool;
-
-		pool_object_count = talloc_pool_objectcount(pool);
-
-		if (*pool_object_count == 0) {
-			TALLOC_ABORT("Pool object count zero!");
-		}
-
-		*pool_object_count -= 1;
-
-		if (*pool_object_count == 0) {
-			free(pool);
-		}
-	}
-	else {
-		free(tc);
-	}
-	return 0;
-}
-
-/* 
-   move a lump of memory from one talloc context to another return the
-   ptr on success, or NULL if it could not be transferred.
-   passing NULL as ptr will always return NULL with no side effects.
-*/
-void *_talloc_steal(const void *new_ctx, const void *ptr)
-{
-	struct talloc_chunk *tc, *new_tc;
-
-	if (unlikely(!ptr)) {
-		return NULL;
-	}
-
-	if (unlikely(new_ctx == NULL)) {
-		new_ctx = null_context;
-	}
-
-	tc = talloc_chunk_from_ptr(ptr);
-
-	if (unlikely(new_ctx == NULL)) {
-		if (tc->parent) {
-			_TLIST_REMOVE(tc->parent->child, tc);
-			if (tc->parent->child) {
-				tc->parent->child->parent = tc->parent;
-			}
-		} else {
-			if (tc->prev) tc->prev->next = tc->next;
-			if (tc->next) tc->next->prev = tc->prev;
-		}
-		
-		tc->parent = tc->next = tc->prev = NULL;
-		return discard_const_p(void, ptr);
-	}
-
-	new_tc = talloc_chunk_from_ptr(new_ctx);
-
-	if (unlikely(tc == new_tc || tc->parent == new_tc)) {
-		return discard_const_p(void, ptr);
-	}
-
-	if (tc->parent) {
-		_TLIST_REMOVE(tc->parent->child, tc);
-		if (tc->parent->child) {
-			tc->parent->child->parent = tc->parent;
-		}
-	} else {
-		if (tc->prev) tc->prev->next = tc->next;
-		if (tc->next) tc->next->prev = tc->prev;
-	}
-
-	tc->parent = new_tc;
-	if (new_tc->child) new_tc->child->parent = NULL;
-	_TLIST_ADD(new_tc->child, tc);
-
-	return discard_const_p(void, ptr);
-}
-
-
-
-/*
-  remove a secondary reference to a pointer. This undo's what
-  talloc_reference() has done. The context and pointer arguments
-  must match those given to a talloc_reference()
-*/
-static inline int talloc_unreference(const void *context, const void *ptr)
-{
-	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
-	struct talloc_reference_handle *h;
-
-	if (unlikely(context == NULL)) {
-		context = null_context;
-	}
-
-	for (h=tc->refs;h;h=h->next) {
-		struct talloc_chunk *p = talloc_parent_chunk(h);
-		if (p == NULL) {
-			if (context == NULL) break;
-		} else if (TC_PTR_FROM_CHUNK(p) == context) {
-			break;
-		}
-	}
-	if (h == NULL) {
-		return -1;
-	}
-
-	return _talloc_free(h);
-}
-
-/*
-  remove a specific parent context from a pointer. This is a more
-  controlled varient of talloc_free()
-*/
-int talloc_unlink(const void *context, void *ptr)
-{
-	struct talloc_chunk *tc_p, *new_p;
-	void *new_parent;
-
-	if (ptr == NULL) {
-		return -1;
-	}
-
-	if (context == NULL) {
-		context = null_context;
-	}
-
-	if (talloc_unreference(context, ptr) == 0) {
-		return 0;
-	}
-
-	if (context == NULL) {
-		if (talloc_parent_chunk(ptr) != NULL) {
-			return -1;
-		}
-	} else {
-		if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) {
-			return -1;
-		}
-	}
-	
-	tc_p = talloc_chunk_from_ptr(ptr);
-
-	if (tc_p->refs == NULL) {
-		return _talloc_free(ptr);
-	}
-
-	new_p = talloc_parent_chunk(tc_p->refs);
-	if (new_p) {
-		new_parent = TC_PTR_FROM_CHUNK(new_p);
-	} else {
-		new_parent = NULL;
-	}
-
-	if (talloc_unreference(new_parent, ptr) != 0) {
-		return -1;
-	}
-
-	talloc_steal(new_parent, ptr);
-
-	return 0;
-}
-
-/*
-  add a name to an existing pointer - va_list version
-*/
-static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0);
-
-static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap)
-{
-	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
-	tc->name = talloc_vasprintf(ptr, fmt, ap);
-	if (likely(tc->name)) {
-		_talloc_set_name_const(tc->name, ".name");
-	}
-	return tc->name;
-}
-
-/*
-  add a name to an existing pointer
-*/
-const char *talloc_set_name(const void *ptr, const char *fmt, ...)
-{
-	const char *name;
-	va_list ap;
-	va_start(ap, fmt);
-	name = talloc_set_name_v(ptr, fmt, ap);
-	va_end(ap);
-	return name;
-}
-
-
-/*
-  create a named talloc pointer. Any talloc pointer can be named, and
-  talloc_named() operates just like talloc() except that it allows you
-  to name the pointer.
-*/
-void *talloc_named(const void *context, size_t size, const char *fmt, ...)
-{
-	va_list ap;
-	void *ptr;
-	const char *name;
-
-	ptr = __talloc(context, size);
-	if (unlikely(ptr == NULL)) return NULL;
-
-	va_start(ap, fmt);
-	name = talloc_set_name_v(ptr, fmt, ap);
-	va_end(ap);
-
-	if (unlikely(name == NULL)) {
-		_talloc_free(ptr);
-		return NULL;
-	}
-
-	return ptr;
-}
-
-/*
-  return the name of a talloc ptr, or "UNNAMED"
-*/
-const char *talloc_get_name(const void *ptr)
-{
-	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
-	if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) {
-		return ".reference";
-	}
-	if (likely(tc->name)) {
-		return tc->name;
-	}
-	return "UNNAMED";
-}
-
-
-/*
-  check if a pointer has the given name. If it does, return the pointer,
-  otherwise return NULL
-*/
-void *talloc_check_name(const void *ptr, const char *name)
-{
-	const char *pname;
-	if (unlikely(ptr == NULL)) return NULL;
-	pname = talloc_get_name(ptr);
-	if (likely(pname == name || strcmp(pname, name) == 0)) {
-		return discard_const_p(void, ptr);
-	}
-	return NULL;
-}
-
-
-/*
-  this is for compatibility with older versions of talloc
-*/
-void *talloc_init(const char *fmt, ...)
-{
-	va_list ap;
-	void *ptr;
-	const char *name;
-
-	/*
-	 * samba3 expects talloc_report_depth_cb(NULL, ...)
-	 * reports all talloc'ed memory, so we need to enable
-	 * null_tracking
-	 */
-	talloc_enable_null_tracking();
-
-	ptr = __talloc(NULL, 0);
-	if (unlikely(ptr == NULL)) return NULL;
-
-	va_start(ap, fmt);
-	name = talloc_set_name_v(ptr, fmt, ap);
-	va_end(ap);
-
-	if (unlikely(name == NULL)) {
-		_talloc_free(ptr);
-		return NULL;
-	}
-
-	return ptr;
-}
-
-/*
-  this is a replacement for the Samba3 talloc_destroy_pool functionality. It
-  should probably not be used in new code. It's in here to keep the talloc
-  code consistent across Samba 3 and 4.
-*/
-void talloc_free_children(void *ptr)
-{
-	struct talloc_chunk *tc;
-
-	if (unlikely(ptr == NULL)) {
-		return;
-	}
-
-	tc = talloc_chunk_from_ptr(ptr);
-
-	while (tc->child) {
-		/* we need to work out who will own an abandoned child
-		   if it cannot be freed. In priority order, the first
-		   choice is owner of any remaining reference to this
-		   pointer, the second choice is our parent, and the
-		   final choice is the null context. */
-		void *child = TC_PTR_FROM_CHUNK(tc->child);
-		const void *new_parent = null_context;
-		if (unlikely(tc->child->refs)) {
-			struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs);
-			if (p) new_parent = TC_PTR_FROM_CHUNK(p);
-		}
-		if (unlikely(_talloc_free(child) == -1)) {
-			if (new_parent == null_context) {
-				struct talloc_chunk *p = talloc_parent_chunk(ptr);
-				if (p) new_parent = TC_PTR_FROM_CHUNK(p);
-			}
-			talloc_steal(new_parent, child);
-		}
-	}
-
-	if ((tc->flags & TALLOC_FLAG_POOL)
-	    && (*talloc_pool_objectcount(tc) == 1)) {
-		tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE);
-#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS)
-		VALGRIND_MAKE_MEM_NOACCESS(
-			tc->pool, tc->size - TALLOC_POOL_HDR_SIZE);
-#endif
-	}
-}
-
-/* 
-   Allocate a bit of memory as a child of an existing pointer
-*/
-void *_talloc(const void *context, size_t size)
-{
-	return __talloc(context, size);
-}
-
-/*
-  externally callable talloc_set_name_const()
-*/
-void talloc_set_name_const(const void *ptr, const char *name)
-{
-	_talloc_set_name_const(ptr, name);
-}
-
-/*
-  create a named talloc pointer. Any talloc pointer can be named, and
-  talloc_named() operates just like talloc() except that it allows you
-  to name the pointer.
-*/
-void *talloc_named_const(const void *context, size_t size, const char *name)
-{
-	return _talloc_named_const(context, size, name);
-}
-
-/* 
-   free a talloc pointer. This also frees all child pointers of this 
-   pointer recursively
-
-   return 0 if the memory is actually freed, otherwise -1. The memory
-   will not be freed if the ref_count is > 1 or the destructor (if
-   any) returns non-zero
-*/
-int talloc_free(void *ptr)
-{
-	return _talloc_free(ptr);
-}
-
-
-
-/*
-  A talloc version of realloc. The context argument is only used if
-  ptr is NULL
-*/
-void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name)
-{
-	struct talloc_chunk *tc;
-	void *new_ptr;
-	bool malloced = false;
-
-	/* size zero is equivalent to free() */
-	if (unlikely(size == 0)) {
-		_talloc_free(ptr);
-		return NULL;
-	}
-
-	if (unlikely(size >= MAX_TALLOC_SIZE)) {
-		return NULL;
-	}
-
-	/* realloc(NULL) is equivalent to malloc() */
-	if (ptr == NULL) {
-		return _talloc_named_const(context, size, name);
-	}
-
-	tc = talloc_chunk_from_ptr(ptr);
-
-	/* don't allow realloc on referenced pointers */
-	if (unlikely(tc->refs)) {
-		return NULL;
-	}
-
-	/* don't shrink if we have less than 1k to gain */
-	if ((size < tc->size) && ((tc->size - size) < 1024)) {
-		tc->size = size;
-		return ptr;
-	}
-
-	/* by resetting magic we catch users of the old memory */
-	tc->flags |= TALLOC_FLAG_FREE;
-
-#if ALWAYS_REALLOC
-	new_ptr = malloc(size + TC_HDR_SIZE);
-	if (new_ptr) {
-		memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE);
-		free(tc);
-	}
-#else
-	if (tc->flags & TALLOC_FLAG_POOLMEM) {
-
-		new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE);
-		*talloc_pool_objectcount((struct talloc_chunk *)
-					 (tc->pool)) -= 1;
-
-		if (new_ptr == NULL) {
-			new_ptr = malloc(TC_HDR_SIZE+size);
-			malloced = true;
-		}
-
-		if (new_ptr) {
-			memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE);
-		}
-	}
-	else {
-		new_ptr = realloc(tc, size + TC_HDR_SIZE);
-	}
-#endif
-	if (unlikely(!new_ptr)) {	
-		tc->flags &= ~TALLOC_FLAG_FREE; 
-		return NULL; 
-	}
-
-	tc = (struct talloc_chunk *)new_ptr;
-	tc->flags &= ~TALLOC_FLAG_FREE;
-	if (malloced) {
-		tc->flags &= ~TALLOC_FLAG_POOLMEM;
-	}
-	if (tc->parent) {
-		tc->parent->child = tc;
-	}
-	if (tc->child) {
-		tc->child->parent = tc;
-	}
-
-	if (tc->prev) {
-		tc->prev->next = tc;
-	}
-	if (tc->next) {
-		tc->next->prev = tc;
-	}
-
-	tc->size = size;
-	_talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name);
-
-	return TC_PTR_FROM_CHUNK(tc);
-}
-
-/*
-  a wrapper around talloc_steal() for situations where you are moving a pointer
-  between two structures, and want the old pointer to be set to NULL
-*/
-void *_talloc_move(const void *new_ctx, const void *_pptr)
-{
-	const void **pptr = discard_const_p(const void *,_pptr);
-	void *ret = _talloc_steal(new_ctx, *pptr);
-	(*pptr) = NULL;
-	return ret;
-}
-
-/*
-  return the total size of a talloc pool (subtree)
-*/
-size_t talloc_total_size(const void *ptr)
-{
-	size_t total = 0;
-	struct talloc_chunk *c, *tc;
-
-	if (ptr == NULL) {
-		ptr = null_context;
-	}
-	if (ptr == NULL) {
-		return 0;
-	}
-
-	tc = talloc_chunk_from_ptr(ptr);
-
-	if (tc->flags & TALLOC_FLAG_LOOP) {
-		return 0;
-	}
-
-	tc->flags |= TALLOC_FLAG_LOOP;
-
-	total = tc->size;
-	for (c=tc->child;c;c=c->next) {
-		total += talloc_total_size(TC_PTR_FROM_CHUNK(c));
-	}
-
-	tc->flags &= ~TALLOC_FLAG_LOOP;
-
-	return total;
-}
-
-/*
-  return the total number of blocks in a talloc pool (subtree)
-*/
-size_t talloc_total_blocks(const void *ptr)
-{
-	size_t total = 0;
-	struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr);
-
-	if (tc->flags & TALLOC_FLAG_LOOP) {
-		return 0;
-	}
-
-	tc->flags |= TALLOC_FLAG_LOOP;
-
-	total++;
-	for (c=tc->child;c;c=c->next) {
-		total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c));
-	}
-
-	tc->flags &= ~TALLOC_FLAG_LOOP;
-
-	return total;
-}
-
-/*
-  return the number of external references to a pointer
-*/
-size_t talloc_reference_count(const void *ptr)
-{
-	struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr);
-	struct talloc_reference_handle *h;
-	size_t ret = 0;
-
-	for (h=tc->refs;h;h=h->next) {
-		ret++;
-	}
-	return ret;
-}
-
-/*
-  report on memory usage by all children of a pointer, giving a full tree view
-*/
-void talloc_report_depth_cb(const void *ptr, int depth, int max_depth,
-			    void (*callback)(const void *ptr,
-			  		     int depth, int max_depth,
-					     int is_ref,
-					     void *private_data),
-			    void *private_data)
-{
-	struct talloc_chunk *c, *tc;
-
-	if (ptr == NULL) {
-		ptr = null_context;
-	}
-	if (ptr == NULL) return;
-
-	tc = talloc_chunk_from_ptr(ptr);
-
-	if (tc->flags & TALLOC_FLAG_LOOP) {
-		return;
-	}
-
-	callback(ptr, depth, max_depth, 0, private_data);
-
-	if (max_depth >= 0 && depth >= max_depth) {
-		return;
-	}
-
-	tc->flags |= TALLOC_FLAG_LOOP;
-	for (c=tc->child;c;c=c->next) {
-		if (c->name == TALLOC_MAGIC_REFERENCE) {
-			struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c);
-			callback(h->ptr, depth + 1, max_depth, 1, private_data);
-		} else {
-			talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data);
-		}
-	}
-	tc->flags &= ~TALLOC_FLAG_LOOP;
-}
-
-static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f)
-{
-	const char *name = talloc_get_name(ptr);
-	FILE *f = (FILE *)_f;
-
-	if (is_ref) {
-		fprintf(f, "%*sreference to: %s\n", depth*4, "", name);
-		return;
-	}
-
-	if (depth == 0) {
-		fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", 
-			(max_depth < 0 ? "full " :""), name,
-			(unsigned long)talloc_total_size(ptr),
-			(unsigned long)talloc_total_blocks(ptr));
-		return;
-	}
-
-	fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", 
-		depth*4, "",
-		name,
-		(unsigned long)talloc_total_size(ptr),
-		(unsigned long)talloc_total_blocks(ptr),
-		(int)talloc_reference_count(ptr), ptr);
-
-#if 0
-	fprintf(f, "content: ");
-	if (talloc_total_size(ptr)) {
-		int tot = talloc_total_size(ptr);
-		int i;
-
-		for (i = 0; i < tot; i++) {
-			if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) {
-				fprintf(f, "%c", ((char *)ptr)[i]);
-			} else {
-				fprintf(f, "~%02x", ((char *)ptr)[i]);
-			}
-		}
-	}
-	fprintf(f, "\n");
-#endif
-}
-
-/*
-  report on memory usage by all children of a pointer, giving a full tree view
-*/
-void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f)
-{
-	talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f);
-	fflush(f);
-}
-
-/*
-  report on memory usage by all children of a pointer, giving a full tree view
-*/
-void talloc_report_full(const void *ptr, FILE *f)
-{
-	talloc_report_depth_file(ptr, 0, -1, f);
-}
-
-/*
-  report on memory usage by all children of a pointer
-*/
-void talloc_report(const void *ptr, FILE *f)
-{
-	talloc_report_depth_file(ptr, 0, 1, f);
-}
-
-/*
-  report on any memory hanging off the null context
-*/
-static void talloc_report_null(void)
-{
-	if (talloc_total_size(null_context) != 0) {
-		talloc_report(null_context, stderr);
-	}
-}
-
-/*
-  report on any memory hanging off the null context
-*/
-static void talloc_report_null_full(void)
-{
-	if (talloc_total_size(null_context) != 0) {
-		talloc_report_full(null_context, stderr);
-	}
-}
-
-/*
-  enable tracking of the NULL context
-*/
-void talloc_enable_null_tracking(void)
-{
-	if (null_context == NULL) {
-		null_context = _talloc_named_const(NULL, 0, "null_context");
-	}
-}
-
-/*
-  disable tracking of the NULL context
-*/
-void talloc_disable_null_tracking(void)
-{
-	_talloc_free(null_context);
-	null_context = NULL;
-}
-
-/*
-  enable leak reporting on exit
-*/
-void talloc_enable_leak_report(void)
-{
-	talloc_enable_null_tracking();
-	atexit(talloc_report_null);
-}
-
-/*
-  enable full leak reporting on exit
-*/
-void talloc_enable_leak_report_full(void)
-{
-	talloc_enable_null_tracking();
-	atexit(talloc_report_null_full);
-}
-
-/* 
-   talloc and zero memory. 
-*/
-void *_talloc_zero(const void *ctx, size_t size, const char *name)
-{
-	void *p = _talloc_named_const(ctx, size, name);
-
-	if (p) {
-		memset(p, '\0', size);
-	}
-
-	return p;
-}
-
-/*
-  memdup with a talloc. 
-*/
-void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name)
-{
-	void *newp = _talloc_named_const(t, size, name);
-
-	if (likely(newp)) {
-		memcpy(newp, p, size);
-	}
-
-	return newp;
-}
-
-static inline char *__talloc_strlendup(const void *t, const char *p, size_t len)
-{
-	char *ret;
-
-	ret = (char *)__talloc(t, len + 1);
-	if (unlikely(!ret)) return NULL;
-
-	memcpy(ret, p, len);
-	ret[len] = 0;
-
-	_talloc_set_name_const(ret, ret);
-	return ret;
-}
-
-/*
-  strdup with a talloc
-*/
-char *talloc_strdup(const void *t, const char *p)
-{
-	if (unlikely(!p)) return NULL;
-	return __talloc_strlendup(t, p, strlen(p));
-}
-
-/*
-  strndup with a talloc
-*/
-char *talloc_strndup(const void *t, const char *p, size_t n)
-{
-	if (unlikely(!p)) return NULL;
-	return __talloc_strlendup(t, p, n);
-}
-
-static inline char *__talloc_strlendup_append(char *s, size_t slen,
-					      const char *a, size_t alen)
-{
-	char *ret;
-
-	ret = talloc_realloc(NULL, s, char, slen + alen + 1);
-	if (unlikely(!ret)) return NULL;
-
-	/* append the string and the trailing \0 */
-	memcpy(&ret[slen], a, alen);
-	ret[slen+alen] = 0;
-
-	_talloc_set_name_const(ret, ret);
-	return ret;
-}
-
-/*
- * Appends at the end of the string.
- */
-char *talloc_strdup_append(char *s, const char *a)
-{
-	if (unlikely(!s)) {
-		return talloc_strdup(NULL, a);
-	}
-
-	if (unlikely(!a)) {
-		return s;
-	}
-
-	return __talloc_strlendup_append(s, strlen(s), a, strlen(a));
-}
-
-/*
- * Appends at the end of the talloc'ed buffer,
- * not the end of the string.
- */
-char *talloc_strdup_append_buffer(char *s, const char *a)
-{
-	size_t slen;
-
-	if (unlikely(!s)) {
-		return talloc_strdup(NULL, a);
-	}
-
-	if (unlikely(!a)) {
-		return s;
-	}
-
-	slen = talloc_get_size(s);
-	if (likely(slen > 0)) {
-		slen--;
-	}
-
-	return __talloc_strlendup_append(s, slen, a, strlen(a));
-}
-
-/*
- * Appends at the end of the string.
- */
-char *talloc_strndup_append(char *s, const char *a, size_t n)
-{
-	if (unlikely(!s)) {
-		return talloc_strdup(NULL, a);
-	}
-
-	if (unlikely(!a)) {
-		return s;
-	}
-
-	return __talloc_strlendup_append(s, strlen(s), a, n);
-}
-
-/*
- * Appends at the end of the talloc'ed buffer,
- * not the end of the string.
- */
-char *talloc_strndup_append_buffer(char *s, const char *a, size_t n)
-{
-	size_t slen;
-
-	if (unlikely(!s)) {
-		return talloc_strdup(NULL, a);
-	}
-
-	if (unlikely(!a)) {
-		return s;
-	}
-
-	slen = talloc_get_size(s);
-	if (likely(slen > 0)) {
-		slen--;
-	}
-
-	return __talloc_strlendup_append(s, slen, a, n);
-}
-
-
-char *talloc_vasprintf(const void *t, const char *fmt, va_list ap)
-{
-	int len;
-	char *ret;
-	va_list ap2;
-	char c;
-
-	/* this call looks strange, but it makes it work on older solaris boxes */
-	va_copy(ap2, ap);
-	len = vsnprintf(&c, 1, fmt, ap2);
-	va_end(ap2);
-	if (unlikely(len < 0)) {
-		return NULL;
-	}
-
-	ret = (char *)__talloc(t, len+1);
-	if (unlikely(!ret)) return NULL;
-
-	va_copy(ap2, ap);
-	vsnprintf(ret, len+1, fmt, ap2);
-	va_end(ap2);
-
-	_talloc_set_name_const(ret, ret);
-	return ret;
-}
-
-
-/*
-  Perform string formatting, and return a pointer to newly allocated
-  memory holding the result, inside a memory pool.
- */
-char *talloc_asprintf(const void *t, const char *fmt, ...)
-{
-	va_list ap;
-	char *ret;
-
-	va_start(ap, fmt);
-	ret = talloc_vasprintf(t, fmt, ap);
-	va_end(ap);
-	return ret;
-}
-
-static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
-						 const char *fmt, va_list ap)
-						 PRINTF_ATTRIBUTE(3,0);
-
-static inline char *__talloc_vaslenprintf_append(char *s, size_t slen,
-						 const char *fmt, va_list ap)
-{
-	ssize_t alen;
-	va_list ap2;
-	char c;
-
-	va_copy(ap2, ap);
-	alen = vsnprintf(&c, 1, fmt, ap2);
-	va_end(ap2);
-
-	if (alen <= 0) {
-		/* Either the vsnprintf failed or the format resulted in
-		 * no characters being formatted. In the former case, we
-		 * ought to return NULL, in the latter we ought to return
-		 * the original string. Most current callers of this
-		 * function expect it to never return NULL.
-		 */
-		return s;
-	}
-
-	s = talloc_realloc(NULL, s, char, slen + alen + 1);
-	if (!s) return NULL;
-
-	va_copy(ap2, ap);
-	vsnprintf(s + slen, alen + 1, fmt, ap2);
-	va_end(ap2);
-
-	_talloc_set_name_const(s, s);
-	return s;
-}
-
-/**
- * Realloc @p s to append the formatted result of @p fmt and @p ap,
- * and return @p s, which may have moved.  Good for gradually
- * accumulating output into a string buffer. Appends at the end
- * of the string.
- **/
-char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap)
-{
-	if (unlikely(!s)) {
-		return talloc_vasprintf(NULL, fmt, ap);
-	}
-
-	return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap);
-}
-
-/**
- * Realloc @p s to append the formatted result of @p fmt and @p ap,
- * and return @p s, which may have moved. Always appends at the
- * end of the talloc'ed buffer, not the end of the string.
- **/
-char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap)
-{
-	size_t slen;
-
-	if (unlikely(!s)) {
-		return talloc_vasprintf(NULL, fmt, ap);
-	}
-
-	slen = talloc_get_size(s);
-	if (likely(slen > 0)) {
-		slen--;
-	}
-
-	return __talloc_vaslenprintf_append(s, slen, fmt, ap);
-}
-
-/*
-  Realloc @p s to append the formatted result of @p fmt and return @p
-  s, which may have moved.  Good for gradually accumulating output
-  into a string buffer.
- */
-char *talloc_asprintf_append(char *s, const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	s = talloc_vasprintf_append(s, fmt, ap);
-	va_end(ap);
-	return s;
-}
-
-/*
-  Realloc @p s to append the formatted result of @p fmt and return @p
-  s, which may have moved.  Good for gradually accumulating output
-  into a buffer.
- */
-char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	s = talloc_vasprintf_append_buffer(s, fmt, ap);
-	va_end(ap);
-	return s;
-}
-
-/*
-  alloc an array, checking for integer overflow in the array size
-*/
-void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name)
-{
-	if (count >= MAX_TALLOC_SIZE/el_size) {
-		return NULL;
-	}
-	return _talloc_named_const(ctx, el_size * count, name);
-}
-
-/*
-  alloc an zero array, checking for integer overflow in the array size
-*/
-void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name)
-{
-	if (count >= MAX_TALLOC_SIZE/el_size) {
-		return NULL;
-	}
-	return _talloc_zero(ctx, el_size * count, name);
-}
-
-/*
-  realloc an array, checking for integer overflow in the array size
-*/
-void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name)
-{
-	if (count >= MAX_TALLOC_SIZE/el_size) {
-		return NULL;
-	}
-	return _talloc_realloc(ctx, ptr, el_size * count, name);
-}
-
-/*
-  a function version of talloc_realloc(), so it can be passed as a function pointer
-  to libraries that want a realloc function (a realloc function encapsulates
-  all the basic capabilities of an allocation library, which is why this is useful)
-*/
-void *talloc_realloc_fn(const void *context, void *ptr, size_t size)
-{
-	return _talloc_realloc(context, ptr, size, NULL);
-}
-
-
-static int talloc_autofree_destructor(void *ptr)
-{
-	autofree_context = NULL;
-	return 0;
-}
-
-static void talloc_autofree(void)
-{
-	_talloc_free(autofree_context);
-}
-
-/*
-  return a context which will be auto-freed on exit
-  this is useful for reducing the noise in leak reports
-*/
-void *talloc_autofree_context(void)
-{
-	if (autofree_context == NULL) {
-		autofree_context = _talloc_named_const(NULL, 0, "autofree_context");
-		talloc_set_destructor(autofree_context, talloc_autofree_destructor);
-		atexit(talloc_autofree);
-	}
-	return autofree_context;
-}
-
-size_t talloc_get_size(const void *context)
-{
-	struct talloc_chunk *tc;
-
-	if (context == NULL)
-		return 0;
-
-	tc = talloc_chunk_from_ptr(context);
-
-	return tc->size;
-}
-
-/*
-  find a parent of this context that has the given name, if any
-*/
-void *talloc_find_parent_byname(const void *context, const char *name)
-{
-	struct talloc_chunk *tc;
-
-	if (context == NULL) {
-		return NULL;
-	}
-
-	tc = talloc_chunk_from_ptr(context);
-	while (tc) {
-		if (tc->name && strcmp(tc->name, name) == 0) {
-			return TC_PTR_FROM_CHUNK(tc);
-		}
-		while (tc && tc->prev) tc = tc->prev;
-		if (tc) {
-			tc = tc->parent;
-		}
-	}
-	return NULL;
-}
-
-/*
-  show the parentage of a context
-*/
-void talloc_show_parents(const void *context, FILE *file)
-{
-	struct talloc_chunk *tc;
-
-	if (context == NULL) {
-		fprintf(file, "talloc no parents for NULL\n");
-		return;
-	}
-
-	tc = talloc_chunk_from_ptr(context);
-	fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context));
-	while (tc) {
-		fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc)));
-		while (tc && tc->prev) tc = tc->prev;
-		if (tc) {
-			tc = tc->parent;
-		}
-	}
-	fflush(file);
-}
-
-/*
-  return 1 if ptr is a parent of context
-*/
-int talloc_is_parent(const void *context, const void *ptr)
-{
-	struct talloc_chunk *tc;
-
-	if (context == NULL) {
-		return 0;
-	}
-
-	tc = talloc_chunk_from_ptr(context);
-	while (tc) {
-		if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1;
-		while (tc && tc->prev) tc = tc->prev;
-		if (tc) {
-			tc = tc->parent;
-		}
-	}
-	return 0;
-}
diff --git a/lib/winsec.c b/lib/winsec.c
index 2aea7ba..ce2c3a6 100644
--- a/lib/winsec.c
+++ b/lib/winsec.c
@@ -1,6 +1,5 @@
 /*
- *
- * Copyright (C) 2005-2006,2009-2010 Timothy D. Morgan
+ * Copyright (C) 2005-2006,2009-2011 Timothy D. Morgan
  * Copyright (C) 1992-2005 Samba development team 
  *               (see individual files under Subversion for details.)
  *
@@ -17,7 +16,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: winsec.c 169 2010-03-03 19:24:58Z tim $
+ * $Id: winsec.c 261 2011-06-17 00:55:49Z tim $
  */
 
 /** @file */
@@ -61,6 +60,7 @@ WINSEC_DESC* winsec_parse_desc(void* talloc_ctx,
   ret_val->sbz1 = buf[1];
   ret_val->control = SVAL(buf, 0x2);
 
+  /* XXX: should probably reject any non-self relative */
   if(!(ret_val->control & WINSEC_DESC_SELF_RELATIVE))
     fprintf(stderr, "DEBUG: NOT self-relative!\n");
 
@@ -225,7 +225,9 @@ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
   ret_val->flags = buf[1];
   ret_val->size = SVAL(buf, 0x2);
   ret_val->access_mask = IVAL(buf, 0x4);
-
+  ret_val->obj_guid = NULL;
+  ret_val->inh_guid = NULL;
+  
   offset = 0x8;
 
   /* check whether object access is present */
@@ -245,8 +247,6 @@ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
       }
       offset += sizeof(WINSEC_UUID);
     }
-    else
-      ret_val->obj_guid = NULL;
 
     if(ret_val->obj_flags & WINSEC_ACE_OBJECT_INHERITED_PRESENT)
     {
@@ -259,8 +259,6 @@ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx,
       }
       offset += sizeof(WINSEC_UUID);
     }
-    else
-      ret_val->inh_guid = NULL;
   }
 
   ret_val->trustee = winsec_parse_dom_sid(ret_val, buf+offset, buf_len-offset);
@@ -409,6 +407,30 @@ bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2)
 
 
 /******************************************************************************
+ ******************************************************************************/
+char* winsec_sid2str(const WINSEC_DOM_SID* sid)
+{
+  uint32_t i, size = WINSEC_MAX_SUBAUTHS*11 + 24;
+  uint32_t left = size;
+  uint8_t comps = sid->num_auths;
+  char* ret_val = malloc(size);
+  
+  if(ret_val == NULL)
+    return NULL;
+
+  if(comps > WINSEC_MAX_SUBAUTHS)
+    comps = WINSEC_MAX_SUBAUTHS;
+
+  left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]);
+
+  for (i = 0; i < comps; i++) 
+    left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]);
+
+  return ret_val;
+}
+
+
+/******************************************************************************
  * Compares two WINSEC_DESC structures.
  ******************************************************************************/
 bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2)
diff --git a/pyregfi-distutils.py b/pyregfi-distutils.py
new file mode 100644
index 0000000..41c11df
--- /dev/null
+++ b/pyregfi-distutils.py
@@ -0,0 +1,4 @@
+# Called from scons with appropriate python version
+
+from distutils.core import setup
+setup(name='pyregfi', version='1.0', package_dir={'':'python'}, packages=['pyregfi'])
diff --git a/python/experimental/SConstruct b/python/experimental/SConstruct
new file mode 100644
index 0000000..e4cae85
--- /dev/null
+++ b/python/experimental/SConstruct
@@ -0,0 +1,46 @@
+import os, os.path, pdb
+from functools import partial
+import utils
+import config
+import class_parser
+
+def build_python_bindings(target, source, env, initialization=''):
+    """ A command to generate python bindings """
+    module_name = os.path.splitext(os.path.basename(target[0].name))[0]
+    utils.warn("Generating automatic python bindings for module %s" % module_name)
+
+    p = class_parser.HeaderParser(module_name, verbose=config.V)
+    p.module.init_string = initialization
+    p.parse_filenames([s.get_abspath() for s in source])
+
+    fd = open(target[0].get_abspath(), 'w')
+    p.write(fd)
+    fd.close()
+
+
+
+nenv = utils.ExtendedEnvironment()
+
+BOUND_FILES = Split("""
+    ../include/regfi.h
+    regfi/pyregfi.h
+    """)
+
+nenv.config = config
+if config.DEBUG:
+    nenv.Append(CFLAGS = "-std=gnu99 -pedantic -Wall -fPIC -ggdb -O0")
+    nenv.Append(CPPPATH=['../include', 'include'])
+    nenv.python_cppflags = '-I/usr/include/python2.5_d'
+else:
+    nenv.Append(CFLAGS = "-std=gnu99 -pedantic -Wall -fPIC")
+    nenv.Append(CPPPATH=['../include', 'include'])
+    
+nenv.Append(LIBPATH="../lib")
+nenv.Append(LINKFLAGS="")
+# XXX: why should I need to call regfi_init() when it should be called only once automatically? 
+nenv.Command('regfi/pyregfi.c', BOUND_FILES, partial(build_python_bindings,
+                                                     initialization='pyregfi_init();regfi_init();'))
+nenv.Depends('regfi/pyregfi.c', 'class_parser.py')
+
+nenv.PythonModule("pyregfi", ['regfi/pyregfi.c', 'regfi/regfi.c', 'regfi/class.c', 'regfi/error.c'],
+                  LIBS=['regfi', 'talloc'])
diff --git a/python/experimental/class_parser.py b/python/experimental/class_parser.py
new file mode 100644
index 0000000..ebd5a58
--- /dev/null
+++ b/python/experimental/class_parser.py
@@ -0,0 +1,2973 @@
+import sys, os, re, pdb, StringIO
+ 
+DEBUG = 0
+
+def log(msg):
+    if DEBUG>0:
+        sys.stderr.write(msg+"\n")
+
+def escape_for_string(string):
+    result = string
+    result = result.encode("string-escape")
+    result = result.replace('"',r'\"')
+
+    return result
+
+class Module:
+    def __init__(self, name):
+        self.name = name
+        self.constants = set()
+        self.classes = {}
+        self.headers = '#include <Python.h>\n'
+
+        self.files = []
+
+    def __str__(self):
+        result = "Module %s\n" % (self.name)
+        l = self.classes.values()
+        l.sort()
+        for attr in l:
+            if attr.is_active():
+                result += "    %s\n" % attr
+
+        l = list(self.constants)
+        l.sort()
+        result += 'Constants:\n'
+        for attr, type in l:
+            result += " %s\n" % attr
+
+        return result
+
+    init_string = ''
+    def initialization(self):
+        result = self.init_string + """
+talloc_set_log_fn((void(*)(const char*))printf);
+//talloc_enable_leak_report();
+"""
+        for cls in self.classes.values():
+            if cls.is_active():
+                result += cls.initialise()
+
+        return result
+
+    def add_constant(self, constant, type="numeric"):
+        """ This will be called to add #define constant macros """
+        self.constants.add((constant, type))
+
+    def add_class(self, cls, handler):
+        self.classes[cls.class_name] = cls
+
+        ## Make a wrapper in the type dispatcher so we can handle
+        ## passing this class from/to python
+        type_dispatcher[cls.class_name] = handler
+
+    def private_functions(self):
+        """ Emits hard coded private functions for doing various things """
+        return """
+/* The following is a static array mapping CLASS() pointers to their
+python wrappers. This is used to allow the correct wrapper to be
+chosen depending on the object type found - regardless of the
+prototype.
+
+This is basically a safer way for us to cast the correct python type
+depending on context rather than assuming a type based on the .h
+definition. For example consider the function
+
+AFFObject Resolver.open(uri, mode)
+
+The .h file implies that an AFFObject object is returned, but this is
+not true as most of the time an object of a derived class will be
+returned. In C we cast the returned value to the correct type. In the
+python wrapper we just instantiate the correct python object wrapper
+at runtime depending on the actual returned type. We use this lookup
+table to do so.
+*/
+static int TOTAL_CLASSES=0;
+
+/* This is a global reference to this module so classes can call each
+   other.
+*/
+static PyObject *g_module = NULL;
+
+static struct python_wrapper_map_t {
+       Object class_ref;
+       PyTypeObject *python_type;
+} python_wrappers[%s];
+
+/** This is a generic wrapper type */
+typedef struct {
+  PyObject_HEAD
+  void *base;
+  void *ctx;
+} Gen_wrapper;
+
+/* Create the relevant wrapper from the item based on the lookup
+table.
+*/
+Gen_wrapper *new_class_wrapper(Object item) {
+   int i;
+   Gen_wrapper *result;
+   Object cls;
+
+   /* Hack to get the current null_context */
+   void* null_context;
+   int* tmp = talloc(NULL, int);
+   null_context = talloc_parent(tmp);
+   talloc_free(tmp);
+
+   // Return None for a NULL pointer
+   if(!item) {
+     Py_INCREF(Py_None);
+     return (Gen_wrapper *)Py_None;
+   }
+
+   // Search for subclasses
+   for(cls=(Object)item->__class__; cls != cls->__super__; cls=cls->__super__) {
+     for(i=0; i<TOTAL_CLASSES; i++) {
+       if(python_wrappers[i].class_ref == cls) {
+         PyErr_Clear();
+
+         result = (Gen_wrapper *)_PyObject_New(python_wrappers[i].python_type);
+         result->ctx = talloc_asprintf(NULL, "new_class_wrapper %%s@%%p",
+                                       NAMEOF(item), (void*)item);
+         result->base = (void *)item;
+
+         /* If its owned by the null_context it means that the function does
+            not want to own the memory - we therefore steal it so it gets freed
+            with the python object. */
+         if(talloc_parent(result->base) == null_context)
+                  talloc_steal(result->ctx, result->base);
+
+         return result;
+       }
+     }
+   }
+
+  PyErr_Format(PyExc_RuntimeError, "Unable to find a wrapper for object %%s", NAMEOF(item));
+  return NULL;
+}
+
+static PyObject *resolve_exception(char **error_buff) {
+  enum _error_type *type = aff4_get_current_error(error_buff);
+  switch(*type) {
+case EProgrammingError:
+    return PyExc_SystemError;
+case EKeyError:
+    return PyExc_KeyError;
+case ERuntimeError:
+    return PyExc_RuntimeError;
+case EWarning:
+    return PyExc_AssertionError;
+default:
+    return PyExc_RuntimeError;
+};
+}
+
+static int type_check(PyObject *obj, PyTypeObject *type) {
+   PyTypeObject *tmp;
+
+   // Recurse through the inheritance tree and check if the types are expected
+   if(obj)
+     for(tmp = obj->ob_type; tmp && tmp != &PyBaseObject_Type; tmp = tmp->tp_base) {
+       if(tmp == type) return 1;
+     }
+
+  return 0;
+}
+
+static int check_error() {
+   if(!CheckError(EZero)) {
+         char *buffer;
+         PyObject *exception = resolve_exception(&buffer);
+
+         PyErr_Format(exception, "%%s", buffer);
+         ClearError();
+         return 1;
+   }
+  return 0;
+}
+
+#define CHECK_ERROR if(check_error()) goto error;
+
+""" % (len(self.classes)+1)
+
+    def initialise_class(self, class_name, out, done = None):
+        if done and class_name in done: return
+
+        done.add(class_name)
+
+        cls = self.classes[class_name]
+        """ Write out class initialisation code into the main init function. """
+        if cls.is_active():
+            base_class = self.classes.get(cls.base_class_name)
+
+            if base_class and base_class.is_active():
+                ## We have a base class - ensure it gets written out
+                ## first:
+                self.initialise_class(cls.base_class_name, out, done)
+
+                ## Now assign ourselves as derived from them
+                out.write(" %s_Type.tp_base = &%s_Type;" % (
+                        cls.class_name, cls.base_class_name))
+            
+            out.write("""
+ %(name)s_Type.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&%(name)s_Type) < 0)
+     return;
+
+ Py_INCREF((PyObject *)&%(name)s_Type);
+ PyModule_AddObject(m, "%(name)s", (PyObject *)&%(name)s_Type);
+""" % {'name': cls.class_name})
+
+    def write(self, out):
+        ## Prepare all classes
+        for cls in self.classes.values():
+            cls.prepare()
+
+        out.write("""
+/**********************************************************************
+     Autogenerated module %s
+
+This module was autogenerated from the following files:
+""" % self.name)
+        for file in self.files:
+            out.write("%s\n" % file)
+
+        out.write("\nThis module implements the following classes:\n")
+        out.write(self.__str__())
+        out.write("""***********************************************************************/
+""")
+        out.write(self.headers)
+        out.write(self.private_functions())
+
+        for cls in self.classes.values():
+            if cls.is_active():
+                out.write("/******************** %s ***********************/" % cls.class_name)
+                cls.struct(out)
+                cls.prototypes(out)
+
+        out.write("/*****************************************************\n             Implementation\n******************************************************/\n\n")
+        for cls in self.classes.values():
+            if cls.is_active():
+                cls.PyMethodDef(out)
+                cls.code(out)
+                cls.PyTypeObject(out)
+
+        ## Write the module initializer
+        out.write("""
+static PyMethodDef %(module)s_methods[] = {
+     {NULL}  /* Sentinel */
+};
+
+PyMODINIT_FUNC init%(module)s(void) {
+   /* Make sure threads are enabled */
+   PyEval_InitThreads();
+
+   /* create module */
+   PyObject *m = Py_InitModule3("%(module)s", %(module)s_methods,
+                                   "%(module)s module.");
+   PyObject *d = PyModule_GetDict(m);
+   PyObject *tmp;
+
+   g_module = m;
+""" % {'module': self.name})
+
+        ## The trick is to initialise the classes in order of their
+        ## inheritance. The following code will order initializations
+        ## according to their inheritance tree
+        done = set()
+        for class_name in self.classes.keys():
+            self.initialise_class(class_name, out, done)
+
+        ## Add the constants in here
+        for constant, type in self.constants:
+            if type == 'integer':
+                out.write(""" tmp = PyLong_FromUnsignedLongLong((int64_t)%s); \n""" % constant)
+            elif type == 'string':
+                out.write(" tmp = PyString_FromString((char *)%s); \n" % constant)
+            else:
+                out.write(" // I dont know how to convert %s type %s\n" % (constant, type))
+                continue
+
+            out.write("""
+ PyDict_SetItemString(d, "%s", tmp);
+ Py_DECREF(tmp);\n""" % (constant))
+
+        out.write(self.initialization())
+        out.write("}\n\n")
+
+class Type:
+    interface = None
+    buildstr = 'O'
+    sense = 'IN'
+    error_value = "return 0;"
+    active = True
+
+    def __init__(self, name, type):
+        self.name = name
+        self.type = type
+        self.attributes = set()
+
+    def comment(self):
+        return "%s %s " % (self.type, self.name)
+
+    def python_name(self):
+        return self.name
+
+    def python_proxy_post_call(self):
+        """ This is called after a proxy call """
+        return ''
+
+    def returned_python_definition(self, *arg, **kw):
+        return self.definition(*arg, **kw)
+
+    def definition(self, default=None, **kw):
+        if default:
+            return "%s %s=%s;\n" % (self.type, self.name, default)
+        else:
+            return "%s __attribute__((unused)) %s;\n" % (self.type, self.name)
+
+    def byref(self):
+        return "&%s" % self.name
+
+    def call_arg(self):
+        return self.name
+
+    def pre_call(self, method):
+        return ''
+
+    def assign(self, call, method, target=None):
+        return "Py_BEGIN_ALLOW_THREADS\n%s = %s;\nPy_END_ALLOW_THREADS\n" % (target or self.name, call)
+
+    def post_call(self, method):
+        if "DESTRUCTOR" in self.attributes:
+            return "talloc_free(self->ctx); self->base = NULL;\n"
+
+        return ''
+
+    def from_python_object(self, source, destination, method, **kw):
+        return ''
+
+    def return_value(self, value):
+        return "return %s;" % value
+
+    def __str__(self):
+        if self.name == 'func_return':
+            return self.type
+        if 'void' in self.type:
+            return ''
+
+        result = "%s : %s" % (self.type, self.name)
+
+        return result
+
+class String(Type):
+    interface = 'string'
+    buildstr = 's'
+    error_value = "return NULL;"
+
+    def __init__(self, name, type):
+        Type.__init__(self, name, type)
+        self.length = "strlen(%s)" % name
+
+    def byref(self):
+        return "&%s" % self.name
+
+    def to_python_object(self, name=None, result='py_result',**kw):
+        name = name or self.name
+
+        result = """PyErr_Clear();
+   if(!%(name)s) { PyErr_Format(PyExc_RuntimeError, "%(name)s is NULL"); goto error; }
+   %(result)s = PyString_FromStringAndSize((char *)%(name)s, %(length)s);\nif(!%(result)s) goto error;
+""" % dict(name=name, result=result,length=self.length)
+
+        if "BORROWED" not in self.attributes and 'BORROWED' not in kw:
+            result += "talloc_unlink(NULL, %s);\n" % name
+
+        return result
+
+    def from_python_object(self, source, destination, method, context='NULL'):
+        method.error_set = True
+        return """
+{
+  char *buff; Py_ssize_t length;
+
+  PyErr_Clear();
+  if(-1==PyString_AsStringAndSize(%(source)s, &buff, &length))
+     goto error;
+
+  %(destination)s = talloc_size(%(context)s, length + 1);
+  memcpy(%(destination)s, buff, length);
+  %(destination)s[length]=0;
+}
+""" % dict(source = source, destination = destination, context =context)
+
+class ZString(String):
+    interface = 'null_terminated_string'
+
+class BorrowedString(String):
+    def to_python_object(self, name=None, result='py_result', **kw):
+        name = name or self.name
+        return "PyErr_Clear();\n" +\
+            "%s = PyString_FromStringAndSize((char *)%(name)s, %(length)s);\n" % dict(
+            name=name, length=self.length, result=result)
+
+class Char_and_Length(Type):
+    interface = 'char_and_length'
+    buildstr = 's#'
+    error_value = "return NULL;"
+
+    def __init__(self, data, data_type, length, length_type):
+        Type.__init__(self, data, data_type)
+
+        self.name = data
+        self.data_type=data_type
+        self.length = length
+        self.length_type = length_type
+
+    def comment(self):
+        return "%s %s, %s %s" % (self.data_type, self.name,
+                                 self.length_type, self.length)
+
+    def definition(self, default = '""', **kw):
+        return "char *%s=%s; Py_ssize_t %s=strlen(%s);\n" % (
+            self.name, default,
+            self.length, default)
+
+    def byref(self):
+        return "&%s, &%s" % (self.name, self.length)
+
+    def call_arg(self):
+        return "(%s)%s, (%s)%s" % (self.data_type, self.name, self.length_type,
+                                   self.length)
+
+    def to_python_object(self, name=None, result='py_result', **kw):
+        return "PyErr_Clear();\n"\
+            "%s = PyString_FromStringAndSize(%s, %s);\nif(!%s) goto error;" % (
+            result, self.name, self.length, result);
+
+class Integer(Type):
+    interface = 'integer'
+    buildstr = 'K'
+
+    def __init__(self, name,type):
+        Type.__init__(self,name,type)
+        self.type = 'uint64_t'
+        self.original_type = type
+
+    def definition(self, default = 0, **kw):
+        return Type.definition(self, default)
+
+    def to_python_object(self, name=None, result='py_result', **kw):
+        name = name or self.name
+        return "PyErr_Clear();\n%s = PyLong_FromLongLong(%s);\n" % (result, name)
+
+    def from_python_object(self, source, destination, method, **kw):
+        return "PyErr_Clear();\n"\
+            "%(destination)s = PyLong_AsUnsignedLong(%(source)s);\n" % dict(
+            source = source, destination= destination)
+
+    def comment(self):
+        return "%s %s " % (self.original_type, self.name)
+
+class Integer32(Integer):
+    buildstr = 'I'
+
+    def __init__(self, name,type):
+        Type.__init__(self,name,type)
+        self.type = 'unsigned int '
+        self.original_type = type
+
+    def to_python_object(self, name=None, result='py_result', **kw):
+        name = name or self.name
+        return "PyErr_Clear();\n%s = PyLong_FromLong(%s);\n" % (result, name)
+
+class Integer64(Integer):
+    buildstr = 'K'
+    type = 'unsigned int'
+
+class Char(Integer):
+    buildstr = "s"
+    interface = 'small_integer'
+
+    def to_python_object(self, name = None, result = 'py_result', **kw):
+        ## We really want to return a string here
+        return """{ char *str_%(name)s = &%(name)s;
+    PyErr_Clear();
+    %(result)s = PyString_FromStringAndSize(str_%(name)s, 1);
+if(!%(result)s) goto error;
+}
+""" % dict(result=result, name = name or self.name)
+
+    def definition(self, default = '"\\x0"', **kw):
+        ## Shut up unused warnings
+        return "char %s __attribute__((unused))=0;\nchar *str_%s __attribute__((unused)) = %s;\n" % (
+            self.name,self.name, default)
+
+    def byref(self):
+        return "&str_%s" % self.name
+
+    def pre_call(self, method):
+        method.error_set = True
+        return """
+if(strlen(str_%(name)s)!=1) {
+  PyErr_Format(PyExc_RuntimeError,
+          "You must only provide a single character for arg %(name)r");
+  goto error;
+}
+
+%(name)s = str_%(name)s[0];
+""" % dict(name = self.name)
+
+class StringOut(String):
+    sense = 'OUT'
+
+class IntegerOut(Integer):
+    sense = 'OUT_DONE'
+    buildstr = ''
+
+    def python_name(self):
+        return None
+
+    def byref(self):
+        return ''
+
+    def call_arg(self):
+        return "&%s" % self.name
+
+class Integer32Out(Integer32):
+    sense = 'OUT_DONE'
+    buildstr = ''
+
+    def python_name(self):
+        return None
+
+    def byref(self):
+        return ''
+
+    def call_arg(self):
+        return "&%s" % self.name
+
+class Char_and_Length_OUT(Char_and_Length):
+    sense = 'OUT_DONE'
+    buildstr = 'l'
+
+    def definition(self, default = 0, **kw):
+        return "char *%s=NULL; Py_ssize_t %s=%s;\n" % (
+            self.name,
+            self.length, default) + "PyObject *tmp_%s;\n" % self.name
+
+    def python_name(self):
+        return self.length
+
+    def byref(self):
+        return "&%s" % self.length
+
+    def pre_call(self, method):
+        return """PyErr_Clear();
+tmp_%s = PyString_FromStringAndSize(NULL, %s);
+if(!tmp_%s) goto error;
+PyString_AsStringAndSize(tmp_%s, &%s, (Py_ssize_t *)&%s);
+""" % (self.name, self.length, self.name, self.name, self.name, self.length)
+
+    def to_python_object(self, name=None, result='py_result', sense='in', **kw):
+        name = name or self.name
+        if 'results' in kw:
+            kw['results'].pop(0)
+
+        if sense=='proxied':
+            return "py_%s = PyLong_FromLong(%s);\n" % (self.name, self.length)
+
+        return  """if(func_return > %(length)s) {
+  func_return = 0;
+}
+
+_PyString_Resize(&tmp_%(name)s, func_return); \n%(result)s = tmp_%(name)s;\n""" % \
+            dict(name= name, result= result, length=self.length)
+
+
+    def python_proxy_post_call(self, result='py_result'):
+        return """
+{
+    char *tmp_buff; int tmp_len;
+    if(-1==PyString_AsStringAndSize(%(result)s, &tmp_buff, &tmp_len)) goto error;
+
+    memcpy(%(name)s,tmp_buff, tmp_len);
+    Py_DECREF(%(result)s);
+    %(result)s = PyLong_FromLong(tmp_len);
+}
+""" % dict(result = result, name=self.name)
+
+class TDB_DATA_P(Char_and_Length_OUT):
+    bare_type = "TDB_DATA"
+
+    def __init__(self, name, type):
+        Type.__init__(self, name, type)
+
+    def definition(self, default=None, **kw):
+        return Type.definition(self)
+
+    def byref(self):
+        return "%s.dptr, &%s.dsize" % (self.name, self.name)
+
+    def pre_call(self, method):
+        return ''
+
+    def call_arg(self):
+        return Type.call_arg(self)
+
+    def to_python_object(self, name=None,result='py_result', **kw):
+        name = name or self.name
+        return "PyErr_Clear();"\
+            "%s = PyString_FromStringAndSize((char *)%s->dptr, %s->dsize);"\
+            "\ntalloc_free(%s);" % (result,
+                                    name, name, name)
+
+    def from_python_object(self, source, destination, method, **kw):
+        method.error_set = True
+        return """
+%(destination)s = talloc(self, %(bare_type)s);
+{ Py_ssize_t tmp; char *buf;
+
+  PyErr_Clear();
+  if(-1==PyString_AsStringAndSize(%(source)s, &buf, &tmp)) {
+  goto error;
+}
+
+  // Take a copy of the python string
+  %(destination)s->dptr = talloc_memdup(%(destination)s, buf, tmp);
+  %(destination)s->dsize = tmp;
+}
+// We no longer need the python object
+Py_DECREF(%(source)s);
+""" % dict(source = source, destination = destination, 
+           bare_type = self.bare_type)
+
+class TDB_DATA(TDB_DATA_P):
+    def to_python_object(self, name = None, result='py_result', **kw):
+        name = name or self.name
+
+        return "PyErr_Clear();\n"\
+            "%s = PyString_FromStringAndSize((char *)%s.dptr, %s.dsize);\n" % (
+            result,
+            name, name)
+
+class Void(Type):
+    buildstr = ''
+    error_value = "return;"
+    original_type = ''
+
+    def __init__(self, *args):
+        Type.__init__(self, None, 'void *')
+
+    def definition(self, default = None, **kw):
+        return ''
+
+    def to_python_object(self, name=None, result = 'py_result', **kw):
+        return "Py_INCREF(Py_None); py_result = Py_None;\n"
+
+    def call_arg(self):
+        return "NULL"
+
+    def byref(self):
+        return None
+
+    def assign(self, call, method, target=None):
+        ## We dont assign the result to anything
+        return "Py_BEGIN_ALLOW_THREADS\n%s;\nPy_END_ALLOW_THREADS\n" % call
+
+    def return_value(self, value):
+        return "return;"
+
+class StringArray(String):
+    interface = 'array'
+    buildstr = 'O'
+
+    def definition(self, default = '""', **kw):
+        return "char **%s=NULL; PyObject *py_%s=NULL;\n" % (
+            self.name, self.name)
+
+    def byref(self):
+        return "&py_%s" % (self.name)
+
+    def from_python_object(self, source, destination, method, context='NULL'):
+        method.error_set = True
+        return """{
+Py_ssize_t i,size=0;
+
+if(%(source)s) {
+   if(!PySequence_Check(%(source)s)) {
+     PyErr_Format(PyExc_ValueError, "%(destination)s must be a sequence");
+     goto error;
+   }
+
+   size = PySequence_Size(%(source)s);
+}
+
+%(destination)s = talloc_zero_array(NULL, char *, size + 1);
+
+for(i=0; i<size;i++) {
+ PyObject *tmp = PySequence_GetItem(%(source)s, i);
+ if(!tmp) goto error;
+ %(destination)s[i] = PyString_AsString(tmp);
+ if(!%(destination)s[i]) {
+   Py_DECREF(tmp);
+   goto error;
+ }
+ Py_DECREF(tmp);
+}
+
+}""" % dict(source = source, destination = destination, context = context)
+
+    def pre_call(self, method):
+        return self.from_python_object("py_%s" % self.name, self.name, method)
+
+    def error_condition(self):
+        return """if(%s) talloc_free(%s);\n""" % (self.name, self.name)
+
+class Wrapper(Type):
+    """ This class represents a wrapped C type """
+    sense = 'IN'
+    error_value = "return NULL;"
+
+    def from_python_object(self, source, destination, method, **kw):
+        return """
+/* First check that the returned value is in fact a Wrapper */
+if(!type_check(%(source)s, &%(type)s_Type)) {
+  PyErr_Format(PyExc_RuntimeError, "function must return an %(type)s instance");
+  goto error;
+}
+
+%(destination)s = ((Gen_wrapper *)%(source)s)->base;
+""" % dict(source = source, destination = destination, type = self.type)
+
+    def to_python_object(self, **kw):
+        return ''
+
+    def returned_python_definition(self, default = 'NULL', sense='in', **kw):
+        return "%s %s;\n" % (self.type, self.name)
+
+    def definition(self, default = 'NULL', sense='in', **kw):
+        result = "Gen_wrapper *%s __attribute__((unused)) = %s;\n" % (self.name, default)
+        if sense == 'in' and not 'OUT' in self.attributes:
+            result += " %s __attribute__((unused)) call_%s;\n" % (self.type, self.name)
+
+        return result
+
+    def call_arg(self):
+        return "call_%s" % self.name
+
+    def pre_call(self, method):
+        if 'OUT' in self.attributes or self.sense == 'OUT':
+            return ''
+
+        return """
+if(!%(name)s || (PyObject *)%(name)s==Py_None) {
+   call_%(name)s = NULL;
+} else if(!type_check((PyObject *)%(name)s,&%(type)s_Type)) {
+     PyErr_Format(PyExc_RuntimeError, "%(name)s must be derived from type %(type)s");
+     goto error;
+} else {
+   call_%(name)s = %(name)s->base;
+}\n""" % self.__dict__
+
+    def assign(self, call, method, target=None):
+        method.error_set = True;
+        args = dict(name = target or self.name, call = call, type = self.type)
+
+        result = """{
+       Object returned_object;
+
+       ClearError();
+
+       Py_BEGIN_ALLOW_THREADS
+       returned_object = (Object)%(call)s;
+       Py_END_ALLOW_THREADS
+
+       CHECK_ERROR;
+""" % args
+
+        ## Is NULL an acceptable return type? In some python code NULL
+        ## can be returned (e.g. in iterators) but usually it should
+        ## be converted to None.
+        if "NULL_OK" in self.attributes:
+            result += """if(returned_object == NULL) {
+     %(name)s = NULL; """ % args
+        else:
+            result += """
+       // A NULL return without errors means we return None
+       if(!returned_object) {
+         %(name)s = (Gen_wrapper *)Py_None;
+         Py_INCREF(Py_None);
+""" % args
+
+        result += """
+       } else {
+         //printf("%%s: Wrapping %%s@%%p\\n", __FUNCTION__, NAMEOF(returned_object), returned_object);
+         %(name)s = new_class_wrapper(returned_object);
+         if(!%(name)s) goto error;
+""" % args
+
+        if "BORROWED" in self.attributes:
+            result += "           talloc_reference(%(name)s->ctx, %(name)s->base);\n" % args
+
+        result += """       }
+    }
+"""
+        return result
+
+    def to_python_object(self, name=None, result = 'py_result', sense='in', **kw):
+        name = name or self.name
+        args = dict(result=result,
+                    name = name)
+
+        if sense=='proxied':
+            return "%(result)s = (PyObject *)new_class_wrapper((Object)%(name)s);\n" % args
+
+        return "%(result)s = (PyObject *)%(name)s;\n" % args
+
+class PointerWrapper(Wrapper):
+    """ A pointer to a wrapped class """
+    def __init__(self, name, type):
+        type = type.split()[0]
+        Wrapper.__init__(self,name, type)
+
+    def definition(self, default = 'NULL', sense='in', **kw):
+        result = "Gen_wrapper *%s = %s;" % (self.name, default)
+        if sense == 'in' and not 'OUT' in self.attributes:
+            result += " %s *call_%s;\n" % (self.type, self.name)
+
+        return result
+
+    def pre_call(self, method):
+        if 'OUT' in self.attributes or self.sense == 'OUT':
+            return ''
+
+        return """
+if(!%(name)s || (PyObject *)%(name)s==Py_None) {
+   call_%(name)s = NULL;
+} else if(!type_check((PyObject *)%(name)s,&%(type)s_Type)) {
+     PyErr_Format(PyExc_RuntimeError, "%(name)s must be derived from type %(type)s");
+     goto error;
+} else {
+   call_%(name)s = (%(type)s *)&%(name)s->base;
+}\n""" % self.__dict__
+
+class StructWrapper(Wrapper):
+    """ A wrapper for struct classes """
+    def assign(self, call, method, target = None):
+        args = dict(name = target or self.name, call = call, type = self.type)
+        result = """
+PyErr_Clear();
+%(name)s = (Gen_wrapper *)PyObject_New(py%(type)s, &%(type)s_Type);
+%(name)s->ctx = talloc_size(NULL, 1);
+%(name)s->base = %(call)s;
+""" % args
+
+        if "NULL_OK" in self.attributes:
+            result += "if(!%(name)s->base) { Py_DECREF(%(name)s); return NULL; }" % args
+
+        result += """
+// A NULL object gets translated to a None
+ if(!%(name)s->base) {
+   Py_DECREF(%(name)s);
+   Py_INCREF(Py_None);
+   %(name)s = (Gen_wrapper *)Py_None;
+ } else {
+""" % args
+
+        if "FOREIGN" in self.attributes:
+            result += '// Not taking references to foreign memory\n'
+        elif "BORROWED" in self.attributes:
+            result += "talloc_reference(%(name)s->ctx, %(name)s->base);\n" % args
+        else:
+            result += "talloc_steal(%(name)s->ctx, %(name)s->base);\n" % args
+
+        result += "}\n"
+
+        return result
+
+    def byref(self):
+        return "&%s" % self.name
+
+    def definition(self, default = 'NULL', sense='in', **kw):
+        result = "Gen_wrapper *%s = %s;" % (self.name, default)
+        if sense == 'in' and not 'OUT' in self.attributes:
+            result += " %s *call_%s;\n" % (self.type, self.name)
+
+        return result;
+
+class PointerStructWrapper(StructWrapper):
+    def __init__(self, name, type):
+        type = type.split()[0]
+        Wrapper.__init__(self,name, type)
+
+class Timeval(Type):
+    """ handle struct timeval values """
+    interface = 'numeric'
+    buildstr = 'f'
+
+    def definition(self, default = None, **kw):
+        return "float %(name)s_flt; struct timeval %(name)s;\n" % self.__dict__
+
+    def byref(self):
+        return "&%s_flt" % self.name
+
+    def pre_call(self, method):
+        return "%(name)s.tv_sec = (int)%(name)s_flt; %(name)s.tv_usec = (%(name)s_flt - %(name)s.tv_sec) * 1e6;\n" % self.__dict__
+
+    def to_python_object(self, name=None, result = 'py_result', **kw):
+        name = name or self.name
+        return """%(name)s_flt = (double)(%(name)s.tv_sec) + %(name)s.tv_usec;
+%(result)s = PyFloat_FromDouble(%(name)s_flt);
+""" % dict(name = name, result=result)
+
+class PyObject(Type):
+    """ Accept an opaque python object """
+    interface = 'opaque'
+    buildstr = 'O'
+    def definition(self, default = 'NULL', **kw):
+        self.default = default
+        return 'PyObject *%(name)s = %(default)s;\n' % self.__dict__
+
+    def byref(self):
+        return "&%s" % self.name
+
+type_dispatcher = {
+    "IN char *": String,
+    "IN unsigned char *": String,
+    "unsigned char *": String,
+    "char *": String,
+    "ZString": ZString,
+
+    "OUT char *": StringOut,
+    "OUT unsigned char *": StringOut,
+    "unsigned int": Integer,
+    'int': Integer,
+    'OUT uint64_t *': IntegerOut,
+    'OUT uint32_t *': Integer32Out,
+    'char': Char,
+    'void': Void,
+    'void *': Void,
+
+    'TDB_DATA *': TDB_DATA_P,
+    'TDB_DATA': TDB_DATA,
+    'uint64_t': Integer,
+    'uint32_t': Integer32,
+    'time_t': Integer32,
+    'uint16_t': Integer,
+    'uint8_t': Integer,
+    'int64_t': Integer,
+    'ssize_t': Integer,
+    'size_t': Integer,
+    'unsigned long int': Integer,
+    'struct timeval': Timeval,
+    'char **': StringArray,
+
+    'PyObject *': PyObject,
+    }
+
+method_attributes = ['BORROWED', 'DESTRUCTOR','IGNORE']
+
+def dispatch(name, type):
+    if not type: return Void()
+
+    m = re.match("struct ([a-zA-Z0-9]+)_t *", type)
+    if m:
+        type = m.group(1)
+
+    type_components = type.split()
+    attributes = set()
+
+    if type_components[0] in method_attributes:
+        attributes.add(type_components.pop(0))
+
+    type = " ".join(type_components)
+    result = type_dispatcher[type](name, type)
+
+    result.attributes = attributes
+
+    return result
+
+class ResultException:
+    value = 0
+    exception = "PyExc_IOError"
+
+    def __init__(self, check, exception, message):
+        self.check = check
+        self.exception = exception
+        self.message = message
+
+    def write(self, out):
+        out.write("\n//Handle exceptions\n")
+        out.write("if(%s) {\n    PyErr_Format(PyExc_%s, %s);\n  goto error; \n}\n\n" % (
+                self.check, self.exception, self.message))
+
+class Method:
+    default_re = re.compile("DEFAULT\(([A-Z_a-z0-9]+)\) =(.+)")
+    exception_re = re.compile("RAISES\(([^,]+),\s*([^\)]+)\) =(.+)")
+    typedefed_re = re.compile(r"struct (.+)_t \*")
+
+    def __init__(self, class_name, base_class_name, method_name, args, return_type,
+                 myclass = None):
+        self.name = method_name
+        ## myclass needs to be a class generator
+        if not isinstance(myclass, ClassGenerator): raise RuntimeError("myclass must be a class generator")
+
+        self.myclass = myclass
+        self.docstring = ''
+        self.defaults = {}
+        self.exception = None
+        self.error_set = False
+        self.class_name = class_name
+        self.base_class_name = base_class_name
+        self.args = []
+        self.definition_class_name = class_name
+        for type,name in args:
+            self.add_arg(type, name)
+
+        try:
+            self.return_type = dispatch('func_return', return_type)
+            self.return_type.attributes.add("OUT")
+            self.return_type.original_type = return_type
+        except KeyError:
+            ## Is it a wrapped type?
+            if return_type:
+                log("Unable to handle return type %s.%s %s" % (self.class_name, self.name, return_type))
+                #pdb.set_trace()
+            self.return_type = Void()
+
+    def clone(self, new_class_name):
+        self.find_optional_vars()
+
+        result = self.__class__(new_class_name, self.base_class_name, self.name,
+                                [], 'void *',
+                                myclass = self.myclass)
+        result.args = self.args
+        result.return_type = self.return_type
+        result.definition_class_name = self.definition_class_name
+        result.defaults = self.defaults
+        result.exception = self.exception
+
+        return result
+
+    def find_optional_vars(self):
+        for line in self.docstring.splitlines():
+            m =self.default_re.search(line)
+            if m:
+                name = m.group(1)
+                value = m.group(2)
+                log("Setting default value for %s of %s" % (m.group(1),
+                                                            m.group(2)))
+                self.defaults[name] = value
+
+            m =self.exception_re.search(line)
+            if m:
+                self.exception = ResultException(m.group(1), m.group(2), m.group(3))
+
+    def write_local_vars(self, out):
+        self.find_optional_vars()
+
+        ## We do it in two passes - first mandatory then optional
+        kwlist = """static char *kwlist[] = {"""
+        ## Mandatory
+        for type in self.args:
+            python_name = type.python_name()
+            if python_name and python_name not in self.defaults:
+                kwlist += '"%s",' % python_name
+
+        for type in self.args:
+            python_name = type.python_name()
+            if python_name and python_name in self.defaults:
+                kwlist += '"%s",' % python_name
+
+        kwlist += ' NULL};\n'
+
+        for type in self.args:
+            python_name = type.python_name()
+            try:
+                out.write(type.definition(default = self.defaults[python_name]))
+            except KeyError:
+                out.write(type.definition())
+
+        ## Make up the format string for the parse args in two pases
+        parse_line = ''
+        for type in self.args:
+            python_name = type.python_name()
+            if type.buildstr and python_name not in self.defaults:
+                parse_line += type.buildstr
+
+        parse_line += '|'
+        for type in self.args:
+            python_name = type.python_name()
+            if type.buildstr and python_name in self.defaults:
+                parse_line += type.buildstr
+
+        if parse_line != '|':
+            ## Now parse the args from python objects
+            out.write(kwlist)
+            out.write("\nif(!PyArg_ParseTupleAndKeywords(args, kwds, \"%s\", kwlist, " % parse_line)
+            tmp = []
+            for type in self.args:
+                ref = type.byref()
+                if ref:
+                    tmp.append(ref)
+
+            out.write(",".join(tmp))
+            self.error_set = True
+            out.write("))\n goto error;\n\n")
+
+    def error_condition(self):
+        result = ""
+        if "DESTRUCTOR" in self.return_type.attributes:
+            result += "talloc_free(self->ctx); self->base = NULL;\n"
+
+        return result +"return NULL;\n";
+
+    def write_definition(self, out):
+        args = dict(method = self.name, class_name = self.class_name)
+        out.write("\n/********************************************************\nAutogenerated wrapper for function:\n")
+        out.write(self.comment())
+        out.write("********************************************************/\n")
+
+        self._prototype(out)
+        out.write("""{
+       PyObject *returned_result, *py_result;
+""" % args)
+
+        out.write(self.return_type.definition())
+
+        self.write_local_vars( out);
+
+        out.write("""// Make sure that we have something valid to wrap
+if(!self->base) return PyErr_Format(PyExc_RuntimeError, "%(class_name)s object no longer valid");
+""" % args)
+
+        ## Precall preparations
+        out.write("// Precall preparations\n")
+        out.write(self.return_type.pre_call(self))
+        for type in self.args:
+            out.write(type.pre_call(self))
+
+        out.write("""// Check the function is implemented
+  {  void *method = ((%(def_class_name)s)self->base)->%(method)s;
+     if(!method || (void *)unimplemented == (void *)method) {
+         PyErr_Format(PyExc_RuntimeError, "%(class_name)s.%(method)s is not implemented");
+         goto error;
+     }
+  }
+""" % dict(def_class_name = self.definition_class_name, method=self.name,
+           class_name = self.class_name))
+
+        out.write("\n// Make the call\n ClearError();")
+        call = "((%s)self->base)->%s(((%s)self->base)" % (self.definition_class_name, self.name, self.definition_class_name)
+        tmp = ''
+        for type in self.args:
+            tmp += ", " + type.call_arg()
+
+        call += "%s)" % tmp
+
+        ## Now call the wrapped function
+        out.write(self.return_type.assign(call, self))
+        if self.exception:
+            self.exception.write(out)
+
+        self.error_set = True
+
+        out.write("\n// Postcall preparations\n")
+        ## Postcall preparations
+        out.write(self.return_type.post_call(self))
+        for type in self.args:
+            out.write(type.post_call(self))
+
+        ## Now assemble the results
+        results = [self.return_type.to_python_object()]
+        for type in self.args:
+            if type.sense == 'OUT_DONE':
+                results.append(type.to_python_object(results = results))
+
+        ## If all the results are returned by reference we dont need
+        ## to prepend the void return value at all.
+        if isinstance(self.return_type, Void) and len(results)>1:
+            results.pop(0)
+
+        out.write("\n// prepare results\n")
+        ## Make a tuple of results and pass them back
+        if len(results)>1:
+            out.write("returned_result = PyList_New(0);\n")
+            for result in results:
+                out.write(result)
+                out.write("PyList_Append(returned_result, py_result); Py_DECREF(py_result);\n");
+            out.write("return returned_result;\n")
+        else:
+            out.write(results[0])
+            ## This useless code removes compiler warnings
+            out.write("returned_result = py_result;\nreturn returned_result;\n");
+
+        ## Write the error part of the function
+        if self.error_set:
+            out.write("\n// error conditions:\n")
+            out.write("error:\n    " + self.error_condition());
+
+        out.write("\n}\n\n")
+
+    def add_arg(self, type, name):
+        try:
+            t = type_dispatcher[type](name, type)
+        except KeyError:
+            ## Sometimes types must be typedefed in advance
+            try:
+                m = self.typedefed_re.match(type)
+                type = m.group(1)
+                log( "Trying %s for %s" % (type, m.group(0)))
+                t = type_dispatcher[type](name, type)
+            except (KeyError, AttributeError):
+                log( "Unable to handle type %s.%s %s" % (self.class_name, self.name, type))
+                return
+
+        ## Here we collapse char * + int type interfaces into a
+        ## coherent string like interface.
+        try:
+            previous = self.args[-1]
+            if t.interface == 'integer' and \
+                    previous.interface == 'string':
+
+                ## We make a distinction between IN variables and OUT
+                ## variables
+                if previous.sense == 'OUT':
+                    cls = Char_and_Length_OUT
+                else:
+                    cls = Char_and_Length
+
+
+                cls = cls(
+                    previous.name,
+                    previous.type,
+                    name, type)
+
+                self.args[-1] = cls
+
+                return
+        except IndexError:
+            pass
+
+        self.args.append(t)
+
+    def comment(self):
+        result = self.return_type.original_type+" "+self.class_name+"."+self.name+"("
+        args = []
+        for type in self.args:
+            args.append( type.comment())
+
+        result += ",".join(args) + ");\n"
+
+        return result
+
+    def prototype(self, out):
+        self._prototype(out)
+        out.write(";\n")
+
+    def _prototype(self, out):
+        out.write("""static PyObject *py%(class_name)s_%(method)s(py%(class_name)s *self, PyObject *args, PyObject *kwds) """ % dict(method = self.name, class_name = self.class_name))
+
+    def __str__(self):
+        result = "def %s %s(%s):" % (
+            self.return_type,
+            self.name, ' , '.join([a.__str__() for a in self.args]))
+        return result
+
+    def PyMethodDef(self, out):
+        docstring = self.comment() + "\n\n" + self.docstring
+        out.write('     {"%s",(PyCFunction)py%s_%s, METH_VARARGS|METH_KEYWORDS, "%s"},\n' % (
+                self.name,
+                self.class_name,
+                self.name, escape_for_string(docstring)))
+
+class IteratorMethod(Method):
+    """ A Method which implements an iterator """
+    def __init__(self, *args, **kw):
+        Method.__init__(self, *args, **kw)
+
+        ## Tell the return type that a NULL python return is ok
+        self.return_type.attributes.add("NULL_OK")
+
+    def _prototype(self, out):
+        out.write("""static PyObject *py%(class_name)s_%(method)s(py%(class_name)s *self)""" % dict(method = self.name, class_name = self.class_name))
+
+    def __str__(self):
+        result = "Iterator returning %s." % (
+            self.return_type)
+        return result
+
+    def PyMethodDef(self, out):
+        ## This method should not go in the method table as its linked
+        ## in directly
+        pass
+
+class SelfIteratorMethod(IteratorMethod):
+    def write_definition(self, out):
+        args = dict(method = self.name, class_name = self.class_name)
+        out.write("\n/********************************************************\nAutogenerated wrapper for function:\n")
+        out.write(self.comment())
+        out.write("********************************************************/\n")
+
+        self._prototype(out)
+        out.write("""{
+          ((%(class_name)s)self->base)->%(method)s((%(class_name)s)self->base);
+          return PyObject_SelfIter((PyObject *)self);
+}
+""" % args)
+
+class ConstructorMethod(Method):
+    ## Python constructors are a bit different than regular methods
+    def _prototype(self, out):
+        out.write("""
+static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds)
+""" % dict(method = self.name, class_name = self.class_name))
+
+    def write_destructor(self, out):
+        ## We make sure that we unlink exactly the reference we need
+        ## (the object will persist if it has some other
+        ## references). Note that if we just used talloc_free here it
+        ## will remove some random reference which may not actually be
+        ## the reference we own (which is NULL).
+        free = """
+    if(self->base) {
+        //printf("Unlinking %s@%p\\n", NAMEOF(self->base), self->base);
+        talloc_free(self->ctx);
+        self->base=NULL;
+    }
+"""
+        out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+%(free)s
+ PyObject_Del(self);
+}\n
+""" % dict(class_name = self.class_name, free=free))
+
+    def error_condition(self):
+        return "return -1;";
+
+    def write_definition(self, out):
+        self._prototype(out)
+        out.write("""{\n""")
+        self.write_local_vars(out)
+
+        ## Precall preparations
+        for type in self.args:
+            out.write(type.pre_call(self))
+
+        ## Now call the wrapped function
+        out.write("\nself->ctx = talloc_strdup(NULL, \"%s\");" % self.class_name)
+        out.write("\n ClearError();\nPy_BEGIN_ALLOW_THREADS\nself->base = CONSTRUCT(%s, %s, %s, self->ctx" % (
+                self.class_name,
+                self.definition_class_name,
+                self.name))
+        tmp = ''
+        for type in self.args:
+            tmp += ", " + type.call_arg()
+
+        self.error_set = True
+        out.write("""%s);\nPy_END_ALLOW_THREADS\n
+       if(!CheckError(EZero)) {
+         char *buffer;
+         PyObject *exception = resolve_exception(&buffer);
+
+         PyErr_Format(exception,
+                    "%%s", buffer);
+         ClearError();
+         goto error;
+  } else if(!self->base) {
+    PyErr_Format(PyExc_IOError, "Unable to construct class %s");
+    goto error;
+  }
+""" % (tmp, self.class_name))
+
+        out.write("  return 0;\n");
+
+        ## Write the error part of the function
+        if self.error_set:
+            out.write("error:\n    " + self.error_condition());
+
+        out.write("\n}\n\n")
+
+class GetattrMethod(Method):
+    def __init__(self, class_name, base_class_name, myclass):
+        self.base_class_name = base_class_name
+        self._attributes = []
+        self.error_set = True
+        self.return_type = Void()
+        self.myclass = myclass
+        self.rename_class_name(class_name)
+
+    def add_attribute(self, attr):
+        if attr.name:
+            self._attributes.append([self.class_name, attr])
+
+    def rename_class_name(self, new_name):
+        """ This allows us to rename the class_name at a later stage.
+        Required for late initialization of Structs whose name is not
+        know until much later on.
+        """
+        self.class_name = new_name
+        self.name = "py%s_getattr" % new_name
+        for x in self._attributes:
+            x[0] = new_name
+
+    def get_attributes(self):
+        for class_name, attr in self._attributes:
+            try:
+                if not self.myclass.module.classes[attr.type].active:
+                    continue
+            except KeyError: pass
+
+            yield class_name, attr
+
+    def __str__(self):
+        result = ""
+        for class_name, attr in self.get_attributes():
+            result += "    %s\n" % attr.__str__()
+
+        return result
+
+
+    def clone(self, class_name):
+        result = self.__class__(class_name, self.base_class_name, self.myclass)
+        result._attributes = self._attributes[:]
+
+        return result
+
+    def prototype(self, out):
+        if self.name:
+            out.write("""
+static PyObject *%(name)s(py%(class_name)s *self, PyObject *name);
+""" % self.__dict__)
+
+    def built_ins(self, out):
+        """ check for some built in attributes we need to support """
+        out.write("""  if(!strcmp(name, "__members__")) {
+     PyObject *result = PyList_New(0);
+     PyObject *tmp;
+     PyMethodDef *i;
+
+     if(!result) goto error;
+""")
+        ## Add attributes
+        for class_name, attr in self.get_attributes():
+            out.write(""" tmp = PyString_FromString("%(name)s");
+    PyList_Append(result, tmp); Py_DECREF(tmp);
+""" % dict(name = attr.name))
+
+        ## Add methods
+        out.write("""
+
+    for(i=%s_methods; i->ml_name; i++) {
+     tmp = PyString_FromString(i->ml_name);
+    PyList_Append(result, tmp); Py_DECREF(tmp);
+    }""" % self.class_name)
+
+        out.write("""
+     return result; 
+   }\n""")
+
+    def write_definition(self, out):
+        if not self.name: return
+        out.write("""
+static PyObject *py%(class_name)s_getattr(py%(class_name)s *self, PyObject *pyname) {
+  char *name;
+  // Try to hand it off to the python native handler first
+  PyObject *result = PyObject_GenericGetAttr((PyObject*)self, pyname);
+
+  if(result) return result;
+
+  PyErr_Clear();
+  // No - nothing interesting was found by python
+  name = PyString_AsString(pyname);
+
+  if(!self->base) return PyErr_Format(PyExc_RuntimeError, "Wrapped object (%(class_name)s.%(name)s) no longer valid");
+  if(!name) return NULL;
+""" % self.__dict__)
+
+        self.built_ins(out)
+
+        for class_name, attr in self.get_attributes():
+            ## what we want to assign
+            if self.base_class_name:
+                call = "(((%s)self->base)->%s)" % (class_name, attr.name)
+            else:
+                call = "(self->base->%s)" % (attr.name)
+
+            out.write("""
+if(!strcmp(name, "%(name)s")) {
+    PyObject *py_result;
+    %(python_def)s
+
+    %(python_assign)s
+    %(python_obj)s
+    return py_result;
+}""" % dict(name = attr.name, python_obj = attr.to_python_object(),
+             python_assign = attr.assign(call, self),
+             python_def = attr.definition(sense='out')))
+
+        out.write("""
+
+  return PyObject_GenericGetAttr((PyObject *)self, pyname);
+""" % self.__dict__)
+
+        ## Write the error part of the function
+        if self.error_set:
+            out.write("error:\n" + self.error_condition());
+
+        out.write("}\n\n")
+
+class ProxiedGetattr(GetattrMethod):
+    def built_ins(self,out):
+        out.write("""  if(!strcmp(name, "__members__")) {
+     PyObject *result;
+     PyObject *tmp;
+     PyMethodDef *i;
+
+     PyErr_Clear();
+     // Get the list of members from our proxied object
+     result  = PyObject_GetAttrString(self->base->proxied, name);
+     if(!result) goto error;
+""")
+        ## Add attributes
+        for class_name, attr in self.get_attributes():
+            out.write(""" tmp = PyString_FromString("%(name)s");
+    PyList_Append(result, tmp); Py_DECREF(tmp);
+""" % dict(name = attr.name))
+
+        ## Add methods
+        out.write("""
+
+    for(i=%s_methods; i->ml_name; i++) {
+     tmp = PyString_FromString(i->ml_name);
+    PyList_Append(result, tmp); Py_DECREF(tmp);
+    } """ % self.class_name)
+
+        out.write("""
+     return result;
+   }\n""")
+
+        out.write(""" /** Just try to get the attribute from our proxied object */  {
+   PyObject *result = PyObject_GetAttrString(self->base->proxied, name);
+   if(result) return result;
+} """)
+
+class ProxiedMethod(Method):
+    def __init__(self, method, myclass):
+        self.name = method.name
+        self.method = method
+        self.myclass = myclass
+        self.class_name = method.class_name
+        self.base_class_name = method.base_class_name
+        self.args = method.args
+        self.definition_class_name = method.definition_class_name
+        self.return_type = method.return_type
+        self.docstring = "Proxy for %s" % self.name
+        self.defaults = {}
+        self.exception = None
+        self.error_set = False
+
+    def get_name(self):
+        return "py%(class_name)s_%(name)s" % dict(class_name =self.myclass.class_name,
+                                                  name = self.name)
+
+    def _prototype(self, out):
+        out.write("""
+static %(return_type)s %(name)s(%(definition_class_name)s self""" % dict(
+                return_type = self.return_type.original_type,
+                class_name = self.myclass.class_name,
+                method = self.name,
+                name = self.get_name(),
+                definition_class_name = self.definition_class_name))
+
+        for arg in self.args:
+            out.write(", %s" % (arg.comment()))
+
+        out.write(")")
+
+    def prototype(self, out):
+        self._prototype(out)
+        out.write(";\n")
+
+    def write_definition(self, out):
+        self._prototype(out)
+        self._write_definition(out)
+
+    def _write_definition(self, out):
+        ## We need to grab the GIL before we do anything
+        out.write("""{
+      //Grab the GIL so we can do python stuff
+      PyGILState_STATE gstate;
+      gstate = PyGILState_Ensure();
+      """)
+
+        out.write("{\nPyObject *py_result=NULL;\n")
+        out.write('PyObject *method_name = PyString_FromString("%s");\n' % self.name)
+        out.write(self.return_type.returned_python_definition())
+
+        for arg in self.args:
+            out.write("PyObject *py_%s=NULL;\n" % arg.name)
+
+        out.write("\n//Obtain python objects for all the args:\n")
+        for arg in self.args:
+            out.write(arg.to_python_object(result = "py_%s" % arg.name,
+                                           sense='proxied', BORROWED=True))
+
+        out.write('if(!((%s)self)->proxied) {\n RaiseError(ERuntimeError, "No proxied object in %s"); goto error;\n}\n' % (self.myclass.class_name, self.myclass.class_name))
+
+        out.write("\n//Now call the method\n")
+        out.write("""PyErr_Clear();
+py_result = PyObject_CallMethodObjArgs(((%s)self)->proxied,method_name,""" % self.myclass.class_name)
+        for arg in self.args:
+            out.write("py_%s," % arg.name)
+
+        ## Sentinal
+        self.error_set = True
+        out.write("""NULL);
+
+/** Check for python errors */
+if(PyErr_Occurred()) {
+   PyObject *exception_t, *exception, *tb;
+   PyObject *str;
+   char *str_c;
+   char *error_str;
+   enum _error_type *error_type = aff4_get_current_error(&error_str);
+
+   // Fetch the exception state and convert it to a string:
+   PyErr_Fetch(&exception_t, &exception, &tb);
+
+   str = PyObject_Repr(exception);
+   str_c = PyString_AsString(str);
+
+   if(str_c) {
+      strncpy(error_str, str_c, BUFF_SIZE-1);
+      error_str[BUFF_SIZE-1]=0;
+      *error_type = ERuntimeError;
+   }
+   Py_DECREF(str);
+   goto error;
+}
+
+""");
+
+        for arg in self.args:
+            out.write(arg.python_proxy_post_call())
+
+        ## Now convert the python value back to a value
+        out.write(self.return_type.from_python_object('py_result',self.return_type.name, self, context = "self"))
+
+        out.write("if(py_result) { Py_DECREF(py_result);}\nPy_DECREF(method_name);\n\n");
+        out.write("PyGILState_Release(gstate);\n")
+
+        ## Decref all our python objects:
+        for arg in self.args:
+            out.write("if(py_%s) { Py_DECREF(py_%s);}\n" %( arg.name, arg.name))
+
+        out.write(self.return_type.return_value('func_return'))
+        if self.error_set:
+            out.write("\nerror:\n")
+            out.write("if(py_result) { Py_DECREF(py_result);}\nPy_DECREF(method_name);\n\n");
+            ## Decref all our python objects:
+            for arg in self.args:
+                out.write("if(py_%s) { Py_DECREF(py_%s);}\n" % (arg.name, arg.name))
+
+            out.write("PyGILState_Release(gstate);\n %s;\n" % self.error_condition())
+
+        out.write("   }\n}\n")
+
+    def error_condition(self):
+        return self.return_type.error_value
+
+class StructConstructor(ConstructorMethod):
+    """ A constructor for struct wrappers - basically just allocate
+    memory for the struct.
+    """
+    def write_definition(self, out):
+        out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {\n""" % dict(method = self.name, class_name = self.class_name))
+        out.write("\nself->ctx = talloc_strdup(NULL, \"%s\");" % self.class_name)
+        out.write("\nself->base = talloc(self->ctx, %s);\n" % self.class_name)
+        out.write("  return 0;\n}\n\n")
+
+    def write_destructor(self, out):
+        out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+   talloc_free(self->ctx);
+}\n
+""" % dict(class_name = self.class_name))
+
+class ProxyConstructor(ConstructorMethod):
+    def write_destructor(self, out):
+        out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+    if(self->base) {
+        // Release the proxied object
+        //Py_DECREF(self->base->proxied);
+        talloc_free(self->ctx);
+        self->base = NULL;
+    }
+}\n
+
+static int %(class_name)s_destructor(void *this) {
+  py%(class_name)s *self = (py%(class_name)s *)this;
+  Py_DECREF(self->base->proxied);
+  return 0;
+}
+""" % dict(class_name = self.class_name))
+
+    def initialise_attributes(self, out):
+        attributes = self.myclass.module.classes[self.base_class_name].attributes.get_attributes()
+        for definition_class_name, attribute in attributes:
+            out.write("""
+{
+  // Converting from %(attribute_name)s
+  PyErr_Clear();
+  PyObject *py_result = PyObject_GetAttrString(self->base->proxied, "%(name)s");
+
+  if(py_result) {
+       %(type)s tmp;
+       %(from_python_object)s;
+       ((%(definition_class_name)s)self->base)->%(name)s = tmp;
+       Py_DECREF(py_result);
+  }
+  PyErr_Clear();
+}""" % dict(definition = attribute.definition(), name=attribute.name,
+             attribute_name = attribute.__class__.__name__,
+             type = attribute.type,
+             definition_class_name = definition_class_name,
+             from_python_object = attribute.from_python_object(
+                        'py_result',"tmp", method=self,
+                        context = 'self->base')))
+
+
+    def write_constructor_proxy(self, out):
+        ## Get the base_class constructor
+        self.base_cons_method = ProxiedMethod(self.myclass.module.classes[self.base_class_name].constructor, self.myclass)
+
+        self.base_cons_method._prototype(out)
+        out.write("{\nPyObject *py_result;\n")
+        out.write('PyObject *method_name;')
+        out.write("%(class_name)s this = (%(class_name)s)self;\n" % self.__dict__)
+        out.write("PyGILState_STATE gstate;\ngstate = PyGILState_Ensure();\n")
+        out.write('method_name = PyString_FromString("__class__");\n')
+        for arg in self.base_cons_method.args:
+            out.write("PyObject *py_%s;\n" % arg.name)
+
+        out.write("\n//Obtain python objects for all the args:\n")
+        for arg in self.base_cons_method.args:
+            out.write(arg.to_python_object(result = "py_%s" % arg.name,
+                                           sense = 'proxied',
+                                           BORROWED=True))
+
+        out.write('if(!((%s)self)->proxied) {\n RaiseError(ERuntimeError, "No proxied object in %s"); goto error;\n}\n' % (self.myclass.class_name, self.myclass.class_name))
+
+        out.write("""
+// Enlarge the object size to accomodate the extended class
+self = talloc_realloc_size(self, self, sizeof(struct %(base_class_name)s_t));
+""" % self.__dict__)
+        out.write("\n//Now call the method\n")
+        out.write("PyErr_Clear();\npy_result = PyObject_CallMethodObjArgs(((%s)self)->proxied,method_name," % self.myclass.class_name)
+
+        call = ''
+        for arg in self.base_cons_method.args:
+            call += "py_%s," % arg.name
+
+        ## Sentinal
+        self.error_set = True
+        call += """NULL"""
+
+        out.write(call + ");\n");
+        out.write("""
+if(!py_result && PyCallable_Check(this->proxied)) {
+   PyErr_Clear();
+   py_result = PyObject_CallFunctionObjArgs(((%(name)s)self)->proxied, %(call)s);
+}
+
+/** Check for python errors */
+if(PyErr_Occurred()) {
+   PyObject *exception_t, *exception, *tb;
+   PyObject *str;
+   char *str_c;
+   char *error_str;
+   enum _error_type *error_type = aff4_get_current_error(&error_str);
+
+   // Fetch the exception state and convert it to a string:
+   PyErr_Fetch(&exception_t, &exception, &tb);
+
+   str = PyObject_Repr(exception);
+   str_c = PyString_AsString(str);
+
+   if(str_c) {
+      strncpy(error_str, str_c, BUFF_SIZE-1);
+      error_str[BUFF_SIZE-1]=0;
+      *error_type = ERuntimeError;
+   }
+   Py_DECREF(str);
+   goto error;
+}
+
+// Take over the proxied object now
+this->proxied = py_result;
+""" % dict(name=self.myclass.class_name, call = call));
+
+        ## Now we try to populate the C struct slots with proxies of
+        ## the python objects
+        for class_name, attr in self.myclass.module.classes[\
+            self.base_class_name].attributes.get_attributes():
+            out.write("""
+// Converting %(name)s from proxy:
+{
+   PyObject *py_result = PyObject_GetAttrString(this->proxied, "%(name)s");
+   if(py_result) {
+""" % dict(name = attr.name))
+            out.write("{ %s " % attr.definition())
+            out.write(attr.from_python_object("py_result",
+                                              "((%s)self)->%s" % (class_name, attr.name),
+                                              self))
+            out.write("}\nPy_DECREF(py_result);\n}\n}\n")
+
+        out.write("PyGILState_Release(gstate);\n")
+        out.write("\n\nreturn self;\n")
+        if self.error_set:
+            out.write("error:\n PyGILState_Release(gstate);\ntalloc_free(self); return NULL;\n")
+
+        out.write("}\n\n")
+
+    def write_definition(self, out):
+        self.write_constructor_proxy(out)
+        out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {
+      PyGILState_STATE gstate = PyGILState_Ensure();
+""" % dict(method = self.name, class_name = self.class_name))
+
+        self.write_local_vars(out)
+
+        ## Precall preparations
+        for type in self.args:
+            out.write(type.pre_call(self))
+
+        ## Make up the call
+        self.call = "talloc_memdup(NULL, &__%(class_name)s, sizeof(__%(class_name)s));\n" % self.__dict__
+
+        ## Now call the wrapped function
+        out.write("""
+ self->base = %(call)s
+
+// Take over a copy of the proxied object
+ self->base->proxied = proxied;
+
+ /* We take a reference to the proxied object, and the proxied base
+ class takes a reference. This way we (the python object) and the
+ proxied C class can be freed independantly and only when both are
+ freed the proxied object is freed.  */
+
+ //Py_INCREF(proxied);
+ Py_INCREF(proxied);
+ talloc_set_destructor((void*)self->base, %(class_name)s_destructor);
+""" % self.__dict__)
+
+        ## Install the handler for the constructor. FIXME - implement
+        ## shortcut bypassing here so if the proxied class is itself a
+        ## python binding to a C class and it does not override
+        ## anything, we call directly into the original C class method
+        ## instead of converting to python, and back.
+        out.write("((%(definition_class_name)s)self->base)->%(name)s = %(func)s;\n" % dict(
+                definition_class_name = self.base_cons_method.definition_class_name,
+                name = self.base_cons_method.name,
+                func = self.base_cons_method.get_name()))
+
+        ## Now iterate over all our methods and install handlers:
+        for method in self.myclass.methods:
+            out.write("""
+   if(1 || PyDict_GetItemString(proxied->ob_type->tp_dict, "%(name)s")) {
+     ((%(definition_class_name)s)self->base)->%(name)s = py%(class_name)s_%(name)s;
+   }
+""" % dict(definition_class_name = method.definition_class_name,
+           name = method.name,
+           class_name = self.myclass.class_name))
+
+        ## Now fill in all attributes from the proxied object. Since
+        ## the C struct attribute access is just memory access its
+        ## difficult to trap it and refill attributes dynamically from
+        ## the python object. Therefore for now we just read all
+        ## attributes initially and populate the C struct with them.
+        self.initialise_attributes(out)
+
+        out.write("\n   PyGILState_Release(gstate);\n  return 0;\n");
+
+        ## Write the error part of the function
+        if self.error_set:
+            out.write("error:\n  PyGILState_Release(gstate);\n  " + self.error_condition());
+
+        out.write("\n}\n\n")
+
+class EmptyConstructor(ConstructorMethod):
+    def write_definition(self, out):
+        out.write("""static int py%(class_name)s_init(py%(class_name)s *self, PyObject *args, PyObject *kwds) {\n""" % dict(method = self.name, class_name = self.class_name))
+        out.write("""return 0;}\n\n""")
+
+class ClassGenerator:
+    docstring = ''
+    def __init__(self, class_name, base_class_name, module):
+        self.class_name = class_name
+        self.methods = []
+        self.module = module
+        self.constructor = EmptyConstructor(class_name, base_class_name,
+                                             "Con", [], '', myclass = self)
+
+        self.base_class_name = base_class_name
+        self.attributes = GetattrMethod(self.class_name, self.base_class_name, self)
+        self.modifier = set()
+        self.active = True
+        self.iterator = None
+
+    def prepare(self):
+        """ This method is called just before we need to write the
+        output and allows us to do any last minute fixups.
+        """
+        pass
+
+    def __str__(self):
+        result = "#%s\n" % self.docstring
+
+        result += "Class %s(%s):\n" % (self.class_name, self.base_class_name)
+        result += " Constructor:%s\n" % self.constructor
+        result += " Attributes:\n%s\n" % self.attributes
+        result += " Methods:\n"
+        for a in self.methods:
+            result += "    %s\n" % a.__str__()
+
+        return result
+
+    def is_active(self):
+        """ Returns true if this class is active and should be generated """
+        if not self.active or self.modifier and \
+                ('PRIVATE' in self.modifier or 'ABSTRACT' in self.modifier):
+            log("%s is not active %s" % (self.class_name, self.modifier))
+            return False
+
+        return True
+
+    def clone(self, new_class_name):
+        """ Creates a clone of this class - usefull when implementing
+        class extensions
+        """
+        result = ClassGenerator(new_class_name, self.class_name, self.module)
+        result.constructor = self.constructor.clone(new_class_name)
+        result.methods = [ x.clone(new_class_name) for x in self.methods ]
+        result.attributes = self.attributes.clone(new_class_name)
+
+        return result
+
+    def add_attribute(self, attr_name, attr_type, modifier):
+        try:
+            if not self.module.classes[attr_type].is_active(): return
+        except KeyError: pass
+
+        try:
+            ## All attribute references are always borrowed - that
+            ## means we dont want to free them after accessing them
+            type_class = dispatch(attr_name, "BORROWED "+attr_type)
+        except KeyError:
+            log("Unknown attribute type %s for  %s.%s" % (attr_type,
+                                                          self.class_name,
+                                                          attr_name))
+            return
+
+        type_class.attributes.add(modifier)
+        self.attributes.add_attribute(type_class)
+
+    def add_constructor(self, method_name, args, return_type, docstring):
+        if method_name.startswith("Con"):
+            self.constructor = ConstructorMethod(self.class_name, self.base_class_name,
+                                                 method_name, args, return_type,
+                                                 myclass = self)
+            self.constructor.docstring = docstring
+
+    def struct(self,out):
+        out.write("""\ntypedef struct {
+  PyObject_HEAD
+  %(class_name)s base;
+  void *ctx;
+} py%(class_name)s;\n
+""" % dict(class_name=self.class_name))
+
+    def code(self, out):
+        if not self.constructor:
+            raise RuntimeError("No constructor found for class %s" % self.class_name)
+
+        self.constructor.write_destructor(out)
+        self.constructor.write_definition(out)
+        if self.attributes:
+            self.attributes.write_definition(out)
+
+        for m in self.methods:
+            m.write_definition(out)
+
+    def initialise(self):
+        return "python_wrappers[TOTAL_CLASSES].class_ref = (Object)&__%s;\n" \
+            "python_wrappers[TOTAL_CLASSES++].python_type = &%s_Type;\n" % (
+            self.class_name, self.class_name)
+
+    def PyMethodDef(self, out):
+        out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
+        for method in self.methods:
+            method.PyMethodDef(out)
+
+        out.write("     {NULL}  /* Sentinel */\n};\n")
+
+    def prototypes(self, out):
+        """ Write prototype suitable for .h file """
+        out.write("""staticforward PyTypeObject %s_Type;\n""" % self.class_name)
+        self.constructor.prototype(out)
+
+        if self.attributes:
+            self.attributes.prototype(out)
+        for method in self.methods:
+            method.prototype(out)
+
+    def numeric_protocol_int(self):
+        pass
+
+    def numeric_protocol_nonzero(self):
+        return """
+static int
+%(class_name)s_nonzero(py%(class_name)s *v)
+{
+        return v->base != 0;
+}
+""" % self.__dict__
+
+    def numeric_protocol(self, out):
+        args = {'class':self.class_name}
+        for type, func in [ ('nonzero', self.numeric_protocol_nonzero),
+                            ('int', self.numeric_protocol_int) ]:
+            definition = func()
+            if definition:
+                out.write(definition)
+                args[type] = "%s_%s" % (self.class_name,type)
+            else:
+                args[type] = '0'
+
+        out.write("""
+static PyNumberMethods %(class)s_as_number = {
+        (binaryfunc)    0,       /*nb_add*/
+        (binaryfunc)    0,       /*nb_subtract*/
+        (binaryfunc)    0,       /*nb_multiply*/
+                        0,       /*nb_divide*/
+                        0,       /*nb_remainder*/
+                        0,       /*nb_divmod*/
+                        0,       /*nb_power*/
+        (unaryfunc)     0,       /*nb_negative*/
+        (unaryfunc)     0,       /*tp_positive*/
+        (unaryfunc)     0,       /*tp_absolute*/
+        (inquiry)       %(nonzero)s,   /*tp_nonzero*/
+        (unaryfunc)     0,       /*nb_invert*/
+                        0,       /*nb_lshift*/
+        (binaryfunc)    0,       /*nb_rshift*/
+                        0,       /*nb_and*/
+                        0,       /*nb_xor*/
+                        0,       /*nb_or*/
+                        0,       /*nb_coerce*/
+         (unaryfunc)    %(int)s,       /*nb_int*/
+                        0,       /*nb_long*/
+                        0,       /*nb_float*/
+                        0,       /*nb_oct*/
+                        0,       /*nb_hex*/
+        0,                              /* nb_inplace_add */
+        0,                              /* nb_inplace_subtract */
+        0,                              /* nb_inplace_multiply */
+        0,                              /* nb_inplace_divide */
+        0,                              /* nb_inplace_remainder */
+        0,                              /* nb_inplace_power */
+        0,                              /* nb_inplace_lshift */
+        0,                              /* nb_inplace_rshift */
+        0,                              /* nb_inplace_and */
+        0,                              /* nb_inplace_xor */
+        0,                              /* nb_inplace_or */
+        0,                              /* nb_floor_divide */
+        0,                              /* nb_true_divide */
+        0,                              /* nb_inplace_floor_divide */
+        0,                              /* nb_inplace_true_divide */
+        0,                              /* nb_index */
+};
+"""  % args)
+        return "&%(class)s_as_number" % args
+
+    def PyTypeObject(self, out):
+        args = {'class':self.class_name, 'module': self.module.name, 
+                'iterator': 0,
+                'iternext': 0,
+                'tp_str': 0,
+                'getattr_func': 0,
+                'docstring': "%s: %s" % (self.class_name, 
+                                         escape_for_string(self.docstring))}
+
+        if self.attributes:
+            args['getattr_func'] = self.attributes.name
+
+        args['numeric_protocol'] = self.numeric_protocol(out)
+        if "ITERATOR" in self.modifier:
+            args['iterator'] = "PyObject_SelfIter"
+            args['iternext'] = "py%s_iternext" % self.class_name
+
+        if "SELF_ITER" in self.modifier:
+            args['iterator'] = 'py%s___iter__' % self.class_name
+
+        if "TP_STR" in self.modifier:
+            args['tp_str'] = 'py%s___str__' % self.class_name
+
+        out.write("""
+static PyTypeObject %(class)s_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,                         /* ob_size */
+    "%(module)s.%(class)s",               /* tp_name */
+    sizeof(py%(class)s),            /* tp_basicsize */
+    0,                         /* tp_itemsize */
+    (destructor)%(class)s_dealloc,/* tp_dealloc */
+    0,                         /* tp_print */
+    0,                         /* tp_getattr */
+    0,                         /* tp_setattr */
+    0,                         /* tp_compare */
+    0,                         /* tp_repr */
+    %(numeric_protocol)s,      /* tp_as_number */
+    0,                         /* tp_as_sequence */
+    0,                         /* tp_as_mapping */
+    0,                         /* tp_hash */
+    0,                         /* tp_call */
+    (reprfunc)%(tp_str)s,      /* tp_str */
+    (getattrofunc)%(getattr_func)s,                         /* tp_getattro */
+    0,                         /* tp_setattro */
+    0,                         /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,        /* tp_flags */
+    "%(docstring)s",     /* tp_doc */
+    0,	                       /* tp_traverse */
+    0,                         /* tp_clear */
+    0,                         /* tp_richcompare */
+    0,                         /* tp_weaklistoffset */
+    (getiterfunc)%(iterator)s,              /* tp_iter */
+    (iternextfunc)%(iternext)s,/* tp_iternext */
+    %(class)s_methods,         /* tp_methods */
+    0,                         /* tp_members */
+    0,                         /* tp_getset */
+    0,                         /* tp_base */
+    0,                         /* tp_dict */
+    0,                         /* tp_descr_get */
+    0,                         /* tp_descr_set */
+    0,                         /* tp_dictoffset */
+    (initproc)py%(class)s_init,      /* tp_init */
+    0,                         /* tp_alloc */
+    0,                         /* tp_new */
+};
+""" % args )
+
+class StructGenerator(ClassGenerator):
+    """ A wrapper generator for structs """
+    def __init__(self, class_name, module):
+        self.class_name = class_name
+        self.methods = []
+        self.module = module
+        self.base_class_name = None
+        self.active = False
+        self.modifier = set()
+        self.constructor = None
+        self.attributes = GetattrMethod(self.class_name, self.base_class_name, self)
+
+    def prepare(self):
+        ## This is needed for late stage initialization - sometimes
+        ## our class_name is not know until now.
+        if not self.constructor:
+            self.constructor = StructConstructor(self.class_name, self.base_class_name,
+                                                 'Con', [], "void", myclass = self)
+
+            self.attributes.rename_class_name(self.class_name)
+            for x in self.attributes._attributes:
+                x[1].attributes.add('FOREIGN')
+
+    def __str__(self):
+        result = "#%s\n" % self.docstring
+
+        result += "Struct %s:\n" % (self.class_name)
+        result += "%s\n" % self.attributes
+
+        return result
+
+    def struct(self, out):
+        out.write("""\ntypedef struct {
+  PyObject_HEAD
+  %(class_name)s *base;
+  void *ctx;
+} py%(class_name)s;\n
+""" % dict(class_name=self.class_name))
+
+    def initialise(self):
+        return ''
+
+class ProxyClassGenerator(ClassGenerator):
+    def __init__(self, class_name, base_class_name, module, *args, **kwargs):
+        ClassGenerator.__init__(self, class_name, base_class_name, module, *args, **kwargs)
+        self.constructor = ProxyConstructor(self.class_name,
+                                            self.base_class_name, '__init__',
+                                            [('PyObject *', 'proxied')],
+                                            'void', myclass = self)
+        self.module = module
+        self.attributes = ProxiedGetattr(self.class_name, self.base_class_name, self)
+
+
+    def initialise(self):
+        return "INIT_CLASS(%(class_name)s);\n" % self.__dict__ + ClassGenerator.initialise(self)
+
+    def struct(self, out):
+        out.write("""
+// The proxied type is an extension of the wrapped type with a pointer
+// to the proxied PyObject.
+CLASS(%(class_name)s, %(base_class_name)s)
+   uint32_t magic;
+   PyObject *proxied;
+END_CLASS
+
+VIRTUAL(%(class_name)s, %(base_class_name)s) {
+} END_VIRTUAL
+
+typedef struct {
+  PyObject_HEAD
+  %(class_name)s base;
+  void *ctx;
+} py%(class_name)s;\n
+""" % self.__dict__)
+
+    def PyMethodDef(self, out):
+        out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
+        ## For now no methods
+        out.write("     {NULL}  /* Sentinel */\n};\n")
+
+class parser:
+    class_re = re.compile(r"^([A-Z]+)?\s*CLASS\(([A-Z_a-z0-9]+)\s*,\s*([A-Z_a-z0-9]+)\)")
+    method_re = re.compile(r"^\s*([0-9A-Z_a-z ]+\s+\*?)METHOD\(([A-Z_a-z0-9]+),\s*([A-Z_a-z0-9]+),?")
+    enum_start_re = re.compile(r'enum ([0-9A-Z_a-z]+) {')
+    enum_re = re.compile(r'([0-9A-Z_a-z]+) = [^,]+,')
+    enum_end_re = re.compile('}')
+    arg_re = re.compile(r"\s*([0-9A-Z a-z_]+\s+\*?)([0-9A-Za-z_]+),?")
+    constant_re = re.compile(r"#define\s+([A-Z_0-9]+)\s+[^\s]+")
+    struct_re = re.compile(r"([A-Z]+)?\s+(typedef )?struct\s+([A-Z_a-z0-9]+)\s+{")
+    proxy_class_re = re.compile(r"^([A-Z]+)?\s*PROXY_CLASS\(([A-Za-z0-9]+)\)")
+    end_class_re = re.compile("END_CLASS")
+    attribute_re = re.compile(r"^\s*([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z]+)\s*;")
+    comment_re = re.compile(r"^\s*//")
+    comment_start_re = re.compile(r"/\*+")
+    comment_end_re = re.compile(r"\*+/")
+    blank_line_re = re.compile("\s+")
+    typedef_re = re.compile("typedef ([A-Za-z_0-9]+) +([A-Za-z_0-9]+) *;")
+    bind_re = re.compile("BIND\(([a-zA-Z_0-9]+)\);")
+    current_class = None
+
+    def __init__(self, module, verbosity=0):
+        self.module = module
+        self.current_comment = ''
+        self.verbosity = verbosity
+        self.state = 'DEFAULT'
+
+        ## this is a list of objects which are to be bound
+        self.to_bind = []
+
+        global DEBUG
+
+        DEBUG = verbosity
+
+        io = StringIO.StringIO("""
+// Base object
+CLASS(Object, Obj)
+END_CLASS
+""")
+        self.parse_fd(io, '')
+
+    def add_class(self, class_name, base_class_name, class_type, handler, docstring, modifier):
+        try:
+            self.current_class = self.module.classes[base_class_name].clone(class_name)
+        except (KeyError, AttributeError):
+            log("Base class %s is not defined !!!!" % base_class_name)
+            self.current_class = class_type(class_name, base_class_name, self.module)
+
+        ## Now add the new class to the module object
+        self.current_class.docstring = docstring
+        self.current_class.modifier = modifier
+        self.module.add_class(self.current_class, handler)
+
+    def parse_filenames(self, filenames):
+        ## Be quiet for the first pass as many problems will be
+        ## resolved on the second pass anyway.
+        global DEBUG
+        DEBUG = 0
+        for f in filenames:
+            self._parse(f)
+
+        DEBUG = self.verbosity
+        log("Second pass: Consolidating definitions")
+        for f in filenames:
+            self._parse(f)
+
+    def _parse(self, filename):
+        fd = open(filename)
+        self.parse_fd(fd, filename)
+        fd.close()
+
+        if filename not in self.module.files:
+              self.module.headers += '#include "%s"\n' % filename
+              self.module.files.append(filename)
+
+    def parse_fd(self, fd, filename):
+        self.current_class = None
+        self.current_comment = ''
+
+        while 1:
+            line = fd.readline()
+            if not line: break
+
+            ## Handle binds
+            m= self.bind_re.search(line)
+            if m:
+                print "Will bind %s" % m.group(1)
+                self.to_bind.append(m.group(1))
+                continue
+
+            ## Handle enums
+            if self.state == 'DEFAULT':
+                m = self.enum_start_re.search(line)
+                if m:
+                    self.state = 'enum'
+                    type_dispatcher[m.group(1)] = type_dispatcher['int']
+                    continue
+
+            elif self.state == 'enum':
+                m = self.enum_re.search(line)
+                if m:
+                    self.module.add_constant(m.group(1), 'integer')
+
+                if '}' in line:
+                    self.state = 'DEFAULT'
+                    continue
+
+            ## Handle c++ style comments //
+            m = self.comment_re.match(line)
+            if m:
+                self.current_comment = line[m.end():]
+                while 1:
+                    line = fd.readline()
+
+                    m = self.comment_re.match(line)
+                    if not m:
+                        break
+
+                    self.current_comment += line[m.end():]
+
+            ## Multiline C style comments
+            m = self.comment_start_re.search(line)
+            if m:
+                line = line[m.end():]
+                while 1:
+                    m = self.comment_end_re.search(line)
+                    if m:
+                        self.current_comment += line[:m.start()]
+                        line = fd.readline()
+                        break
+                    else:
+                        self.current_comment += line
+
+                    line = fd.readline()
+                    if not line: break
+
+            ## Handle simple typdefs (as if typedef uint64_t my_fancy_type;)
+            m = self.typedef_re.search(line)
+            if m:
+                ## We basically add a new type as a copy of the old
+                ## type
+                old, new = m.group(1), m.group(2)
+                if old in type_dispatcher:
+                    type_dispatcher[new] = type_dispatcher[old]
+
+            ## Handle constant #define
+            m = self.constant_re.search(line)
+            if m:
+                ## We need to determine if it is a string or integer
+                if re.search('"', line):
+                    ## Its a string
+                    self.module.add_constant(m.group(1), 'string')
+                else:
+                    self.module.add_constant(m.group(1), 'integer')
+
+            ## Wrap structs
+            m = self.struct_re.search(line)
+            if m:
+                modifier = m.group(1)
+                class_name = m.group(3)
+                base_class_name = None
+                ## Structs may be refered to as a pointer or absolute
+                ## - its the same thing ultimatley.
+
+                ## We only wrap structures which are explicitely bound
+                if (modifier and 'BOUND' in modifier) or \
+                        class_name in self.to_bind:
+                    self.add_class(class_name, base_class_name, StructGenerator, StructWrapper,
+                                   self.current_comment, modifier)
+                    type_dispatcher["%s *" % class_name] = PointerStructWrapper
+
+                continue
+
+            m = self.class_re.search(line)
+            if m:
+                ## We need to make a new class now... We basically
+                ## need to build on top of previously declared base
+                ## classes - so we try to find base classes, clone
+                ## them if possible:
+                modifier = m.group(1)
+                class_name = m.group(2)
+                base_class_name = m.group(3)
+                self.add_class(class_name, base_class_name, ClassGenerator, Wrapper,
+                               self.current_comment, modifier)
+                type_dispatcher["%s *" % class_name] = PointerWrapper
+
+                continue
+
+            ## Make a proxy class for python callbacks
+            m = self.proxy_class_re.search(line)
+            if m:
+                modifier = m.group(1)
+                base_class_name = m.group(2)
+                class_name = "Proxied%s" % base_class_name
+                try:
+                    proxied_class = self.module.classes[base_class_name]
+                except KeyError:
+                    raise RuntimeError("Need to create a proxy for %s but it has not been defined (yet). You must place the PROXIED_CLASS() instruction after the class definition" % base_class_name)
+                self.current_class = ProxyClassGenerator(class_name,
+                                                         base_class_name, self.module)
+                #self.current_class.constructor.args += proxied_class.constructor.args
+                self.current_class.docstring = self.current_comment
+
+                ## Create proxies for all these methods
+                for method in proxied_class.methods:
+                    self.current_class.methods.append(ProxiedMethod(method, self.current_class))
+
+                self.module.add_class(self.current_class, Wrapper)
+
+                ## Make sure that further lines are not interpreted as part of this class.
+                self.current_class = None
+
+            m = self.method_re.search(line)
+            if self.current_class and m:
+                args = []
+                method_name = m.group(3)
+                return_type = m.group(1).strip()
+                ## Ignore private methods
+                if return_type.startswith("PRIVATE"): continue
+
+                ## Now parse the args
+                offset = m.end()
+                while 1:
+                    m = self.arg_re.match(line[offset:])
+                    if not m:
+                        ## Allow multiline definitions if there is \\
+                        ## at the end of the line
+                        if line.strip().endswith("\\"):
+                            line = fd.readline()
+                            offset = 0
+                            if line:
+                                continue
+
+                        break
+
+                    offset += m.end()
+                    args.append([m.group(1).strip(), m.group(2).strip()])
+
+                if return_type == self.current_class.class_name and \
+                        method_name.startswith("Con"):
+                    self.current_class.add_constructor(method_name, args, return_type,
+                                                       self.current_comment)
+                else:
+                    self.current_class.add_method(method_name, args, return_type,
+                                                  self.current_comment)
+
+            m = self.attribute_re.search(line)
+            if self.current_class and m:
+                type = m.group(1)
+                name = m.group(2)
+                self.current_class.add_attribute(name, type)
+
+            m = self.end_class_re.search(line)
+            if m:
+                ## Just clear the current class context
+                self.current_class = None
+
+            ## If this is a shadow file we do not include it from the
+            ## main module. A shadow file is a way for us to redefine
+            ## other C constructs which will be imported from an
+            ## external header file so we can bind them slightly
+            ## differently.
+            if "this is a shadow file" in self.current_comment \
+                    and filename not in self.module.files:
+                self.module.files.append(filename)
+
+            ## We only care about comment immediately above methods
+            ## etc as we take them to be documentation. If we get here
+            ## the comment is not above anything we care about - so we
+            ## clear it:
+            self.current_comment = ''
+
+    def write(self, out):
+        self.module.write(out)
+
+
+import lexer
+
+class EnumConstructor(ConstructorMethod):
+    def write_destructor(self, out):
+        out.write("""static void
+%(class_name)s_dealloc(py%(class_name)s *self) {
+ Py_DECREF(self->value);
+ PyObject_Del(self);
+}\n
+""" % dict(class_name = self.class_name))
+    def write_definition(self, out):
+        self.myclass.modifier.add("TP_STR")
+        self._prototype(out)
+        out.write("""{
+static char *kwlist[] = {"value", NULL};
+
+if(!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &self->value))
+ goto error;
+
+Py_INCREF(self->value);
+
+  return 0;
+error:
+    return -1;
+}
+
+static PyObject *py%(class_name)s___str__(py%(class_name)s *self) {
+  PyObject *result = PyDict_GetItem(%(class_name)s_rev_lookup, self->value);
+
+  if(result) {
+     Py_INCREF(result);
+ } else {
+     result = PyObject_Str(self->value);
+ }
+
+ return result;
+}
+
+""" % self.__dict__)
+
+class Enum(StructGenerator):
+    def __init__(self, name, module):
+        StructGenerator.__init__(self, name, module)
+        self.values = []
+        self.name = name
+        self.attributes = None
+        self.active = True
+
+    def prepare(self):
+        self.constructor = EnumConstructor(self.class_name, self.base_class_name,
+                                           'Con', [], "void", myclass = self)
+        StructGenerator.prepare(self)
+
+    def __str__(self):
+        result = "Enum %s:\n" % (self.name)
+        for attr in self.values:
+            result += "    %s\n" % attr.__str__()
+
+        return result
+
+    def struct(self,out):
+        out.write("""\ntypedef struct {
+  PyObject_HEAD
+  PyObject *value;
+} py%(class_name)s;\n
+
+
+static PyObject *%(class_name)s_Dict_lookup;
+static PyObject *%(class_name)s_rev_lookup;
+""" % dict(class_name=self.class_name))
+
+    def PyMethodDef(self, out):
+        out.write("static PyMethodDef %s_methods[] = {\n" % self.class_name)
+        out.write("     {NULL}  /* Sentinel */\n};\n")
+
+    def numeric_protocol_nonzero(self):
+        pass
+
+    def numeric_protocol_int(self):
+        return """
+static PyObject *%(class_name)s_int(py%(class_name)s *self) {
+    Py_INCREF(self->value);
+    return self->value;
+}
+""" % self.__dict__
+
+    def initialise(self):
+        result = """
+%(class_name)s_Dict_lookup = PyDict_New();
+%(class_name)s_rev_lookup = PyDict_New();
+""" % self.__dict__
+
+        if self.values:
+            result += "{ PyObject *tmp, *tmp2;\n"
+            for attr in self.values:
+                result += ''' tmp = PyLong_FromLong(%(value)s);
+  tmp2 = PyString_FromString("%(value)s");
+  PyDict_SetItem(%(class_name)s_Dict_lookup, tmp2, tmp);
+  PyDict_SetItem(%(class_name)s_rev_lookup, tmp, tmp2);
+  Py_DECREF(tmp);
+  Py_DECREF(tmp2);
+
+''' % dict(value = attr, class_name=self.class_name)
+            result += "};\n"
+
+        return result
+
+class EnumType(Integer):
+    buildstr = 'i'
+
+    def __init__(self, name, type):
+        Integer.__init__(self, name, type)
+        self.type = type
+
+    def to_python_object(self, name=None, result='py_result', **kw):
+        name = name or self.name
+        return """PyErr_Clear();
+%s = PyObject_CallMethod(g_module, "%s", "K", (uint64_t)%s);
+""" % (result, self.type, name)
+
+    def pre_call(self, method):
+        method.error_set = True
+        return """
+// Check if the integer passed is actually a valid member of the enum
+// Enum value of 0 is always allowed
+if(%(name)s) { PyObject *py_%(name)s = PyLong_FromLong(%(name)s);
+  PyObject *tmp = PyDict_GetItem(%(type)s_rev_lookup, py_%(name)s);
+
+  Py_DECREF(py_%(name)s);
+  if(!tmp) {
+    PyErr_Format(PyExc_RuntimeError, "value %%lu is not valid for Enum %(type)s of arg '%(name)s'", (unsigned long)%(name)s);
+    goto error;
+  }
+  Py_DECREF(tmp);
+}
+""" % self.__dict__
+
+class HeaderParser(lexer.SelfFeederMixIn):
+    tokens = [
+        [ 'INITIAL', r'#define\s+', 'PUSH_STATE', 'DEFINE' ],
+        [ 'DEFINE', r'([A-Za-z_0-9]+)\s+[^\n]+', 'DEFINE,POP_STATE', None ],
+        [ 'DEFINE', r'\n', 'POP_STATE', None],
+
+        ## Recognize ansi c comments
+        [ '.', r'/\*(.)', 'PUSH_STATE', 'COMMENT' ],
+        [ 'COMMENT', r'(.+?)\*/\s+', 'COMMENT_END,POP_STATE', None],
+        [ 'COMMENT', r'(.+)', 'COMMENT', None],
+
+        ## And c++ comments
+        [ '.', r'//([^\n]+)', 'COMMENT', None],
+
+        ## An empty line clears the current comment
+        [ '.', r'\r?\n\r?\n', 'CLEAR_COMMENT', None],
+
+        ## Ignore whitespace
+        [ '.', r'\s+', 'SPACE', None ],
+        [ '.', r'\\\n', 'SPACE', None ],
+
+        ## Recognize CLASS() definitions
+        [ 'INITIAL', r"^([A-Z]+)?\s*CLASS\(([A-Z_a-z0-9]+)\s*,\s*([A-Z_a-z0-9]+)\)",
+                     'PUSH_STATE,CLASS_START', 'CLASS'],
+
+        [ 'CLASS', r"^\s*(FOREIGN|ABSTRACT|PRIVATE)?([0-9A-Z_a-z ]+( |\*))METHOD\(([A-Z_a-z0-9]+),\s*([A-Z_a-z0-9]+),?",
+                     "PUSH_STATE,METHOD_START", "METHOD"],
+        [ 'METHOD', r"\s*([0-9A-Z a-z_]+\s+\*?\*?)([0-9A-Za-z_]+),?", "METHOD_ARG", None ],
+        [ 'METHOD', r'\);', 'POP_STATE,METHOD_END', None],
+
+        [ 'CLASS', r"^\s*(FOREIGN|ABSTRACT)?([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z0-9]+)\s*;",
+                   'CLASS_ATTRIBUTE', None],
+        [ 'CLASS', "END_CLASS", 'END_CLASS,POP_STATE', None],
+
+        ## Recognize struct definitions (With name)
+        [ 'INITIAL', "([A-Z_a-z0-9 ]+)?struct\s+([A-Z_a-z0-9]+)\s+{",
+                     'PUSH_STATE,STRUCT_START', 'STRUCT'],
+
+        ## Without name (using typedef)
+        [ 'INITIAL', "typedef\s+struct\s+{",
+                     'PUSH_STATE,TYPEDEF_STRUCT_START', 'STRUCT'],
+
+        [ 'STRUCT', r"^\s*([0-9A-Z_a-z ]+\s+\*?)\s*([A-Z_a-z0-9]+)\s*;",
+                     'STRUCT_ATTRIBUTE', None],
+
+        [ 'STRUCT', r"^\s*([0-9A-Z_a-z ]+)\*\s+([A-Z_a-z0-9]+)\s*;",
+                     'STRUCT_ATTRIBUTE_PTR', None],
+
+        ## Struct ended with typedef
+        [ 'STRUCT', '}\s+([0-9A-Za-z_]+);', 'POP_STATE,TYPEDEF_STRUCT_END', None],
+        [ 'STRUCT', '}', 'POP_STATE,STRUCT_END', None],
+
+        ## Handle recursive struct or union definition (At the moment
+        ## we cant handle them at all)
+        [ '(RECURSIVE_)?STRUCT', '(struct|union)\s+([_A-Za-z0-9]+)?\s*{', 'PUSH_STATE', 'RECURSIVE_STRUCT'],
+        [ 'RECURSIVE_STRUCT', '}\s+[0-9A-Za-z]+', 'POP_STATE', None],
+
+        ## Process enums (2 forms - named and typedefed)
+        [ 'INITIAL', r'enum\s+([0-9A-Za-z_]+)\s+{', 'PUSH_STATE,ENUM_START', 'ENUM' ],
+        ## Unnamed
+        [ 'INITIAL', r'typedef\s+enum\s+{', 'PUSH_STATE,TYPEDEF_ENUM_START', 'ENUM' ],
+        [ 'ENUM', r'([0-9A-Za-z_]+)\s+=[^\n]+', 'ENUM_VALUE', None],
+
+        ## Typedefed ending
+        [ 'ENUM', r'}\s+([0-9A-Za-z_]+);', 'POP_STATE,TYPEDEFED_ENUM_END', None],
+        [ 'ENUM', r'}', 'POP_STATE,ENUM_END', None],
+
+        [ 'INITIAL', r'BIND_STRUCT\(([0-9A-Za-z_ \*]+)\)', 'BIND_STRUCT', None],
+
+        ## A simple typedef of one type for another type:
+        [ 'INITIAL', r"typedef ([A-Za-z_0-9]+) +([^;]+);", 'SIMPLE_TYPEDEF', None],
+
+        ## Handle proxied directives
+        [ 'INITIAL', r"PROXY_CLASS\(([A-Za-z0-9]+)\);", 'PROXY_CLASS', None],
+
+        ]
+
+    def __init__(self, name, verbose = 1):
+        self.module = Module(name)
+        lexer.SelfFeederMixIn.__init__(self, verbose = 0)
+
+        io = StringIO.StringIO("""
+// Base object
+CLASS(Object, Obj)
+END_CLASS
+""")
+        self.parse_fd(io)
+
+    current_comment = ''
+    def COMMENT(self, t, m):
+        self.current_comment += m.group(1) + "\n"
+
+    def COMMENT_END(self, t, m):
+        self.current_comment += m.group(1)
+
+    def CLEAR_COMMENT(self, t, m):
+        self.current_comment = ''
+
+    def DEFINE(self, t, m):
+        line = m.group(0)
+        line = line.split('/*')[0]
+        if '"' in line:
+            type = 'string'
+        else:
+            type = 'integer'
+
+        name = m.group(1).strip()
+        if len(name)>3 and name[0]!='_' and name==name.upper():
+            self.module.add_constant(name, type)
+
+    current_class = None
+    def CLASS_START(self, t, m):
+        class_name = m.group(2).strip()
+        base_class_name = m.group(3).strip()
+
+        try:
+            self.current_class = self.module.classes[base_class_name].clone(class_name)
+        except (KeyError, AttributeError):
+            log("Base class %s is not defined !!!!" % base_class_name)
+            self.current_class = ClassGenerator(class_name, base_class_name, self.module)
+
+        self.current_class.docstring = self.current_comment
+        self.current_class.modifier.add(m.group(1))
+        self.module.add_class(self.current_class, Wrapper)
+        type_dispatcher["%s *" % class_name] = PointerWrapper
+
+    current_method = None
+    def METHOD_START(self, t, m):
+        return_type = m.group(2).strip()
+        method_name = m.group(5).strip()
+        modifier = m.group(1) or ''
+
+        if 'PRIVATE' in modifier: return
+
+        ## Is it a regular method or a constructor?
+        self.current_method = Method
+        if return_type == self.current_class.class_name and \
+                method_name.startswith("Con"):
+            self.current_method = ConstructorMethod
+        elif method_name == 'iternext':
+            self.current_method = IteratorMethod
+            self.current_class.modifier.add("ITERATOR")
+        elif method_name == '__iter__':
+            self.current_method = SelfIteratorMethod
+            self.current_class.modifier.add("SELF_ITER")
+        elif method_name == '__str__':
+            self.current_class.modifier.add("TP_STR")
+
+        self.current_method = self.current_method(self.current_class.class_name,
+                                                  self.current_class.base_class_name,
+                                                  method_name, [], return_type,
+                                                  myclass = self.current_class)
+        self.current_method.docstring = self.current_comment
+        self.current_method.modifier = modifier
+
+    def METHOD_ARG(self, t, m):
+        name = m.group(2).strip()
+        type = m.group(1).strip()
+        if self.current_method:
+            self.current_method.add_arg(type, name)
+
+    def METHOD_END(self, t, m):
+        if not self.current_method: return
+
+        if isinstance(self.current_method, ConstructorMethod):
+            self.current_class.constructor = self.current_method
+        else:
+            found = False
+            for i in range(len(self.current_class.methods)):
+                ## Try to replace existing methods with this new method
+                method = self.current_class.methods[i]
+                if method.name == self.current_method.name:
+                    self.current_class.methods[i] = self.current_method
+                    self.current_method = None
+                    return
+
+            ## Method does not exist, just add to the end
+            self.current_class.methods.append(self.current_method)
+
+        self.current_method = None
+
+    def CLASS_ATTRIBUTE(self, t, m):
+        modifier = m.group(1) or ''
+        type = m.group(2).strip()
+        name = m.group(3).strip()
+        self.current_class.add_attribute(name, type, modifier)
+
+    def END_CLASS(self, t, m):
+        self.current_class = None
+
+    current_struct = None
+    def STRUCT_START(self, t, m):
+        self.current_struct = StructGenerator(m.group(2).strip(), self.module)
+        self.current_struct.docstring = self.current_comment
+        self.current_struct.modifier.add(m.group(1))
+
+    def TYPEDEF_STRUCT_START(self, t, m):
+        self.current_struct = StructGenerator(None, self.module)
+        self.current_struct.docstring = self.current_comment
+
+    def STRUCT_ATTRIBUTE(self, t, m):
+        name = m.group(2).strip()
+        type = m.group(1).strip()
+        self.current_struct.add_attribute(name, type, '')
+
+    def STRUCT_ATTRIBUTE_PTR(self, t, m):
+        type = "%s *" % m.group(1).strip()
+        name = m.group(2).strip()
+        self.current_struct.add_attribute(name, type, '')
+
+    def STRUCT_END(self, t, m):
+        self.module.add_class(self.current_struct, StructWrapper)
+        type_dispatcher["%s *" % self.current_struct.class_name] = PointerStructWrapper
+        self.current_struct = None
+
+    def TYPEDEF_STRUCT_END(self, t, m):
+        self.current_struct.class_name = m.group(1).strip()
+
+        self.STRUCT_END(t, m)
+
+    current_enum = None
+    def ENUM_START(self, t, m):
+        self.current_enum = Enum(m.group(1).strip(), self.module)
+
+    def TYPEDEF_ENUM_START(self, t, m):
+        self.current_enum = Enum(None, self.module)
+
+    def ENUM_VALUE(self, t, m):
+        self.current_enum.values.append(m.group(1).strip())
+
+    def ENUM_END(self, t, m):
+        self.module.classes[self.current_enum.name] = self.current_enum
+
+        ## For now we just treat enums as an integer, and also add
+        ## them to the constant table. In future it would be nice to
+        ## have them as a proper python object so we can override
+        ## __str__ and __int__.
+        for attr in self.current_enum.values:
+            self.module.add_constant(attr, 'integer')
+
+        #type_dispatcher[self.current_enum.name] = Integer
+        type_dispatcher[self.current_enum.name] = EnumType
+        self.current_enum = None
+
+    def TYPEDEFED_ENUM_END(self, t, m):
+        self.current_enum.name = self.current_enum.class_name = m.group(1)
+        self.ENUM_END(t, m)
+
+    def BIND_STRUCT(self, t, m):
+        self.module.classes[m.group(1)].active = True
+
+    def SIMPLE_TYPEDEF(self, t, m):
+        ## We basically add a new type as a copy of the old
+        ## type
+        old, new = m.group(1).strip(), m.group(2).strip()
+        if old in type_dispatcher:
+            type_dispatcher[new] = type_dispatcher[old]
+
+    def PROXY_CLASS(self, t, m):
+        base_class_name = m.group(1).strip()
+        class_name = "Proxied%s" % base_class_name
+        try:
+            proxied_class = self.module.classes[base_class_name]
+        except KeyError:
+            raise RuntimeError("Need to create a proxy for %s but it has not been defined (yet). You must place the PROXIED_CLASS() instruction after the class definition" % base_class_name)
+        current_class = ProxyClassGenerator(class_name,
+                                            base_class_name, self.module)
+        #self.current_class.constructor.args += proxied_class.constructor.args
+        current_class.docstring = self.current_comment
+
+        ## Create proxies for all these methods
+        for method in proxied_class.methods:
+            current_class.methods.append(ProxiedMethod(method, current_class))
+
+        self.module.add_class(current_class, Wrapper)
+
+    def parse_filenames(self, filenames):
+        for f in filenames:
+            self._parse(f)
+
+        ## Second pass
+        for f in filenames:
+            self._parse(f)
+
+    def _parse(self, filename):
+        fd = open(filename)
+        self.parse_fd(fd)
+        fd.close()
+
+        if filename not in self.module.files:
+              self.module.headers += '#include "%s"\n' % filename
+              self.module.files.append(filename)
+
+    def write(self, out):
+        self.module.write(out)
+
+
+if __name__ == '__main__':
+    p = HeaderParser('pyaff4', verbose = 1)
+    for arg in sys.argv[1:]:
+        p.parse_fd(open(arg))
+
+    log("second parse")
+    for arg in sys.argv[1:]:
+        p.parse_fd(open(arg))
+
+    pdb.set_trace()
+    p.write(sys.stdout)
+
+#    p = parser(Module("pyaff4"))
+#    for arg in sys.argv[1:]:
+#        p.parse(arg)
+#        log("second parse")
+#        p.parse(arg)
+
+#    p.write(sys.stdout)
+
diff --git a/python/experimental/config.py b/python/experimental/config.py
new file mode 100644
index 0000000..81d3e6b
--- /dev/null
+++ b/python/experimental/config.py
@@ -0,0 +1,4 @@
+MINGW_XCOMPILE = False
+V = True
+
+DEBUG = True
diff --git a/python/experimental/include/aff4_errors.h b/python/experimental/include/aff4_errors.h
new file mode 100644
index 0000000..a14dc1f
--- /dev/null
+++ b/python/experimental/include/aff4_errors.h
@@ -0,0 +1,67 @@
+/*
+** aff4_errors.h
+** 
+** Made by mic
+** Login   <mic at laptop>
+** 
+** Started on  Sat Mar  6 20:54:25 2010 mic
+** Last update Sat Mar  6 20:54:25 2010 mic
+*/
+
+#ifndef   	AFF4_ERRORS_H_
+# define   	AFF4_ERRORS_H_
+
+// Some helpful little things
+#define ERROR_BUFFER_SIZE 1024
+
+/** This is used for error reporting. This is similar to the way
+    python does it, i.e. we set the error flag and return NULL.
+*/
+enum _error_type {
+  EZero,EGeneric,EOverflow,EWarning,
+  EUnderflow,EIOError, ENoMemory, EInvalidParameter, ERuntimeError, EKeyError,
+
+  // Reserved for impossible conditions
+  EProgrammingError
+};
+
+void *raise_errors(enum _error_type t, char *string,  ...);
+
+/** We only set the error state if its not already set */
+#define RaiseError(t, message, ...)                                     \
+  if(*aff4_get_current_error(NULL) == EZero) {                          \
+    raise_errors(t, "%s: (%s:%d) " message, __FUNCTION__, __FILE__, __LINE__, ## __VA_ARGS__); \
+  };
+
+#define LogWarnings(format, ...)		\
+  do {						\
+    RaiseError(EWarning, format, ## __VA_ARGS__);	\
+    PrintError();				\
+  } while(0);
+  
+#define ClearError()				\
+  do {*aff4_get_current_error(NULL) = EZero;} while(0);
+
+#define PrintError()				\
+  do {char *error_str; if(*aff4_get_current_error(&error_str)) fprintf(stdout, "%s", error_str); fflush(stdout); ClearError(); }while(0);
+
+#define CheckError(error)			\
+  (*aff4_get_current_error(NULL) == error)
+
+/** The current error state is returned by this function.
+
+    This is done in a thread safe manner.
+ */
+enum _error_type *aff4_get_current_error(char **error_str);
+
+
+// These macros are used when we need to do something which might
+// change the error state on the error path of a function.
+#define PUSH_ERROR_STATE { enum _error_type *tmp_error_p = aff4_get_current_error(NULL); enum _error_type tmp_error = *tmp_error_p; enum _error_type exception __attribute__((unused));
+
+#define POP_ERROR_STATE *tmp_error_p = tmp_error;};
+
+
+void error_init();
+
+#endif 	    /* !AFF4_ERRORS_H_ */
diff --git a/python/experimental/include/class.h b/python/experimental/include/class.h
new file mode 100644
index 0000000..89a804c
--- /dev/null
+++ b/python/experimental/include/class.h
@@ -0,0 +1,447 @@
+/***************************************************
+  Classes and objects in C
+
+  This file makes it easy to implement classes and objects in C. To
+  define a class we need to perform three steps:
+
+  Define the class prototype. This is suitable to go in a .h file for
+  general use by other code.
+
+  Note all classes extend Object.
+
+  Example::
+
+CLASS(Foo, Object)
+    int x;
+    int y;
+
+    //This declares a method of a class Foo, called Con returning a
+    //Foo object. In other words it is a constructor.
+    Foo METHOD(Foo, Con, int x, int y);
+    int METHOD(Foo, add);
+
+END_CLASS
+
+Now we need to define some functions for the constructor and
+methods. Note that the constuctor is using ALLOCATE_CLASS to allocate
+space for the class structures. Callers may call with self==NULL to
+force allocation of a new class. Note that we do not call the
+constructor of our superclass implicitly here. (Calling the sperclass
+constructor is optional, but ALLOCATE_CLASS is not.).
+
+Foo Foo_Con(Foo self,int x,int y) {
+  self->x = x;
+  self->y = y;
+
+  return self;
+};
+
+int Foo_add(Foo this) {
+  return (this->x + this->y);
+};
+
+Now we need to define the Virtual function table - These are those
+functions and attributes which are defined in this class (over its
+superclass). Basically these are all those things in the class
+definition above, with real function names binding them. (Note that by
+convention we preceed the name of the method with the name of the
+class):
+
+VIRTUAL(Foo,Object)
+   VMETHOD(Con) = Foo_Con;
+   VMETHOD(add) = Foo_add;
+END_VIRTUAL
+
+We can use inheritance too:
+
+CLASS(Bar, Foo)
+   Bar METHOD(Bar, Con, char *something)
+END_CLASS
+
+Here Bar extends Foo and defines a new constructor with a different prototype:
+
+VIRTUAL(Bar,Foo)
+   VMETHOD(Con) = Bar_Con
+END_VIRTUAL
+
+If there is a function which expects a Foo, we will need to over ride
+the Foo constructor in the Bar, so the function will not see the
+difference between the Foo and Bar:
+
+CLASS(Bar,Foo)
+  int bar_attr;
+END_CLASS
+
+Foo Bar_Con(Foo self, int x, int y) {
+...
+}
+
+VIRTUAL(Bar, Foo)
+  VMETHOD(super.Con) = Bar_Con
+END_VIRTUAL
+
+Note that in this case we are over riding the Con method defined in
+Foo while creating derived Bar classes. The notation in the VIRTUAL
+table is to use super.Con, because Foo's Con method (the one we are
+over riding), can be located by using super.Con inside a Bar object.
+
+Imagine now that in Bar_Con we wish to use methods and attributes
+defined in Bar. Since Bar_Con over rides Bar's base class (Foo) it
+must have the prototype described above. Since self is of type Foo its
+impossible to use self->bar_attr (There is no bar_attr in Foo - its in
+Bar).
+
+In this case, we need to make a type cast to convice C that self is
+actually a Bar not a Foo:
+
+Foo Bar_Con(Foo self, int x, int y) {
+   Bar this = (Bar)self;
+
+   this->bar_attr=1
+};
+
+This allows us to access bars attributes.
+
+This is a general oddity with C style classes, which C++ and Java
+hide. In C we must always know which class defines which method and
+attribute and reference the right class's method. So for example if we
+want to call a Bar's add method:
+
+Bar a;
+
+a->super.add()
+
+because add is defined in Bar's super class (Foo). Constract this with
+C++ or Java which hide where methods are defined and simply make all
+methods appear like they were defined inside the derived class. This
+takes a while to get used to but the compiler will ensure that the
+references are correct - otherwise things will generally not compile
+properly.
+
+This difference can be used for good and bad. It is possible in C to
+call the base class's version of the method at any time (despite the
+fact it was over ridden). 
+
+For example:
+
+CLASS(Derived, Foo)
+      int METHOD(Derived, add);
+END_CLASS
+
+VIRTUAL(Derived, Foo)
+   VMETHOD(add) = Derived_add
+END_VIRTUAL
+
+If d is a Derived object, we can call Foo's version like this:
+d->super.add()
+
+But Derived's version is accessed by:
+d->add()
+
+Sometimes a derived class may want to over ride the base class's
+methods as well, in this case the VIRTUAL section should over ride
+super.add as well.
+
+*/
+/******************************************************
+# Copyright 2004: Commonwealth of Australia.
+#
+# Developed by the Computer Network Vulnerability Team,
+# Information Security Group.
+# Department of Defence.
+#
+# Michael Cohen <scudette at users.sourceforge.net>
+#
+# ******************************************************
+#  Version: FLAG  $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$
+# ******************************************************
+#
+# * This program is free software; you can redistribute it and/or
+# * modify it under the terms of the GNU General Public License
+# * as published by the Free Software Foundation; either version 2
+# * of the License, or (at your option) any later version.
+# *
+# * This program is distributed in the hope that it will be useful,
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# * GNU General Public License for more details.
+# *
+# * You should have received a copy of the GNU General Public License
+# * along with this program; if not, write to the Free Software
+# * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# ******************************************************/
+#ifndef __CLASS_H__
+#define __CLASS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifdef min
+#undef min
+#endif
+#define min(X, Y)  ((X) < (Y) ? (X) : (Y))
+
+#ifdef max
+#undef max
+#endif
+#define max(X, Y)  ((X) > (Y) ? (X) : (Y))
+
+
+#include <talloc.h>
+
+#define CLASS(class,super_class)                                 \
+  typedef struct class ## _t *class;                             \
+  int class ## _init(Object self);                               \
+  extern struct class ## _t __ ## class;                         \
+  struct class ## _t { struct super_class ## _t super;		 \
+  class   __class__;                                             \
+  super_class  __super__;
+
+
+#define METHOD(cls, name, ... )		\
+  (* name)(cls self, ## __VA_ARGS__ )
+
+  // Class methods are attached to the class but are not called with
+  // an instance. This is similar to the python class method or java
+  // static methods.
+#define CLASS_METHOD(name, ... )                \
+  (*name)(__VA_ARGS__)
+
+/***************************************************
+   This is a convenience macro which may be used if x if really large
+
+***************************************************/
+#define CALL(x, method, ... )			\
+  (x)->method((x), ## __VA_ARGS__)
+
+#define END_CLASS };
+
+/***************************************************
+   This is used to set the classes up for use:
+
+   class_init = checks the class template (__class) to see if it has
+   been allocated. otherwise allocates it in the global context.
+
+   class_Alloc = Allocates new memory for an instance of the
+   class. This is a recursive function calling each super class in
+   turn and setting the currently over ridden defaults. So for eample
+   suppose this class (foo) derives from bar, we first fill the
+   template with bars methods, and attributes. Then we over write
+   those with foos methods and attributes.
+
+**********************************************************/
+#define VIRTUAL(class,superclass)				\
+  struct class ## _t __ ## class;                                       \
+                                                                        \
+  int class ## _init(Object this) {                                    \
+  class self = (class)this;                                             \
+  if(self->__super__) return 1;                                         \
+  superclass ##_init(this);                                             \
+  this->__class__ = (Object)&__ ## class;                               \
+  self->__class__ = (class)&__ ## class;                               \
+  this->__super__ = (Object)&__ ## superclass;                          \
+  self->__super__ = (superclass)&__ ## superclass;                      \
+  this->__size = sizeof(struct class ## _t);                            \
+  this->__name__ = #class;
+
+#define SET_DOCSTRING(string)			\
+  ((Object)self)->__doc__ = string
+
+#define END_VIRTUAL return 1; }
+
+#define VMETHOD(method)				\
+  (self)->method
+
+#define VMETHOD_BASE(base, method)		\
+  (((base)self)->method)
+
+#define CLASS_ATTR(self, base, method)		\
+  (((base)self)->method)
+
+#define VATTR(attribute)			\
+  (self)->attribute
+
+#define NAMEOF(obj)				\
+  ((Object)obj)->__name__
+
+#define SIZEOF(obj)                             \
+  ((Object)obj)->__size
+
+#define DOCSTRING(obj)				\
+  ((Object)obj)->__doc__
+
+#define INIT_CLASS(class)                       \
+  class ## _init((Object)&__ ## class)
+
+/*************************************************************
+   This MACRO is used to construct a new Class using a constructor.
+
+    This is done to try and hide the bare (unbound) method names in
+    order to prevent name space pollution. (Bare methods may be
+    defined as static within the implementation file). This macro
+    ensures that class structures are initialised properly before
+    calling their constructors.
+
+   We require the following args:
+    class - the type of class to make
+    virt_class - The class where the method was defined
+    constructors - The constructor method to use
+    context - a talloc context to use.
+
+
+    Note that the class and virt_class do not have to be the same if
+    the method was not defined in the current class. For example
+    suppose Foo extends Bar, but method is defined in Bar but
+    inherited in Foo:
+
+    CONSTRUCT(Foo, Bar, super.method, context)
+
+    virt_class is Bar because thats where method was defined.
+*************************************************************/
+
+// The following only initialises the class if the __super__ element
+// is NULL. This is fast as it wont call the initaliser unnecessaily
+#define CONSTRUCT(class, virt_class, constructor, context, ... )        \
+  (class)( __## class.__super__ == NULL ?                               \
+           class ## _init((Object)&__ ## class) : 0,                    \
+           __## virt_class.__super__ == NULL ?                          \
+           virt_class ## _init((Object)&__ ## virt_class): 0,           \
+             ((virt_class)(&__ ## class))->constructor(                 \
+                       (virt_class)_talloc_memdup(context, &__ ## class, sizeof(struct class ## _t),  __location__ "(" #class ")"), \
+				   ## __VA_ARGS__) )
+
+/** This variant is useful when all we have is a class reference
+    (GETCLASS(Foo)) or &__Foo
+*/
+#define CONSTRUCT_FROM_REFERENCE(class, constructor, context, ... )	\
+  ( (class)->constructor(						\
+                       (void *)_talloc_memdup(context, ((Object)class), ((Object)class)->__size,  __location__ "(" #class "." #constructor ")"), \
+		      ## __VA_ARGS__) )
+
+/** Finds the size of the class in x */
+#define CLASS_SIZE(class)			\
+  ((Object)class)->__size
+
+typedef struct Object_t *Object;
+
+struct Object_t {
+  //A reference to a class instance - this is useful to be able to
+  //tell which class an object really belongs to:
+  Object __class__;
+
+  //And its super class:
+  Object __super__;
+
+  char *__name__;
+
+  /** Objects may have a doc string associated with them. */
+  char *__doc__;
+
+  //How large the class is:
+  int __size;
+};
+
+#define SUPER(base, imp, method, ...)            \
+  ((base)&__ ## imp)->method((base)self, ## __VA_ARGS__)
+
+#define GETCLASS(class)				\
+  (Object)&__ ## class
+
+// Returns true if the obj belongs to the class
+#define ISINSTANCE(obj,class)			\
+  (((Object)obj)->__class__ == GETCLASS(class))
+
+// This is a string comparison version of ISINSTANCE which works
+// across different shared objects.
+#define ISNAMEINSTANCE(obj, class)		\
+  (obj && !strcmp(class, NAMEOF(obj)))
+
+// We need to ensure that class was properly initialised:
+#define ISSUBCLASS(obj,class)			\
+  issubclass((Object)obj, (Object)&__ ## class)
+
+#define CLASSOF(obj)				\
+  ((Object)obj)->__class__
+
+void Object_init(Object);
+
+extern struct Object_t __Object;
+
+int issubclass(Object obj, Object class);
+
+extern void unimplemented(Object self);
+
+#define UNIMPLEMENTED(class, method)             \
+  ((class)self)->method = (void *)unimplemented;
+
+#define ZSTRING_NO_NULL(str) str , (strlen(str))
+#define ZSTRING(str) str , (strlen(str)+1)
+
+  // These dont do anything but are useful to indicate when a function
+  // parameter is used purely to return a value. They are now used to
+  // assist the python binding generator in generating the right sort
+  // of code
+#define OUT
+#define IN
+
+  // This modifier before a class means that the class is abstract and
+  // does not have an implementation - we do not generate bindings for
+  // that class then.
+#define ABSTRACT
+
+  // This modifier indicates that the following pointer is pointing to
+  // a borrowed reference - callers must not free the memory after use.
+#define BORROWED
+
+  // This tells the autobinder to generated bindings to this struct
+#define BOUND
+
+  // This tells the autobinder to ignore this class as it should be
+  // private to the implementation - external callers should not
+  // access this.
+#define PRIVATE
+
+  // This attribute of a method means that this method is a
+  // desctructor - the object is no longer valid after this method is
+  // run
+#define DESTRUCTOR
+
+  // including this after an argument definition will cause the
+  // autogenerator to assign default values to that parameter and make
+  // it optional
+#define DEFAULT(x)
+
+  // This explicitely denote that the type is a null terminated char
+  // ptr as opposed to a pointer to char and length.
+#define ZString char *
+
+  /* The following is a direction for the autogenerator to proxy the
+     given class. This is done in the following way:
+
+1) a new python type is created called Proxy_class_name() with a
+constructor which takes a surrogate object.
+
+2) The proxy class contains a member "base" of the type of the proxied
+C class.
+
+3) The returned python object may be passed to any C functions which
+expect the proxied class, and internal C calls will be converted to
+python method calls on the proxied object.
+  */
+#define PROXY_CLASS(name)
+
+  /* This signals the autogenerator to bind the named struct */
+#define BIND_STRUCT(name)
+
+  // This means that the memory owned by this pointer is managed
+  // externally (not using talloc). It is dangerous to use this
+  // keyword too much because we are unable to manage its memory
+  // appropriately and it can be free'd from under us.
+#define FOREIGN
+
+#endif
+#ifdef __cplusplus
+} /* closing brace for extern "C" */
+#endif
diff --git a/python/experimental/inspect.py b/python/experimental/inspect.py
new file mode 100644
index 0000000..b2d0684
--- /dev/null
+++ b/python/experimental/inspect.py
@@ -0,0 +1,278 @@
+#!/usr/bin/python
+""" Module to inspect the contents of an AFF4 volume """
+import sys, pdb, os
+import re, cmd, readline
+import optparse
+import pyaff4
+import shlex
+import fnmatch, time
+import subprocess
+import readline
+
+time.sleep(1)
+
+## Set more sane completer delimiters
+readline.set_completer_delims(' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?')
+
+## Configure colors
+colors = {}
+colors['cyan']   = '\033[96m'
+colors['purple'] = '\033[95m'
+colors['blue']   = '\033[94m'
+colors['green']  = '\033[92m'
+colors['yellow'] = '\033[93m'
+colors['red']    = '\033[91m'
+colors['end']    = '\033[0m'
+
+#If the output is not a terminal, remove the colors
+if not sys.stdout.isatty():
+   for key, value in colors.iteritems():
+      colors[key] = ''
+
+parser = optparse.OptionParser()
+(options, args) = parser.parse_args()
+
+## Load the specified args as volumes
+oracle = pyaff4.Resolver()
+VOLUMES = []
+STREAMS = {}
+
+for arg in args:
+    volume = pyaff4.RDFURN()
+    volume.set(arg)
+    if oracle.load(volume):
+        VOLUMES.append(volume)
+        urn = pyaff4.RDFURN()
+        iter = oracle.get_iter(volume, pyaff4.AFF4_CONTAINS)
+        while oracle.iter_next(iter, urn):
+           print urn.value
+           if not urn: break
+
+           ## We store is as a simplified path
+           path = "/%s%s" % (urn.parser.netloc, urn.parser.query)
+           STREAMS[path] = urn
+
+class Inspector(cmd.Cmd):
+    prompt = "/:>"
+    intro = "Inspecting the volumes %s. Type 'help' for help." % args
+    CWD = "/"
+
+    def cmdloop(self, *args):
+        while 1:
+            try:
+                if not cmd.Cmd.cmdloop(self, *args):
+                    break
+            except KeyboardInterrupt,e:
+                print "Type %sexit%s to exit, %shelp%s for help" % (
+                    colors['yellow'],
+                    colors['end'],
+                    colors['cyan'],
+                    colors['end'])
+
+    def do_EOF(self, args):
+        """ Exit this program """
+        print "%sGoodbye%s" % (colors['yellow'],
+                               colors['end'])
+        return True
+
+    def do_cd(self, line):
+        """ Change directory """
+        args = shlex.split(line)
+        if not args[0].startswith("/"):
+            args[0] = self.CWD + args[0]
+
+        try:
+            potential_cd = os.path.normpath(args[0])
+            if not potential_cd.endswith('/'):
+                potential_cd += "/"
+
+            for s in STREAMS:
+                if s.startswith(potential_cd):
+                    self.CWD = potential_cd
+                    self.prompt = "%s:>" % self.CWD
+                    return
+
+            raise IOError("No such directory %s" % potential_cd)
+        except IndexError:
+            print "CWD: %s" % self.CWD
+
+    def complete_cd(self, *args):
+        return self.complete_stream(*args)
+
+    def do_help(self, line):
+        """ Provide help for commands """
+        args = shlex.split(line)
+        if not args:
+            ## List all commands
+            for k in dir(self):
+                if k.startswith("do_"):
+                    method = getattr(self, k)
+                    print "%s%s%s: %s" % (colors['red'],
+                                          k[3:],
+                                          colors['end'],
+                                          method.__doc__)
+            return
+
+        for arg in args:
+            try:
+                method = getattr(self, "do_%s" % arg)
+            except AttributeError:
+                print "%sError:%s Command %s%s%s not found" % (colors['red'],
+                                                               colors['end'],
+                                                               colors['yellow'],
+                                                               arg,
+                                                               colors['end'])
+
+            print "%s: %s" % (arg, method.__doc__)
+
+    def do_ls(self, line):
+        """ List streams matching a glob expression """
+        globs = shlex.split(line)
+        if not globs: globs=[self.CWD]
+
+        result = []
+
+        for s in STREAMS:
+            for g in globs:
+                if fnmatch.fnmatch(s, g + "*"):
+                    if s.startswith(self.CWD):
+                        s = s[len(self.CWD):]
+
+                    path = s.split("/")[0]
+                    if path == s:
+                        decoration = colors['blue']
+                    else:
+                        decoration = colors['end']
+
+                    if path not in result:
+                        print "%s%s%s" % (decoration, path, colors['end'])
+                        result.append(path)
+
+    def do_less(self, line):
+        """ Read the streams specified and pipe them through the pager """
+        globs = shlex.split(line)
+        for s in STREAMS:
+            for g in globs:
+                if not g.startswith("/"):
+                    g = self.CWD + g
+
+                g = os.path.normpath(g)
+                if fnmatch.fnmatch(s, g):
+                    ## Try to open the stream
+                    try:
+                        fd = oracle.open(STREAMS[s], 'r')
+                    except IOError, e:
+                        raise
+
+                    pager = os.environ.get("PAGER","less")
+                    pipe=os.popen(pager,"w")
+
+                    while 1:
+                        data = fd.read(1024 * 1024)
+                        if not data: break
+
+                        pipe.write(data)
+
+                    pipe.close()
+
+    def do_cp(self, line):
+        """ Copy a stream from a source to a destination. """
+        globs = shlex.split(line)
+        src = globs[0]
+        dest = globs[1]
+        if(len(globs) > 2):
+           print "usage: cp src dest"
+           return
+
+        if not src.startswith("/"):
+           src = self.CWD + src
+
+        src = os.path.normpath(src)
+        src_urn = pyaff4.RDFURN()
+        src_urn.set("aff4:/" + src)
+        ## Try to open the stream
+        try:
+           fd = oracle.open(src_urn, 'r')
+        except IOError, e:
+           raise
+
+        dest_fd = open(dest, "w")
+        while 1:
+           data = fd.read(1024 * 1024)
+           if not data: break
+
+           dest_fd.write(data)
+
+        dest_fd.close()
+
+    def complete_cp(self, *args):
+       return self.complete_stream(*args)
+
+    def complete_less(self, *args):
+        return self.complete_stream(*args)
+
+    def _display_attribute(self, iter):
+       while 1:
+          obj = oracle.alloc_from_iter(iter)
+          if not obj: break
+
+          print "    -> type (%s) " % (obj.dataType)
+          print "    -> data (%s) " % (obj.serialise(iter.urn))
+
+
+    def do_resolve(self, line):
+       globs = shlex.split(line)
+       attribute = pyaff4.XSDString()
+       subject = pyaff4.RDFURN()
+       iter = pyaff4.RESOLVER_ITER()
+
+       subject.set(globs[0])
+       try:
+          attribute.set(globs[1])
+          print attribute.value
+          self._display_attribute(iter)
+
+       except IndexError:
+          ## Just display all the attributes
+          while oracle.attributes_iter(subject, attribute, iter):
+             print attribute.value
+             self._display_attribute(iter)
+
+    def complete_stream(self, text, line, begidx, endidx):
+        if not text.startswith("/"):
+            text = self.CWD + text
+
+        if not text:
+            completions = STREAMS.keys()
+
+        completions = [ text + f[len(text):].split("/")[0]
+                        for f in STREAMS.keys()
+                        if f.startswith(text)
+                        ]
+
+        return completions
+
+    def do_pwd(self, line):
+        print "CWD: %s" % self.CWD
+
+    def do_exit(self, *args):
+        """ Exits the program """
+        return self.do_EOF(*args)
+
+    def emptyline(self):
+        return
+
+    def onecmd(self, line):
+        try:
+            return cmd.Cmd.onecmd(self, line)
+        except Exception,e:
+            print "%sError:%s %s%s%s %s" % (colors['red'],
+                                            colors['end'],
+                                            colors['yellow'],
+                                            e.__class__.__name__,
+                                            colors['end'],
+                                            e)
+
+        return None
+
+Inspector().cmdloop()
diff --git a/python/experimental/lexer.py b/python/experimental/lexer.py
new file mode 100644
index 0000000..3def511
--- /dev/null
+++ b/python/experimental/lexer.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+# ******************************************************
+# Michael Cohen <scudette at users.sourceforge.net>
+#
+# ******************************************************
+#  Version: FLAG $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$
+# ******************************************************
+#
+# * This program is free software; you can redistribute it and/or
+# * modify it under the terms of the GNU General Public License
+# * as published by the Free Software Foundation; either version 2
+# * of the License, or (at your option) any later version.
+# *
+# * This program is distributed in the hope that it will be useful,
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# * GNU General Public License for more details.
+# *
+# * You should have received a copy of the GNU General Public License
+# * along with this program; if not, write to the Free Software
+# * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# ******************************************************
+""" A simple feed lexer.
+"""
+
+import re,sys
+
+class Lexer:
+    """ A generic feed lexer """
+    ## The following is a description of the states we have and the
+    ## way we move through them: format is an array of
+    ## [ state_re, re, token/action, next state ]
+    tokens = []
+    state = "INITIAL"
+    buffer = ''
+    error = 0
+    verbose = 0
+    state_stack = []
+    processed = 0
+    processed_buffer = ''
+    saved_state = None
+    flags = 0
+    
+    def __init__(self, verbose=0, fd=None):
+        if not self.verbose:
+            self.verbose = verbose
+
+        if len(self.tokens[0])==4:
+            for row in self.tokens:
+                row.append(re.compile(row[0], re.DOTALL))
+                row.append(re.compile(row[1], re.DOTALL | re.M | re.S | self.flags ))
+                
+        self.fd = fd
+
+    def save_state(self, t=None, m=None):
+        """ Returns a dict which represents the current state of the lexer.
+
+        When provided to restore_state, the lexer is guaranteed to be
+        in the same state as when the save_state was called.
+
+        Note that derived classes may need to extend this.
+        """
+        ## Cant save our state if we have errors. We need to guarantee
+        ## that we rewind to a good part of the file.
+        if self.error: return
+        try:
+            end = m.end()
+        except: end = 0
+        
+        self.saved_state = dict(state_stack = self.state_stack[:],
+                                processed = self.processed - end,
+                                processed_buffer = self.processed_buffer,
+                                readptr = self.fd.tell() - len(self.buffer) - end,
+                                state = self.state,
+                                objects = self.objects[:],
+                                error = self.error,
+                                )
+
+        if self.verbose>1:
+            print "Saving state %s" % self.processed
+
+    def restore_state(self):
+        state = self.saved_state
+        if not state: return
+        
+        self.state_stack = state['state_stack']
+        self.processed = state['processed']
+        self.processed_buffer = state['processed_buffer']
+        self.buffer = ''
+        self.fd.seek(state['readptr'])
+        self.state = state['state']
+        self.objects = state['objects']
+        self.error = state['error']
+        if self.verbose>1:
+            print "Restoring state to offset %s" % self.processed
+
+    def next_token(self, end = True):
+        ## Now try to match any of the regexes in order:
+        current_state = self.state
+        for state_re, re_str, token, next, state, regex in self.tokens:
+            ## Does the rule apply for us now?
+            if state.match(current_state):
+                if self.verbose > 2:
+                    print "%s: Trying to match %r with %r" % (self.state, self.buffer[:10], re_str)
+                m = regex.match(self.buffer)
+                if m:
+                    if self.verbose > 3:
+                        print "%s matched %s" % (re_str, m.group(0).encode("utf8"))
+                    ## The match consumes the data off the buffer (the
+                    ## handler can put it back if it likes)
+                    self.processed_buffer += self.buffer[:m.end()]
+                    self.buffer = self.buffer[m.end():]
+                    self.processed += m.end()
+
+                    ## Try to iterate over all the callbacks specified:
+                    for t in token.split(','):
+                        try:
+                            if self.verbose > 0:
+                                print "0x%X: Calling %s %r" % (self.processed, t, m.group(0))
+                            cb = getattr(self, t, self.default_handler)
+                        except AttributeError:
+                            continue
+
+                        ## Is there a callback to handle this action?
+                        next_state = cb(t, m)
+                        if next_state == "CONTINUE":
+                            continue
+                        
+                        elif next_state:
+                            next = next_state
+                            self.state = next
+
+                    
+                    if next:
+                        self.state = next
+                
+                    return token
+
+        ## Check that we are making progress - if we are too full, we
+        ## assume we are stuck:
+        if end and len(self.buffer)>0 or len(self.buffer)>1024:
+            self.processed_buffer += self.buffer[:1]
+            self.buffer = self.buffer[1:]
+            self.ERROR("Lexer Stuck, discarding 1 byte (%r) - state %s" % (self.buffer[:10], self.state))
+            return "ERROR"
+
+        ## No token were found
+        return None
+    
+    def feed(self, data):
+        self.buffer += data
+
+    def empty(self):
+        return not len(self.buffer)
+
+    def default_handler(self, token, match):
+        if self.verbose > 2:
+            print "Default handler: %s with %r" % (token,match.group(0))
+
+    def ERROR(self, message = None, weight =1):
+        if self.verbose > 0 and message:
+            print "Error(%s): %s" % (weight,message)
+
+        self.error += weight
+
+    def PUSH_STATE(self, token = None, match = None):
+        if self.verbose > 1:
+            print "Storing state %s" % self.state
+        self.state_stack.append(self.state)
+
+    def POP_STATE(self, token = None, match = None):
+        try:
+            state = self.state_stack.pop()
+            if self.verbose > 1:
+                print "Returned state to %s" % state
+                
+            return state
+        except IndexError:
+            print "Tried to pop the state but failed - possible recursion error"
+            return None
+
+    def close(self):
+        """ Just a conveniece function to force us to parse all the data """
+        while self.next_token(): pass
+
+class SelfFeederMixIn(Lexer):
+    """ This mixin is used to make a lexer which feeds itself one
+    sector at the time.
+
+    Note that self.fd must be the fd we read from.
+    """
+    def parse_fd(self, fd):
+        self.feed(fd.read())
+        while self.next_token(): pass
+
diff --git a/python/experimental/regfi/class.c b/python/experimental/regfi/class.c
new file mode 100644
index 0000000..bcd3b74
--- /dev/null
+++ b/python/experimental/regfi/class.c
@@ -0,0 +1,63 @@
+/******************************************************
+# Copyright 2004: Commonwealth of Australia.
+#
+# Developed by the Computer Network Vulnerability Team,
+# Information Security Group.
+# Department of Defence.
+#
+# Michael Cohen <scudette at users.sourceforge.net>
+#
+# ******************************************************
+#  Version: FLAG  $Version: 0.87-pre1 Date: Thu Jun 12 00:48:38 EST 2008$
+# ******************************************************
+#
+# * This program is free software; you can redistribute it and/or
+# * modify it under the terms of the GNU General Public License
+# * as published by the Free Software Foundation; either version 2
+# * of the License, or (at your option) any later version.
+# *
+# * This program is distributed in the hope that it will be useful,
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# * GNU General Public License for more details.
+# *
+# * You should have received a copy of the GNU General Public License
+# * along with this program; if not, write to the Free Software
+# * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# ******************************************************/
+#include "class.h"
+
+#define BUFF_SIZE 1024
+
+// Noone should instantiate Object directly. this should be already
+// allocated therefore:
+
+void Object_init(Object this) {
+  this->__class__ = &__Object;
+  this->__super__ = NULL;
+}
+
+struct Object_t __Object = {
+  .__class__ = &__Object,
+  .__super__ = &__Object,
+  .__size = sizeof(struct Object_t),
+  .__name__ = "Object"
+};
+
+int issubclass(Object obj, Object class) {
+  obj = obj->__class__;
+  while(1) {
+    if(obj == class->__class__)
+      return 1;
+
+    obj=obj->__super__;
+
+    if(obj == &__Object || obj==NULL) 
+      return 0;
+  };
+}
+
+void unimplemented(Object self) {
+  printf("%s contains unimplemented functions.. is it an abstract class?\n", NAMEOF(self));
+  abort();
+}
diff --git a/python/experimental/regfi/error.c b/python/experimental/regfi/error.c
new file mode 100644
index 0000000..6c2a181
--- /dev/null
+++ b/python/experimental/regfi/error.c
@@ -0,0 +1,84 @@
+/*
+** error.c
+** 
+** Made by (mic)
+** Login   <mic at laptop>
+** 
+** Started on  Mon Mar 15 20:45:09 2010 mic
+** Last update Sun May 12 01:17:25 2002 Speed Blue
+*/
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <talloc.h>
+#include "aff4_errors.h"
+
+/** These slots carry the TLS error keys */
+static pthread_key_t error_str_slot;
+static pthread_key_t error_value_slot;
+
+void error_dest(void *slot) {
+  if(slot) talloc_free(slot);
+}
+
+void *raise_errors(enum _error_type t, char *reason, ...) {
+  char *error_buffer;
+  // This has to succeed:
+  enum _error_type *type = aff4_get_current_error(&error_buffer);
+
+  if(reason) {
+    va_list ap;
+    va_start(ap, reason);
+
+    vsnprintf(error_buffer, ERROR_BUFFER_SIZE-1, reason,ap);
+    error_buffer[ERROR_BUFFER_SIZE-1]=0;
+    va_end(ap);
+  };
+
+  //update the error type
+  *type = t;
+
+  return NULL;
+}
+
+static int error_subsystem_initialised=0;
+
+enum _error_type *aff4_get_current_error(char **error_buffer) {
+  enum _error_type *type;
+
+  if(!error_subsystem_initialised) error_init();
+
+  type = pthread_getspecific(error_value_slot);
+
+  // This is optional
+  if(error_buffer) {
+    *error_buffer = pthread_getspecific(error_str_slot);
+
+  // If TLS buffers are not set we need to create them
+    if(!*error_buffer) {
+      *error_buffer =talloc_size(NULL, ERROR_BUFFER_SIZE);
+      pthread_setspecific(error_str_slot, *error_buffer);
+    };
+  };
+
+  if(!type) {
+    type = talloc(NULL, enum _error_type);
+    pthread_setspecific(error_value_slot, type);
+  };
+
+  return type;
+}
+
+/** Initialise the error subsystem */
+void error_init() {
+  error_subsystem_initialised = 1;
+
+  // We create the error buffer slots
+  if(pthread_key_create(&error_str_slot, error_dest) ||
+     pthread_key_create(&error_value_slot, error_dest)) {
+    printf("Unable to set up TLS variables\n");
+    abort();
+  };
+}
diff --git a/python/experimental/regfi/pyregfi.h b/python/experimental/regfi/pyregfi.h
new file mode 100644
index 0000000..c7923b8
--- /dev/null
+++ b/python/experimental/regfi/pyregfi.h
@@ -0,0 +1,182 @@
+/*
+ * Top-level definitions for pyregfi to be processed by Michael Cohen's 
+ * automated Python bindings generator.
+ *
+ * Copyright (C) 2010 Michael I. Cohen
+ * Copyright (C) 2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: $
+ *
+ */
+
+#ifndef   	PYREGFI_H_
+# define   	PYREGFI_H_
+
+#include "class.h"
+#include "aff4_errors.h"
+#include "regfi.h"
+
+/** Forward declarations */
+struct RegistryFile_t;
+struct RegistryKey_t;
+struct SubkeyIterator_t;
+struct ValueIterator_t;
+struct TreeIterator_t;
+
+BIND_STRUCT(REGFI_NK)
+BIND_STRUCT(REGFI_VK)
+BIND_STRUCT(REGFI_DATA)
+
+/** This is the base class for data objects */
+CLASS(RawData, Object)
+    const REGFI_DATA *data;
+    const REGFI_VK *rec;
+
+    RawData METHOD(RawData, Con, REGFI_DATA *data, REGFI_VK *record);
+
+    /** Return the raw buffer as a string. By default we only return
+        this much data - specify a required length to return more.
+
+        DEFAULT(len) = 4096;
+    */
+    int METHOD(RawData, get_value, OUT char *buffer, int len);
+END_CLASS
+
+CLASS(DataString, RawData)
+     BORROWED char *METHOD(DataString, get_value);
+END_CLASS
+
+CLASS(DWORDData, RawData)
+     uint64_t METHOD(DWORDData, get_value);
+END_CLASS
+
+/** This is an iterator for traversing an entire registry hive */
+CLASS(TreeIterator, Object)
+     PRIVATE REGFI_ITERATOR *iter;
+     PRIVATE struct RegistryFile_t *file;
+     PRIVATE bool root_traversed;
+
+     struct TreeIterator_t *METHOD(TreeIterator, Con, struct RegistryFile_t *file,
+				   char **path, REGFI_ENCODING encoding);
+
+     void METHOD(TreeIterator, __iter__);
+     struct RegistryKey_t *METHOD(TreeIterator, iternext);
+
+
+     int METHOD(TreeIterator, down);
+     int METHOD(TreeIterator, up);
+
+     struct RegistryKey_t *METHOD(TreeIterator, current);
+     int METHOD(TreeIterator, to_root);
+
+END_CLASS
+
+
+/** XXX */
+CLASS(RegistryKey, Object)
+     struct RegistryFile_t *file;
+     const REGFI_NK *key;
+
+     struct RegistryKey_t *METHOD(RegistryKey, Con,
+				  struct RegistryFile_t *file, REGFI_NK *base_key);
+
+     struct SubkeyIterator_t *METHOD(RegistryKey, subkeys);
+     struct ValueIterator_t *METHOD(RegistryKey, values);
+
+END_CLASS
+
+
+/** This is an iterator for reading keys from the registry */
+CLASS(SubkeyIterator, Object)
+     struct RegistryFile_t *file;
+     PRIVATE const REGFI_SUBKEY_LIST *list;
+     PRIVATE uint32_t cur;
+     
+     SubkeyIterator METHOD(SubkeyIterator, Con, 
+			   struct RegistryFile_t *file, REGFI_NK *key);
+
+     void METHOD(SubkeyIterator, __iter__);
+     RegistryKey METHOD(SubkeyIterator, iternext);
+END_CLASS
+
+
+
+/** This is an iterator for reading values from the registry */
+CLASS(ValueIterator, Object)
+     struct RegistryFile_t *file;
+     PRIVATE const REGFI_VALUE_LIST *list;
+     PRIVATE uint32_t cur;
+     
+     ValueIterator METHOD(ValueIterator, Con, 
+			  struct RegistryFile_t *file, REGFI_NK *key);
+
+     void METHOD(ValueIterator, __iter__);
+     REGFI_VK *METHOD(ValueIterator, iternext);
+END_CLASS
+
+
+
+CLASS(RegistryFile, Object)
+  REGFI_FILE *reg;
+  int fd;
+
+  RegistryFile METHOD(RegistryFile, Con, char *filename);
+
+  /* Get an iterator for a specific path in the register if path is
+     specified.
+
+     XXX: can we switch to UTF-8 and have Python properly import that?
+
+     DEFAULT(path) == NULL;
+     DEFAULT(encoding) = REGFI_ENCODING_ASCII;
+  */
+  TreeIterator METHOD(RegistryFile, TreeIterator, char **path, REGFI_ENCODING encoding);
+
+  /** Set the verbosity level of messages generated by the library for the 
+ *  current thread.
+ *
+ * @param mask   An integer representing the types of messages desired.
+ *               Acceptable values are created through bitwise ORs of 
+ *               REGFI_LOG_* values.  For instance, if only errors and
+ *               informational messages were desired (but not warnings),
+ *               then one would specify: REGFI_LOG_ERROR|REGFI_LOG_INFO
+ *               By default the message mask is: REGFI_LOG_ERROR|REGFI_LOG_WARN.
+ *
+ * @return       true on success and false on failure.  Failure occurs if 
+ *               underlying pthread functions fail.  errno is set in this case.
+ *
+ * Message masks are set in a thread-specific way.  If one were to set a message
+ * mask in one thread and then spawn a new thread, then the new thread will have
+ * it's message mask reset to the default.  This function may be called at any 
+ * time and will take effect immediately for the current thread.
+ *
+ * @note When a non-zero message mask is set, messages will
+ *       accumulate in memory without limit if they are not fetched using
+ *       @ref regfi_get_log_str and subsequently freed by the caller.  It is
+ *       recommended that messsages be fetched after each regfi API call in
+ *       order to provide the most context.
+ *
+ * @ingroup regfiBase
+ */
+  int METHOD(RegistryFile, set_log_mask, uint16_t mask);
+
+  
+END_CLASS
+
+
+void pyregfi_init();
+
+#endif 	    /* !PYREGFI_H_ */
diff --git a/python/experimental/regfi/regfi.c b/python/experimental/regfi/regfi.c
new file mode 100644
index 0000000..13a058c
--- /dev/null
+++ b/python/experimental/regfi/regfi.c
@@ -0,0 +1,443 @@
+/*
+ * pyregfi/libregfi glue code 
+ *
+ * Copyright (C) 2010 Michael I. Cohen
+ * Copyright (C) 2010 Timothy D. Morgan
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: $
+ */
+
+#include "pyregfi.h"
+
+static int RegistryFile_dest(void *self) 
+{
+  RegistryFile this = (RegistryFile)self;
+
+  regfi_free(this->reg);
+  close(this->fd);
+
+  return 0;
+}
+
+static RegistryFile RegistryFile_Con(RegistryFile self, char *filename) 
+{
+  self->fd = open(filename, O_RDONLY);
+  if(self->fd < 0) 
+  {
+    RaiseError(EIOError, "Unable to open %s", filename);
+    goto error;
+  }
+
+  self->reg = regfi_alloc(self->fd);
+
+  if(!self->reg) 
+  {
+    RaiseError(ERuntimeError, "REGFI Error: %s", regfi_log_get_str());
+    /*char* e = regfi_log_get_str();*/
+    /*fprintf(stderr, "%p\n", e);*/
+    goto error;
+  }
+
+  talloc_set_destructor((void *)self, RegistryFile_dest);
+  return self;
+
+ error:
+  talloc_free(self);
+  return NULL;
+}
+
+static TreeIterator RegistryFile_TreeIterator(RegistryFile self, char **path, REGFI_ENCODING encoding) 
+{
+  return CONSTRUCT(TreeIterator, TreeIterator, Con, NULL, self, path, encoding);
+}
+
+
+VIRTUAL(RegistryFile, Object) {
+  VMETHOD(Con) = RegistryFile_Con;
+  VMETHOD(TreeIterator) = RegistryFile_TreeIterator;
+} END_VIRTUAL
+
+
+static int RegistryKey_dest(void *self) 
+{
+  RegistryKey this = (RegistryKey)self;
+
+  talloc_unlink(this, (void*)this->key);
+
+  return 0;
+}
+
+static RegistryKey RegistryKey_Con(RegistryKey self, 
+				   RegistryFile file, REGFI_NK* base_key)
+{
+  if(base_key == NULL)
+    goto error;
+
+  self->key = base_key;
+  self->file = file;
+  talloc_reference(self, self->key);
+  talloc_set_destructor((void *)self, RegistryKey_dest);
+
+  return self;
+
+ error:
+  talloc_free(self);
+  return NULL;
+}
+
+/* XXX: would be nice to change this into a property, rather than a function,
+ *      but that would require a custom __getattr__.  Can that be done? */
+static SubkeyIterator RegistryKey_subkeys(RegistryKey self)
+{
+  return CONSTRUCT(SubkeyIterator, SubkeyIterator, Con, NULL, self->file, self->key);
+}
+
+/* XXX: would be nice to change this into a property, rather than a function,
+ *      but that would require a custom __getattr__.  Can that be done? */
+static SubkeyIterator RegistryKey_values(RegistryKey self)
+{
+  return CONSTRUCT(ValueIterator, ValueIterator, Con, NULL, self->file, self->key);
+}
+
+
+VIRTUAL(RegistryKey, Object) {
+  VMETHOD(Con) = RegistryKey_Con;
+  VMETHOD(subkeys) = RegistryKey_subkeys;
+  VMETHOD(values) = RegistryKey_values;
+} END_VIRTUAL
+
+
+static int TreeIterator_dest(void *self) 
+{
+  TreeIterator this = (TreeIterator)self;
+
+  regfi_iterator_free(this->iter);
+  return 0;
+}
+
+static TreeIterator TreeIterator_Con(TreeIterator self, 
+				     RegistryFile file, 
+				     char **path,
+				     REGFI_ENCODING encoding)
+{
+  self->iter = regfi_iterator_new(file->reg, encoding);
+  self->file = file;
+
+  if(!self->iter) 
+  {
+    RaiseError(ERuntimeError, "Error: %s", regfi_log_get_str());
+    goto error;
+  }
+
+  talloc_set_destructor((void*)self, TreeIterator_dest);
+
+  /* Traverse to the path */
+  if(path[0]) 
+  {
+    if(!regfi_iterator_walk_path(self->iter, (const char **)path)) 
+    {
+      RaiseError(ERuntimeError, "Unable to walk down key path");
+      goto error;
+    }
+  }
+
+  self->root_traversed = false;
+
+  return self;
+ error:
+  return NULL;
+}
+
+static void TreeIterator__iter__(TreeIterator self) 
+{
+  return;
+}
+
+
+static RegistryKey TreeIterator_next(TreeIterator self)
+{
+  if(!self->root_traversed)
+    self->root_traversed = true;
+  else if(!regfi_iterator_down(self->iter))
+  {
+    do
+    {
+      if(!regfi_iterator_up(self->iter))
+	return NULL;
+    } while(!regfi_iterator_next_subkey(self->iter));
+
+    /* XXX: This is an error condition.  
+     *      Probably should throw an exception or something. */
+    if(!regfi_iterator_down(self->iter))
+      return NULL;
+  }
+
+  regfi_iterator_first_subkey(self->iter);
+  return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file,
+		   regfi_iterator_cur_key(self->iter));
+}
+
+static int TreeIterator_down(TreeIterator self) 
+{
+  int result = regfi_iterator_down(self->iter);
+  regfi_iterator_first_subkey(self->iter);
+  regfi_iterator_first_value(self->iter);
+  return result;
+}
+
+static int TreeIterator_up(TreeIterator self) 
+{
+  return regfi_iterator_up(self->iter);
+}
+
+static RegistryKey TreeIterator_current(TreeIterator self)
+{
+  return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file,
+		   regfi_iterator_cur_key(self->iter));
+}
+
+static int TreeIterator_to_root(TreeIterator self)
+{
+  return regfi_iterator_to_root(self->iter);
+}
+
+
+VIRTUAL(TreeIterator, Object) {
+  VMETHOD(Con) = TreeIterator_Con;
+  VMETHOD(iternext) = TreeIterator_next;
+  VMETHOD(down) = TreeIterator_down;
+  VMETHOD(up) = TreeIterator_up;
+  VMETHOD(current) = TreeIterator_current;
+  VMETHOD(to_root) = TreeIterator_to_root;
+  VMETHOD(__iter__) = TreeIterator__iter__;
+  /*  VMETHOD(list_values) = TreeIterator_list_values;*/
+} END_VIRTUAL
+
+
+
+static int SubkeyIterator_dest(void *self) 
+{
+  SubkeyIterator this = (SubkeyIterator)self;
+
+  talloc_unlink(this, (void*)this->list);
+
+  return 0;
+}
+
+static SubkeyIterator SubkeyIterator_Con(SubkeyIterator self, 
+					 struct RegistryFile_t* file, 
+					 REGFI_NK* key)
+{
+  /* XXX: add a talloc reference? */
+  self->file = file;
+
+  /* Add a reference to the list */
+  self->list = key->subkeys;
+  talloc_reference(self, self->list);
+
+  self->cur = 0;
+  talloc_set_destructor((void *)self, SubkeyIterator_dest);
+
+  return self;
+}
+
+static void SubkeyIterator__iter__(SubkeyIterator self)
+{
+  return;
+}
+
+static RegistryKey SubkeyIterator_iternext(SubkeyIterator self)
+{
+  const REGFI_NK* nk;
+
+  if(self->list != NULL && self->cur < self->list->num_keys)
+  {
+    /* XXX: can we switch to UTF-8 and have Python properly import that? */
+    nk = regfi_load_key(self->file->reg, 
+			self->list->elements[self->cur].offset+REGFI_REGF_SIZE,
+			REGFI_ENCODING_ASCII, true);
+    self->cur++;
+    return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, self->file, nk);
+  }
+
+  return NULL;
+}
+
+VIRTUAL(SubkeyIterator, Object) {
+  VMETHOD(Con) = SubkeyIterator_Con;
+  VMETHOD(__iter__) = SubkeyIterator__iter__;
+  VMETHOD(iternext) = SubkeyIterator_iternext;
+} END_VIRTUAL
+
+
+
+static int ValueIterator_dest(void *self) 
+{
+  ValueIterator this = (ValueIterator)self;
+
+  if(this->list != NULL)
+    talloc_unlink(this, (void*)this->list);
+
+  return 0;
+}
+
+static ValueIterator ValueIterator_Con(ValueIterator self,
+				       struct RegistryFile_t* file, 
+				       REGFI_NK* key)
+{
+  /* XXX: add a talloc reference? */
+  self->file = file;
+  self->cur = 0;
+
+  /* Add a reference to the list */
+  self->list = key->values;
+  if(self->list != NULL)
+    talloc_reference(self, self->list);
+
+  talloc_set_destructor((void *)self, ValueIterator_dest);
+
+  return self;
+}
+
+static void ValueIterator__iter__(ValueIterator self)
+{
+  return;
+}
+
+static REGFI_VK* ValueIterator_iternext(ValueIterator self)
+{
+  const REGFI_VK* vk;
+
+  if(self->list != NULL && self->cur < self->list->num_values)
+  {
+    /* XXX: can we switch to UTF-8 and have Python properly import that? */
+    vk = regfi_load_value(self->file->reg, 
+			  self->list->elements[self->cur]+REGFI_REGF_SIZE,
+			  REGFI_ENCODING_ASCII, true);
+    self->cur++;
+    /*return CONSTRUCT(RegistryKey, RegistryKey, Con, NULL, vk);    */
+    return vk; 
+  }
+
+  return NULL;
+
+
+  /* XXX: shouldn't parse data here every time we walk over a value.  
+   *      Instead, make data fetching a method and parse it then. 
+   */
+  /*
+  data = (REGFI_DATA *)regfi_iterator_fetch_data(self->iter, rec);
+  if(!data) {
+    RaiseError(ERuntimeError, "Unable to fetch data: %s", regfi_log_get_str());
+    goto error;
+  }
+
+  switch(rec->type) {
+  case REG_EXPAND_SZ:
+  case REG_SZ:
+    result = (RawData)CONSTRUCT(DataString, RawData, Con, NULL, data, rec);
+    break;
+
+  case REG_DWORD:
+    result = (RawData)CONSTRUCT(DWORDData, RawData, Con, NULL, data, rec);
+    break;
+
+  case REG_BINARY:
+  default:
+    result = (RawData)CONSTRUCT(RawData, RawData, Con, NULL, data, rec);
+    break;
+  }
+
+  return result;
+ error:
+  talloc_free(self);
+  return NULL;
+  */
+}
+
+VIRTUAL(ValueIterator, Object) {
+  VMETHOD(Con) = ValueIterator_Con;
+  VMETHOD(__iter__) = ValueIterator__iter__;
+  VMETHOD(iternext) = ValueIterator_iternext;
+} END_VIRTUAL
+
+
+
+static int RawData_dest(void *self)
+{
+  RawData this = (RawData)self;
+
+  if(this->data) {
+    regfi_free_record(this->data);
+  };
+
+  if(this->rec) {
+    regfi_free_record(this->rec);
+  };
+
+  return 0;
+}
+
+static RawData RawData_Con(RawData self, REGFI_DATA *data, REGFI_VK *record)
+{
+  self->rec = record;
+  self->data = data;
+
+  talloc_set_destructor((void *)self, RawData_dest);
+
+  return self;
+}
+
+static int RawData_get_value(RawData self, char *buff, int len)
+{
+  int available_to_read = min(len, self->data->interpreted_size);
+
+  memcpy(buff, self->data->raw, available_to_read);
+
+  return available_to_read;
+}
+
+VIRTUAL(RawData, Object) {
+  VMETHOD(Con) = RawData_Con;
+  VMETHOD(get_value) = RawData_get_value;
+} END_VIRTUAL
+
+static char* DataString_get_value(DataString self)
+{
+  RawData this = (RawData)self;
+
+  return (char*)this->data->interpreted.string;
+}
+
+VIRTUAL(DataString, RawData) {
+  VMETHOD(get_value) = DataString_get_value;
+} END_VIRTUAL
+
+static uint64_t DWORDData_get_value(DWORDData self)
+{
+  RawData this = (RawData)self;
+
+  return this->data->interpreted.dword;
+}
+
+VIRTUAL(DWORDData, RawData) {
+  VMETHOD(get_value) = DWORDData_get_value;
+} END_VIRTUAL
+
+void pyregfi_init()
+{
+  INIT_CLASS(RegistryFile);
+}
diff --git a/python/experimental/utils.py b/python/experimental/utils.py
new file mode 100644
index 0000000..84ab10d
--- /dev/null
+++ b/python/experimental/utils.py
@@ -0,0 +1,511 @@
+import os, sys, re, pdb
+import distutils.sysconfig as sysconfig
+import distutils.util
+import platform
+import SCons.SConf as SConf
+import config
+
+# taken from scons wiki
+def CheckPKGConfig(context, version):
+    context.Message( 'Checking for pkg-config version > %s... ' % version)
+    ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
+    context.Result( ret )
+    return ret
+
+def CheckFramework(context, name):
+    ret = 0
+    if (platform.system().lower() == 'darwin'):
+        context.Message( '\nLooking for framework %s... ' % name )
+        lastFRAMEWORKS = context.env['FRAMEWORKS']
+        context.env.Append(FRAMEWORKS = [name])
+        ret = context.TryLink("""
+              int main(int argc, char **argv) {
+                return 0;
+              }
+              """, '.c')
+        if not ret:
+            context.env.Replace(FRAMEWORKS = lastFRAMEWORKS
+)
+
+    return ret
+
+def CheckFink(context):
+    context.Message( 'Looking for fink... ')
+    prog = context.env.WhereIs('fink')
+    if prog:
+        ret = 1
+        prefix = prog.rsplit(os.sep, 2)[0]
+        context.env.Append(LIBPATH = [prefix + os.sep +'lib'],
+                           CPPPATH = [prefix + os.sep +'include'])
+        context.Message( 'Adding fink lib and include path')
+    else:
+        ret = 0
+        
+    context.Result(ret)    
+    return int(ret)
+
+def CheckMacports(context):
+    context.Message( 'Looking for macports... ')
+    prog = context.env.WhereIs('port')
+    if prog:
+        ret = 1
+        prefix = prog.rsplit(os.sep, 2)[0]
+        context.env.Append(LIBPATH = [prefix + os.sep + 'lib'],
+                           CPPPATH = [prefix + os.sep + 'include'])
+        context.Message( 'Adding port lib and include path')
+    else:
+        ret = 0
+        
+    context.Result(ret)    
+    return int(ret)
+
+# TODO: We should use the scons one instead
+def CheckLib(context, name):
+    context.Message( 'Looking for lib %s... ' % name )
+    lastLIBS = context.env['LIBS']
+    context.env.Append(LIBS = [name])
+    ret = context.TryLink("""
+              int main(int argc, char **argv) {
+                return 0;
+              }
+              """,'.c')
+    if not ret:
+        context.env.Replace(LIBS = lastLIBS)
+
+    return ret
+
+def ConfigPKG(context, name):
+    context.Message( '\nUsing pkg-config for %s... ' % name )
+    ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0]
+    context.Result(  ret )
+    if ret:
+        context.env.ParseConfig('pkg-config --cflags --libs \'%s\'' % name)
+    return int(ret)
+
+def CheckPKG(context, name):
+    context.Message( 'Checking for %s... ' % name )
+    if platform.system().lower() == 'windows':
+        return 0 
+    ret = 1
+    if not CheckFramework(context, name):
+        if not ConfigPKG(context, name.lower()):
+            ret = CheckLib(context, name) 
+
+    context.Result(ret)
+    return int(ret)
+
+
+## Configure colors for pretty builds
+colors = {}
+colors['cyan']   = '\033[96m'
+colors['purple'] = '\033[95m'
+colors['blue']   = '\033[94m'
+colors['green']  = '\033[92m'
+colors['yellow'] = '\033[93m'
+colors['red']    = '\033[91m'
+colors['end']    = '\033[0m'
+
+#If the output is not a terminal, remove the colors
+if not sys.stdout.isatty():
+   for key, value in colors.iteritems():
+      colors[key] = ''
+
+def error(msg):
+   print "%s%s%s" % (colors['red'], msg, colors['end'])
+   sys.exit(1)
+
+def warn(msg):
+   print "%s%s%s" % (colors['yellow'], msg, colors['end'])
+
+compile_source_message = '%sCompiling %s==> %s$SOURCE%s' % \
+   (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
+
+compile_shared_source_message = '%sCompiling shared %s==> %s$SOURCE%s' % \
+   (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
+
+compile_python_source_message = '%sCompiling python module %s==> %s$SOURCE%s' % \
+   (colors['blue'], colors['purple'], colors['yellow'], colors['end'])
+
+link_program_message = '%sLinking Program %s==> %s$TARGET%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+link_library_message = '%sLinking Static Library %s==> %s$TARGET%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+ranlib_library_message = '%sRanlib Library %s==> %s$TARGET%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+link_shared_library_message = '%sLinking Shared Library %s==> %s$TARGET%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+link_python_module_message = '%sLinking Native Python module %s==> %s${TARGET}%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+java_library_message = '%sCreating Java Archive %s==> %s$TARGET%s' % \
+   (colors['red'], colors['purple'], colors['yellow'], colors['end'])
+
+def install_colors(args):
+    """ Installs colors into an environment """
+    args.update(dict( CXXCOMSTR = compile_source_message,
+                      CCCOMSTR = compile_source_message,
+                      SHCCCOMSTR = compile_shared_source_message,
+                      SHCXXCOMSTR = compile_shared_source_message,
+                      ARCOMSTR = link_library_message,
+                      RANLIBCOMSTR = ranlib_library_message,
+                      SHLINKCOMSTR = link_shared_library_message,
+                      LINKCOMSTR = link_program_message,
+                      JARCOMSTR = java_library_message,
+                      JAVACCOMSTR = compile_source_message,))
+
+import optparse
+
+### This workaround is because scons does not provide access to the
+### parser, and by setting Help() we are unable to generate the option
+### listing from AddOption
+my_parser = optparse.OptionParser()
+
+import SCons.Script.Main as Main
+import SCons.Script as Script
+
+def add_option(arg, option, *args, **kwargs):
+    opt = "--%s" % option
+    Main.AddOption(opt, *args, **kwargs)
+    my_parser.add_option(opt, *args, **kwargs)
+
+    arg[option] = Main.GetOption(option)
+
+def generate_help(vars, env):
+    Script.Help("AFF4 build system configuration.\n\nFollowing are compile time options:\n")
+    Script.Help(my_parser.format_help())
+    Script.Help("\nThe following variables can be used on the command line:\n")
+    Script.Help(vars.GenerateHelpText(env))
+
+HEADERS = {}
+
+def check_size(conf, types):
+    global _DEFAULTS
+
+    for t in types:
+        name = "SIZEOF_" + t.replace(" ","_").upper()
+        HEADERS[name] = conf.CheckTypeSize(
+            t, size = _DEFAULTS[t][0])
+
+def check_type(conf, types):
+    header = None
+    for t in types:
+        if ':' in t:
+            t, header = t.split(':')
+        define = "HAVE_" + t.upper().replace(".","_")
+
+        result = 0
+        if conf.CheckType(t, includes="#include <%s>\n" % header):
+            result = 1
+
+        HEADERS[define] = result
+
+def check_build(conf, message, define, prog):
+    """ Build and links prog and adds define if that succeeds """
+    context = SConf.CheckContext(conf)
+    context.Message("Checking for %s ..." % message)
+    if context.TryLink(prog, ".c"):
+        HEADERS[define] = 1
+        context.Message("yes\n")
+    else:
+        context.Message("no\n")
+
+def check(type, conf, headers, extra_include =''):
+    for header in headers:
+        if ":" in header:
+            define, header = header.split(':')
+        else:
+            if "/" in header:
+                tmp = header.split("/")[-1]
+            else:
+                tmp = header
+
+            define = "HAVE_" + tmp.upper().replace(".","_")
+
+        global HEADERS
+        result = 0
+        if type == 'header':
+            #pdb.set_trace()
+            if conf.CheckCHeader(header): result = 1
+            HEADERS[define] = result
+        elif type == 'func':
+            if conf.CheckFunc(header, header=extra_include): result = 1
+            HEADERS[define] = result
+        elif type == 'lib':
+            if conf.CheckLib(header): result =1
+            HEADERS[define] = result
+
+## Build the config.h file
+def config_h_build(target, source, env):
+    config_h_defines = env.Dictionary()
+    config_h_defines.update(env.config.__dict__)
+    warn("Generating %s" % (target[0].path))
+
+    for a_target, a_source in zip(target, source):
+        config_h = file(str(a_target), "w")
+        config_h_in = file(str(a_source), "r")
+        config_h.write(config_h_in.read() % config_h_defines)
+        config_h_in.close()
+
+    keys = HEADERS.keys()
+    keys.sort()
+
+    for k in keys:
+        if HEADERS[k]:
+            config_h.write("#define %s %s\n" % (k,HEADERS[k]))
+        else:
+            config_h.write("/** %s unset */\n" % k)
+
+    config_h.close()
+
+import SCons.Environment
+
+class ExtendedEnvironment(SCons.Environment.Environment):
+    """ Implementation from Richard Levitte email to
+    org.tigris.scons.dev dated Jan 26, 2006 7:05:10 am."""
+    python_cppflags = "-I"+sysconfig.get_python_inc()
+
+    def PythonModule(self, libname, lib_objs=[], **kwargs):
+        """ This builds a python module which is almost a library but
+        is sometimes named differently.
+
+        We have two modes - a cross compile mode where we do our best
+        to guess the flags. In the native mode we can get the required
+        flags directly from distutils.
+        """
+        if config.MINGW_XCOMPILE:
+            shlib_suffix = ".pyd"
+            cppflags = "-I%s" % config.XCOMPILE_PYTHON_PATH
+            shlink_flags = ['']
+
+        else:
+            platform = self.subst('$PLATFORM')
+            shlib_pre_action = None
+            shlib_suffix = distutils.util.split_quoted(
+                sysconfig.get_config_var('SO'))
+            shlib_post_action = None
+            cppflags = distutils.util.split_quoted(self.python_cppflags)
+            shlink_flags = str(self['LINKFLAGS']).split()
+
+        install_dest = distutils.util.split_quoted(
+            os.path.join(
+                sysconfig.get_config_var('BINLIBDEST'),os.path.dirname(libname)))
+
+        flags = distutils.util.split_quoted(
+            sysconfig.get_config_var('LDSHARED'))
+
+        ## For some stupid reason they include the compiler in LDSHARED
+        shlink_flags.extend([x for x in flags if 'gcc' not in x])
+
+        shlink_flags.append(sysconfig.get_config_var('LOCALMODLIBS'))
+
+        ## TODO cross compile mode
+        kwargs['LIBPREFIX'] = ''
+        kwargs['CPPFLAGS'] = cppflags
+        kwargs['SHLIBSUFFIX'] = shlib_suffix
+        kwargs['SHLINKFLAGS'] = shlink_flags
+
+        if not self.config.V:
+            kwargs['SHCCCOMSTR'] = compile_python_source_message
+            kwargs['SHLINKCOMSTR'] = link_python_module_message
+
+        lib = self.SharedLibrary(libname,lib_objs,
+                                 **kwargs)
+
+        ## Install it to the right spot
+        self.Install(install_dest, lib)
+        self.Alias('install', install_dest)
+
+        return lib
+
+    def VersionedSharedLibrary(self, libname, libversion, lib_objs=[]):
+        """ This creates a version library similar to libtool.
+
+        We name the library with the appropriate soname.
+        """
+        platform = self.subst('$PLATFORM')
+        shlib_pre_action = None
+        shlib_suffix = self.subst('$SHLIBSUFFIX')
+        shlib_post_action = None
+        shlink_flags = SCons.Util.CLVar(self.subst('$SHLINKFLAGS'))
+
+        if platform == 'posix':
+            shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ]
+            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ]
+            shlib_suffix += '.' + libversion
+            shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,-soname=${LIBPREFIX}%s%s' % (
+                    libname, shlib_suffix) ]
+        elif platform == 'aix':
+            shlib_pre_action = [ "nm -Pg $SOURCES > ${TARGET}.tmp1", "grep ' [BDT] ' < ${TARGET}.tmp1 > ${TARGET}.tmp2", "cut -f1 -d' ' < ${TARGET}.tmp2 > ${TARGET}", "rm -f ${TARGET}.tmp[12]" ]
+            shlib_pre_action_output_re = [ '$', '.exp' ]
+            shlib_post_action = [ 'rm -f $TARGET', 'ln -s $SOURCE $TARGET' ]
+            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*' % re.escape(shlib_suffix), shlib_suffix ]
+            shlib_suffix += '.' + libversion
+            shlink_flags += ['-G', '-bE:${TARGET}.exp', '-bM:SRE']
+        elif platform == 'cygwin':
+            shlink_flags += [ '-Wl,-Bsymbolic', '-Wl,--out-implib,${TARGET.base}.a' ]
+        elif platform == 'darwin':
+            shlib_suffix = '.' + libversion + shlib_suffix
+            shlink_flags += [ '-dynamiclib', '-current-version %s' % libversion ]
+
+        lib = self.SharedLibrary(libname,lib_objs,
+                                 SHLIBSUFFIX=shlib_suffix,
+                                 SHLINKFLAGS=shlink_flags)
+
+        if shlib_pre_action:
+            shlib_pre_action_output = re.sub(shlib_pre_action_output_re[0], shlib_pre_action_output_re[1], str(lib[0]))
+            self.Command(shlib_pre_action_output, [ lib_objs ], shlib_pre_action)
+            self.Depends(lib, shlib_pre_action_output)
+
+        if shlib_post_action:
+            shlib_post_action_output = re.sub(shlib_post_action_output_re[0], shlib_post_action_output_re[1], str(lib[0]))
+            self.Command(shlib_post_action_output, lib, shlib_post_action)
+
+        return lib
+
+    def InstallVersionedSharedLibrary(self, destination, lib):
+        platform = self.subst('$PLATFORM')
+        shlib_suffix = self.subst('$SHLIBSUFFIX')
+        shlib_install_pre_action = None
+        shlib_install_post_action = None
+
+        if platform == 'posix':
+            shlib_post_action = [ 'rm -f $TARGET', 'ln -s ${SOURCE.file} $TARGET' ]
+            shlib_post_action_output_re = [ '%s\\.[0-9\\.]*$' % re.escape(shlib_suffix), shlib_suffix ]
+            shlib_install_post_action = shlib_post_action
+            shlib_install_post_action_output_re = shlib_post_action_output_re
+
+        ilib = self.Install(destination,lib)
+
+        if shlib_install_pre_action:
+            shlib_install_pre_action_output = re.sub(shlib_install_pre_action_output_re[0], shlib_install_pre_action_output_re[1], str(ilib[0]))
+            self.Command(shlib_install_pre_action_output, ilib, shlib_install_pre_action)
+            self.Depends(shlib_install_pre_action_output, ilib)
+
+        if shlib_install_post_action:
+            shlib_install_post_action_output = re.sub(shlib_install_post_action_output_re[0], shlib_install_post_action_output_re[1], str(ilib[0]))
+            self.Command(shlib_install_post_action_output, ilib, shlib_install_post_action)
+
+
+import subprocess
+
+def pkg_config(pkg, type):
+    try:
+        result = subprocess.Popen(["%s-config" % pkg, "--%s" % type],
+                                  stdout=subprocess.PIPE).communicate()[0]
+    except:
+        error("Unable to run %s-config - do you have the dev package installed?" % pkg)
+
+    return result.strip()
+
+
+# Sensible default for common types on common platforms.
+_DEFAULTS = {
+    'char': [1,],
+    'short' : [2,],
+    'int' : [4, 2],
+    'long' : [4, 8],
+    'long long' : [8, 4],
+    # Normally, there is no need to check unsigned types, because they are
+    # guaranteed to be of the same size than their signed counterpart.
+    'unsigned char': [1,],
+    'unsigned short' : [2,],
+    'unsigned int' : [4, 2],
+    'unsigned long' : [4, 8],
+    'unsigned long long' : [8, 4],
+    'float' : [4,],
+    'double' : [8,],
+    'long double' : [12,],
+    'size_t' : [4,],
+}
+
+def CheckTypeSize(context, type, includes = None, language = 'C', size = None):
+    """This check can be used to get the size of a given type, or to check whether
+    the type is of expected size.
+
+    Arguments:
+        - type : str
+            the type to check
+        - includes : sequence
+            list of headers to include in the test code before testing the type
+        - language : str
+            'C' or 'C++'
+        - size : int
+            if given, will test wether the type has the given number of bytes.
+            If not given, will test against a list of sizes (all sizes between
+            0 and 16 bytes are tested).
+
+        Returns:
+                status : int
+                        0 if the check failed, or the found size of the type if the check succeeded."""
+    minsz = 0
+    maxsz = 16
+
+    if includes:
+        src = "\n".join([r"#include <%s>\n" % i for i in includes])
+    else:
+        src = ""
+
+    if language == 'C':
+        ext = '.c'
+    elif language == 'C++':
+        ext = '.cpp'
+    else:
+        raise NotImplementedError("%s is not a recognized language" % language)
+
+    # test code taken from autoconf: this is a pretty clever hack to find that
+    # a type is of a given size using only compilation. This speeds things up
+    # quite a bit compared to straightforward code using TryRun
+    src += r"""
+typedef %s scons_check_type;
+
+int main()
+{
+    static int test_array[1 - 2 * !(((long int) (sizeof(scons_check_type))) <= %d)];
+    test_array[0] = 0;
+
+    return 0;
+}
+"""
+
+    if size:
+        # Only check if the given size is the right one
+        context.Message('Checking %s is %d bytes... ' % (type, size))
+        st = context.TryCompile(src % (type, size), ext)
+        context.Result(st)
+
+        if st:
+            return size
+        else:
+            return 0
+    else:
+        # Only check if the given size is the right one
+        context.Message('Checking size of %s ... ' % type)
+
+        # Try sensible defaults first
+        try:
+            szrange = _DEFAULTS[type]
+        except KeyError:
+            szrange = []
+        szrange.extend(xrange(minsz, maxsz))
+        st = 0
+
+        # Actual test
+        for sz in szrange:
+            st = context.TryCompile(src % (type, sz), ext)
+            if st:
+                break
+
+        if st:
+            context.Result('%d' % sz)
+            return sz
+        else:
+            context.Result('Failed !')
+            return 0
+
+#For example, to check wether long is 4 bytes on your platform, you can do:
+#config.CheckTypeSize('long', size = 4).
+## Now check the sizes
diff --git a/python/pyregfi/__init__.py b/python/pyregfi/__init__.py
new file mode 100644
index 0000000..84d06b0
--- /dev/null
+++ b/python/pyregfi/__init__.py
@@ -0,0 +1,1143 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2010-2011 Timothy D. Morgan
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Id: $
+
+## @package pyregfi
+# Python interface to the regfi library.
+#
+
+## @mainpage API Documentation
+#
+# The pyregfi module provides a Python interface to the @ref regfi Windows 
+# registry library.  
+#
+# The library operates on registry hives, each of which is contained within a
+# single file.  The quickest way to get started, is to use the @ref openHive() 
+# function to obtain a Hive object.  For example:
+# @code
+# >>> import pyregfi
+# >>> myHive = pyregfi.openHive('/mnt/win/c/WINDOWS/system32/config/system')
+# @endcode
+#
+# Using this Hive object, one can begin investigating what top-level keys
+# exist by starting with the root Key attribute:
+# @code
+# >>> for key in myHive.root.subkeys:
+# ...   print(key.name)
+# ControlSet001
+# ControlSet003
+# LastKnownGoodRecovery
+# MountedDevices
+# Select
+# Setup
+# WPA
+# @endcode
+#
+# From there, accessing subkeys and values by name is a simple matter of:
+# @code
+# >>> myKey = myHive.root.subkeys['Select']
+# >>> myValue = myKey.values['Current']
+# @endcode
+#
+# The data associated with a Value can be obtained through the fetch_data()
+# method:
+# @code
+# >>> print(myValue.fetch_data())
+# 1
+# @endcode
+# 
+# While useful for simple exercises, using the subkeys object for deeply nested
+# paths is not efficient and doesn't make for particularly attractive code.  
+# Instead, a special-purpose HiveIterator class is provided for simplicity of
+# use and fast access to specific known paths:
+# @code
+# >>> myIter = pyregfi.HiveIterator(myHive)
+# >>> myIter.descend(['ControlSet001','Control','NetworkProvider','HwOrder'])
+# >>> myKey = myIter.current_key()
+# >>> print(myKey.values['ProviderOrder'].fetch_data())
+# RDPNP,LanmanWorkstation,WebClient
+# @endcode
+# 
+# The first two lines above can be simplified in some "syntactic sugar" provided
+# by the Hive.subtree() method.  Also, as one might expect, the HiveIterator 
+# also acts as an iterator, producing keys in a depth-first order.
+# For instance, to traverse all keys under the ControlSet003\\Services key, 
+# printing their names as we go, we could do:
+# @code
+# >>> for key in Hive.subtree(['ControlSet003','Services']):
+# >>>   print(key.name)
+# Services
+# Abiosdsk
+# abp480n5
+# Parameters
+# PnpInterface
+# ACPI
+# [...]
+# @endcode
+#
+# Note that "Services" was printed first, since the subtree is traversed as a 
+# "preordering depth-first" search starting with the HiveIterator's current_key().  
+# As one might expect, traversals of subtrees stops when all elements in a 
+# specific subtree (and none outside of it) have been traversed.
+#
+# For more information, peruse the various attributes and methods available on 
+# the Hive, HiveIterator, Key, Value, and Security classes.
+#
+# @note @ref regfi is a read-only library by design and there 
+# are no plans to implement write support.
+# 
+# @note At present, pyregfi has been tested with Python versions 2.6 and 3.1
+#
+# @note Developers strive to make pyregfi thread-safe.
+# 
+import sys
+import time
+import ctypes
+import ctypes.util
+import threading
+from pyregfi.structures import *
+
+
+## An enumeration of registry Value data types
+#
+# @note This is a static class, there is no need to instantiate it. 
+#       Just access its attributes directly as DATA_TYPES.SZ, etc
+class DATA_TYPES(object):
+    # XXX: add dictionary lookup attributes to convert both directions between
+    #      the integers and typenames
+
+    ## None / Unknown
+    NONE                       =  0
+    ## String
+    SZ                         =  1
+    ## String with %...% expansions
+    EXPAND_SZ                  =  2
+    ## Binary buffer
+    BINARY                     =  3
+    ## 32 bit integer (little endian)
+    DWORD                      =  4 # DWORD, little endian
+    ## 32 bit integer (little endian)
+    DWORD_LE                   =  4
+    ## 32 bit integer (big endian)
+    DWORD_BE                   =  5 # DWORD, big endian
+    ## Symbolic link
+    LINK                       =  6
+    ## List of strings
+    MULTI_SZ                   =  7
+    ## Unknown structure
+    RESOURCE_LIST              =  8
+    ## Unknown structure
+    FULL_RESOURCE_DESCRIPTOR   =  9
+    ## Unknown structure
+    RESOURCE_REQUIREMENTS_LIST = 10
+    ## 64 bit integer
+    QWORD                      = 11 # 64-bit little endian
+
+
+## An enumeration of log message types
+#
+# @note This is a static class, there is no need to instantiate it. 
+#       Just access its attributes directly as LOG_TYPES.INFO, etc
+class LOG_TYPES(object):
+    ## Informational messages, useful in debugging
+    INFO  =  0x01
+    ## Non-critical problems in structure parsing or intepretation
+    WARN  =  0x04
+    ## Major failures
+    ERROR =  0x10
+
+
+def _buffer2bytearray(char_pointer, length):
+    if length == 0 or char_pointer == None:
+        return None
+    
+    ret_val = bytearray(length)
+    for i in range(0,length):
+        ret_val[i] = char_pointer[i][0]
+
+    return ret_val
+
+
+def _strlist2charss(str_list):
+    ret_val = []
+    for s in str_list:
+        ret_val.append(s.encode('utf-8', 'replace'))
+
+    ret_val = (ctypes.c_char_p*(len(str_list)+1))(*ret_val)
+    # Terminate the char** with a NULL pointer
+    ret_val[-1] = 0
+
+    return ret_val
+
+
+def _charss2strlist(chars_pointer):
+    ret_val = []
+    i = 0
+    s = chars_pointer[i]
+    while s:
+        ret_val.append(s.decode('utf-8', 'replace'))
+        i += 1
+        s = chars_pointer[i]
+
+    return ret_val
+
+
+
+## Returns the (py)regfi library version
+#
+# @return A string indicating the version
+def getVersion():
+    return regfi.regfi_version()
+
+
+## Retrieves messages produced by regfi during parsing and interpretation
+#
+# The regfi C library may generate log messages stored in a special thread-safe
+# global data structure.  These messages should be retrieved periodically or 
+# after each major operation by callers to determine if any errors or warnings
+# should be reported to the user.  Failure to retrieve these could result in 
+# excessive memory consumption.
+def getLogMessages():
+    msgs = regfi.regfi_log_get_str()
+    if not msgs:
+        return ''
+    return msgs.decode('utf-8')
+
+
+## Sets the types of log messages to record
+#
+# @param log_types A sequence of message types that regfi should generate.
+#                  Message types can be found in the LOG_TYPES enumeration.
+#
+# @return True on success, False on failure.  Failures are rare, but could
+#         indicate that global logging is not operating as expected.
+# 
+# Example:
+# @code
+# setLogMask((LOG_TYPES.ERROR, LOG_TYPES.WARN, LOG_TYPES.INFO))
+# @endcode
+#
+# The message mask is a global (all hives, iterators), thread-specific value.
+# For more information, see @ref regfi_log_set_mask.
+#
+def setLogMask(log_types):
+    mask = 0
+    for m in log_types:
+        mask |= m
+    return regfi.regfi_log_set_mask(mask)
+
+
+## Opens a file as a registry hive
+#
+# @param path The file path of a hive, as one would provide to the 
+#             open() built-in
+#
+# @return A new Hive instance
+def openHive(path):
+    fh = open(path, 'rb')
+    return Hive(fh)
+
+
+## Abstract class for most objects returned by the library
+class _StructureWrapper(object):
+    _hive = None
+    _base = None
+
+    def __init__(self, hive, base):
+        if not hive:
+            raise Exception("Could not create _StructureWrapper,"
+                            + " hive is NULL.  Current log:\n"
+                            + getLogMessages())
+        if not base:
+            raise Exception("Could not create _StructureWrapper,"
+                            + " base is NULL.  Current log:\n"
+                            + getLogMessages())
+        self._hive = hive
+        self._base = base
+
+
+    # Memory management for most regfi structures is taken care of here
+    def __del__(self):
+        if self._base:
+            regfi.regfi_free_record(self._hive.file, self._base)
+
+
+    # Any attribute requests not explicitly defined in subclasses gets passed
+    # to the equivalent REGFI_* structure defined in structures.py
+    def __getattr__(self, name):
+        return getattr(self._base.contents, name)
+
+    
+    ## Test for equality
+    #
+    # Records returned by pyregfi may be compared with one another.  For example:
+    # @code
+    #  >>> key2 = key1.subkeys['child']
+    #  >>> key1 == key2
+    #  False
+    #  >>> key1 != key2
+    #  True
+    #  >>> key1 == key2.get_parent()
+    #  True
+    # @endcode
+    def __eq__(self, other):
+        return (type(self) == type(other)) and (self.offset == other.offset)
+
+
+    def __ne__(self, other):
+        return (not self.__eq__(other))
+
+
+class Key():
+    pass
+
+
+class Value():
+    pass
+
+
+
+## Represents a registry SK record which contains a security descriptor
+# 
+class Security(_StructureWrapper):
+    ## Number of registry Keys referencing this SK record
+    ref_count = 1
+
+    ## The absolute file offset of the SK record's cell in the Hive file
+    offset = 0xCAFEBABE
+
+    ## The @ref winsec.SecurityDescriptor for this SK record
+    descriptor = object()
+
+    def __init__(self, hive, base):
+        super(Security, self).__init__(hive, base)
+        # XXX: add checks for NULL pointers
+        self.descriptor = winsec.SecurityDescriptor(base.contents.sec_desc.contents)
+
+    ## Loads the "next" Security record in the hive
+    #
+    # @note
+    # SK records are included in a circular, doubly-linked list.
+    # To iterate over all SK records, be sure to check for the repetition of
+    # the SK record you started with to determine when all have been traversed.
+    def next_security(self):
+        return Security(self._hive,
+                        regfi.regfi_next_sk(self._hive.file, self._base))
+
+    ## Loads the "previous" Security record in the hive
+    #
+    # @note
+    # SK records are included in a circular, doubly-linked list.
+    # To iterate over all SK records, be sure to check for the repetition of
+    # the SK record you started with to determine when all have been traversed.
+    def prev_security(self):
+        return Security(self._hive,
+                        regfi.regfi_prev_sk(self._hive.file, self._base))
+
+
+## Abstract class for ValueList and SubkeyList
+class _GenericList(object):
+    # XXX: consider implementing keys(), values(), items() and other dictionary methods
+    _hive = None
+    _key_base = None
+    _length = None
+    _current = None
+
+    # implementation-specific functions for SubkeyList and ValueList
+    _fetch_num = None
+    _find_element = None
+    _get_element = None
+    _constructor = None
+
+    def __init__(self, key):
+        if not key:
+            raise Exception("Could not create _GenericList; key is NULL."
+                            + "Current log:\n" + getLogMessages())
+
+        base = regfi.regfi_reference_record(key._hive.file, key._base)
+        if not base:
+            raise Exception("Could not create _GenericList; memory error."
+                            + "Current log:\n" + getLogMessages())
+        self._key_base = cast(base, type(key._base))
+        self._length = self._fetch_num(self._key_base)
+        self._hive = key._hive
+
+    
+    def __del__(self):
+        regfi.regfi_free_record(self._hive.file, self._key_base)
+
+
+    ## Length of list
+    def __len__(self):
+        return self._length
+
+
+    ## Retrieves a list element by name
+    #
+    # @param name The name of the subkey or value desired.  
+    #             This is case-insensitive.
+    #
+    # @note The registry format does not inherently prevent multiple
+    #       subkeys or values from having the same name, having a key
+    #       and a value with the same name, or having the same name in
+    #       different cases that could both match. 
+    #       This interface simply returns the first match in the list.
+    #       Lookups using this method could also fail due to incorrectly
+    #       encoded strings stored as names.
+    #       To identify any duplicates or elements with malformed names,
+    #       use the iterator interface to check every list element.
+    #
+    # @return the first element whose name matches, or None if the element
+    #         could not be found
+    def __getitem__(self, name):
+        # XXX: Consider interpreting integer names as offsets in the underlying list
+        index = ctypes.c_uint32()
+        if isinstance(name, str):
+            name = name.encode('utf-8')
+
+        if name != None:
+            name = create_string_buffer(bytes(name))
+
+        if self._find_element(self._hive.file, self._key_base, 
+                              name, byref(index)):
+            return self._constructor(self._hive,
+                                     self._get_element(self._hive.file,
+                                                       self._key_base,
+                                                       index))
+        raise KeyError('')
+
+
+    ## Fetches the requested element by name, or the default value if the lookup
+    #  fails.
+    # 
+    def get(self, name, default):
+        try:
+            return self[name]
+        except KeyError:
+            return default
+    
+    def __iter__(self):
+        self._current = 0
+        return self
+    
+    def __next__(self):
+        if self._current >= self._length:
+            raise StopIteration('')
+
+        elem = self._get_element(self._hive.file, self._key_base,
+                                 ctypes.c_uint32(self._current))
+        self._current += 1
+        return self._constructor(self._hive, elem)
+    
+    # For Python 2.x
+    next = __next__
+
+
+## The list of subkeys associated with a Key
+#
+# This attribute is both iterable:
+# @code
+#   for k in myKey.subkeys: 
+#     ...
+# @endcode
+# and accessible as a dictionary:
+# @code
+#   mySubkey = myKey.subkeys["keyName"]
+# @endcode
+#
+# You may also request the len() of a subkeys list. 
+# However keys(), values(), items() and similar methods are not currently 
+# implemented.
+class SubkeyList(_GenericList):
+    _fetch_num = regfi.regfi_fetch_num_subkeys
+    _find_element = regfi.regfi_find_subkey
+    _get_element = regfi.regfi_get_subkey
+
+
+## The list of values associated with a Key
+#
+# This attribute is both iterable:
+# @code
+#   for v in myKey.values: 
+#     ...
+# @endcode
+# and accessible as a dictionary:
+# @code
+#   myValue = myKey.values["valueName"]
+# @endcode
+#
+# You may also request the len() of a values list. 
+# However keys(), values(), items() and similar methods are not currently 
+# implemented.
+class ValueList(_GenericList):
+    _fetch_num = regfi.regfi_fetch_num_values
+    _find_element = regfi.regfi_find_value
+    _get_element = regfi.regfi_get_value
+
+
+## Registry key 
+# These represent registry keys (@ref REGFI_NK records) and provide
+# access to their subkeys, values, and other metadata.
+#
+# @note Key instances may provide access to more attributes than are
+#       documented here.  However, undocumented attributes may change over time
+#       and are not officially supported.  If you need access to an attribute 
+#       not shown here, see @ref pyregfi.structures.
+class Key(_StructureWrapper):
+    ## A @ref ValueList object representing the list of Values 
+    #  stored on this Key
+    values = None
+
+    ## A @ref SubkeyList object representing the list of subkeys 
+    #  stored on this Key
+    subkeys = None
+
+    ## The raw Key name as an uninterpreted bytearray
+    name_raw = (b"...")
+    
+    ## The name of the Key as a (unicode) string
+    name = "..."
+    
+    ## The string encoding used to store the Key's name ("ascii" or "utf-16-le")
+    name_encoding = "ascii"
+
+    ## The absolute file offset of the Key record's cell in the Hive file
+    offset = 0xCAFEBABE
+
+    ## This Key's last modified time represented as the number of seconds 
+    #  since the UNIX epoch in UTC; similar to what time.time() returns
+    modified = 1300000000.123456
+
+    ## The NK record's flags field
+    flags = 0x10110001
+
+    def __init__(self, hive, base):
+        super(Key, self).__init__(hive, base)
+        self.values = ValueList(self)
+        self.subkeys = SubkeyList(self)
+
+    def __getattr__(self, name):
+        if name == "name":
+            ret_val = super(Key, self).__getattr__(name)
+
+            if not ret_val:
+                ret_val = self.name_raw
+                if ret_val != None:
+                    ret_val = ret_val.decode(self.name_encoding, 'replace')
+            else:
+                ret_val = ret_val.decode('utf-8', 'replace')
+                
+        elif name == "name_encoding":
+            flags = super(Key, self).__getattr__("flags")
+            if (flags & structures.REGFI_NK_FLAG_ASCIINAME) > 0:
+                ret_val = "ascii"
+            ret_val = "utf-16-le"
+
+        elif name == "name_raw":
+            ret_val = super(Key, self).__getattr__(name)
+            length = super(Key, self).__getattr__('name_length')
+            ret_val = _buffer2bytearray(ret_val, length)
+
+        elif name == "modified":
+            ret_val = regfi.regfi_nt2unix_time(self._base.contents.mtime)
+
+        else:
+            ret_val = super(Key, self).__getattr__(name)
+
+        return ret_val
+
+
+    ## Retrieves the Security properties for this key
+    def fetch_security(self):
+        return Security(self._hive,
+                        regfi.regfi_fetch_sk(self._hive.file, self._base))
+
+
+    ## Retrieves the class name for this key
+    #
+    # Class names are typically stored as UTF-16LE strings, so these are decoded
+    # into proper python (unicode) strings.  However, if this fails, a bytearray
+    # is instead returned containing the raw buffer stored for the class name.
+    #
+    # @return The class name as a string or bytearray.  None if a class name
+    #         doesn't exist or an unrecoverable error occurred during retrieval.
+    def fetch_classname(self):
+        ret_val = None
+        cn_p = regfi.regfi_fetch_classname(self._hive.file, self._base)
+        if cn_p:
+            cn_struct = cn_p.contents
+            if cn_struct.interpreted:
+                ret_val = cn_struct.interpreted.decode('utf-8', 'replace')
+            else:
+                ret_val = _buffer2bytearray(cn_struct.raw,
+                                            cn_struct.size)
+            regfi.regfi_free_record(self._hive.file, cn_p)
+
+        return ret_val
+
+
+    ## Retrieves this key's parent key
+    #
+    # @return The parent's Key instance or None if current key is root 
+    #         (or an error occured) 
+    def get_parent(self):
+        if self.is_root():
+            return None
+        parent_base = regfi.regfi_get_parentkey(self._hive.file, self._base)
+        if parent_base:
+            return Key(self._hive, parent_base)
+        return None
+
+
+    ## Checks to see if this Key is the root of its Hive
+    #
+    #  @return True if it is, False otherwise
+    def is_root(self):
+        return (self._hive.root == self)
+
+
+## Registry value (metadata)
+#
+# These represent registry values (@ref REGFI_VK records) and provide
+# access to their associated data.
+#
+# @note Value instances may provide access to more attributes than are
+#       documented here.  However, undocumented attributes may change over time
+#       and are not officially supported.  If you need access to an attribute
+#       not shown here, see @ref pyregfi.structures.
+class Value(_StructureWrapper):
+    ## The raw Value name as an uninterpreted bytearray
+    name_raw = (b"...")
+    
+    ## The name of the Value as a (unicode) string
+    name = "..."
+    
+    ## The string encoding used to store the Value's name ("ascii" or "utf-16-le")
+    name_encoding = "ascii"
+
+    ## The absolute file offset of the Value record's cell in the Hive file
+    offset = 0xCAFEBABE
+
+    ## The length of data advertised in the VK record
+    data_size = 0xCAFEBABE
+
+    ## An integer which represents the data type for this Value's data
+    # Typically this value is one of 12 types defined in @ref DATA_TYPES,
+    # but in some cases (the SAM hive) it may be used for other purposes
+    type = DATA_TYPES.NONE
+
+    ## The VK record's flags field
+    flags = 0x10110001
+
+    ## Retrieves the Value's data according to advertised type
+    #
+    # Data is loaded from its cell(s) and then interpreted based on the data
+    # type recorded in the Value.  It is not uncommon for data to be stored with
+    # the wrong type or even with invalid types.  If you have difficulty 
+    # obtaining desired data here, use @ref fetch_raw_data().
+    #
+    # @return The interpreted representation of the data as one of several
+    #         possible Python types, as listed below.  None if any failure 
+    #         occurred during extraction or conversion.
+    #
+    # @retval string for SZ, EXPAND_SZ, and LINK
+    # @retval int for DWORD, DWORD_BE, and QWORD
+    # @retval list(string) for MULTI_SZ
+    # @retval bytearray for NONE, BINARY, RESOURCE_LIST, 
+    #         FULL_RESOURCE_DESCRIPTOR, and RESOURCE_REQUIREMENTS_LIST
+    #
+    def fetch_data(self):
+        ret_val = None
+        data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
+        if not data_p:
+            return None
+        data_struct = data_p.contents
+
+        if data_struct.interpreted_size == 0:
+            ret_val = None
+        elif data_struct.type in (DATA_TYPES.SZ, DATA_TYPES.EXPAND_SZ, DATA_TYPES.LINK):
+            # Unicode strings
+            ret_val = data_struct.interpreted.string.decode('utf-8', 'replace')
+        elif data_struct.type in (DATA_TYPES.DWORD, DATA_TYPES.DWORD_BE):
+            # 32 bit integers
+            ret_val = data_struct.interpreted.dword
+        elif data_struct.type == DATA_TYPES.QWORD:
+            # 64 bit integers
+            ret_val = data_struct.interpreted.qword
+        elif data_struct.type == DATA_TYPES.MULTI_SZ:
+            ret_val = _charss2strlist(data_struct.interpreted.multiple_string)
+        elif data_struct.type in (DATA_TYPES.NONE, DATA_TYPES.RESOURCE_LIST,
+                                  DATA_TYPES.FULL_RESOURCE_DESCRIPTOR,
+                                  DATA_TYPES.RESOURCE_REQUIREMENTS_LIST,
+                                  DATA_TYPES.BINARY):
+            ret_val = _buffer2bytearray(data_struct.interpreted.none,
+                                        data_struct.interpreted_size)
+
+        regfi.regfi_free_record(self._hive.file, data_p)
+        return ret_val
+    
+
+    ## Retrieves raw representation of Value's data
+    #
+    # @return A bytearray containing the data
+    #
+    def fetch_raw_data(self):
+        ret_val = None
+        # XXX: should we load the data without interpretation instead?
+        data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
+        if not data_p:
+            return None
+
+        data_struct = data_p.contents
+        ret_val = _buffer2bytearray(data_struct.raw,
+                                    data_struct.size)
+        regfi.regfi_free_record(self._hive.file, data_p)
+        return ret_val
+
+
+    def __getattr__(self, name):
+        ret_val = None
+        if name == "name":
+            ret_val = super(Value, self).__getattr__(name)
+            if not ret_val:
+                ret_val = self.name_raw
+                if ret_val != None:
+                    ret_val = ret_val.decode(self.name_encoding, 'replace')
+            else:
+                ret_val = ret_val.decode('utf-8', 'replace')
+
+        elif name == "name_encoding":
+            flags = super(Value, self).__getattr__("flags")
+            if (flags & structures.REGFI_VK_FLAG_ASCIINAME) > 0:
+                ret_val = "ascii"
+            else:
+                ret_val = "utf-16-le"
+
+        elif name == "name_raw":
+            ret_val = super(Value, self).__getattr__(name)
+            length = super(Value, self).__getattr__('name_length')
+            ret_val = _buffer2bytearray(ret_val, length)
+
+        else:
+            ret_val = super(Value, self).__getattr__(name)
+
+        return ret_val
+
+
+# Avoids chicken/egg class definitions.
+# Also makes for convenient code reuse in these lists' parent classes.
+SubkeyList._constructor = Key
+ValueList._constructor = Value
+
+
+
+## Represents a single registry hive (file)
+class Hive():
+    file = None
+    raw_file = None
+    _fh = None
+    #_root = None
+
+
+    ## The root Key of this Hive
+    root = None
+
+    ## This Hives's last modified time represented as the number of seconds 
+    #  since the UNIX epoch in UTC; similar to what time.time() returns
+    modified = 1300000000.123456
+
+    ## First sequence number
+    sequence1 = 12345678
+
+    ## Second sequence number
+    sequence2 = 12345678
+
+    ## Major version
+    major_version = 1
+
+    ## Minor version
+    minor_version = 5
+
+    ## Constructor
+    #
+    # Initialize a new Hive based on a Python file object.  To open a file by 
+    # path, see @ref openHive.
+    #
+    # @param fh A Python file object.  The constructor first looks for a valid
+    #           fileno attribute on this object and uses it if possible.  
+    #           Otherwise, the seek and read methods are used for file
+    #           access.
+    #
+    # @note Supplied file must be seekable.  Do not perform any operation on
+    #       the provided file object while a Hive is using it.  Do not 
+    #       construct multiple Hive instances from the same file object.
+    #       If a file must be accessed by separate code and pyregfi 
+    #       simultaneously, use a separate file descriptor.  Hives are 
+    #       thread-safe, so multiple threads may use a single Hive object.
+    def __init__(self, fh):
+        # The fileno method may not exist, or it may throw an exception
+        # when called if the file isn't backed with a descriptor.
+        self._fh = fh
+        fn = None
+        try:
+            # XXX: Native calls to Windows filenos don't seem to work.  
+            #      Need to investigate why.
+            if not is_win32 and hasattr(fh, 'fileno'):
+                fn = fh.fileno()
+        except:
+            pass
+
+        if fn != None:
+            self.file = regfi.regfi_alloc(fn, REGFI_ENCODING_UTF8)
+            if not self.file:
+                # XXX: switch to non-generic exception
+                raise Exception("Could not open registry file.  Current log:\n"
+                                + getLogMessages())
+        else:
+            fh.seek(0)
+            self.raw_file = structures.REGFI_RAW_FILE()
+            self.raw_file.fh = fh
+            self.raw_file.seek = seek_cb_type(self.raw_file.cb_seek)
+            self.raw_file.read = read_cb_type(self.raw_file.cb_read)
+            self.file = regfi.regfi_alloc_cb(pointer(self.raw_file), REGFI_ENCODING_UTF8)
+            if not self.file:
+                # XXX: switch to non-generic exception
+                raise Exception("Could not open registry file.  Current log:\n"
+                                + getLogMessages())
+
+
+    def __getattr__(self, name):
+        if name == "root":
+            # XXX: This creates reference loops.  Need to cache better inside regfi
+            #if self._root == None:
+            #    self._root = Key(self, regfi.regfi_get_rootkey(self.file))
+            #return self._root
+            return Key(self, regfi.regfi_get_rootkey(self.file))
+
+        elif name == "modified":
+            return regfi.regfi_nt2unix_time(self._base.contents.mtime)
+
+        return getattr(self.file.contents, name)
+
+    
+    def __del__(self):
+        if self.file:
+            regfi.regfi_free(self.file)
+
+    def __iter__(self):
+        return HiveIterator(self)
+
+
+    ## Creates a @ref HiveIterator initialized at the specified path in
+    #  the hive.
+    #
+    # @param path A list of Key names which represent an absolute path within
+    #             the Hive
+    #
+    # @return A @ref HiveIterator which is positioned at the specified path.
+    # 
+    # @exception Exception If the path could not be found/traversed
+    def subtree(self, path):
+        hi = HiveIterator(self)
+        hi.descend(path)
+        return hi
+
+
+## A special purpose iterator for registry hives
+#
+# Iterating over an object of this type causes all keys in a specific
+# hive subtree to be returned in a depth-first manner. These iterators
+# are typically created using the @ref Hive.subtree() function on a @ref Hive
+# object.
+#
+# HiveIterators can also be used to manually traverse up and down a
+# registry hive as they retain information about the current position in
+# the hive, along with which iteration state for subkeys and values for
+# every parent key.  See the @ref up and @ref down methods for more
+# information.
+class HiveIterator():
+    _hive = None
+    _iter = None
+    _iteration_root = None
+    _lock = None
+
+    def __init__(self, hive):
+        self._iter = regfi.regfi_iterator_new(hive.file)
+        if not self._iter:
+            raise Exception("Could not create iterator.  Current log:\n"
+                            + getLogMessages())
+        self._hive = hive
+        self._lock = threading.RLock()
+    
+    def __getattr__(self, name):
+        self._lock.acquire()
+        ret_val = getattr(self._iter.contents, name)
+        self._lock.release()
+        return ret_val
+
+    def __del__(self):
+        self._lock.acquire()
+        regfi.regfi_iterator_free(self._iter)
+        self._lock.release()
+
+    def __iter__(self):
+        self._lock.acquire()
+        self._iteration_root = None
+        self._lock.release()
+        return self
+
+    def __next__(self):
+        self._lock.acquire()
+        if self._iteration_root == None:
+            self._iteration_root = self.current_key().offset
+        elif not regfi.regfi_iterator_down(self._iter):
+            up_ret = regfi.regfi_iterator_up(self._iter)
+            while (up_ret and
+                   not regfi.regfi_iterator_next_subkey(self._iter)):
+                if self._iteration_root == self.current_key().offset:
+                    self._iteration_root = None
+                    self._lock.release()
+                    raise StopIteration('')
+                up_ret = regfi.regfi_iterator_up(self._iter)
+
+            if not up_ret:
+                self._iteration_root = None
+                self._lock.release()
+                raise StopIteration('')
+            
+            # XXX: Use non-generic exception
+            if not regfi.regfi_iterator_down(self._iter):
+                self._lock.release()
+                raise Exception('Error traversing iterator downward.'+
+                                ' Current log:\n'+ getLogMessages())
+
+        regfi.regfi_iterator_first_subkey(self._iter)
+        ret_val = self.current_key()
+        self._lock.release()
+
+        return ret_val
+
+
+    # For Python 2.x
+    next = __next__
+
+    # XXX: Should add sanity checks on some of these traversal functions
+    #      to throw exceptions if a traversal/retrieval *should* have worked
+    #      but failed for some reason.
+
+    ## Descends the iterator to a subkey
+    #
+    # Descends the iterator one level to the current subkey, or a subkey
+    # specified by name.
+    #
+    # @param subkey_name If specified, locates specified subkey by name
+    #                    (via find_subkey()) and descends to it.
+    #
+    # @return True if successful, False otherwise
+    def down(self, subkey_name=None):
+        ret_val = None
+        if subkey_name == None:
+            self._lock.acquire()
+            ret_val = regfi.regfi_iterator_down(self._iter)
+        else:
+            if name != None:
+                name = name.encode('utf-8')
+            self._lock.acquire()
+            ret_val = (regfi.regfi_iterator_find_subkey(self._iter, name) 
+                       and regfi.regfi_iterator_down(self._iter))
+        
+        self._lock.release()
+        return ret_val
+
+
+    ## Causes the iterator to ascend to the current Key's parent
+    #
+    # @return True if successful, False otherwise
+    #
+    # @note The state of current subkeys and values at this level in the tree
+    #       is lost as a side effect.  That is, if you go up() and then back
+    #       down() again, current_subkey() and current_value() will return 
+    #       default selections.
+    def up(self):
+        self._lock.acquire()
+        ret_val = regfi.regfi_iterator_up(self._iter)
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects first subkey of current key
+    #
+    # @return A Key instance for the first subkey.  
+    #         None on error or if the current key has no subkeys.
+    def first_subkey(self):
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_first_subkey(self._iter):
+            ret_val = self.current_subkey()
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects first value of current Key
+    #
+    # @return A Value instance for the first value.  
+    #         None on error or if the current key has no values.
+    def first_value(self):
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_first_value(self._iter):
+            ret_val = self.current_value()
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects the next subkey in the current Key's list
+    #
+    # @return A Key instance for the next subkey.
+    #         None if there are no remaining subkeys or an error occurred.
+    def next_subkey(self):
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_next_subkey(self._iter):
+            ret_val = self.current_subkey()
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects the next value in the current Key's list
+    #  
+    # @return A Value instance for the next value.
+    #         None if there are no remaining values or an error occurred.
+    def next_value(self):
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_next_value(self._iter):
+            ret_val = self.current_value()
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects the first subkey which has the specified name
+    #
+    # @return A Key instance for the selected key. 
+    #         None if it could not be located or an error occurred.
+    def find_subkey(self, name):
+        if name != None:
+            name = name.encode('utf-8')
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_find_subkey(self._iter, name):
+            ret_val = self.current_subkey()
+        self._lock.release()
+        return ret_val
+
+
+    ## Selects the first value which has the specified name
+    #
+    # @return A Value instance for the selected value.
+    #         None if it could not be located or an error occurred.
+    def find_value(self, name):
+        if name != None:
+            name = name.encode('utf-8')
+        ret_val = None
+        self._lock.acquire()
+        if regfi.regfi_iterator_find_value(self._iter, name):
+            ret_val = self.current_value()
+        self._lock.release()
+        return ret_val
+
+    ## Retrieves the currently selected subkey
+    #
+    # @return A Key instance of the current subkey
+    def current_subkey(self):
+        self._lock.acquire()
+        ret_val = Key(self._hive, regfi.regfi_iterator_cur_subkey(self._iter))
+        self._lock.release()
+        return ret_val
+
+    ## Retrieves the currently selected value
+    #
+    # @return A Value instance of the current value
+    def current_value(self):
+        self._lock.acquire()
+        ret_val = Value(self._hive, regfi.regfi_iterator_cur_value(self._iter))
+        self._lock.release()
+        return ret_val
+
+    ## Retrieves the current key
+    #
+    # @return A Key instance of the current position of the iterator
+    def current_key(self):
+        self._lock.acquire()
+        ret_val = Key(self._hive, regfi.regfi_iterator_cur_key(self._iter))
+        self._lock.release()
+        return ret_val
+
+    ## Traverse downward multiple levels
+    #
+    # This is more efficient than calling down() multiple times
+    #
+    # @param path A list of Key names which represent the path to descend
+    #
+    # @exception Exception If path could not be located
+    def descend(self, path):
+        cpath = _strlist2charss(path)
+
+        self._lock.acquire()
+        result = regfi.regfi_iterator_descend(self._iter, cpath)
+        self._lock.release()
+        if not result:
+            # XXX: Use non-generic exception
+            raise Exception('Could not locate path.\n'+getLogMessages())
+
+    ## Obtains a list of the current key's ancestry
+    #
+    # @return A list of all parent keys starting with the root Key and ending
+    #         with the current Key
+    def ancestry(self):
+        self._lock.acquire()
+        result = regfi.regfi_iterator_ancestry(self._iter)
+        self._lock.release()
+
+        ret_val = []
+        i = 0
+        k = result[i]
+        while k:
+            k = cast(regfi.regfi_reference_record(self._hive.file, k), POINTER(REGFI_NK))
+            ret_val.append(Key(self._hive, k))
+            i += 1
+            k = result[i]
+
+        regfi.regfi_free_record(self._hive.file, result)
+        return ret_val
+
+    ## Obtains the current path of the iterator
+    #
+    # @return A list of key names starting with the root up to and
+    #         including the current key
+    #
+    def current_path(self):
+        ancestry = self.ancestry()
+        return [a.name for a in ancestry]
+
+
+# Freeing symbols defined for the sake of documentation
+del Value.name,Value.name_encoding,Value.name_raw,Value.offset,Value.data_size,Value.type,Value.flags
+del Key.name,Key.name_encoding,Key.name_raw,Key.offset,Key.modified,Key.flags
+del Hive.root,Hive.modified,Hive.sequence1,Hive.sequence2,Hive.major_version,Hive.minor_version
+del Security.ref_count,Security.offset,Security.descriptor
diff --git a/python/pyregfi/structures.py b/python/pyregfi/structures.py
new file mode 100644
index 0000000..7dec55a
--- /dev/null
+++ b/python/pyregfi/structures.py
@@ -0,0 +1,368 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2010-2011 Timothy D. Morgan
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Id: $
+
+## @package pyregfi.structures
+# Low-level data structures and C API mappings.
+#
+# Most users need not venture here.  For more information, see the source.
+
+import sys
+import os
+import traceback
+import ctypes
+import ctypes.util
+from ctypes import *
+
+is_win32 = hasattr(ctypes, 'windll')
+
+# XXX: can we always be sure enums are this size?
+REGFI_ENCODING = c_uint32
+REGFI_ENCODING_UTF8 = REGFI_ENCODING(1)
+
+REGFI_NK_FLAG_ASCIINAME = 0x0020
+REGFI_VK_FLAG_ASCIINAME = 0x0001
+
+REGFI_DATA_TYPE = c_uint32
+REGFI_NTTIME = c_uint64
+
+REGFI_REGF_SIZE = 0x1000
+
+# Prototype everything first so we don't have to worry about reference order
+class REGFI_VK(Structure):
+    pass
+
+class REGFI_SK(Structure):
+    pass
+
+class REGFI_SUBKEY_LIST(Structure):
+    pass
+
+class REGFI_VALUE_LIST(Structure):
+    pass
+
+class REGFI_CLASSNAME(Structure):
+    pass
+
+class REGFI_DATA(Structure):
+    pass
+
+class REGFI_NK(Structure):
+    pass
+
+class REGFI_ITERATOR(Structure):
+    pass
+
+class REGFI_FILE(Structure):
+    pass
+
+class REGFI_RAW_FILE(Structure):
+    fh = None
+    
+    def cb_seek(self, raw_file, offset, whence):
+        try:
+            self.fh.seek(offset, whence)
+        except Exception:
+            traceback.print_exc()
+            set_errno(74) # os.EX_IOERR
+            return -1
+
+        return self.fh.tell()
+
+
+    def cb_read(self, raw_file, buf, count):
+        try:
+            # XXX: anyway to do a readinto() here?
+            tmp = self.fh.read(count)
+            memmove(buf,tmp,len(tmp))
+
+        except Exception:
+            traceback.print_exc()
+            set_errno(74) # os.EX_IOERR
+            return -1
+        return len(tmp)
+
+
+# Load libregfi according to platform
+regfi = None
+if is_win32:
+    # XXX: Using C calling conventions on cross-compiled DLLs seems to work fine
+    #      on Windows, but I'm not sure if stdcall symbols are supported 
+    #      correctly for native Windows binaries...
+    #regfi = ctypes.windll.libregfi
+    #CB_FACTORY = ctypes.WINFUNCTYPE
+    regfi = ctypes.CDLL('libregfi.dll', use_errno=True)
+    CB_FACTORY = ctypes.CFUNCTYPE
+else:
+    regfi = ctypes.CDLL(ctypes.util.find_library('regfi'), use_errno=True)
+    CB_FACTORY = ctypes.CFUNCTYPE
+
+seek_cb_type = CB_FACTORY(c_int64, POINTER(REGFI_RAW_FILE), c_uint64, c_int, use_errno=True)
+read_cb_type = CB_FACTORY(c_int64, POINTER(REGFI_RAW_FILE), POINTER(c_char), c_size_t, use_errno=True)
+
+
+from .winsec import *
+
+REGFI_VK._fields_ = [('offset', c_uint32),
+                     ('cell_size', c_uint32),
+                     ('name', c_char_p),
+                     ('name_raw', POINTER(c_char)),
+                     ('name_length', c_uint16),
+                     ('hbin_off', c_uint32),
+                     ('data_size', c_uint32),
+                     ('data_off', c_uint32),
+                     ('type', REGFI_DATA_TYPE),
+                     ('magic', c_char * 2),
+                     ('flags', c_uint16),
+                     ('unknown1', c_uint16),
+                     ('data_in_offset', c_bool),
+                     ]
+
+
+REGFI_SK._fields_ = [('offset', c_uint32),
+                     ('cell_size', c_uint32),
+                     ('sec_desc', POINTER(WINSEC_DESC)),
+                     ('hbin_off', c_uint32),
+                     ('prev_sk_off', c_uint32),
+                     ('next_sk_off', c_uint32),
+                     ('ref_count', c_uint32),
+                     ('desc_size', c_uint32),
+                     ('unknown_tag', c_uint16),
+                     ('magic', c_char * 2),
+                     ]
+
+
+REGFI_NK._fields_ = [('offset', c_uint32),
+                     ('cell_size', c_uint32),
+                     ('values', POINTER(REGFI_VALUE_LIST)),
+                     ('subkeys', POINTER(REGFI_SUBKEY_LIST)),
+                     ('flags', c_uint16),
+                     ('magic', c_char * 2),
+                     ('mtime', REGFI_NTTIME),
+                     ('name_length', c_uint16),
+                     ('classname_length', c_uint16),
+                     ('name', c_char_p),
+                     ('name_raw', POINTER(c_char)),
+                     ('parent_off', c_uint32),
+                     ('classname_off', c_uint32),
+                     ('max_bytes_subkeyname', c_uint32),
+                     ('max_bytes_subkeyclassname', c_uint32),
+                     ('max_bytes_valuename', c_uint32),
+                     ('max_bytes_value', c_uint32),
+                     ('unknown1', c_uint32),
+                     ('unknown2', c_uint32),
+                     ('unknown3', c_uint32),
+                     ('unk_index', c_uint32),
+                     ('num_subkeys', c_uint32),
+                     ('subkeys_off', c_uint32),
+                     ('num_values', c_uint32),
+                     ('values_off', c_uint32),
+                     ('sk_off', c_uint32),
+                     ]
+
+
+REGFI_SUBKEY_LIST._fields_ = [('offset', c_uint32),
+                              ('cell_size', c_uint32),
+                              ('num_children', c_uint32),
+                              ('num_keys', c_uint32),
+                              ('elements', c_void_p),
+                              ('magic', c_char * 2),
+                              ('recursive_type', c_bool),
+                              ]
+
+
+REGFI_VALUE_LIST._fields_ = [('offset', c_uint32),
+                             ('cell_size', c_uint32),
+                             ('num_values', c_uint32),
+                             ('elements', c_void_p),
+                             ]
+
+REGFI_CLASSNAME._fields_ = [('offset', c_uint32),
+                            ('interpreted', c_char_p),
+                            ('raw', POINTER(c_char)),
+                            ('size', c_uint16),
+                            ]
+
+
+class REGFI_DATA__interpreted(Union):
+    _fields_ = [('none',POINTER(c_char)),
+                ('string', c_char_p),
+                ('expand_string', c_char_p),
+                ('binary',POINTER(c_char)),
+                ('dword', c_uint32),
+                ('dword_be', c_uint32),
+                ('link', c_char_p),
+                ('multiple_string', POINTER(c_char_p)),
+                ('qword', c_uint64),
+                ('resource_list',POINTER(c_char)),
+                ('full_resource_descriptor',POINTER(c_char)),
+                ('resource_requirements_list',POINTER(c_char)),
+                ]
+REGFI_DATA._fields_ = [('offset', c_uint32),
+                       ('type', REGFI_DATA_TYPE),
+                       ('size', c_uint32),
+                       ('raw', POINTER(c_char)),
+                       ('interpreted_size', c_uint32),
+                       ('interpreted', REGFI_DATA__interpreted),
+                       ]
+
+
+REGFI_FILE._fields_ = [('magic', c_char * 4),
+                       ('sequence1', c_uint32),
+                       ('sequence2', c_uint32),
+                       ('mtime', REGFI_NTTIME),
+                       ('major_version', c_uint32),
+                       ('minor_version', c_uint32),
+                       ('type', c_uint32),
+                       ('format', c_uint32),
+                       ('root_cell', c_uint32),
+                       ('last_block', c_uint32),
+                       ('cluster', c_uint32),
+                       ]
+
+
+REGFI_RAW_FILE._fields_ = [('seek', seek_cb_type),
+                           ('read', read_cb_type),
+                           ('cur_off', c_uint64),
+                           ('size', c_uint64),
+                           ('state', c_void_p),
+                           ]
+
+
+# Define function prototypes
+regfi.regfi_version.argtypes = []
+regfi.regfi_version.restype = c_char_p
+
+regfi.regfi_alloc.argtypes = [c_int, REGFI_ENCODING]
+regfi.regfi_alloc.restype = POINTER(REGFI_FILE)
+
+regfi.regfi_alloc_cb.argtypes = [POINTER(REGFI_RAW_FILE), REGFI_ENCODING]
+regfi.regfi_alloc_cb.restype = POINTER(REGFI_FILE)
+
+regfi.regfi_free.argtypes = [POINTER(REGFI_FILE)]
+regfi.regfi_free.restype = None
+
+regfi.regfi_log_get_str.argtypes = []
+regfi.regfi_log_get_str.restype = c_char_p
+
+regfi.regfi_log_set_mask.argtypes = [c_uint16]
+regfi.regfi_log_set_mask.restype = c_bool
+
+regfi.regfi_get_rootkey.argtypes = [POINTER(REGFI_FILE)]
+regfi.regfi_get_rootkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_free_record.argtypes = [POINTER(REGFI_FILE), c_void_p]
+regfi.regfi_free_record.restype = None
+
+regfi.regfi_reference_record.argtypes = [POINTER(REGFI_FILE), c_void_p]
+regfi.regfi_reference_record.restype = c_void_p
+
+regfi.regfi_fetch_num_subkeys.argtypes = [POINTER(REGFI_NK)]
+regfi.regfi_fetch_num_subkeys.restype = c_uint32
+
+regfi.regfi_fetch_num_values.argtypes = [POINTER(REGFI_NK)]
+regfi.regfi_fetch_num_values.restype = c_uint32
+
+regfi.regfi_fetch_classname.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
+regfi.regfi_fetch_classname.restype = POINTER(REGFI_CLASSNAME)
+
+regfi.regfi_fetch_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
+regfi.regfi_fetch_sk.restype = POINTER(REGFI_SK)
+
+regfi.regfi_next_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_SK)]
+regfi.regfi_next_sk.restype = POINTER(REGFI_SK)
+
+regfi.regfi_prev_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_SK)]
+regfi.regfi_prev_sk.restype = POINTER(REGFI_SK)
+
+regfi.regfi_fetch_data.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_VK)]
+regfi.regfi_fetch_data.restype = POINTER(REGFI_DATA)
+
+regfi.regfi_find_subkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+                                    c_char_p, POINTER(c_uint32)]
+regfi.regfi_find_subkey.restype = c_bool
+
+regfi.regfi_find_value.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+                                   c_char_p, POINTER(c_uint32)]
+regfi.regfi_find_value.restype = c_bool
+
+regfi.regfi_get_subkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+                                   c_uint32]
+regfi.regfi_get_subkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_get_value.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
+                                  c_uint32]
+regfi.regfi_get_value.restype = POINTER(REGFI_VK)
+
+regfi.regfi_get_parentkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
+regfi.regfi_get_parentkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_nt2unix_time.argtypes = [REGFI_NTTIME]
+regfi.regfi_nt2unix_time.restype = c_double
+
+regfi.regfi_iterator_new.argtypes = [POINTER(REGFI_FILE)]
+regfi.regfi_iterator_new.restype = POINTER(REGFI_ITERATOR)
+
+regfi.regfi_iterator_free.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_free.restype = None
+
+regfi.regfi_iterator_down.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_down.restype = c_bool
+
+regfi.regfi_iterator_up.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_up.restype = c_bool
+
+regfi.regfi_iterator_to_root.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_to_root.restype = c_bool
+
+regfi.regfi_iterator_descend.argtypes = [POINTER(REGFI_ITERATOR), POINTER(c_char_p)]
+regfi.regfi_iterator_descend.restype = c_bool
+
+regfi.regfi_iterator_cur_key.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_cur_key.restype = POINTER(REGFI_NK)
+
+regfi.regfi_iterator_first_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_first_subkey.restype = c_bool
+
+regfi.regfi_iterator_cur_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_cur_subkey.restype = POINTER(REGFI_NK)
+
+regfi.regfi_iterator_next_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_next_subkey.restype = c_bool
+
+regfi.regfi_iterator_find_subkey.argtypes = [POINTER(REGFI_ITERATOR), c_char_p]
+regfi.regfi_iterator_find_subkey.restype = c_bool
+
+regfi.regfi_iterator_first_value.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_first_value.restype = c_bool
+
+regfi.regfi_iterator_cur_value.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_cur_value.restype = POINTER(REGFI_VK)
+
+regfi.regfi_iterator_next_value.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_next_value.restype = c_bool
+
+regfi.regfi_iterator_find_value.argtypes = [POINTER(REGFI_ITERATOR), c_char_p]
+regfi.regfi_iterator_find_value.restype = c_bool
+
+regfi.regfi_iterator_ancestry.argtypes = [POINTER(REGFI_ITERATOR)]
+regfi.regfi_iterator_ancestry.restype = POINTER(POINTER(REGFI_NK))
+
+regfi.regfi_init.argtypes = []
+regfi.regfi_init.restype = None
+regfi.regfi_init()
diff --git a/python/pyregfi/winsec.py b/python/pyregfi/winsec.py
new file mode 100644
index 0000000..bdce699
--- /dev/null
+++ b/python/pyregfi/winsec.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2011 Timothy D. Morgan
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# $Id: $
+
+## @package pyregfi.winsec
+# Low-level data structures for winsec library
+#
+
+import sys
+import os
+import uuid
+import ctypes
+import ctypes.util
+from ctypes import *
+from .structures import regfi
+
+is_win32 = hasattr(ctypes, 'windll')
+WINSEC_MAX_SUBAUTHS = 15
+
+if is_win32:
+    libc = cdll.msvcrt
+else:
+    libc = cdll.LoadLibrary("libc.so.6")
+
+class WINSEC_UUID(Structure):
+    pass
+
+class WINSEC_DOM_SID(Structure):
+    pass
+
+class WINSEC_ACE(Structure):
+    pass
+
+class WINSEC_ACL(Structure):
+    pass
+
+class WINSEC_DESC(Structure):
+    pass
+
+WINSEC_UUID._fields_ = [('time_low', c_uint32),
+                        ('time_mid', c_uint16),
+                        ('time_hi_and_version', c_uint16),
+                        ('clock_seq', c_uint8*2),
+                        ('node', c_uint8*6),
+                        ]
+
+WINSEC_DOM_SID._fields_ = [('sid_rev_num', c_uint8),
+                           ('num_auths', c_uint8),
+                           ('id_auths', c_uint8*6),
+                           ('sub_auths', c_uint32*WINSEC_MAX_SUBAUTHS),
+                           ]
+
+WINSEC_ACE._fields_ = [('type', c_uint8),
+                       ('flags', c_uint8),
+                       ('size', c_uint16),
+                       ('access_mask', c_uint32),
+                       ('obj_flags', c_uint32),
+                       ('obj_guid', POINTER(WINSEC_UUID)),
+                       ('inh_guid', POINTER(WINSEC_UUID)),
+                       ('trustee', POINTER(WINSEC_DOM_SID)),
+                       ]
+
+WINSEC_ACL._fields_ = [('revision', c_uint16),
+                       ('size', c_uint16),
+                       ('num_aces', c_uint32),
+                       ('aces', POINTER(POINTER(WINSEC_ACE))),
+                       ]
+
+WINSEC_DESC._fields_ = [('revision', c_uint8),
+                        ('sbz1', c_uint8),
+                        ('control', c_uint16),
+                        ('off_owner_sid', c_uint32),
+                        ('off_grp_sid', c_uint32),
+                        ('off_sacl', c_uint32),
+                        ('off_dacl', c_uint32),
+                        ('owner_sid', POINTER(WINSEC_DOM_SID)),
+                        ('grp_sid', POINTER(WINSEC_DOM_SID)),
+                        ('sacl', POINTER(WINSEC_ACL)),
+                        ('dacl', POINTER(WINSEC_ACL)),
+                        ]
+regfi.winsec_sid2str.argtypes = [POINTER(WINSEC_DOM_SID)]
+regfi.winsec_sid2str.restype = POINTER(c_char)
+
+
+def _guid2uuid(guid):
+    if not guid:
+        return None
+    return uuid.UUID(fields=(guid.contents.time_low,
+                             guid.contents.time_mid,
+                             guid.contents.time_hi_and_version,
+                             guid.contents.clock_seq[0],
+                             guid.contents.clock_seq[1],
+                             guid.contents.node[0]<<40
+                             ^ guid.contents.node[1]<<32
+                             ^ guid.contents.node[2]<<24
+                             ^ guid.contents.node[3]<<16
+                             ^ guid.contents.node[4]<<8
+                             ^ guid.contents.node[5]))
+
+## Represents a Microsoft access control entry, which are elements of access
+#  control lists.  For more information, see: 
+#    http://msdn.microsoft.com/en-us/library/aa374868%28v=vs.85%29.aspx
+#
+#  @note
+#  This interface is subject to change
+class ACE(object):
+    ## The type of entry as an integer
+    type = 1234
+    
+    ## The flags as an integer
+    flags = 0x1234
+
+    ## The access mask/permissions as an integer
+    access_mask = 0x1234
+
+    ## The trustee's SID as a string
+    trustee = "S-1-2..."
+    
+    ## The object GUID as a Python UUID
+    # May be None
+    object = uuid.UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
+
+    ## The inherited object GUID as a Python UUID
+    # May be None
+    inherited_object = uuid.UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678))
+
+    def __init__(self, ace):
+        # Just copy all of the values out so we don't need to manage memory
+        self.object = _guid2uuid(ace.obj_guid)
+        self.inherited_object = _guid2uuid(ace.inh_guid)
+
+        c_str = regfi.winsec_sid2str(ace.trustee)
+        self.trustee = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace')
+        libc.free(c_str)
+
+        self.type = int(ace.type)
+        self.flags = int(ace.flags)
+        self.access_mask = int(ace.access_mask)
+
+
+## A Microsoft security descriptor
+# For more information, see:
+#   http://msdn.microsoft.com/en-us/library/aa379563%28v=vs.85%29.aspx
+#
+class SecurityDescriptor(object):
+    ## The security descriptor's owner SID, as a string
+    owner = "S-1-2-..."
+
+    ## The security descriptor's group SID, as a string
+    group = "S-1-2-..."
+
+    ## The system access control list represented as a list of @ref ACE objects.
+    # 
+    # Is set to None if a sacl isn't defined
+    sacl = []
+
+    ## The discretionary access control list represented as a list of @ref ACE objects
+    #
+    # Is set to None if a dacl isn't defined
+    dacl = []
+
+    def __init__(self, sec_desc):
+        c_str = regfi.winsec_sid2str(sec_desc.owner_sid)
+        self.owner = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace')
+        libc.free(c_str)
+        
+        c_str = regfi.winsec_sid2str(sec_desc.grp_sid)
+        self.group = ctypes.cast(c_str, c_char_p).value.decode('utf-8', 'replace')
+        libc.free(c_str)
+
+        self.sacl = None
+        if sec_desc.sacl:
+            self.sacl = []
+            for i in range(0,sec_desc.sacl.contents.num_aces):
+                self.sacl.append(ACE(sec_desc.sacl.contents.aces[i].contents))
+
+        self.dacl = None
+        if sec_desc.dacl:
+            self.dacl = []
+            for i in range(0,sec_desc.dacl.contents.num_aces):
+                self.dacl.append(ACE(sec_desc.dacl.contents.aces[i].contents))
+
+
+# Free class objects used for documentation
+del ACE.type,ACE.flags,ACE.access_mask,ACE.object,ACE.inherited_object
+del SecurityDescriptor.owner,SecurityDescriptor.group,SecurityDescriptor.sacl,SecurityDescriptor.dacl
diff --git a/regfi_version.py b/regfi_version.py
new file mode 100644
index 0000000..30e9180
--- /dev/null
+++ b/regfi_version.py
@@ -0,0 +1 @@
+REGFI_VERSION="1.0.1"
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644
index 880485d..0000000
--- a/src/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-# $Id: Makefile 143 2009-02-13 03:24:27Z tim $
-
-################################################################################
-
-REGLOOKUP=$(BUILD_BIN)/reglookup$(BIN_EXT)
-REGLOOKUP_RECOVER=$(BUILD_BIN)/reglookup-recover$(BIN_EXT)
-OBJ=$(wildcard ../lib/*.o)
-FILES=$(REGLOOKUP) $(REGLOOKUP_RECOVER)
-
-all: $(FILES)
-
-$(REGLOOKUP): reglookup.o $(OBJ)
-	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup.o $(OBJ) $(EXTRA_OBJ)
-
-$(REGLOOKUP_RECOVER): reglookup-recover.o $(OBJ)
-	$(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup-recover.o $(OBJ) $(EXTRA_OBJ)
-
-reglookup.o: reglookup.c
-	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ reglookup.c
-
-reglookup-recover.o: reglookup-recover.c
-	$(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ reglookup-recover.c
-
-install:
-	install -m 0755 $(FILES) $(BIN_PREFIX)
-
-clean:
-	rm -f *.o
diff --git a/src/common.c b/src/common.c
index a0e40e5..99f58aa 100644
--- a/src/common.c
+++ b/src/common.c
@@ -2,7 +2,7 @@
  * This file stores code common to the command line tools.
  * XXX: This should be converted to a proper library.
  *
- * Copyright (C) 2005-2008 Timothy D. Morgan
+ * Copyright (C) 2005-2008,2011 Timothy D. Morgan
  * Copyright (C) 2002 Richard Sharpe, rsharpe at richardsharpe.com
  *
  * This program is free software; you can redistribute it and/or modify
@@ -18,17 +18,15 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: common.c 172 2010-03-08 03:04:34Z tim $
+ * $Id: common.c 256 2011-06-15 22:05:37Z tim $
  */
 
 #include <iconv.h>
 iconv_t conv_desc;
 
-const char* key_special_chars = ",\"\\/";
-const char* subfield_special_chars = ",\"\\|";
-const char* common_special_chars = ",\"\\";
-
-#define REGLOOKUP_VERSION "0.12.0"
+const char* key_special_chars = ",\"/";
+const char* subfield_special_chars = ",\"|";
+const char* common_special_chars = ",\"";
 
 #define REGLOOKUP_EXIT_OK       0
 #define REGLOOKUP_EXIT_OSERR   71
@@ -37,15 +35,23 @@ const char* common_special_chars = ",\"\\";
 #define REGLOOKUP_EXIT_NOINPUT 66
 
 
+/* Windows is lame */
+#ifdef O_BINARY
+#define REGLOOKUP_OPEN_FLAGS O_RDONLY|O_BINARY
+#else
+#define REGLOOKUP_OPEN_FLAGS O_RDONLY
+#endif
+
+
 void bailOut(int code, char* message)
 {
-  fprintf(stderr, message);
+  fprintf(stderr, "%s", message);
   exit(code);
 }
 
-void printMsgs(REGFI_FILE* f)
+void printMsgs()
 {
-  char* msgs = regfi_get_messages(f);
+  char* msgs = regfi_log_get_str();
   if(msgs != NULL)
   {
     fprintf(stderr, "%s", msgs);
@@ -53,9 +59,9 @@ void printMsgs(REGFI_FILE* f)
   }
 }
 
-void clearMsgs(REGFI_FILE* f)
+void clearMsgs()
 {
-  char* msgs = regfi_get_messages(f);
+  char* msgs = regfi_log_get_str();
   if(msgs != NULL)
     free(msgs);
 }
@@ -63,7 +69,7 @@ void clearMsgs(REGFI_FILE* f)
 
 /* 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
+ * with the syntax '%QQ' where QQ is the hex ascii value of the quoted
  * character.  A null terminator is added, since only ascii, not binary,
  * is returned.
  */
@@ -112,10 +118,11 @@ static char* quote_buffer(const unsigned char* str,
       ret_val = tmp_buf;
     }
     
-    if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
+    if(str[i] < 32 || str[i] > 126 
+       || str[i] == '%' || strchr(special, str[i]) != NULL)
     {
       num_written += snprintf(ret_val + num_written, buf_len - num_written,
-			      "\\x%.2X", str[i]);
+			      "%%%.2X", str[i]);
     }
     else
       ret_val[num_written++] = str[i];
@@ -128,7 +135,7 @@ static char* quote_buffer(const unsigned char* str,
 
 /* 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
+ * with the syntax '%QQ' where QQ is the hex ascii value of the quoted
  * character.
  */
 static char* quote_string(const char* str, const char* special)
@@ -153,7 +160,7 @@ static char* quote_string(const char* str, const char* special)
  * is the responsibility of the caller to free both a non-NULL return
  * value, and a non-NULL (*error_msg).
  */
-static char* data_to_ascii(REGFI_DATA* data, char** error_msg)
+static char* data_to_ascii(const REGFI_DATA* data, char** error_msg)
 {
   char* ret_val;
   char* cur_quoted;
@@ -250,7 +257,7 @@ static char* data_to_ascii(REGFI_DATA* data, char** error_msg)
     {
       cur_quoted = quote_string((char*)data->interpreted.multiple_string[i],
 				subfield_special_chars);
-      if(cur_quoted != NULL && cur_quoted[0] != '\0')
+      if(cur_quoted != NULL)
       {
 	tmp_len = snprintf(tmp_ptr, ret_val_left, "%s%s",delim, cur_quoted);
 	tmp_ptr += tmp_len;
@@ -311,28 +318,58 @@ static char* data_to_ascii(REGFI_DATA* data, char** error_msg)
 }
 
 
-static char* get_quoted_keyname(const REGFI_NK_REC* nk)
+static char* get_quoted_keyname(const REGFI_NK* nk)
 {
   char* ret_val;
 
-  if(nk->keyname == NULL)
-    ret_val = quote_buffer(nk->keyname_raw, nk->name_length, key_special_chars);
+  if(nk->name == NULL)
+    ret_val = quote_buffer(nk->name_raw, nk->name_length, key_special_chars);
   else
-    ret_val = quote_string(nk->keyname, key_special_chars);
+    ret_val = quote_string(nk->name, key_special_chars);
 
   return ret_val;
 }
 
 
-static char* get_quoted_valuename(const REGFI_VK_REC* vk)
+static char* get_quoted_valuename(const REGFI_VK* vk)
 {
   char* ret_val;
 
-  if(vk->valuename == NULL)
-    ret_val = quote_buffer(vk->valuename_raw, vk->name_length, 
+  if(vk->name == NULL)
+    ret_val = quote_buffer(vk->name_raw, vk->name_length, 
 			   key_special_chars);
   else
-    ret_val = quote_string(vk->valuename, key_special_chars);
+    ret_val = quote_string(vk->name, key_special_chars);
+
+  return ret_val;
+}
+
+
+int openHive(const char* filename)
+{
+  int ret_val;
+
+  /* open an existing file */
+  if ((ret_val = open(filename, REGLOOKUP_OPEN_FLAGS)) == -1)
+  {
+    fprintf(stderr, "ERROR: Failed to open hive.  Error returned: %s\n", 
+	    strerror(errno));
+    return -1;
+  }
 
   return ret_val;
 }
+
+
+void formatTime(REGFI_NTTIME nttime, char* output)
+{
+  time_t tmp_time[1];
+  struct tm* tmp_time_s = NULL;
+
+  *tmp_time = (time_t)regfi_nt2unix_time(nttime);
+  tmp_time_s = gmtime(tmp_time);
+  strftime(output, 
+	   (4+1+2+1+2)+1+(2+1+2+1+2)+1, 
+              "%Y-%m-%d %H:%M:%S", 
+	   tmp_time_s);
+}
diff --git a/src/reglookup-recover.c b/src/reglookup-recover.c
index 0cd06f2..6e17895 100644
--- a/src/reglookup-recover.c
+++ b/src/reglookup-recover.c
@@ -16,7 +16,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * $Id: reglookup-recover.c 173 2010-03-08 03:39:09Z tim $
+ * $Id: reglookup-recover.c 273 2011-07-22 02:05:25Z tim $
  */
 
 #include <stdio.h>
@@ -34,18 +34,18 @@ bool print_security = false;
 bool print_header = true;
 bool print_leftover = false;
 bool print_parsedraw = false;
-char* registry_file = NULL;
+const char* registry_file = NULL;
 
 #include "common.c"
 
 
-char* getQuotedData(int fd, uint32_t offset, uint32_t length)
+char* getQuotedData(REGFI_RAW_FILE* file_cb, uint32_t offset, uint32_t length)
 {
   uint8_t* buf;
   char* quoted_buf;
   uint32_t len;
 
-  if((lseek(fd, offset, SEEK_SET)) == -1)
+  if((regfi_seek(file_cb, offset, SEEK_SET)) == -1)
     return NULL;
 
   buf = (uint8_t*)malloc(length);
@@ -53,7 +53,7 @@ char* getQuotedData(int fd, uint32_t offset, uint32_t length)
     return NULL;
 
   len = length;
-  if((regfi_read(fd, buf, &length) != 0) || length != len)
+  if((regfi_read(file_cb, buf, &length) != 0) || length != len)
   {
     free(buf);
     return NULL;
@@ -66,20 +66,16 @@ char* getQuotedData(int fd, uint32_t offset, uint32_t length)
 }
 
 /* XXX: Somewhere in here, need to start looking for and handling classnames */
-void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
+void printKey(REGFI_FILE* f, REGFI_NK* nk, const char* prefix)
 {
-  char mtime[20];
-  time_t tmp_time[1];
-  struct tm* tmp_time_s = NULL;
+  char mtime[24];
   char* quoted_name = NULL;
   char* quoted_raw = "";
 
-  *tmp_time = regfi_nt2unix_time(&nk->mtime);
-  tmp_time_s = gmtime(tmp_time);
-  strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
-
+  formatTime(nk->mtime, mtime);
+  
   /* XXX: Add command line option to choose output encoding */
-  regfi_interpret_keyname(f, nk, REGFI_ENCODING_ASCII, true);
+  regfi_interpret_keyname(f, nk, true);
 
   quoted_name = get_quoted_keyname(nk);
   if (quoted_name == NULL)
@@ -95,7 +91,7 @@ void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
   }
 
   if(print_parsedraw)
-    quoted_raw = getQuotedData(f->fd, nk->offset, nk->cell_size);
+    quoted_raw = getQuotedData(f->cb, nk->offset, nk->cell_size);
 
   printf("%.8X,%.8X,KEY,%s,%s,%s,%d,,,,,,,,%s\n", nk->offset, nk->cell_size,
 	 prefix, quoted_name, mtime, nk->num_values, quoted_raw);
@@ -106,7 +102,7 @@ void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix)
 }
 
 
-void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
+void printValue(REGFI_FILE* f, REGFI_VK* vk, const char* prefix)
 {
   char* quoted_value = NULL;
   char* quoted_name = NULL;
@@ -115,7 +111,7 @@ void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
   const char* str_type = NULL;
 
   /* XXX: Add command line option to choose output encoding */
-  regfi_interpret_valuename(f, vk, REGFI_ENCODING_ASCII, true);
+  regfi_interpret_valuename(f, vk, true);
   
   quoted_name = get_quoted_valuename(vk);
   if (quoted_name == NULL)
@@ -132,7 +128,7 @@ void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
 
   /* XXX: Add command line option to choose output encoding */
   if(vk->data != NULL 
-     && !regfi_interpret_data(f, REGFI_ENCODING_ASCII, vk->type, vk->data))
+     && !regfi_interpret_data(f, vk->type, vk->data))
   {
     fprintf(stderr, "WARN: Error occurred while interpreting data for VK record"
 	    " at offset 0x%.8X.\n", vk->offset);
@@ -161,7 +157,7 @@ void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
 
 
   if(print_parsedraw)
-    quoted_raw = getQuotedData(f->fd, vk->offset, vk->cell_size);
+    quoted_raw = getQuotedData(f->cb, vk->offset, vk->cell_size);
 
   str_type = regfi_type_val2str(vk->type);
   if(str_type == NULL)
@@ -184,7 +180,7 @@ void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix)
 }
 
 
-void printSK(REGFI_FILE* f, REGFI_SK_REC* sk)
+void printSK(REGFI_FILE* f, REGFI_SK* sk)
 {
   char* quoted_raw = NULL;
   char* empty_str = "";
@@ -194,7 +190,7 @@ void printSK(REGFI_FILE* f, REGFI_SK_REC* sk)
   char* dacl = regfi_get_dacl(sk->sec_desc);
 
   if(print_parsedraw)
-    quoted_raw = getQuotedData(f->fd, sk->offset, sk->cell_size);
+    quoted_raw = getQuotedData(f->cb, sk->offset, sk->cell_size);
 
   if(owner == NULL)
     owner = empty_str;
@@ -228,10 +224,10 @@ int printCell(REGFI_FILE* f, uint32_t offset)
   uint32_t cell_length;
   bool unalloc;
 
-  if(!regfi_parse_cell(f->fd, offset, NULL, 0, &cell_length, &unalloc))
+  if(!regfi_parse_cell(f->cb, offset, NULL, 0, &cell_length, &unalloc))
     return 1;
 
-  quoted_buf = getQuotedData(f->fd, offset, cell_length);
+  quoted_buf = getQuotedData(f->cb, offset, cell_length);
   if(quoted_buf == NULL)
     return 2;
 
@@ -249,10 +245,10 @@ int printCell(REGFI_FILE* f, uint32_t offset)
 /* XXX: This is not terribly efficient, as it may reparse many keys 
  *      repeatedly.  Should try to add caching.
  */
-char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
+char* getParentPath(REGFI_FILE* f, REGFI_NK* nk)
 {
   void_stack* path_stack = void_stack_new(REGFI_MAX_DEPTH);
-  REGFI_NK_REC* cur_ancestor;
+  REGFI_NK* cur_ancestor;
   char* ret_val;
   uint32_t virt_offset, i, stack_size, ret_val_size, ret_val_used, offset;
   int32_t max_size;
@@ -285,7 +281,7 @@ char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
 	if(path_element != NULL)
 	{
 	  /* XXX: Add command line option to choose output encoding */
-	  regfi_interpret_keyname(f, cur_ancestor, REGFI_ENCODING_ASCII, true);
+	  regfi_interpret_keyname(f, cur_ancestor, true);
 	  
 	  path_element->buf = (uint8_t*)get_quoted_keyname(cur_ancestor);
 	}
@@ -294,7 +290,7 @@ char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
 	   || !void_stack_push(path_stack, path_element))
 	{
 	  /* XXX: Need to add a warning here */
-	  regfi_free_key(cur_ancestor);
+	  regfi_free_record(f, cur_ancestor);
 	  void_stack_free(path_stack);
 	  return NULL;
 	}
@@ -306,7 +302,7 @@ char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
 	path_element->len = strlen((char*)path_element->buf);
 	ret_val_size += path_element->len + 1;
 
-	regfi_free_key(cur_ancestor);
+	regfi_free_record(f, cur_ancestor);
       }
     }
   }
@@ -339,7 +335,7 @@ char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk)
 static void usage(void)
 {
   fprintf(stderr, "Usage: reglookup-recover [options] <REGISTRY_FILE>\n");
-  fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION);
+  fprintf(stderr, "Version: %s\n", regfi_version());
   fprintf(stderr, "Options:\n");
   fprintf(stderr, "\t-v\t sets verbose mode.\n");
   fprintf(stderr, "\t-h\t enables header row. (default)\n");
@@ -413,7 +409,7 @@ int extractVKs(REGFI_FILE* f,
 	       range_list* unalloc_values)
 {
   const range_list_element* cur_elem;
-  REGFI_VK_REC* vk;
+  REGFI_VK* vk;
   uint32_t i, j;
 
   for(i=0; i < range_list_size(unalloc_cells); i++)
@@ -456,7 +452,7 @@ int extractDataCells(REGFI_FILE* file,
 		     range_list* unalloc_values)
 {
   const range_list_element* cur_elem;
-  REGFI_VK_REC* vk;
+  REGFI_VK* vk;
   range_list* bd_cells;
   REGFI_BUFFER data;
   uint32_t i, j, offset, cell_length, length;
@@ -472,7 +468,7 @@ int extractDataCells(REGFI_FILE* file,
   for(i=0; i<range_list_size(unalloc_values); i++)
   {
     cur_elem = range_list_get(unalloc_values, i);
-    vk = (REGFI_VK_REC*)cur_elem->data;
+    vk = (REGFI_VK*)cur_elem->data;
     if(vk == NULL)
       return 11;
 
@@ -489,7 +485,7 @@ int extractDataCells(REGFI_FILE* file,
       {
 	max_size = regfi_calc_maxsize(file, offset);
 	if(max_size >= 0 
-	   && regfi_parse_cell(file->fd, offset, NULL, 0,
+	   && regfi_parse_cell(file->cb, offset, NULL, 0,
 			       &cell_length, &unalloc)
 	   && (cell_length & 0x00000007) == 0
 	   && cell_length <= max_size)
@@ -576,7 +572,7 @@ int extractDataCells(REGFI_FILE* file,
        *      vk->data item can be removed from the structure.
        */
       vk->data = regfi_buffer_to_data(data);
-      talloc_steal(vk, vk->data);
+      talloc_reparent(NULL, vk, vk->data);
     }
   }
 
@@ -591,7 +587,7 @@ int extractKeys(REGFI_FILE* f,
 		range_list* unalloc_keys)
 {
   const range_list_element* cur_elem;
-  REGFI_NK_REC* key;
+  REGFI_NK* key = NULL;
   uint32_t i, j;
   int error_code = 0;
 
@@ -615,7 +611,7 @@ int extractKeys(REGFI_FILE* f,
 	  error_code = 20;
 	  goto fail;
 	}
-	talloc_steal(unalloc_keys, key);
+	talloc_reparent(NULL, unalloc_keys, key);
 	j+=key->cell_size-8;
       }
     }
@@ -634,7 +630,7 @@ int extractKeys(REGFI_FILE* f,
   return 0;
 
  fail:
-  regfi_free_key(key);
+  regfi_free_record(f, key);
   return error_code;
 }
 
@@ -643,8 +639,8 @@ int extractValueLists(REGFI_FILE* f,
 		      range_list* unalloc_keys,
 		      range_list* unalloc_linked_values)
 {
-  REGFI_NK_REC* nk;
-  REGFI_VK_REC* vk;
+  REGFI_NK* nk;
+  REGFI_VK* vk;
   const range_list_element* cur_elem;
   uint32_t i, j, num_keys, off, values_length;
   int32_t max_size;
@@ -735,7 +731,7 @@ int extractSKs(REGFI_FILE* f,
 	       range_list* unalloc_sks)
 {
   const range_list_element* cur_elem;
-  REGFI_SK_REC* sk;
+  REGFI_SK* sk;
   uint32_t i, j;
 
   for(i=0; i < range_list_size(unalloc_cells); i++)
@@ -756,7 +752,7 @@ int extractSKs(REGFI_FILE* f,
 	  fprintf(stderr, "ERROR: Couldn't add sk to unalloc_sks.\n");
 	  return 20;
 	}
-	talloc_steal(unalloc_sks, sk);
+	talloc_reparent(NULL, unalloc_sks, sk);
 	j+=sk->cell_size-8;
       }
     }
@@ -785,10 +781,11 @@ int main(int argc, char** argv)
   char** parent_paths;
   char* tmp_name;
   char* tmp_path;
-  REGFI_NK_REC* tmp_key;
-  REGFI_VK_REC* tmp_value;
+  REGFI_NK* tmp_key;
+  REGFI_VK* tmp_value;
   uint32_t argi, arge, i, j, ret, num_unalloc_keys;
-  
+  int fd;
+
   /* Process command line arguments */
   if(argc < 2)
   {
@@ -820,21 +817,27 @@ int main(int argc, char** argv)
       bailOut(REGLOOKUP_EXIT_USAGE, "");
     }
   }
-  /*test_offset = strtol(argv[argi++], NULL, 16);*/
-
-  if((registry_file = strdup(argv[argi])) == NULL)
-    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
+  registry_file = argv[argi];
 
-  f = regfi_open(registry_file);
-  if(f == NULL)
+  fd = openHive(registry_file);
+  if(fd < 0)
   {
     fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
     bailOut(REGLOOKUP_EXIT_NOINPUT, "");
   }
+
   if(print_verbose)
-    regfi_set_message_mask(f, REGFI_MSG_ERROR|REGFI_MSG_WARN|REGFI_MSG_INFO);
+    regfi_log_set_mask(REGFI_LOG_ERROR|REGFI_LOG_WARN|REGFI_LOG_INFO);
   else
-    regfi_set_message_mask(f, REGFI_MSG_ERROR);
+    regfi_log_set_mask(REGFI_LOG_ERROR);
+
+  /* XXX: add command line option to choose output encoding */
+  f = regfi_alloc(fd, REGFI_ENCODING_ASCII);
+  if(f == NULL)
+  {
+    close(fd);
+    bailOut(REGLOOKUP_EXIT_NOINPUT, "ERROR: Failed to create REGFI_FILE structure.\n");
+  }
 
   if(print_header)
     printf("OFFSET,REC_LENGTH,REC_TYPE,PATH,NAME,"
@@ -919,7 +922,7 @@ int main(int argc, char** argv)
   for(i=0; i < num_unalloc_keys; i++)
   {
     cur_elem = range_list_get(unalloc_keys, i);
-    tmp_key = (REGFI_NK_REC*)cur_elem->data;
+    tmp_key = (REGFI_NK*)cur_elem->data;
 
     if(tmp_key == NULL)
       return 20;
@@ -933,13 +936,13 @@ int main(int argc, char** argv)
   for(i=0; i < num_unalloc_keys; i++)
   {
     cur_elem = range_list_get(unalloc_keys, i);
-    tmp_key = (REGFI_NK_REC*)cur_elem->data;
+    tmp_key = (REGFI_NK*)cur_elem->data;
 
     printKey(f, tmp_key, parent_paths[i]);
     if(tmp_key->num_values > 0 && tmp_key->values != NULL)
     {
       /* XXX: Add command line option to choose output encoding */
-      regfi_interpret_keyname(f, tmp_key, REGFI_ENCODING_ASCII, true);
+      regfi_interpret_keyname(f, tmp_key, true);
 
       tmp_name = get_quoted_keyname(tmp_key);
       tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2);
@@ -953,9 +956,9 @@ int main(int argc, char** argv)
       for(j=0; j < tmp_key->values->num_values; j++)
       {
 	tmp_value = 
-	  (REGFI_VK_REC*)range_list_find_data(unalloc_linked_values, 
-					      tmp_key->values->elements[j]
-					      + REGFI_REGF_SIZE);
+	  (REGFI_VK*)range_list_find_data(unalloc_linked_values, 
+					  tmp_key->values->elements[j]
+					  + REGFI_REGF_SIZE);
 	if(tmp_value != NULL)
 	  printValue(f, tmp_value, tmp_path);
       }
@@ -970,7 +973,7 @@ int main(int argc, char** argv)
   for(i=0; i < range_list_size(unalloc_values); i++)
   {
     cur_elem = range_list_get(unalloc_values, i);
-    tmp_value = (REGFI_VK_REC*)cur_elem->data; 
+    tmp_value = (REGFI_VK*)cur_elem->data; 
 
     printValue(f, tmp_value, "");
   }
@@ -990,5 +993,8 @@ int main(int argc, char** argv)
   range_list_free(unalloc_values);
   range_list_free(unalloc_sks);
 
+  regfi_free(f);
+  close(fd);
+
   return 0;
 }
diff --git a/src/reglookup.c b/src/reglookup.c
index 9969065..810e55c 100644
--- a/src/reglookup.c
+++ b/src/reglookup.c
@@ -1,7 +1,7 @@
 /*
  * A utility to read a Windows NT and later registry files.
  *
- * Copyright (C) 2005-2010 Timothy D. Morgan
+ * Copyright (C) 2005-2011 Timothy D. Morgan
  * Copyright (C) 2010 Tobias Mueller (portions of '-i' code)
  * Copyright (C) 2002 Richard Sharpe, rsharpe at richardsharpe.com
  *
@@ -18,7 +18,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
  *
- * $Id: reglookup.c 172 2010-03-08 03:04:34Z tim $
+ * $Id: reglookup.c 261 2011-06-17 00:55:49Z tim $
  */
 
 
@@ -39,7 +39,7 @@ bool path_filter_enabled = false;
 bool type_filter_enabled = false;
 char* path_filter = NULL;
 int type_filter;
-char* registry_file = NULL;
+const char* registry_file = NULL;
 
 /* Other globals */
 REGFI_FILE* f;
@@ -51,9 +51,15 @@ REGFI_FILE* f;
 #include "common.c"
 
 
-void printValue(REGFI_ITERATOR* iter, const REGFI_VK_REC* vk, char* prefix)
+static bool keysEqual(const REGFI_NK* x, const REGFI_NK* y)
 {
-  REGFI_DATA* data;
+  return (x != NULL && y != NULL && x->offset == y->offset);
+}
+
+void printValue(REGFI_ITERATOR* iter, const REGFI_VK* vk, char* prefix)
+{
+  const REGFI_NK* cur_key;
+  const REGFI_DATA* data;
   char* quoted_value = NULL;
   char* quoted_name = NULL;
   char* conv_error = NULL;
@@ -76,7 +82,7 @@ void printValue(REGFI_ITERATOR* iter, const REGFI_VK_REC* vk, char* prefix)
     quoted_name[0] = '\0';
   }
   
-  data = regfi_iterator_fetch_data(iter, vk);
+  data = regfi_fetch_data(iter->f, vk);
 
   printMsgs(iter->f);
   if(data != NULL)
@@ -94,14 +100,16 @@ void printValue(REGFI_ITERATOR* iter, const REGFI_VK_REC* vk, char* prefix)
     else if(conv_error != NULL)
       fprintf(stderr, "WARN: While quoting value for '%s/%s', "
 	      "warning returned: %s\n", prefix, quoted_name, conv_error);
-    regfi_free_data(data);
+    regfi_free_record(iter->f, data);
   }
 
   if(print_value_mtime)
   {
-    *tmp_time = regfi_nt2unix_time(&iter->cur_key->mtime);
+    cur_key = regfi_iterator_cur_key(iter);
+    *tmp_time = regfi_nt2unix_time(cur_key->mtime);
     tmp_time_s = gmtime(tmp_time);
     strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+    regfi_free_record(iter->f, cur_key);
   }
   else
     mtime[0] = '\0';
@@ -200,11 +208,11 @@ void freePath(char** path)
 }
 
 
-/* Returns a quoted path from an iterator's stack */
+/* Returns a quoted path of the current iterator's position */
 char* iter2Path(REGFI_ITERATOR* i)
 {
-  const REGFI_ITER_POSITION* cur;
-  const REGFI_NK_REC* tmp_key;
+  const REGFI_NK** path;
+  uint32_t k;
   uint32_t buf_left = 127;
   uint32_t buf_len = buf_left+1;
   uint32_t name_len = 0;
@@ -212,77 +220,71 @@ char* iter2Path(REGFI_ITERATOR* i)
   char* buf;
   char* new_buf;
   char* name;
-  void_stack_iterator* iter;
   
   buf = (char*)malloc((buf_len)*sizeof(char));
   if (buf == NULL)
     return NULL;
   buf[0] = '\0';
 
-  iter = void_stack_iterator_new(i->key_positions);
-  if (iter == NULL)
+  path = regfi_iterator_ancestry(i);
+  if(path == NULL)
   {
     free(buf);
     return NULL;
   }
 
-  /* skip root element */
-  if(void_stack_size(i->key_positions) < 1)
-  {
-    buf[0] = '/';
-    buf[1] = '\0';
-    return buf;
-  }
-  cur = void_stack_iterator_next(iter);
-
-  do
+  for(k=0; path[k] != NULL; k++)
   {
-    cur = void_stack_iterator_next(iter);
-    if (cur == NULL)
-      tmp_key = i->cur_key;
+    /* skip root element's name */
+    if(k == 0)
+    {
+      buf[0] = '/';
+      buf[1] = '\0';
+    }
     else
-      tmp_key = cur->nk;
-
-    name = get_quoted_keyname(tmp_key);
-
-    buf[buf_len-buf_left-1] = '/';
-    buf_left -= 1;
-    name_len = strlen(name);
-    if(name_len+1 > buf_left)
     {
-      grow_amt = (uint32_t)(buf_len/2);
-      buf_len += name_len+1+grow_amt-buf_left;
-      if((new_buf = realloc(buf, buf_len)) == NULL)
+      name = get_quoted_keyname(path[k]);
+
+      buf[buf_len-buf_left-1] = '/';
+      buf_left -= 1;
+      name_len = strlen(name);
+      if(name_len+1 > buf_left)
       {
-	free(name);
-	free(buf);
-	free(iter);
-	return NULL;
+        grow_amt = (uint32_t)(buf_len/2);
+        buf_len += name_len+1+grow_amt-buf_left;
+        if((new_buf = realloc(buf, buf_len)) == NULL)
+        {
+          regfi_free_record(i->f, path);
+          free(name);
+          free(buf);
+          return NULL;
+        }
+        buf = new_buf;
+        buf_left = grow_amt + name_len + 1;
       }
-      buf = new_buf;
-      buf_left = grow_amt + name_len + 1;
+      strncpy(buf+(buf_len-buf_left-1), name, name_len);
+      buf_left -= name_len;
+      buf[buf_len-buf_left-1] = '\0';
+      free(name);
     }
-    strncpy(buf+(buf_len-buf_left-1), name, name_len);
-    buf_left -= name_len;
-    buf[buf_len-buf_left-1] = '\0';
-    free(name);
-  } while(cur != NULL);
+  }
 
+  regfi_free_record(i->f, path);
   return buf;
 }
 
 
 void printValueList(REGFI_ITERATOR* iter, char* prefix)
 {
-  REGFI_VK_REC* value;
+  const REGFI_VK* value;
 
-  value = regfi_iterator_first_value(iter);
-  while(value != NULL)
+  regfi_iterator_first_value(iter);
+  while((value = regfi_iterator_cur_value(iter)) != NULL)
   {
     if(!type_filter_enabled || (value->type == type_filter))
       printValue(iter, value, prefix);
-    regfi_free_value(value);
-    value = regfi_iterator_next_value(iter);
+    regfi_free_record(iter->f, value);
+    regfi_iterator_next_value(iter);
     printMsgs(iter->f);
   }
 }
@@ -295,24 +297,22 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
   char* group = NULL;
   char* sacl = NULL;
   char* dacl = NULL;
+  char mtime[24];
   char* quoted_classname;
-  char mtime[20];
-  time_t tmp_time[1];
-  struct tm* tmp_time_s = NULL;
-  const REGFI_SK_REC* sk;
-  const REGFI_NK_REC* k = regfi_iterator_cur_key(iter);
-  REGFI_CLASSNAME* classname;
+  const REGFI_SK* sk;
+  const REGFI_NK* key = regfi_iterator_cur_key(iter);
+  const REGFI_CLASSNAME* classname;
 
-  *tmp_time = regfi_nt2unix_time(&k->mtime);
-  tmp_time_s = gmtime(tmp_time);
-  strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s);
+  formatTime(key->mtime, mtime);
 
-  if(print_security && (sk=regfi_iterator_cur_sk(iter)))
+  if(print_security && (sk=regfi_fetch_sk(iter->f, key)))
   {
     owner = regfi_get_owner(sk->sec_desc);
     group = regfi_get_group(sk->sec_desc);
     sacl = regfi_get_sacl(sk->sec_desc);
     dacl = regfi_get_dacl(sk->sec_desc);
+    regfi_free_record(iter->f, sk);
+
     if(owner == NULL)
       owner = empty_str;
     if(group == NULL)
@@ -322,7 +322,7 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
     if(dacl == NULL)
       dacl = empty_str;
 
-    classname = regfi_iterator_fetch_classname(iter, k);
+    classname = regfi_fetch_classname(iter->f, key);
     printMsgs(iter->f);
     if(classname != NULL)
     {
@@ -346,7 +346,7 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
     }
     else
       quoted_classname = empty_str;
-    regfi_free_classname(classname);
+    regfi_free_record(iter->f, classname);
 
     printMsgs(iter->f);
     printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime, 
@@ -365,20 +365,24 @@ void printKey(REGFI_ITERATOR* iter, char* full_path)
   }
   else
     printf("%s,KEY,,%s\n", full_path, mtime);
+
+  regfi_free_record(iter->f, key);
 }
 
 
 void printKeyTree(REGFI_ITERATOR* iter)
 {
-  const REGFI_NK_REC* root = NULL;
-  const REGFI_NK_REC* cur = NULL;
-  REGFI_NK_REC* sub = NULL;
+  const REGFI_NK* root = NULL;
+  const REGFI_NK* cur = NULL;
+  const REGFI_NK* sub = NULL;
   char* path = NULL;
   int key_type = regfi_type_str2val("KEY");
   bool print_this = true;
 
-  root = cur = regfi_iterator_cur_key(iter);
-  sub = regfi_iterator_first_subkey(iter);
+  root = regfi_iterator_cur_key(iter);
+  cur = root = regfi_reference_record(iter->f, root);
+  regfi_iterator_first_subkey(iter);
+  sub = regfi_iterator_cur_subkey(iter);
   printMsgs(iter->f);
 
   if(root == NULL)
@@ -402,8 +406,10 @@ void printKeyTree(REGFI_ITERATOR* iter)
     
     if(sub == NULL)
     {
-      if(cur != root)
+      if(!keysEqual(cur, root))
       {
+        regfi_free_record(iter->f, cur);
+        cur = NULL;
 	/* We're done with this sub-tree, going up and hitting other branches. */
 	if(!regfi_iterator_up(iter))
 	{
@@ -418,7 +424,8 @@ void printKeyTree(REGFI_ITERATOR* iter)
 	  bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: unexpected NULL for key.\n");
 	}
 	
-	sub = regfi_iterator_next_subkey(iter);
+	regfi_iterator_next_subkey(iter);
+	sub = regfi_iterator_cur_subkey(iter);
       }
       print_this = false;
     }
@@ -426,6 +433,8 @@ void printKeyTree(REGFI_ITERATOR* iter)
     { /* 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);
@@ -433,12 +442,16 @@ void printKeyTree(REGFI_ITERATOR* iter)
       }
 
       cur = regfi_iterator_cur_key(iter);
-      regfi_free_key(sub);
-      sub = regfi_iterator_first_subkey(iter);
+      regfi_free_record(iter->f, sub);
+      regfi_iterator_first_subkey(iter);
+      sub = regfi_iterator_cur_subkey(iter);
       print_this = true;
     }
     printMsgs(iter->f);
-  } while(!((cur == root) && (sub == NULL)));
+  } 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");
@@ -456,7 +469,7 @@ void printKeyTree(REGFI_ITERATOR* iter)
  */
 int retrievePath(REGFI_ITERATOR* iter, char** path)
 {
-  REGFI_VK_REC* value;
+  const REGFI_VK* value;
   char* tmp_path_joined;
   const char** tmp_path;
   uint32_t i;
@@ -489,7 +502,7 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
     return 2;
   }
 
-  if(!regfi_iterator_walk_path(iter, tmp_path))
+  if(!regfi_iterator_descend(iter, tmp_path))
   {
     printMsgs(iter->f);
     free(tmp_path);
@@ -511,7 +524,7 @@ int retrievePath(REGFI_ITERATOR* iter, char** path)
     if(!type_filter_enabled || (value->type == type_filter))
       printValue(iter, value, tmp_path_joined);
 
-    regfi_free_value(value);
+    regfi_free_record(iter->f, value);
     free(tmp_path);
     free(tmp_path_joined);
     return 1;
@@ -544,7 +557,7 @@ static void usage(void)
   fprintf(stderr, "Usage: reglookup [-v] [-s]"
 	  " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
 	  " <REGISTRY_FILE>\n");
-  fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION);
+  fprintf(stderr, "Version: %s\n", regfi_version());
   fprintf(stderr, "Options:\n");
   fprintf(stderr, "\t-v\t sets verbose mode.\n");
   fprintf(stderr, "\t-h\t enables header row. (default)\n");
@@ -562,7 +575,7 @@ int main(int argc, char** argv)
 {
   char** path = NULL;
   REGFI_ITERATOR* iter;
-  int retr_path_ret;
+  int retr_path_ret, fd;
   uint32_t argi, arge;
 
   /* Process command line arguments */
@@ -620,21 +633,27 @@ int main(int argc, char** argv)
       bailOut(REGLOOKUP_EXIT_USAGE, "");
     }
   }
-  if((registry_file = strdup(argv[argi])) == NULL)
-    bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n");
+  registry_file = argv[argi];
 
-  f = regfi_open(registry_file);
-  if(f == NULL)
+  if(print_verbose)
+    regfi_log_set_mask(REGFI_LOG_INFO|REGFI_LOG_WARN|REGFI_LOG_ERROR);
+
+  fd = openHive(registry_file);
+  if(fd < 0)
   {
     fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file);
     bailOut(REGLOOKUP_EXIT_NOINPUT, "");
   }
-
-  if(print_verbose)
-    regfi_set_message_mask(f, REGFI_MSG_INFO|REGFI_MSG_WARN|REGFI_MSG_ERROR);
-
+    
   /* XXX: add command line option to choose output encoding */
-  iter = regfi_iterator_new(f, REGFI_ENCODING_ASCII);
+  f = regfi_alloc(fd, REGFI_ENCODING_ASCII);
+  if(f == NULL)
+  {
+    close(fd);
+    bailOut(REGLOOKUP_EXIT_NOINPUT, "ERROR: Failed to create REGFI_FILE structure.\n");
+  }
+
+  iter = regfi_iterator_new(f);
   if(iter == NULL)
   {
     printMsgs(f);
@@ -674,7 +693,8 @@ int main(int argc, char** argv)
     printKeyTree(iter);
 
   regfi_iterator_free(iter);
-  regfi_close(f);
+  regfi_free(f);
+  close(fd);
 
   return 0;
 }

-- 
debian-forensics/reglookup



More information about the forensics-changes mailing list