diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/Makefile sleuthkit-2.04/Makefile
--- sleuthkit-2.04-orig/Makefile	2006-05-11 21:06:16.000000000 +0200
+++ sleuthkit-2.04/Makefile	2006-06-20 21:33:21.000000000 +0200
@@ -24,6 +24,7 @@
 	cd src/mmtools; $(MAKE) "CC=$(CC)" MAKELEVEL=
 	cd src/srchtools; $(MAKE) "CC=$(CC)" MAKELEVEL=
 	cd src/disktools; $(MAKE) "CC=$(CC)" MAKELEVEL=
+	cd src/searchtools; $(MAKE) "CC=$(CC)" MAKELEVEL=
 
 no-perl-static:
 	cd src/auxtools; $(MAKE) "CC=$(CC)" OPT=-static MAKELEVEL=
@@ -35,6 +36,7 @@
 	cd src/mmtools; $(MAKE) "CC=$(CC)" OPT=-static MAKELEVEL= 
 	cd src/srchtools; $(MAKE) "CC=$(CC)" OPT=-static MAKELEVEL= 
 	cd src/disktools; $(MAKE) "CC=$(CC)" OPT=-static MAKELEVEL= 
+	cd src/searchtools; $(MAKE) "CC=$(CC)" OPT=-static MAKELEVEL= 
 	cd src/file; CC="$(CC)" sh ./TSK.install
 
 
@@ -60,6 +62,7 @@
 	cd src/mmtools; $(MAKE) $@ "CC=$(CC)" MAKELEVEL=
 	cd src/srchtools; $(MAKE) $@ "CC=$(CC)" MAKELEVEL=
 	cd src/disktools; $(MAKE) $@ "CC=$(CC)" MAKELEVEL=
+	cd src/searchtools; $(MAKE) $@ "CC=$(CC)" MAKELEVEL=
 	cd src/file; sh ./TSK.clean
 	cd src/timeline; $(MAKE) $@
 	cd src/sorter; sh ./clean
Binary files sleuthkit-2.04-orig/lib/libafflib.a and sleuthkit-2.04/lib/libafflib.a differ
Binary files sleuthkit-2.04-orig/lib/libewf.a and sleuthkit-2.04/lib/libewf.a differ
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/COPYING sleuthkit-2.04/src/searchtools/COPYING
--- sleuthkit-2.04-orig/src/searchtools/COPYING	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/COPYING	2004-02-15 22:21:33.000000000 +0100
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/Makefile sleuthkit-2.04/src/searchtools/Makefile
--- sleuthkit-2.04-orig/src/searchtools/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/Makefile	2006-06-20 21:43:06.000000000 +0200
@@ -0,0 +1,64 @@
+SHELL	= /bin/sh
+CC	= gcc
+OPT	= -O2 -Wall -Werror
+DEBUG	= -g
+BIN	= ../../bin
+GEN_LIBS=-L../../lib -lfstools -lauxtools \
+	-limgtools -L../afflib/lib -lafflib \
+	-L../libewf -lewf -lz -lcrypto -lstdc++
+INCLUDES= -I. -I.. -I../fstools -I../auxtools -I../imgtools
+PROGS	= $(BIN)/indexer $(BIN)/searcher regression 	\
+	$(BIN)/print_config $(BIN)/print_header		\
+	$(BIN)/print_keywords $(BIN)/counter		\
+	$(BIN)/combiner
+CFLAGS	= $(DEFS) $(INCL) $(OPT) $(DEBUG) $(INCLUDES) $(XFLAGS)
+
+defs:
+	@sh ../makedefs
+
+all:	$(PROGS)
+
+$(BIN)/indexer: indexer.o index_common.o indexing.o	\
+		index_raw.o lli_byte.o index_functions.o\
+		helpers.o st_error.o st_common.o	\
+		index_raw_frag.o
+	$(CC) $(CFLAGS) -o $@ $^ $(GEN_LIBS)
+	
+$(BIN)/combiner: combiner.o index_common.o combining.o	\
+		lli_byte.o helpers.o st_common.o	\
+		st_error.o combining_raw.o		\
+		combining_raw_frag.o
+	$(CC) $(CFLAGS) -o $@ $^
+	
+$(BIN)/searcher: searcher.o index_common.o combining.o	\
+		lli_byte.o helpers.o st_common.o	\
+		st_error.o index_raw.o indexing.o	\
+		index_raw_frag.o combining_raw.o	\
+		combining_raw_frag.o
+	$(CC) $(CFLAGS) -o $@ $^ $(GEN_LIBS)
+	
+regression:	regression.o lli_byte.o st_error.o	\
+		st_common.o helpers.o
+
+$(BIN)/print_config:	print_config.o lli_byte.o st_common.o	\
+		index_common.o st_error.o helpers.o
+	$(CC) $(CFLAGS) -o $@ $^
+		
+$(BIN)/print_header:	print_header.o lli_byte.o st_common.o	\
+		index_common.o st_error.o helpers.o
+	$(CC) $(CFLAGS) -o $@ $^
+
+$(BIN)/print_keywords:	print_keywords.o lli_byte.o st_common.o	\
+		index_common.o st_error.o helpers.o		\
+		keyword_printing.o keyword_printing_raw.o	\
+		keyword_printing_raw_frag.o
+	$(CC) $(CFLAGS) -o $@ $^
+
+$(BIN)/counter:	counter.o lli_byte.o st_common.o		\
+		index_common.o st_error.o helpers.o
+	$(CC) $(CFLAGS) -o $@ $^
+
+clean:
+	rm -f $(PROGS) *.o core *.core
+
+.o: .c .h
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/README sleuthkit-2.04/src/searchtools/README
--- sleuthkit-2.04-orig/src/searchtools/README	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/README	2006-06-20 21:45:33.000000000 +0200
@@ -0,0 +1,12 @@
+HOW TO CREATE PATCH
+Current patches are created with the following line:
+diff -x CVS --unidirectional-new-file -r -u sleuthkit-X.XX-orig/ sleuthkit-X.XX > ../staging/sleuthkit-X.XX-searchtools.patch
+
+INTERNAL STRUCTURE
+The structure of the nodes can be described as follows:
+
+children_count	1		%c
+offset_count	1		lli_byte
+index_chars	children_count	%c raw_lli_byte
+offsets		offset_count	funcs.skip_offset
+
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/TODO sleuthkit-2.04/src/searchtools/TODO
--- sleuthkit-2.04-orig/src/searchtools/TODO	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/TODO	2004-08-01 23:45:18.000000000 +0200
@@ -0,0 +1,18 @@
+CURRENT
+
+Global
+ - Group defines in more global locations than now.
+ - Performance checking between old "fread" version and new "fstools" version.
+
+Combiner
+ - Update to new index_directories
+ - Ability to automate process (Specify max size of new index or all-in-one/xxx-in-one combine)
+
+Documentation
+ - Should be created. Especially the format of the index_files.
+
+FUTURE
+
+Global
+ - File index support should be added.
+ - Designing this and interpreter handling.
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/combiner.c sleuthkit-2.04/src/searchtools/combiner.c
--- sleuthkit-2.04-orig/src/searchtools/combiner.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/combiner.c	2006-03-14 00:59:34.000000000 +0100
@@ -0,0 +1,448 @@
+/* combiner
+ * 
+ * Combines multiple indexes to a single and faster index.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <combining.h>
+#include <lli_byte.h>
+#include <helpers.h>
+
+#define MAX_FILES		1024
+#define MAX_DEPTH		BUFFER_OVERLAP
+#define STATISTICS_COMBINER	"%lld/%lld Nodes"
+
+FILE			*input_file[MAX_FILES];
+
+long long int		combined_file_offset = INDEX_HEADER_SIZE;
+long long int		total_nodes = 0;
+long long int		total_offsets = 0;
+long long int		combined_node_count = 0;
+long long int		combined_total_nodes = 0;
+
+unsigned char		index_chars[MAX_FILES][MAX_DEPTH][255];
+long long int		index_jumps[MAX_DEPTH][255];
+unsigned char		index_chars_index[MAX_FILES][MAX_DEPTH];
+unsigned char		index_chars_count[MAX_FILES][MAX_DEPTH];
+unsigned char		index_depth[MAX_FILES];
+unsigned char		children_count[MAX_FILES][MAX_DEPTH];
+int			offset_count[MAX_FILES][MAX_DEPTH];
+
+typedef struct list_data {
+	int data[MAX_FILES];
+	int count;
+} list_data;
+
+void read_single_node(FILE *input, int index, int depth)
+{
+	int i;
+
+	fscanf(input, "%c",
+			&children_count[index][depth]);
+	offset_count[index][depth] = read_lli_byte(input);
+	
+	++combined_node_count;
+
+	for (i = 0; i < children_count[index][depth]; ++i) {
+		fscanf(input, "%c", &index_chars[index][depth][i]);
+		read_raw_lli_byte(input);
+	}
+	
+	index_chars_index[index][depth] = 0;
+	index_chars_count[index][depth] = children_count[index][depth];
+
+	if (depth) {
+		--children_count[index][depth - 1];
+		++index_chars_index[index][depth - 1];
+	}
+
+	index_depth[index] = depth + 1;		
+}
+
+void clear_list(list_data *list)
+{
+	list->count = 0;
+}
+
+void add_to_list(list_data *list, int index)
+{
+	list->data[list->count] = index;
+	++list->count;
+}
+
+void remove_from_list(list_data *list, int index)
+{
+	int i;
+	
+	for (i = 0; i < list->count; ++i) {
+		if (list->data[i] == index) {
+			if (i != list->count - 1)
+				list->data[i] = list->data[list->count - 1];
+
+			--list->count;
+			return;
+		}
+	}
+	
+	fprintf(stderr, "remove_from_list: Index %d is not found count %d\n",
+		index,
+		list->count);
+	exit(1);
+}
+
+int get_list_count(list_data *list)
+{
+	return list->count;
+}
+
+int get_item(list_data *list, int index)
+{
+	if (index >= list->count) {
+		fprintf(stderr, "Index %d is too large for this depth/count %d\n",
+				index,
+				list->count);
+		exit(1);
+	}
+	return list->data[index];
+}
+
+void do_statistics()
+{
+	static int	old_percentage = 0;
+	int percentage = 1.0 * combined_node_count / combined_total_nodes * 1000.0;
+
+	if (old_percentage != percentage) {
+		old_percentage = percentage;
+		print_statistics(percentage,
+				STATISTICS_COMBINER,
+				combined_node_count,
+				combined_total_nodes);
+	}
+	
+}
+
+void copy_node(struct combining_funcs *funcs, FILE *from, FILE *to, int index, int depth) {
+	int		i, count;
+	unsigned char	buffer[BUFFER_SIZE];
+	unsigned char	*ptr = buffer;
+	long long int	offsetcount, write_offset;
+
+	do_statistics();
+
+	fputc_unlocked(children_count[index][depth], to);
+	++combined_file_offset;
+
+	++total_nodes;
+	offsetcount = offset_count[index][depth];
+	total_offsets += offsetcount;
+	combined_file_offset += write_lli_byte(to, offsetcount);
+	
+	write_offset = combined_file_offset; 
+
+	for (i = 0; i < children_count[index][depth]; ++i)
+		fprintf(to, "000000000");
+	combined_file_offset += 9 * children_count[index][depth];
+
+	for (i = 0; i < offset_count[index][depth]; ++i) {
+		combined_file_offset += funcs->copy_offset(from, to);
+	}
+
+		// children_count will lessen over time.
+		//
+	count = children_count[index][depth];
+	for (i = 0; i < count; ++i) {
+		read_single_node(from, index, depth + 1);
+		*(ptr++) = index_chars[index][depth][i];
+		ptr += sprint_raw_lli_byte(ptr, &combined_file_offset);
+		copy_node(funcs, from, to, index, depth + 1);
+	}
+
+	if (count) {
+		long long int end_offset = combined_file_offset; 
+		fseeko64(to, write_offset, SEEK_SET);
+		fwrite(buffer, 1, ptr - buffer, to);
+		fseeko64(to, end_offset, SEEK_SET);
+	}
+}
+
+void combine_input(struct combining_funcs *funcs, FILE *output, list_data *list, int depth)
+{
+	int i;
+	int number = 0;
+	list_data lowest_list;
+
+	do {
+		do_statistics();
+
+		int minimum_index = 266;
+		clear_list(&lowest_list);	
+		
+			// First determine the nodes with the minimum index;
+			// 
+		for (i = 0; i < list->count; ++i) {
+			int index = list->data[i];
+		
+			if (index_depth[index] > depth) {
+				unsigned char c = index_chars[index][depth][index_chars_index[index][depth]];
+				if (minimum_index > c) {
+					clear_list(&lowest_list);
+					add_to_list(&lowest_list, index);
+					minimum_index = c;
+				} else if (minimum_index == c) {
+					add_to_list(&lowest_list, index);
+				}
+			}
+		}
+
+		if (lowest_list.count == 0) {
+			fprintf(stderr, "Count is 0... Should not happen.\n%d in list\n",
+					list->count);
+			exit(1);
+		}
+
+		
+		index_jumps[depth][number++] = ftello64(output);
+
+		if (lowest_list.count == 1) {
+			int index = get_item(&lowest_list, 0);
+
+			read_single_node(input_file[index], index, depth + 1);
+			copy_node(funcs, input_file[index], output, index, depth + 1);
+		} else {
+			long long int	count, write_offset;
+			unsigned char	combine_count;
+			
+			list_data send_list;
+
+			clear_list(&send_list);
+			
+			for (i = 0; i < get_list_count(&lowest_list); ++i) {
+				int index = lowest_list.data[i];
+
+				read_single_node(input_file[index], index, depth + 1);
+				
+				if (children_count[index][depth + 1]) {
+					add_to_list(&send_list, index);
+				}
+			}
+			
+			clear_char_combine_string();
+			clear_index_combine_list();
+			for (i = 0; i < get_list_count(&lowest_list); ++i) {
+				int index = lowest_list.data[i];
+				add_to_char_combine_string(index_chars[index][depth + 1],
+						index_chars_count[index][depth + 1]);
+				funcs->read_indexes(funcs, input_file[index],
+						offset_count[index][depth + 1]);
+			}
+
+			fputc_unlocked(get_char_combine_string_count(), output);
+			++combined_file_offset;
+			count = get_index_combine_list_count();
+			combined_file_offset += write_lli_byte(output, count);
+
+			++total_nodes;
+			total_offsets += count;
+
+			write_offset = combined_file_offset; 
+			combine_count = get_char_combine_string_count();
+			//TODO: Change to print to ptr array so x fseeko's less.
+			combined_file_offset += print_char_combine_string(output);
+			combined_file_offset += funcs->print_index_combine_list(output);
+
+			if (send_list.count)
+				combine_input(funcs, output, &send_list, depth + 1);
+			
+			if (combine_count) {
+				long long int end_offset = combined_file_offset;
+				fseeko64(output, write_offset + 1, SEEK_SET);
+				for (i = 0; i < combine_count; ++i) {
+					write_raw_lli_byte(output, &index_jumps[depth + 1][i]);
+					fseeko64(output, 1, SEEK_CUR);
+				}
+				fseeko64(output, end_offset, SEEK_SET);
+			}
+		}
+
+		for (i = 0; i < get_list_count(&lowest_list); ++i) {
+			int index = get_item(&lowest_list, i);
+			
+			if (index_chars_index[index][depth] >= index_chars_count[index][depth]) {
+				remove_from_list(list, index);
+			}
+		}
+	} while(get_list_count(list));
+}	
+
+static void usage (char *prog)
+{
+	printf("Usage: %s index_config index_files\n", prog);
+	printf("Combines multiple index_files into a single index.\n");
+	printf("\n");
+	printf("The required options are:\n");
+	printf("\t-o\t\tThe output prefix.\n");
+	printf("\nThe options are:\n");
+	printf("\t-h\t\tDisplay this information.\n");
+	printf("\t-v\t\tVerbose output.\n");
+	printf("\n");
+	printf("Report bugs to <p.j.bakker@brainspark.nl>.\n");
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	char 				output_prefix[BUFFER_SIZE];
+	char 				name[BUFFER_SIZE];
+
+	FILE 				*output;
+
+	char 				ch;
+	
+	int 				use_file = 0;
+	int 				i;
+	int 				input_file_count = 0;
+	
+	struct index_configuration	config;
+	struct index_header		output_header;
+	
+	long long int			write_offset;
+	int				combined_count;
+
+	list_data			list;
+	
+	while ((ch = getopt(argc, argv, "o:vh")) > 0) {
+		switch (ch) {
+		case '?':
+		case 'h':
+		default:
+			fprintf(stderr, "Unknown argument '%c'.\n", ch);
+			usage(argv[0]);
+			break;
+		case 'v':
+			++st_verbose;
+			break;
+		case 'o':
+			strncpy(output_prefix, optarg, BUFFER_SIZE);
+			use_file = 1;
+			break;
+		}
+	}
+
+	if (optind + 2 > argc)
+		usage(argv[0]);
+
+	if (!use_file) {
+		fprintf(stderr, "ERROR: Option -o not specified.\n");
+		usage(argv[0]);
+	}
+	
+		/* Opening the input stream
+		*/
+	for (i = optind + 1; i < argc; ++i) {
+		if (st_verbose)
+			printf("Using input file '%s'.\n", argv[i]);
+
+		input_file[input_file_count++] = safe_open(argv[i], "r");
+	}
+		
+		/* Opening the output index
+		*/
+	snprintf(name, BUFFER_SIZE - 1, "%s.idx", output_prefix);
+	
+	if (st_verbose)
+		printf("Using '%s'.\n", output_prefix);
+
+	output = safe_open(name, "w");
+
+		/* Copying config file
+		*/ 
+	if (st_verbose)
+		printf("Using %s to read configuration.\n", argv[optind]);
+
+	strncpy(index_dir, argv[optind], 255);
+	index_dir[255] = '\0';
+	config = read_config();
+	
+	snprintf(name, BUFFER_SIZE - 1, "%s.cnf", output_prefix);
+
+	if (st_verbose)
+		printf("Using %s to write configuration.\n", name);
+
+	write_config(config);
+	write_empty_header(output);
+	
+		/* Clearing necessary values
+		*/
+	clear_char_combine_string();
+
+	enum IndexType combine_type = Unknown;
+
+		/* Read in the first data
+		*/
+	for (i = 0; i < input_file_count; ++i) {
+		struct index_header	header = read_header(input_file[i]);
+
+		if (combine_type == Unknown)
+			combine_type = header.type;
+		else if (header.type != combine_type)
+			st_error(__FILE__, __LINE__, "Cannot combine different types.\n");
+
+		combined_total_nodes += header.node_count;
+
+		index_depth[i] = 0;
+		read_single_node(input_file[i], i, 0);
+		add_to_char_combine_string(index_chars[i][0], index_chars_count[i][0]);
+		
+		list.data[i] = i;
+	}
+	list.count = input_file_count;
+
+	struct combining_funcs	funcs = get_combining_funcs(combine_type);
+
+	combined_file_offset += fprintf(output, "%c%c", get_char_combine_string_count(), 0);
+	
+	++total_nodes;
+	
+	write_offset = combined_file_offset; 
+	combined_count = get_char_combine_string_count();
+	
+	combined_file_offset += print_char_combine_string(output);
+	
+	combine_input(&funcs, output, &list, 0);
+
+	fseeko64(output, write_offset + 1, SEEK_SET);
+	for (i = 0; i < combined_count; ++i) {
+		write_raw_lli_byte(output, &index_jumps[0][i]);
+		fseeko64(output, 1, SEEK_CUR);
+	}
+
+	fseeko64(output, 0, SEEK_SET);
+
+	output_header.type = combine_type;
+	output_header.node_count = total_nodes;
+	output_header.offset_count = total_offsets;
+	
+	write_header(output, output_header);
+	fclose(output);
+	printf("\n");
+	return 1;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/combining.c sleuthkit-2.04/src/searchtools/combining.c
--- sleuthkit-2.04-orig/src/searchtools/combining.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/combining.c	2004-03-07 14:49:13.000000000 +0100
@@ -0,0 +1,200 @@
+/* combining.c
+ * 
+ * Combining different lists.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <lli_byte.h>
+#include <index_raw_frag.h>
+#include <combining.h>
+#include <combining_raw.h>
+#include <combining_raw_frag.h>
+
+int			combine_string_count;
+int			index_combine_list_count;
+
+unsigned char		combine_string[255];
+struct offset_sorter	*first_offset_sorter = 0, *last_offset_sorter = 0;
+unsigned char 		last_data[BUFFER_SIZE];
+
+void clear_index_combine_list()
+{
+	struct offset_sorter *tmp;
+	
+	while(first_offset_sorter) {
+		tmp = first_offset_sorter;
+		first_offset_sorter = tmp->next;
+		free(tmp->data);
+		free(tmp);
+	}
+	index_combine_list_count = 0;
+	last_offset_sorter = 0;
+}
+
+void add_to_index_combine_list(struct combining_funcs *funcs, void *data)
+{
+	int skip = 0;
+	struct offset_sorter *tmp;
+	struct offset_sorter *current = first_offset_sorter;
+	
+	if (last_offset_sorter) {
+		if (!funcs->compare(data, last_data))
+			return;
+		if (funcs->compare(data, last_data) > 0) {
+			skip = 1;
+			current = last_offset_sorter;
+		}
+	}
+
+	if (!skip) {
+		if (!first_offset_sorter) {
+			first_offset_sorter = malloc(sizeof(struct offset_sorter));
+			funcs->add_offset(first_offset_sorter, data);
+			first_offset_sorter->next = 0;
+			++index_combine_list_count;
+			return;
+		}
+
+		if (!funcs->compare(data, first_offset_sorter->data))
+			return;
+
+		if (funcs->compare(data, first_offset_sorter->data) < 0) {
+			tmp = malloc(sizeof(struct offset_sorter));
+			funcs->add_offset(tmp, data);
+			tmp->next = first_offset_sorter;
+			first_offset_sorter = tmp;
+			++index_combine_list_count;
+			return;
+		}
+	}
+
+	while(current->next) {
+		if (!funcs->compare(data, current->next->data)) {
+			last_offset_sorter = current;
+			funcs->set_last_data(data);
+			return;
+		}
+		
+		if (funcs->compare(data, current->next->data) < 0) {
+			tmp = malloc(sizeof(struct offset_sorter));
+			tmp->next = current->next;
+			current->next = tmp;
+			funcs->add_offset(tmp, data);
+			++index_combine_list_count;
+			last_offset_sorter = tmp;
+			funcs->set_last_data(data);
+			return;
+		}
+
+		current = current->next;
+	}
+
+	tmp = malloc(sizeof(struct offset_sorter));
+	tmp->next = 0;
+	funcs->add_offset(tmp, data);
+	current->next = tmp;
+	++index_combine_list_count;
+	last_offset_sorter = tmp;
+	funcs->set_last_data(data);
+}
+			
+void *get_first_index_combine_list_item()
+{
+	return first_offset_sorter;
+}
+
+void *get_next_index_combine_list_item(void *ptr)
+{
+	return ((struct offset_sorter *) ptr)->next;
+}
+
+void *get_index_combine_list_offset(void *ptr)
+{
+	return ((struct offset_sorter *) ptr)->data;
+}
+
+int get_index_combine_list_count()
+{
+	return index_combine_list_count;
+}
+
+void clear_char_combine_string()
+{
+	int i;
+	
+	for (i = 0; i < 255; ++i)
+		combine_string[i] = 0;
+	combine_string_count = 0;
+}
+
+void add_to_char_combine_string(unsigned char *chars, int count)
+{
+	int i;
+	
+	for (i = 0; i < count; ++i) {
+		unsigned char c = chars[i];
+		if (combine_string[c] == 0) {
+			combine_string[c] = 1;
+			++combine_string_count;
+		}
+	}
+}
+
+int print_char_combine_string(FILE *output)
+{
+	int i, count = 0;
+	
+	for (i = 0; i < 255; ++i) {
+		if (combine_string[i]) {
+			++count;
+			if (st_human_readable)
+				fprintf(output, " %x", i);
+			else
+				fprintf(output, "%c00000000", i);
+		}
+	}
+
+	return 9 * count;
+}
+
+int get_char_combine_string_count()
+{
+	return combine_string_count;
+}
+
+struct combining_funcs get_combining_funcs(enum IndexType type)
+{
+	struct combining_funcs funcs;
+
+	switch(type) {
+	case Raw:
+		init_combining_funcs_raw(&funcs);
+		break;
+	case RawFragment:
+		init_combining_funcs_raw_frag(&funcs);
+		break;
+	default:
+		st_error(__FILE__, __LINE__, ERROR_UNHANDLED_INDEX_TYPE, type);
+	}
+
+	return funcs;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/combining.h sleuthkit-2.04/src/searchtools/combining.h
--- sleuthkit-2.04-orig/src/searchtools/combining.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/combining.h	2004-03-07 15:14:05.000000000 +0100
@@ -0,0 +1,64 @@
+/* combining.c
+ * 
+ * Combining different lists.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 _H_COMBINER
+#define _H_COMBINER
+
+struct offset_sorter {
+	void			*data;
+	struct offset_sorter    *next;
+};
+
+struct combining_funcs;
+
+typedef void (*add_offset_sorter_offset)(struct offset_sorter *, void *);
+typedef int (*offset_sorter_compare)(void *, void *);
+typedef void (*set_offset_sorter_last_data)(void *);
+typedef void (*read_indexes_to_combine_list)(struct combining_funcs *, FILE *input, int count);
+typedef int (*print_index_combine_list)(FILE *);
+typedef int (*copy_offset_func)(FILE *, FILE *);
+
+struct combining_funcs {
+	add_offset_sorter_offset        add_offset;
+	offset_sorter_compare           compare;
+	set_offset_sorter_last_data     set_last_data;
+	read_indexes_to_combine_list	read_indexes;
+	print_index_combine_list	print_index_combine_list;
+	copy_offset_func		copy_offset;
+};
+
+struct combining_funcs get_combining_funcs(enum IndexType type);
+
+void clear_index_combine_list();
+void add_to_index_combine_list(struct combining_funcs *funcs, void *data);
+void *get_index_combine_list_offset(void *ptr);
+int get_index_combine_list_count();
+void *get_first_index_combine_list_item();
+void *get_next_index_combine_list_item(void *ptr);
+
+void clear_char_combine_string();
+void add_to_char_combine_string(unsigned char *chars, int count);
+int print_char_combine_string(FILE *output);
+int get_char_combine_string_count();
+
+#endif // _H_COMBINER
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/combining_raw.c sleuthkit-2.04/src/searchtools/combining_raw.c
--- sleuthkit-2.04-orig/src/searchtools/combining_raw.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/combining_raw.c	2004-03-07 15:14:05.000000000 +0100
@@ -0,0 +1,108 @@
+/* combining_raw.c
+ * 
+ * Combining different lists.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <lli_byte.h>
+#include <index_raw_frag.h>
+#include <combining.h>
+
+extern unsigned char last_data[BUFFER_SIZE];
+extern struct offset_sorter *first_offset_sorter;
+
+void add_offset_sorter_offset_raw(struct offset_sorter *node, void *data)
+{
+	node->data = malloc(sizeof(long long int));
+	(*(long long int *) node->data) = *((long long int *) data);
+}
+
+int offset_sorter_compare_raw(void *orig, void *data)
+{
+	long long int orig_offset = *((long long int*) orig);
+	long long int data_offset = *((long long int*) data);
+	
+	if (orig_offset < data_offset)
+		return -1;
+	if (orig_offset == data_offset)
+		return 0;
+	return 1;
+}
+
+void set_offset_sorter_last_data_raw(void *data)
+{
+	*((long long int *) last_data) = *((long long int *) data);
+}
+
+void read_indexes_to_index_combine_list_raw(struct combining_funcs *funcs,
+		FILE *input, int count)
+{
+	int i;
+	long long int offset = 0;
+	
+	if (count) {
+		offset = read_lli_byte(input);
+		add_to_index_combine_list(funcs, &offset);
+	}
+	
+	for (i = 1; i < count; ++i) {
+		offset += read_lli_byte(input);
+		add_to_index_combine_list(funcs, &offset);
+	}
+}
+
+int print_index_combine_list_raw(FILE *output)
+{
+	struct offset_sorter *current = first_offset_sorter;
+	long long int old_offset = 0;
+	int count = 0;
+
+	while (current) {
+		long long int offset = *((long long int *) current->data);
+		long long int diff = offset - old_offset;
+		if (st_human_readable)
+			fprintf(output, " %llx", diff);
+		else
+			count += write_lli_byte(output, diff);
+
+		old_offset = offset;
+		current = current->next;
+	}
+
+	return count;
+}
+
+int copy_offset_raw(FILE *from, FILE *to)
+{
+	long long int offset = read_lli_byte(from);
+	return write_lli_byte(to, offset);
+}
+
+void init_combining_funcs_raw(struct combining_funcs *funcs)
+{
+	funcs->add_offset = &add_offset_sorter_offset_raw;
+	funcs->compare = &offset_sorter_compare_raw;
+	funcs->set_last_data = &set_offset_sorter_last_data_raw;
+	funcs->read_indexes = &read_indexes_to_index_combine_list_raw;
+	funcs->print_index_combine_list = &print_index_combine_list_raw;
+	funcs->copy_offset = &copy_offset_raw;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/combining_raw.h sleuthkit-2.04/src/searchtools/combining_raw.h
--- sleuthkit-2.04-orig/src/searchtools/combining_raw.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/combining_raw.h	2004-03-07 14:49:13.000000000 +0100
@@ -0,0 +1,3 @@
+#include <combining.h>
+
+void init_combining_funcs_raw(struct combining_funcs *funcs);
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/combining_raw_frag.c sleuthkit-2.04/src/searchtools/combining_raw_frag.c
--- sleuthkit-2.04-orig/src/searchtools/combining_raw_frag.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/combining_raw_frag.c	2006-03-12 15:34:10.000000000 +0100
@@ -0,0 +1,114 @@
+/* combining_raw_frag.c
+ * 
+ * Combining different lists.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <lli_byte.h>
+#include <index_raw_frag.h>
+#include <combining.h>
+
+extern unsigned char last_data[BUFFER_SIZE];
+extern struct offset_sorter *first_offset_sorter;
+
+void add_offset_sorter_offset_raw_frag(struct offset_sorter *node, void *data)
+{
+	node->data = malloc(sizeof(struct frag_data));
+	(*(struct frag_data *) node->data) = *((struct frag_data *) data);
+}
+
+int offset_sorter_compare_raw_frag(void *orig, void *data)
+{
+	struct frag_data *orig_offset = (struct frag_data *) orig;
+	struct frag_data *data_offset = (struct frag_data *) data;
+	
+	if (orig_offset->inode < data_offset->inode)
+		return -1;
+	
+	if (orig_offset->inode > data_offset->inode)
+		return 1;
+	
+	if (orig_offset->offset < data_offset->offset)
+		return -1;
+
+	if (orig_offset->offset == data_offset->offset)
+		return 0;
+
+	return 1;
+}
+
+void set_offset_sorter_last_data_raw_frag(void *data)
+{
+	*((struct frag_data *) last_data) = *((struct frag_data *) data);
+}
+
+void read_indexes_to_index_combine_list_raw_frag(struct combining_funcs *funcs,
+		FILE *input, int count)
+{
+	int i;
+	
+	struct frag_data	index_data;
+
+	for (i = 0; i < count; ++i) {
+		fread(&index_data, 1, sizeof(struct frag_data), input);
+		add_to_index_combine_list(funcs, &index_data);
+	}
+}
+
+int print_index_combine_list_raw_frag(FILE *output)
+{
+	struct offset_sorter	*current = first_offset_sorter;
+	struct frag_data	*index_data;
+	int count = 0;
+
+	while (current) {
+		index_data = (struct frag_data *) current->data;
+
+		if (st_human_readable)
+			fprintf(output, " (%llx, %llx)", index_data->inode, index_data->offset);
+		else
+			count += fwrite_unlocked(index_data, 1, sizeof(struct frag_data), output);
+
+		current = current->next;
+	}
+
+	return count;
+}
+
+int copy_offset_raw_frag(FILE *from, FILE *to)
+{
+	struct frag_data	data;
+	fread(&data, 1, sizeof(struct frag_data), from);
+	fwrite(&data, 1, sizeof(struct frag_data), to);	
+
+	return sizeof(struct frag_data);
+}
+
+void init_combining_funcs_raw_frag(struct combining_funcs *funcs)
+{
+	funcs->add_offset = &add_offset_sorter_offset_raw_frag;
+	funcs->compare = &offset_sorter_compare_raw_frag;
+	funcs->set_last_data = &set_offset_sorter_last_data_raw_frag;
+	funcs->read_indexes = &read_indexes_to_index_combine_list_raw_frag;
+	funcs->print_index_combine_list = &print_index_combine_list_raw_frag;
+	funcs->copy_offset = &copy_offset_raw_frag;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/combining_raw_frag.h sleuthkit-2.04/src/searchtools/combining_raw_frag.h
--- sleuthkit-2.04-orig/src/searchtools/combining_raw_frag.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/combining_raw_frag.h	2004-03-07 14:49:13.000000000 +0100
@@ -0,0 +1,3 @@
+#include <combining.h>
+
+void init_combining_funcs_raw_frag(struct combining_funcs *funcs);
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/counter.c sleuthkit-2.04/src/searchtools/counter.c
--- sleuthkit-2.04-orig/src/searchtools/counter.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/counter.c	2004-08-01 22:51:32.000000000 +0200
@@ -0,0 +1,161 @@
+/* Counter
+ * 
+ * Counts the nodes and offsets found in the index.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <lli_byte.h>
+#include <helpers.h>
+
+int	no_count = 0;
+
+long long int	current_offsets, total_offsets = 0;
+long long int	current_nodes, total_nodes = 0;
+
+void count(FILE *index_file)
+{ 
+	unsigned char children_count;
+	long long int offset_count;
+	
+	children_count = fgetc(index_file);
+	offset_count = read_lli_byte(index_file);
+	
+	current_offsets += offset_count;
+	++current_nodes;
+	
+	if (children_count) {
+		unsigned char	buffer[BUFFER_SIZE];
+		unsigned char	*ptr = buffer, c;
+		long long int	offset;
+		int		i;
+
+		fread(buffer, 1, 9 * children_count, index_file);
+
+		for (i = 0; i < children_count; ++i) {
+			c = *(ptr++);
+			offset = sscan_raw_lli_byte(ptr);
+			ptr += 8;
+
+			fseeko64(index_file, offset, SEEK_SET);
+			
+			count(index_file);
+		}
+	}
+}
+
+static void usage (char *prog)
+{
+	printf("Usage: %s <options> [-d index_dir] [-f index_file]\n", prog);
+	printf("Print all the keywords in the index_dir or index_file.\n");
+	printf("\n");
+	printf("The options are:\n");
+	printf("\t-h\t\tDisplay this information.\n");
+	printf("\t-v\t\tVerbose output.\n");
+	printf("\n");
+	printf("Report bugs to <p.j.bakker@brainspark.nl>.\n");
+	exit(1);
+}
+
+void action(FILE *index_file, char *name, void *ptr)
+{
+	struct index_header	header;
+	header = read_header(index_file);
+
+	current_nodes = 0;
+	current_offsets = 0;
+
+	count(index_file);
+
+	printf("File %s\n", name);
+	printf("Total nodes:\t%10lld ", current_nodes);
+	if (current_nodes == header.node_count)
+		printf("CORRECT");
+	else
+		printf("INCORRECT (Should be %lld)", header.node_count);
+	printf("\n");
+	printf("Total offsets:\t%10lld ", current_offsets);
+	if (current_offsets == header.offset_count)
+		printf("CORRECT");
+	else
+		printf("INCORRECT (Should be %lld)", header.offset_count);
+	printf("\n");
+	printf("\n");
+			
+	total_nodes += current_nodes;
+	total_offsets += current_offsets;
+}
+
+int main(int argc, char **argv)
+{
+	char		ch;
+	enum WalkType	type = NoWalk;
+	char		index_name[256];
+	
+	while ((ch = getopt(argc, argv, "vhd:f:")) > 0) {
+		switch(ch) {
+		case '?':
+		case 'h':
+		default:
+			fprintf(stderr, "Unknown argument '%c'.\n", ch);
+			usage(argv[0]);
+			break;
+		case 'v':
+			++st_verbose;
+			break;
+		case 'd':
+			if (type == WalkDir)
+				st_error(__FILE__, __LINE__, "Already a directory defined.");
+
+			if (type == WalkFile)
+				st_error(__FILE__, __LINE__, "Already a file defined.");
+			
+			strncpy(index_name, optarg, 255);
+			index_name[255] = '\0';
+			type = WalkDir;
+			break;
+		case 'f':
+			if (type == WalkDir)
+				st_error(__FILE__, __LINE__, "Already a directory defined.");
+
+			if (type == WalkFile)
+				st_error(__FILE__, __LINE__, "Already a file defined.");
+			
+			strncpy(index_name, optarg, 255);
+			index_name[255] = '\0';
+			type = WalkFile;
+			break;
+		}
+	}
+
+	if (argc == 1 || optind != argc)
+		usage(argv[0]);
+	
+	if (type == NoWalk)
+		st_error(__FILE__, __LINE__, "No directory or file selected.");
+	
+	index_walk(type, index_name, action, 0);
+
+	printf("Total:\n");
+	printf("Total nodes:\t%10lld\n", total_nodes);
+	printf("Total offsets:\t%10lld\n", total_offsets);
+	return 0;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/helpers.c sleuthkit-2.04/src/searchtools/helpers.c
--- sleuthkit-2.04-orig/src/searchtools/helpers.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/helpers.c	2004-03-07 18:58:43.000000000 +0100
@@ -0,0 +1,52 @@
+/* helpers.c
+ * 
+ * Additional helper functions.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <stdarg.h>
+#include <helpers.h>
+
+FILE * safe_open(char *filename, char *mode)
+{
+	FILE *file;
+
+	if (NULL == (file = fopen(filename, mode))) {
+		fprintf(stderr, "ERROR: Failed to open '%s'.\n", filename);
+		exit(1);
+	}
+
+	return file;
+}
+
+void print_statistics(double promile, char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	
+	printf("Done %5.1f percent: ", promile / 10);
+//	printf("\033[A\033[KDone %5.1f percent: ", promile / 10);
+	vprintf(fmt, ap);
+	printf("\r");
+	fflush(stdout);
+
+	va_end(ap);
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/helpers.h sleuthkit-2.04/src/searchtools/helpers.h
--- sleuthkit-2.04-orig/src/searchtools/helpers.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/helpers.h	2004-03-05 23:33:18.000000000 +0100
@@ -0,0 +1,25 @@
+/* helpers.h
+ * 
+ * Additional helper functions.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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
+ */
+
+FILE * safe_open(char *filename, char *mode);
+void print_statistics(double promile, char *fmt, ...);
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/index_common.c sleuthkit-2.04/src/searchtools/index_common.c
--- sleuthkit-2.04-orig/src/searchtools/index_common.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/index_common.c	2004-03-07 15:35:23.000000000 +0100
@@ -0,0 +1,223 @@
+/* index_common.c
+ * 
+ * Common parts used for indexing.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <helpers.h>
+#include <index_functions.h>
+#include <lli_byte.h>
+
+#define	INDEX_FILE_NAME		"index.cnf"
+
+unsigned char is_valid_reference[256];
+unsigned char mangle_reference[256];
+
+void init_parse_buffer(index_function is_valid_char, index_function mangle_char,
+		index_function fold_char)
+{
+	int i;
+	
+	for (i = 0; i < 256; ++i) {
+		is_valid_reference[i]=is_valid_char(i);
+		mangle_reference[i]=mangle_char(fold_char(i));
+	}
+}
+
+void parse_buffer(int length, unsigned char *buffer, unsigned char *valid, unsigned char *mangle)
+{
+	int i;
+
+	for (i = 0; i < length; ++i) {
+		valid[i] = is_valid_reference[buffer[i]];
+		mangle[i] = mangle_reference[buffer[i]];
+	}
+}
+
+void write_parse_buffer(FILE *file)
+{
+	fwrite(is_valid_reference, 256, 1, file);
+	fwrite(mangle_reference, 256, 1, file);
+}
+
+void read_parse_buffer(FILE *file)
+{
+	fread(is_valid_reference, 256, 1, file);
+	fread(mangle_reference, 256, 1, file);
+}
+
+unsigned char mangle_char(unsigned char c)
+{
+	return mangle_reference[c];
+}
+
+unsigned char is_valid_char(unsigned char c)
+{
+	return is_valid_reference[c];
+}
+
+struct index_configuration read_config()
+{
+	struct index_configuration	config;
+	char				name[256];
+	FILE				*index_config;
+	
+	snprintf(name, 255, "%s/%s", index_dir, INDEX_FILE_NAME);
+	index_config = safe_open(name, "r");
+	
+	read_parse_buffer(index_config);
+	config.min_length = fgetc(index_config);
+	config.max_length = fgetc(index_config);
+	config.raw_files = read_lli_byte(index_config);
+	config.raw_frag_files = read_lli_byte(index_config);
+
+	fclose(index_config);
+
+	return config;
+}
+
+void write_config(struct index_configuration config)
+{
+	char				name[256];
+	FILE				*index_config;
+	
+	snprintf(name, 255, "%s/%s", index_dir, INDEX_FILE_NAME);
+	index_config = safe_open(name, "w");
+
+	write_parse_buffer(index_config);
+
+	fputc(config.min_length, index_config);
+	fputc(config.max_length, index_config);
+	write_lli_byte(index_config, config.raw_files);
+	write_lli_byte(index_config, config.raw_frag_files);
+
+	fclose(index_config);
+}
+
+struct index_header read_header(FILE *input)
+{
+	struct index_header	header;
+	char			buffer[10];
+	
+	fread(buffer, 4, 1, input);
+
+	if (0 != strncmp(buffer, "SIDX", 4))
+		st_error(__FILE__, __LINE__, "Not a valid header file.");
+
+	header.version = fgetc(input);
+	
+	if (header.version != INDEX_HEADER_VERSION)
+		st_error(__FILE__, __LINE__, "Header version not compatible '%d' != '%d'",
+				header.version, INDEX_HEADER_VERSION);
+
+	header.type = fgetc(input);
+	header.node_count = read_raw_lli_byte(input);
+	header.offset_count = read_raw_lli_byte(input);
+
+	return header;
+}
+
+void write_empty_header(FILE *output)
+{
+	int	i;
+	
+	for (i = 0; i < INDEX_HEADER_SIZE; ++i)
+		fputc(0, output);
+}
+
+void write_header(FILE *output, struct index_header header)
+{
+	fprintf(output, "SIDX");
+
+	fputc(INDEX_HEADER_VERSION, output);
+	fputc(header.type, output);
+	write_raw_lli_byte(output, &header.node_count);
+	write_raw_lli_byte(output, &header.offset_count);
+}
+
+void print_header(struct index_header header)
+{
+	printf("Header version:\t%d\n", header.version);
+	printf("Index type:\t");
+	switch (header.type) {
+	case Raw:
+		printf("Raw");
+		break;
+	case RawFragment:
+		printf("RawFragment");
+		break;
+	default:
+		printf("Unknown");
+	}
+	printf("\n");
+	printf("Total nodes:\t%lld\n", header.node_count);
+	printf("Total offsets:\t%lld\n", header.offset_count);
+}
+
+void index_walk(enum WalkType type, char *use_name, walk_action action, void *ptr)
+{
+	if (st_verbose) {
+		if (type == WalkDir)
+			printf("Using index_directory '%s'.\n", use_name);
+		if (type == WalkFile)
+			printf("Using index_file '%s'.\n", use_name);
+	} 
+
+	if (type == WalkDir) {
+		int                             i;
+		struct index_configuration      config;
+		FILE                    	*index_file;
+		char                    	name[256];
+
+		strncpy(index_dir, use_name, 256);
+
+		config = read_config();
+
+		for (i = 0; i < config.raw_files; ++i) {
+			snprintf(name, 255, "%s/%s.%03d", index_dir,
+					FILE_PREFIX_RAW, i);
+
+			index_file = safe_open(name, "r");
+
+			action(index_file, name, ptr);
+
+			fclose(index_file);
+		}
+
+		for (i = 0; i < config.raw_frag_files; ++i) {
+			snprintf(name, 255, "%s/%s.%03d", index_dir,
+					FILE_PREFIX_RAW_FRAG, i);
+
+			index_file = safe_open(name, "r");
+
+			action(index_file, name, ptr);
+
+			fclose(index_file);
+		}
+	} else if (type == WalkFile) {
+		FILE                    *index_file = safe_open(use_name, "r");
+
+		action(index_file, use_name, ptr);
+
+		fclose(index_file);
+	}
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/index_common.h sleuthkit-2.04/src/searchtools/index_common.h
--- sleuthkit-2.04-orig/src/searchtools/index_common.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/index_common.h	2004-03-07 15:35:23.000000000 +0100
@@ -0,0 +1,95 @@
+/* index_common.h
+ * 
+ * Common parts used for indexing.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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	_H_INDEX_COMMON
+#define	_H_INDEX_COMMON
+
+#include <stdio.h>
+
+#define BUFFER_SIZE		65535
+#define BUFFER_OVERLAP		25
+#define INITIAL_OFFSET_COUNT	2
+
+#define INDEX_HEADER_SIZE	22
+#define INDEX_HEADER_VERSION	1
+
+#define FILE_PREFIX_RAW		"raw_idx"
+#define FILE_PREFIX_RAW_FRAG	"raw_frag_idx"
+
+enum IndexType {
+	Raw,
+	RawFragment,
+	Unknown
+};
+
+enum WalkType {
+	NoWalk,
+	WalkDir,
+	WalkFile
+};
+
+struct index_node {
+	struct index_node	*children;
+	struct index_node	*next;
+	int			offset_index;
+	int			offset_size;
+	unsigned char		character;
+};
+
+struct index_configuration {
+	int	max_length;
+	int	min_length;
+	int	raw_files;
+	int	raw_frag_files;
+};
+
+struct index_header {
+	int			version;
+	enum IndexType		type;
+	long long int		node_count;
+	long long int		offset_count;
+};
+
+typedef unsigned char (*index_function)(unsigned char);
+
+void init_parse_buffer(index_function is_valid_char, index_function mangle_char,
+		index_function fold_char);
+void parse_buffer(int length, unsigned char *buffer, unsigned char *valid, unsigned char *mangle);
+void write_parse_buffer(FILE *file);
+void read_parse_buffer(FILE *file);
+
+unsigned char mangle_char(unsigned char c);
+unsigned char is_valid_char(unsigned char c);
+
+struct index_configuration read_config();
+void write_config(struct index_configuration config);
+
+struct index_header read_header(FILE *input);
+void write_header(FILE *output, struct index_header header);
+void write_empty_header(FILE *output);
+void print_header(struct index_header header);
+
+typedef void (*walk_action)(FILE *, char *, void *);
+
+void index_walk(enum WalkType type, char *use_name, walk_action action, void *ptr);
+#endif
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/index_functions.c sleuthkit-2.04/src/searchtools/index_functions.c
--- sleuthkit-2.04-orig/src/searchtools/index_functions.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/index_functions.c	2004-03-05 23:33:18.000000000 +0100
@@ -0,0 +1,136 @@
+/* index_functions.c
+ * 
+ * Indexing functions for the different index types.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_functions.h>
+
+unsigned char is_valid_char_alpha(unsigned char c) {
+	if (c >= 97 && c <= 122)
+		return '1';
+	if (c >= 65 && c <= 90)
+		return '1';
+
+	return '0';
+}
+
+unsigned char mangle_char_alpha(unsigned char c) {
+	if (c >= 97 && c <= 122)
+		return c;
+	if (c >= 65 && c <= 90)
+		return c + 32;
+
+        return ' ';
+}
+
+unsigned char is_valid_char_alphanum(unsigned char c) {
+	if (c >= 48 && c <= 57)
+		return '1';
+	if (c >= 97 && c <= 122)
+		return '1';
+	if (c >= 65 && c <= 90)
+		return '1';
+
+	return '0';
+}
+
+unsigned char mangle_char_alphanum(unsigned char c) {
+	if (c >= 48 && c <= 57)
+		return c;
+	if (c >= 97 && c <= 122)
+		return c;
+	if (c >= 65 && c <= 90)
+		return c + 32;
+
+        return ' ';
+}
+
+unsigned char is_valid_char_inetalphanum(unsigned char c) {
+	if (c == 45 || c == 46 || c == 95)
+		return '1';
+	if (c >= 48 && c <= 57)
+		return '1';
+	if (c >= 97 && c <= 122)
+		return '1';
+	if (c >= 64 && c <= 90)
+		return '1';
+
+	return '0';
+}
+
+unsigned char mangle_char_inetalphanum(unsigned char c) {
+	if (c >= 48 && c <= 57)
+		return c;
+	if (c >= 97 && c <= 122)
+		return c;
+	if (c >= 65 && c <= 90)
+		return c + 32;
+	if (c == 45 || c == 46 || c == 95 || c == 64)
+		return c;
+
+        return ' ';
+}
+
+unsigned char fold_no(unsigned char c)
+{
+	return c;
+}
+
+unsigned char fold_iso_8859_1(unsigned char c)
+{
+	if (c >= 192 && c <= 198)
+		return 'A';
+	if (c == 199)
+		return 'C';
+	if (c >= 200 && c <= 203)
+		return 'E';
+	if (c >= 204 && c <= 207)
+		return 'I';
+	if (c == 209)
+		return 'N';
+	if ((c >= 210 && c <= 214) || c == 216)
+		return 'O';
+	if (c >= 217 && c <= 220)
+		return 'U';
+	if (c == 221)
+		return 'Y';
+	if (c == 223)
+		return 's';
+	if (c >= 224 && c <= 230)
+		return 'a';
+	if (c == 231)
+		return 'c';
+	if (c >= 232 && c <= 235)
+		return 'e';
+	if (c >= 236 && c <= 239)
+		return 'i';
+	if (c == 241)
+		return 'n';
+	if ((c >= 242 && c <= 246) || c == 248)
+		return 'o';
+	if (c >= 249 && c <= 252)
+		return 'u';
+	if (c == 253 || c == 255)
+		return 'y';
+
+	return c;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/index_functions.h sleuthkit-2.04/src/searchtools/index_functions.h
--- sleuthkit-2.04-orig/src/searchtools/index_functions.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/index_functions.h	2004-03-05 23:33:18.000000000 +0100
@@ -0,0 +1,34 @@
+/* index_functions.h
+ * 
+ * Indexing functions for the different index types.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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
+ */
+
+unsigned char is_valid_char_alpha(unsigned char c); 
+unsigned char mangle_char_alpha(unsigned char c);
+
+unsigned char is_valid_char_alphanum(unsigned char c); 
+unsigned char mangle_char_alphanum(unsigned char c);
+
+unsigned char is_valid_char_inetalphanum(unsigned char c); 
+unsigned char mangle_char_inetalphanum(unsigned char c);
+
+unsigned char fold_no(unsigned char c);
+unsigned char fold_iso_8859_1(unsigned char c);
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/index_raw.c sleuthkit-2.04/src/searchtools/index_raw.c
--- sleuthkit-2.04-orig/src/searchtools/index_raw.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/index_raw.c	2006-06-15 00:00:19.000000000 +0200
@@ -0,0 +1,267 @@
+#include <st_common.h>
+#include <index_common.h>
+#include <index_raw.h>
+#include <indexing.h>
+#include <lli_byte.h>
+#include <helpers.h>
+
+#define STATISTICS_RAW  "%6lld kNodes %6lld kOffsets %4lldM Mem"
+
+extern long long int    total_nodes, current_nodes;
+extern long long int    total_offsets, current_offsets;
+extern long long int    total_size, current_size;
+
+// For FSTOOLS verbose
+extern int verbose;
+extern FILE *logfp;
+
+void add_offset_raw(struct index_node *node, void *data)
+{
+	long long int offset = *((long long int *) data);
+	
+	if (!node->offset_index) {
+		node->offset_size = 8;
+
+		resize_index_node(&node, 8);
+
+		node->offset_index = 8;
+	} else if (node->offset_index == 8) {
+		long long int n = *((long long int *) get_node_offsets(node));
+		long long int diff = offset - n;
+		int len = get_lli_byte_length(&n) + get_lli_byte_length(&diff);
+
+		node->offset_size += len;
+
+		resize_index_node(&node, len);
+
+		node->offset_index += sprint_lli_byte(get_node_offsets(node) +
+				node->offset_index, &n);
+		node->offset_index += sprint_lli_byte(get_node_offsets(node) +
+				node->offset_index, &diff);
+	} else {
+		long long int diff;
+
+		while (node->offset_index + 9 >= node->offset_size - 8) {
+			int increase = node->offset_size;
+			
+			node->offset_size *= 2;
+
+			resize_index_node(&node, increase);
+		}
+
+		diff = offset - (*((long long int *) get_node_offsets(node)));
+		node->offset_index += sprint_lli_byte(get_node_offsets(node) +
+				node->offset_index, &diff);
+	}
+
+	memcpy(get_node_offsets(node), &offset, sizeof(long long int));
+	current_offsets++;
+}
+
+long long int get_node_offset_count_raw(struct index_node *node)
+{
+	if (node->offset_index == 0)
+		return 0;
+	else if (node->offset_index == 8)
+		return 1;
+	else {
+		long long int count = 0;
+
+		unsigned char *ptr = get_node_offsets(node) + 8;
+		unsigned char *end = get_node_offsets(node) +
+			node->offset_index;
+
+		while (ptr != end) {
+			ptr += sget_lli_byte_length(ptr);
+			++count;
+		}
+		
+		return count;
+	}
+}
+
+int get_file_count_raw(struct index_configuration *config)
+{
+	return config->raw_files++;
+}
+
+long long int get_node_offset_length_raw(struct index_node *node)
+{
+	if (node->offset_index == 0)
+		return 0;
+
+	if (node->offset_index == 8)
+		return get_lli_byte_length((long long int *)
+				get_node_offsets(node));
+
+	return node->offset_index - 8;
+}
+
+long long int write_node_offsets_raw(FILE *output, struct index_node *node)
+{
+	if (node->offset_index == 0)
+		return 0;
+	
+	if (node->offset_index == 8) {
+		return write_lli_byte(output,
+				*((long long int *) get_node_offsets(node)));
+	} else {
+		fwrite_unlocked(get_node_offsets(node) + 8, 1,
+				node->offset_index - 8, output);
+		return node->offset_index - 8;
+	}
+}
+
+void print_node_offsets_raw(FILE *output, struct index_node *node)
+{
+	if (node->offset_index == 8) {
+		long long int *offset = (long long int*) get_node_offsets(node);
+
+		fprintf(output, "%lld ", *offset);
+	} else {
+		unsigned char *cur = get_node_offsets(node) + 8;
+		long long int offset;
+
+		while (cur != get_node_offsets(node) +
+				node->offset_index) {
+			cur += sscan_lli_byte(cur, &offset);
+			fprintf(output, "%lld ", offset);
+		}
+	}
+	fprintf (output, ".\n");
+}
+
+void init_tree_raw(struct index_tree *tree)
+{
+		// Initialize value of the index_tree structure
+		// 
+	tree->type = Raw;
+	strncpy(tree->file_prefix, FILE_PREFIX_RAW, 255 - 1);
+	
+		// Set the function pointers
+		//
+	tree->add_offset = &add_offset_raw;
+	tree->get_node_offset_count = &get_node_offset_count_raw;
+	tree->get_node_offset_length = &get_node_offset_length_raw;
+	tree->write_node_offsets = &write_node_offsets_raw;
+	tree->print_node_offsets = &print_node_offsets_raw;
+	tree->get_file_count = &get_file_count_raw;
+}
+
+void index_raw(struct FS_INFO *fs_image, struct index_configuration *config)
+{
+	unsigned char	buffer[BUFFER_SIZE + BUFFER_OVERLAP];
+	unsigned char	valid_chars[BUFFER_SIZE + BUFFER_OVERLAP];
+	unsigned char	mangle_buffer[BUFFER_SIZE + BUFFER_OVERLAP];
+	unsigned char	word[BUFFER_SIZE + BUFFER_OVERLAP];
+
+	unsigned char	*ptr, *valid_ptr, *mangle_ptr;
+	unsigned char	*valid_tmp, *max_buffer;	
+	struct index_tree	tree;
+	long long int		file_size;
+	long long int		offset, read_offset;
+
+	int		buflen;
+	int		count = 0, use_count;
+	int		percentage = 0, old_percentage = 0;
+
+	file_size = (long long int) fs_image->block_count * fs_image->block_size;
+		
+	init_tree(&tree, Raw);
+	
+	offset = 0;
+	read_offset = 0;
+	ptr = buffer + config->max_length;
+	valid_ptr = valid_chars + config->max_length;
+	mangle_ptr = mangle_buffer + config->max_length;
+
+	while ((buflen = fs_read_random(fs_image, (char *) buffer + config->max_length, BUFFER_SIZE, read_offset)) > 0) {
+		read_offset += buflen;
+
+		parse_buffer(buflen + config->max_length, buffer, valid_chars, mangle_buffer);
+		while (ptr < buffer + buflen) {
+			valid_tmp = valid_ptr; 
+			max_buffer = valid_chars + buflen + config->max_length;
+			
+			while (valid_tmp < max_buffer && *valid_tmp == '1')
+				++valid_tmp;
+				
+			count = valid_tmp - valid_ptr;
+		
+			if (count < config->min_length) {
+				++offset;
+				++ptr;
+				++valid_ptr;
+				++mangle_ptr;
+			} else {
+				while (count >= config->min_length && ptr < buffer + buflen) {
+					if (count > config->max_length)
+						use_count = config->max_length;
+					else
+						use_count = count;
+
+					strncpy((char *) word, (char *) mangle_ptr, use_count);
+					word[use_count] = '\0';
+				
+					add_offset(&tree, word, &offset);
+					++offset;
+					++ptr;
+					++valid_ptr;
+					++mangle_ptr;
+					--count;
+				}
+			}
+		}
+			
+			/* Set the new pointers
+			*/
+		memcpy(buffer, ptr, config->max_length);
+		ptr = buffer;
+		valid_ptr = valid_chars;
+		mangle_ptr = mangle_buffer;
+		
+		if (!st_quiet && file_size) {
+			percentage = 1.0 * offset / file_size * 1000;
+			if (old_percentage != percentage) {
+				old_percentage = percentage;
+				print_statistics(percentage,
+						STATISTICS_RAW,
+						current_nodes / 1000,
+						current_offsets / 1000,
+						current_size / 1024 / 1024);
+			}
+		}
+					
+		if (current_size / 1024 / 1024 >= st_max_mem || (st_max_nodes != -1 && current_nodes >= st_max_nodes)) {
+			if (!st_quiet) {
+				percentage = 1.0 * offset / file_size * 1000;
+				print_statistics(percentage,
+					STATISTICS_RAW,
+					current_nodes / 1000,
+					current_offsets / 1000,
+					current_size / 1024 / 1024);
+
+				printf("\nMemory or nodes filled. Saving and cleaning up.\n");
+			}
+
+			write_output(config, &tree);
+			tree.root = get_new_node('\0');
+		}
+	}
+
+	if (!st_quiet) {
+		if (file_size) {
+			print_statistics(1000,
+					STATISTICS_RAW,
+					current_nodes / 1000,
+					current_offsets / 1000,
+					current_size / 1024 / 1024);
+		}
+		printf("\nSaving.\n");
+	}
+	write_output(config, &tree);
+
+	printf("Read %lld bytes.\n", offset + config->max_length);
+	printf("Total nodes %lld.\n", total_nodes);
+	printf("Total offsets %lld.\n", total_offsets);
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/index_raw.h sleuthkit-2.04/src/searchtools/index_raw.h
--- sleuthkit-2.04-orig/src/searchtools/index_raw.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/index_raw.h	2004-03-06 22:47:32.000000000 +0100
@@ -0,0 +1,6 @@
+#include <indexing.h>
+
+void init_tree_raw(struct index_tree *tree);
+void index_raw(FS_INFO *fs_image, struct index_configuration *config);
+int st_fs_read_random(FS_INFO *fs, char *buf, int len, OFF_T offset,
+		const char *comment);
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/index_raw_frag.c sleuthkit-2.04/src/searchtools/index_raw_frag.c
--- sleuthkit-2.04-orig/src/searchtools/index_raw_frag.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/index_raw_frag.c	2006-06-15 00:02:04.000000000 +0200
@@ -0,0 +1,234 @@
+#include <st_common.h>
+#include <index_common.h>
+#include <index_raw_frag.h>
+#include <indexing.h>
+#include <lli_byte.h>
+#include <helpers.h>
+
+#define STATISTICS_RAW_FRAG	"%6lld kNodes %6lld kOffsets %4lldM Mem %lld/%lld Inodes"
+
+extern long long int    total_nodes, current_nodes;
+extern long long int    total_offsets, current_offsets;
+extern long long int    total_size, current_size;
+
+struct index_tree	frag_tree;
+
+void add_offset_raw_frag(struct index_node *node, void *data)
+{
+	struct frag_data	*offset = (struct frag_data *) data;
+	
+	if (!node->offset_index) {
+		node->offset_size = sizeof(struct frag_data);
+
+		resize_index_node(&node, node->offset_size);
+	} else {
+		if (node->offset_size == node->offset_index) {
+			int increase = node->offset_size;
+			node->offset_size *= 2;
+			resize_index_node(&node, increase);
+		}
+	}
+
+	memcpy(get_node_offsets(node) + node->offset_index, offset, sizeof(struct frag_data));
+	node->offset_index += sizeof(struct frag_data);
+	current_offsets++;
+}
+
+long long int get_node_offset_count_raw_frag(struct index_node *node)
+{
+	return node->offset_index / sizeof(struct frag_data);
+}
+
+int get_file_count_raw_frag(struct index_configuration *config)
+{
+	return config->raw_frag_files++;
+}
+
+long long int get_node_offset_length_raw_frag(struct index_node *node)
+{
+	return node->offset_index;
+}
+
+long long int write_node_offsets_raw_frag(FILE *output, struct index_node *node)
+{
+	fwrite_unlocked(get_node_offsets(node), 1, node->offset_index, output);
+	return node->offset_index;
+}
+
+void print_node_offsets_raw_frag(FILE *output, struct index_node *node)
+{
+	struct frag_data *cur = (struct frag_data *) get_node_offsets(node);
+
+	while ((unsigned char *) cur != get_node_offsets(node) + node->offset_index) {
+		fprintf(output, " (%lld,%lld)", cur->inode, cur->offset);
+		++cur;
+	}
+	fprintf (output, ".\n");
+}
+
+void init_tree_raw_frag(struct index_tree *tree)
+{
+		// Initialize value of the index_tree structure
+		// 
+	tree->type = RawFragment;
+	strncpy(tree->file_prefix, FILE_PREFIX_RAW_FRAG, 255 - 1);
+	
+		// Set the function pointers
+		//
+	tree->add_offset = &add_offset_raw_frag;
+	tree->get_node_offset_count = &get_node_offset_count_raw_frag;
+	tree->get_node_offset_length = &get_node_offset_length_raw_frag;
+	tree->write_node_offsets = &write_node_offsets_raw_frag;
+	tree->print_node_offsets = &print_node_offsets_raw_frag;
+	tree->get_file_count = &get_file_count_raw_frag;
+}
+
+DADDR_T 	prev_addr = 0;
+int		first = 1;
+long long int	inode_offset = 0;
+INUM_T		cur_inode;
+
+u_int8_t index_frag_file(FS_INFO *fs, DADDR_T addr, char *buf, unsigned int size, int flags, void *ptr)
+{
+	if (!first && prev_addr + 1 != addr) {
+		struct index_configuration 	*config = (struct index_configuration *) ptr;
+
+		unsigned char	buffer[BUFFER_OVERLAP * 2];
+		unsigned char	valid_chars[BUFFER_OVERLAP * 2];
+		unsigned char	mangle_buffer[BUFFER_OVERLAP * 2];
+		unsigned char	word[BUFFER_OVERLAP * 2];
+		unsigned char	*ptr = buffer, *valid_ptr = valid_chars;
+		unsigned char	*mangle_ptr = mangle_buffer;
+		unsigned char	*valid_tmp, *max_buffer;	
+		long long int	use_offset = inode_offset - config->max_length;
+		int		count, use_count;
+		
+		fs_read_random(fs, (char *) buffer, config->max_length,
+				fs->block_size * prev_addr + fs->block_size - config->max_length
+				);
+		fs_read_random(fs, (char *) buffer + config->max_length, config->max_length,
+				fs->block_size * addr);
+
+		parse_buffer(config->max_length * 2, buffer, valid_chars, mangle_buffer);
+		
+		while (ptr < buffer + config->max_length) {
+			valid_tmp = valid_ptr; 
+			max_buffer = valid_chars + 2 * config->max_length;
+			
+			while (valid_tmp < max_buffer && *valid_tmp == '1')
+				++valid_tmp;
+				
+			count = valid_tmp - valid_ptr;
+		
+			if (count < config->min_length || ptr + count < buffer + config->max_length) {
+				++use_offset;
+				++ptr;
+				++valid_ptr;
+				++mangle_ptr;
+			} else {
+				while (count >= config->min_length &&
+						ptr < buffer + config->max_length) {
+					struct frag_data	index_data;
+					
+					if (count > config->max_length)
+						use_count = config->max_length;
+					else
+						use_count = count;
+
+					strncpy((char *) word, (char *) mangle_ptr, use_count);
+					word[use_count] = '\0';
+				
+					index_data.inode = cur_inode;
+					index_data.offset = use_offset;
+					
+					add_offset(&frag_tree, word, &index_data);
+
+					++use_offset;
+					++ptr;
+					++valid_ptr;
+					++mangle_ptr;
+					--count;
+				}
+			}
+		}
+		
+	} else {
+		first = 0;
+	}
+
+	prev_addr = addr;
+	inode_offset += fs->block_size;
+
+	return WALK_CONT;
+}
+
+u_int8_t index_frag_inode(FS_INFO *fs, FS_INODE *fs_inode, int flags, void *ptr)
+{
+	static int 			old_percentage = 0;
+	struct index_configuration	*config = (struct index_configuration *) ptr;
+	int				percentage;
+
+	first = 1;
+	inode_offset = 0;
+	cur_inode = fs_inode->addr;
+	
+	fs->file_walk(fs, fs_inode, 0, 0, flags, index_frag_file, ptr);
+
+	percentage = 1.0 * cur_inode / fs->last_inum * 1000;
+
+	if (!st_quiet && old_percentage != percentage) {
+			old_percentage = percentage;
+
+			print_statistics(percentage,
+					STATISTICS_RAW_FRAG,
+					current_nodes / 1000,
+					current_offsets / 1000,
+					current_size / 1024 / 1024,
+					cur_inode, fs->last_inum);
+	}
+
+	if (current_size / 1024 / 1024 >= st_max_mem) {
+		if (!st_quiet) {
+			print_statistics(percentage,
+				STATISTICS_RAW_FRAG,
+				current_nodes / 1000,
+				current_offsets / 1000,
+				current_size / 1024 / 1024,
+				cur_inode, fs->last_inum);
+
+			printf("\nMemory filled. Saving and cleaning up.\n");
+		}
+
+		write_output(config, &frag_tree);
+		frag_tree.root = get_new_node('\0');
+	}
+
+	return WALK_CONT;
+}
+
+void index_raw_frag(struct FS_INFO *fs, struct index_configuration *config)
+{
+	int 			flags = 0;
+	
+	init_tree(&frag_tree, RawFragment);
+	
+	flags |= FS_FLAG_META_ALLOC | FS_FLAG_META_UNALLOC;
+	flags |= FS_FLAG_META_LINK | FS_FLAG_META_UNLINK;
+	flags |= FS_FLAG_META_USED | FS_FLAG_META_UNUSED;
+
+	fs->inode_walk(fs, fs->first_inum, fs->last_inum, flags, index_frag_inode, (char *) config);
+
+	if (!st_quiet) {
+		print_statistics(1000,
+				STATISTICS_RAW_FRAG,
+				current_nodes / 1000,
+				current_offsets / 1000,
+				current_size / 1024 / 1024,
+				fs->last_inum, fs->last_inum);
+		printf("\nSaving.\n");
+	}
+	write_output(config, &frag_tree);
+
+	printf("Total nodes %lld.\n", total_nodes);
+	printf("Total offsets %lld.\n", total_offsets);
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/index_raw_frag.h sleuthkit-2.04/src/searchtools/index_raw_frag.h
--- sleuthkit-2.04-orig/src/searchtools/index_raw_frag.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/index_raw_frag.h	2004-03-06 22:47:32.000000000 +0100
@@ -0,0 +1,9 @@
+#include <indexing.h>
+
+struct frag_data {
+	INUM_T		inode;
+	long long int	offset;
+};
+
+void init_tree_raw_frag(struct index_tree *tree);
+void index_raw_frag(FS_INFO *fs_image, struct index_configuration *config);
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/indexer.c sleuthkit-2.04/src/searchtools/indexer.c
--- sleuthkit-2.04-orig/src/searchtools/indexer.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/indexer.c	2006-06-14 23:27:26.000000000 +0200
@@ -0,0 +1,252 @@
+/* indexer
+ * 
+ * Creates an index from an image file.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <indexing.h>
+#include <index_common.h>
+#include <index_functions.h>
+#include <index_raw.h>
+#include <index_raw_frag.h>
+#include <helpers.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+FILE			*logfp;
+
+extern long long int	total_nodes, current_nodes;
+extern long long int	total_offsets, current_offsets;
+extern long long int	total_size, current_size;
+
+struct index_tree	tree;
+
+static void usage (char *prog)
+{
+	fprintf(stderr, "Usage: %s index_directory image\n", prog);
+	fprintf(stderr, "Indexes an image and stores the results in the index_directory.\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "The options are:\n");
+	fprintf(stderr, "\t-l <nr>\t\tMinimum length of words (Default is 4).\n");
+	fprintf(stderr, "\t-L <nr>\t\tMaximum length of words (Default is 15).\n");
+	fprintf(stderr, "\t-b <size>\tRead buffer length (Default is 65535).\n");
+	fprintf(stderr, "\t-m <size>\tMaximum memory to be used in Mb (Default is 200).\n");
+	fprintf(stderr, "\t-n <size>\tMaximum nodes to be used (Default is unlimited).\n");
+	fprintf(stderr, "\t-q\t\tDon't display tracking bar.\n");
+	fprintf(stderr, "\t-h\t\tDisplay this information.\n");
+	fprintf(stderr, "\t-v\t\tVerbose output.\n");
+	fprintf(stderr, "\t-i <type>\tSpecify the image type.\n");
+	fprintf(stderr, "\t-o <type>\tSpecify the offset within the image.\n");
+	fprintf(stderr, "\t-t <type>\tSpecify the filesystem type of the image.\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "To specify the indexing used (Only one can be used at the same time!):\n");
+	fprintf(stderr, "\t-r\t\tRaw index mode only.\n");
+	fprintf(stderr, "\t-d <type>\tIndexing definition to use:\n");
+	fprintf(stderr, "\t\t\t * inetalphanum\t- Internet and Alphanumeric\t[a-z,A-Z,0-9,@.-_]\n");
+	fprintf(stderr, "\t\t\t * alphanum\t- Alphanumeric\t[a-z,A-Z,0-9] (default)\n");
+	fprintf(stderr, "\t\t\t * alpha\t- Alphabet\t[a-z,A-Z]\n");
+/*	printf(stderr, "\t-f <file>\tIndexing definition file to use.\n"); */
+	fprintf(stderr, "\t-f <type>\tFolding to use:\n");
+	fprintf(stderr, "\t\t\t * no_fold\t- No folding (default)\n");
+	fprintf(stderr, "\t\t\t * iso_8859-1\t- Fold all diacritic iso_8859-1 characters\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Supported filesystem types are:\n");
+	fs_print_types(stderr);
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Report bugs to <p.j.bakker@brainspark.nl>.\n");
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	char		ch;
+
+	index_function	is_valid_char = is_valid_char_alphanum;
+	index_function	mangle_char = mangle_char_alphanum;
+	index_function	fold_char = fold_no;
+
+//	++verbose;
+	char		*imgtype = NULL;
+	IMG_INFO	*img = 0;
+	SSIZE_T		imgoff = 0;
+	char		*fstype = NULL;
+	FS_INFO		*fs = 0;
+	
+	int		raw_only = 0;
+
+	struct index_configuration config;
+	config.min_length = 4;
+	config.max_length = 15;
+	config.raw_files = 0;
+	config.raw_frag_files = 0;
+
+	logfp = stderr;
+
+	while ((ch = getopt(argc, argv, "m:l:L:vhqi:o:t:f:rd:n:")) > 0) {
+		switch (ch) {
+		case '?':
+		case 'h':
+		default:
+			fprintf(stderr, "Unknown argument '%c'.\n", ch);
+			usage(argv[0]);
+			break;
+		case 'v':
+			++st_verbose;
+			break;
+		case 'q':
+			++st_quiet;
+			break;
+		case 'r':
+			raw_only = 1;
+			break;
+		case 'l':
+			config.min_length = atoi(optarg);
+			break;
+		case 'L':
+			config.max_length = atoi(optarg);
+			break;
+		case 'm':
+			st_max_mem = atoi(optarg);
+			break;
+		case 'n':
+			st_max_nodes = atoi(optarg);
+			break;
+		case 't':
+			fstype = optarg;
+			break;
+		case 'i':
+			imgtype = optarg;
+			break;
+		case 'o':
+			if ((imgoff = parse_offset(optarg)) == -1) {
+		                tsk_error_print(stderr);
+                		exit(1);
+            		}
+			break;
+		case 'd':
+			if (!strcmp("inetalphanum", optarg)) {
+				is_valid_char = is_valid_char_inetalphanum;
+				mangle_char = mangle_char_inetalphanum;
+			} else if (!strcmp("alphanum", optarg)) {
+				is_valid_char = is_valid_char_alphanum;
+				mangle_char = mangle_char_alphanum;
+			} else if (!strcmp("alpha", optarg)) {
+				is_valid_char = is_valid_char_alpha;
+				mangle_char = mangle_char_alpha;
+			} else {
+				fprintf(stderr, "ERROR: Not a valid index type: %s\n", optarg);
+				exit(1);
+			}
+			break;
+		case 'f':
+			if (!strcmp("no_fold", optarg)) {
+				fold_char = fold_no;
+			} else if (!strcmp("iso_8859-1", optarg)) {
+				fold_char = fold_iso_8859_1;
+			} else {
+				fprintf(stderr, "ERROR: Not a valid folding type: %s\n", optarg);
+				exit(1);
+			}
+			break;
+		}
+	}
+
+	if (config.min_length < 1) {
+		fprintf(stderr, "ERROR: Minimim length too small.\n");
+		exit(1);
+	}
+
+	if (config.min_length > config.max_length) {
+		fprintf(stderr, "WARNING: Minimum length larger than maximum length.\n");
+		fprintf(stderr, "Adjusting maximum length to compensate.\n");
+		config.max_length = config.min_length;
+	}
+
+	if (config.max_length > BUFFER_OVERLAP) {
+		fprintf(stderr, "ERROR: Maximum length larger than %d.\n", BUFFER_OVERLAP);
+		exit(1);
+	}
+	
+	if (st_max_mem < 1) {
+		fprintf(stderr, "ERROR: Maximum memory size is too small.\n");
+		exit(1);
+	}
+	
+	if (st_max_nodes != -1 && st_max_nodes < 100) {
+		fprintf(stderr, "ERROR: Maximum nodes size is too small.\n");
+		exit(1);
+	}
+	
+	if (optind + 2 > argc) {
+		fprintf(stderr, "ERROR: Too few arguments.\n");
+		usage(argv[0]);
+	}
+
+	if (st_verbose) {
+		int i;
+
+		printf("Using %d images:", argc - optind - 1);
+		for (i = optind + 1; i < argc; ++i)
+			printf(" %s", argv[i]);
+		printf("\n");
+	}
+
+	img = img_open(imgtype, argc - optind - 1,
+			(const char **) &argv[optind + 1]);
+	fs = fs_open(img, imgoff, fstype);
+
+	strncpy(index_dir, argv[optind], 255);
+	index_dir[255] = '\0';
+	
+		// Create the index_directory
+		// 
+	if (-1 == mkdir(index_dir, S_IRWXU) && errno != EEXIST) {
+		st_error(__FILE__, __LINE__, "Cannot create '%s'.\n", index_dir);
+	}
+		
+	init_parse_buffer(is_valid_char, mangle_char, fold_char);
+
+	write_config(config);
+
+
+	if (st_verbose)
+		printf("Starting raw indexing.\n");
+
+	index_raw(fs, &config);
+
+	if (!raw_only) {
+		if (fs->ftype == RAWFS_TYPE || fs->ftype == SWAPFS_TYPE) {
+			if (st_verbose)
+				printf("Not indexing raw fragments, due to filesystem type.\n");
+		} else {
+			if (st_verbose)
+				printf("Starting raw fragment indexing.\n");
+
+			index_raw_frag(fs, &config);
+		}
+	}
+
+	fs->close(fs);
+
+	return 1;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/indexing.c sleuthkit-2.04/src/searchtools/indexing.c
--- sleuthkit-2.04-orig/src/searchtools/indexing.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/indexing.c	2004-03-07 14:49:13.000000000 +0100
@@ -0,0 +1,417 @@
+/* index_common.c
+ * 
+ * Common parts used for indexing.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <indexing.h>
+#include <index_raw.h>
+#include <index_raw_frag.h>
+#include <helpers.h>
+#include <lli_byte.h>
+
+long long int 	total_nodes = 0, current_nodes = 0;
+long long int 	total_offsets = 0, current_offsets = 0;
+long long int 	total_size = 0, current_size = 0;
+
+long long int	index_file_offset = INDEX_HEADER_SIZE;
+
+#define	SIZE_OF_INDEX_NODE	17
+
+struct node_mem_data {
+	unsigned char		*data_nodes;
+	unsigned char		*cur;
+	struct node_mem_data	*next;
+};
+
+struct node_mem_data	*first_node_mem_data = 0;
+int			nodes_left = 0;
+unsigned char		*free_block = 0;
+struct index_node	*last_parent_node = 0;
+
+struct index_node *get_free_node()
+{
+	struct index_node *node;
+	
+	if (free_block) {
+		node = (struct index_node *) free_block;
+		memcpy(&free_block, free_block, sizeof(unsigned char *));
+	} else {
+		if (!nodes_left) {
+			struct node_mem_data *ptr = malloc(sizeof(
+						struct node_mem_data));
+			current_size += sizeof(struct node_mem_data);
+			ptr->next = first_node_mem_data;
+			first_node_mem_data = ptr;
+
+			ptr->data_nodes = malloc(1000 *
+					SIZE_OF_INDEX_NODE);
+			nodes_left = 1000;
+
+			current_size += 1000 * SIZE_OF_INDEX_NODE;
+
+			ptr->cur = (unsigned char *) ptr->data_nodes;
+		}
+
+		node = (struct index_node *) first_node_mem_data->cur;
+		first_node_mem_data->cur += SIZE_OF_INDEX_NODE;
+		--nodes_left;
+	}	
+
+	current_nodes++;
+
+	return node;
+} 
+
+void free_node_mem_data()
+{
+	struct node_mem_data *cur = first_node_mem_data;
+
+	while (cur) {
+		struct node_mem_data *next = cur->next;
+		free(cur->data_nodes);
+		free(cur);
+		cur = next;
+	}
+
+	first_node_mem_data = 0;
+	nodes_left = 0;
+	free_block = 0;
+}
+				
+void resize_index_node(struct index_node **node, int increase)
+{
+	struct index_node *old_node = *node;
+	
+	if (!(*node)->offset_index) {
+		struct index_node *use_node = malloc(SIZE_OF_INDEX_NODE + (*node)->offset_size);
+		memcpy(use_node, *node, SIZE_OF_INDEX_NODE);
+
+		memcpy(*node, &free_block, sizeof(unsigned char *));
+		free_block = (unsigned char *) *node;
+
+		current_size += SIZE_OF_INDEX_NODE + (*node)->offset_size;
+
+		*node = use_node;
+	} else {
+		*node = realloc(*node, SIZE_OF_INDEX_NODE + (*node)->offset_size);
+		current_size += increase;
+	}
+
+	if (last_parent_node->children == old_node) {
+		last_parent_node->children = *node;
+	} else {
+		struct index_node *cur = last_parent_node->children;
+
+		while (cur->next != old_node)
+			cur = cur->next;
+		
+		cur->next = *node;
+	}
+}
+
+struct index_node *get_new_node(unsigned char c)
+{
+	struct index_node *node = get_free_node();
+
+		/* Set pointers to null
+		*/
+	node->children = 0;
+	node->next = 0;
+	
+		/* Set counter to 0
+		*/
+	node->offset_size = 0;
+	node->offset_index = 0;
+	node->character = c;
+	
+	return node;
+}
+
+void free_tree(struct index_node *node)
+{
+	struct index_node	*child = node->children;
+
+	while (child) {
+		struct index_node	*next = child->next;
+		free_tree(child);
+		child = next;
+	}
+
+	if (node->offset_index)
+		free(node);
+
+	total_nodes += current_nodes;
+	total_offsets += current_offsets;
+	total_size += current_size;
+
+	current_nodes = 0;
+	current_offsets = 0;
+	current_size = 0;
+
+	index_file_offset = INDEX_HEADER_SIZE;
+}
+
+void init_tree(struct index_tree *tree, enum IndexType type)
+{
+	bzero(tree, sizeof(struct index_tree));
+
+	tree->root = get_new_node('\0');
+
+	total_nodes = 0;
+	total_offsets = 0;
+	total_size = 0;
+
+	switch(type) {
+	case Raw:
+		init_tree_raw(tree);
+		break;
+	case RawFragment:
+		init_tree_raw_frag(tree);
+		break;
+	default:
+		st_error(__FILE__, __LINE__, ERROR_UNHANDLED_INDEX_TYPE, type);
+	}
+}
+
+unsigned char *get_node_offsets(struct index_node *node)
+{
+	return ((unsigned char *) node) + SIZE_OF_INDEX_NODE;
+}
+
+unsigned char get_node_children_count(struct index_node *node)
+{
+	if (!node->children)
+		return 0;
+	else {
+		struct index_node *ptr = node->children;
+		unsigned char count = 0;
+
+		while (ptr) {
+			++count;
+			ptr = ptr->next;
+		}
+
+		return count;
+	}
+}
+
+void print_node_index(FILE *output, struct index_tree *tree, struct index_node *node)
+{
+	struct index_node	*child;
+	unsigned char		buffer[BUFFER_SIZE];
+	unsigned char		*ptr = buffer;
+	long long int		offset_count, write_offset;
+	unsigned char		children_count;
+	
+		/* Write out the number of children
+		*/
+	children_count = get_node_children_count(node);
+	fputc_unlocked(children_count, output);
+	++index_file_offset;
+		
+		/* Write out the number of offsets
+		*/ 
+	offset_count = tree->get_node_offset_count(node);
+	index_file_offset += write_lli_byte(output, offset_count);
+	
+		/* Save out write_offset
+		*/ 
+	write_offset = index_file_offset;
+	
+	if (node->children) {
+			/* Reserve space for the offsets of all our children
+			*/
+		index_file_offset += 9 * children_count;
+
+		if (children_count == 1) {
+			long long int child_offset = index_file_offset;
+			child_offset += tree->get_node_offset_length(node);
+
+			fputc(node->children->character, output);
+			write_raw_lli_byte(output, &child_offset);
+		} else {
+			int i;
+		
+			for (i = 0; i < children_count; ++i)
+				fprintf(output, "000000000");
+		}
+	}
+	
+		/* Write out the offsets
+		*/ 
+	if (offset_count)
+		index_file_offset += tree->write_node_offsets(output, node);
+
+	if (node->children) {
+		child = node->children;
+		
+			/* Write all children nodes
+			*/ 
+		while (child) {
+			if (children_count != 1) {
+				*(ptr++) = child->character;
+				ptr += sprint_raw_lli_byte(ptr, &index_file_offset);
+			}
+			print_node_index(output, tree, child);
+			child = child->next;
+		}
+
+			/* Jump back and write the file offsets of the child
+			// nodes.
+			*/ 
+		if (children_count != 1) {
+			long long int end_offset = index_file_offset;
+			fseeko64(output, write_offset, SEEK_SET);
+			fwrite(buffer, 1, ptr - buffer, output);
+			fseeko64(output, end_offset, SEEK_SET);
+		}
+	}
+}
+
+void print_tree_index(FILE *output, struct index_tree *tree)
+{
+	print_node_index(output, tree, tree->root);
+}
+
+void print_node_rec(FILE *output, struct index_tree *tree,
+		struct index_node *node, unsigned char *word, int index)
+{
+	struct index_node	*child;
+		
+	word[index] = node->character;
+	
+	if (node->offset_index) {
+		fprintf (output, "For index '%s': ", word);
+
+		tree->print_node_offsets(output, node);
+	}
+
+	child = node->children;
+	while (child) {
+		print_node_rec(output, tree, child, word, index + 1);
+		child = child->next;
+	}
+
+	word[index] = '\0';
+}
+
+void print_tree(FILE *output, struct index_tree *tree)
+{
+	unsigned char		word[BUFFER_SIZE];
+	struct index_node	*child;
+	
+	if (!tree->root->children)
+		return;
+
+	memset(word, 0, BUFFER_SIZE);
+
+	child = tree->root->children;
+	while (child) {
+		print_node_rec(output, tree, child, word, 0);
+		child = child->next;
+	}
+}
+
+struct index_node *get_word_node(struct index_node *node, unsigned char *word)
+{
+	struct index_node	*child = 0;
+	unsigned char		c = *word;
+
+	if (c == '\0')
+		return node;
+
+	last_parent_node = node;
+
+	if (!node->children) {
+		child = get_new_node(c);
+		node->children = child;
+	} else {
+		struct index_node *ptr = node->children;
+
+		if (ptr->character == c) {
+			child = ptr;
+		} else if (ptr->character > c) {
+			child = get_new_node(c);
+			node->children = child;
+			child->next = ptr;
+		} else {
+			while (ptr->next) {
+				if (ptr->next->character == c) {
+					child = ptr->next;
+					break;
+				} else if (ptr->next->character > c) {
+					child = get_new_node(c);
+					child->next = ptr->next;
+					ptr->next = child;
+					break;
+				}
+				ptr = ptr->next;
+			}
+		}
+
+			/* Add a new child at the end of the list
+			*/
+		if (!child) {
+			child = get_new_node(c);
+			ptr->next = child;
+		}
+	}
+
+	return get_word_node(child, word + 1);
+}
+
+void add_offset(struct index_tree *tree, unsigned char *word, void *data)
+{
+	struct index_node	*node = get_word_node(tree->root, word);
+	
+	tree->add_offset(node, data);
+}
+
+void write_output(struct index_configuration *config, struct index_tree *tree)
+{
+	char			name[BUFFER_SIZE];
+	FILE			*index_file;
+	struct index_header 	header;
+	
+	if (current_offsets == 0) {
+		if (st_verbose)
+			printf("Skipping the write. No offsets in tree.\n");
+	} else {
+		sprintf(name, "%s/%s.%03d", index_dir, tree->file_prefix,
+			tree->get_file_count(config));
+	
+		index_file = safe_open(name, "w");
+
+		header.type = tree->type;
+		header.node_count = current_nodes;
+		header.offset_count = current_offsets;
+
+		write_header(index_file, header);
+	
+		print_tree_index(index_file, tree);
+		fclose(index_file);
+		write_config(*config);
+	}
+
+	free_tree(tree->root);
+	free_node_mem_data();
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/indexing.h sleuthkit-2.04/src/searchtools/indexing.h
--- sleuthkit-2.04-orig/src/searchtools/indexing.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/indexing.h	2004-03-06 22:47:32.000000000 +0100
@@ -0,0 +1,63 @@
+/* indexing.h
+ * 
+ * Common parts used for indexing.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 _H_INDEXING
+#define _H_INDEXING
+
+#include <index_common.h>
+#include <fstools/fs_tools.h>
+
+typedef void (*add_offset_func)(struct index_node *, void *);
+typedef long long int (*get_node_offset_count_func)(struct index_node *);
+typedef long long int (*get_node_offset_length_func)(struct index_node *);
+typedef long long int (*write_node_offsets_func)(FILE *, struct index_node *);
+typedef void (*print_node_offsets_func)(FILE *, struct index_node *);
+typedef int (*get_file_count_func)(struct index_configuration *);
+
+struct index_tree {
+	enum IndexType			type;
+	char				file_prefix[255];
+	
+	add_offset_func			add_offset;
+	get_node_offset_count_func	get_node_offset_count;
+	get_node_offset_length_func	get_node_offset_length;
+	write_node_offsets_func		write_node_offsets;
+	print_node_offsets_func		print_node_offsets;
+	get_file_count_func		get_file_count;
+	
+	struct index_node		*root;
+};
+
+struct index_node *get_new_node(unsigned char c);
+void free_tree(struct index_node *node);
+void init_tree(struct index_tree *tree, enum IndexType type);
+void resize_index_node(struct index_node **node, int increase);
+
+unsigned char *get_node_offsets(struct index_node *node);
+
+void print_tree_index(FILE *output, struct index_tree *tree);
+void print_tree(FILE *output, struct index_tree *tree);
+void add_offset(struct index_tree *tree, unsigned char *word, void *data);
+void write_output(struct index_configuration *config, struct index_tree *tree);
+
+#endif
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/keyword_printing.c sleuthkit-2.04/src/searchtools/keyword_printing.c
--- sleuthkit-2.04-orig/src/searchtools/keyword_printing.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/keyword_printing.c	2004-07-27 21:00:23.000000000 +0200
@@ -0,0 +1,48 @@
+/* keyword_printing.c
+ * 
+ * Printing of keywords.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <lli_byte.h>
+#include <index_raw_frag.h>
+#include <keyword_printing.h>
+#include <keyword_printing_raw.h>
+#include <keyword_printing_raw_frag.h>
+
+struct keyword_printing_funcs get_keyword_printing_funcs(enum IndexType type)
+{
+	struct keyword_printing_funcs funcs;
+
+	switch(type) {
+	case Raw:
+		init_keyword_printing_funcs_raw(&funcs);
+		break;
+	case RawFragment:
+		init_keyword_printing_funcs_raw_frag(&funcs);
+		break;
+	default:
+		st_error(__FILE__, __LINE__, ERROR_UNHANDLED_INDEX_TYPE, type);
+	}
+
+	return funcs;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/keyword_printing.h sleuthkit-2.04/src/searchtools/keyword_printing.h
--- sleuthkit-2.04-orig/src/searchtools/keyword_printing.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/keyword_printing.h	2004-07-27 21:00:24.000000000 +0200
@@ -0,0 +1,37 @@
+/* keyword_printing.h
+ * 
+ * Combining different lists.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 _H_KEYWORD_PRINTING
+#define _H_KEYWORD_PRINTING
+
+#include <index_common.h>
+
+typedef void (*skip_offset_func)(FILE *);
+
+struct keyword_printing_funcs {
+	skip_offset_func	skip_offset;
+};
+
+struct keyword_printing_funcs get_keyword_printing_funcs(enum IndexType type);
+
+#endif // _H_KEYWORD_PRINTING
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/keyword_printing_raw.c sleuthkit-2.04/src/searchtools/keyword_printing_raw.c
--- sleuthkit-2.04-orig/src/searchtools/keyword_printing_raw.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/keyword_printing_raw.c	2004-07-27 21:00:23.000000000 +0200
@@ -0,0 +1,36 @@
+/* keyword_printing_raw.c
+ * 
+ * Printing keyword lists.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <keyword_printing.h>
+#include <lli_byte.h>
+
+void skip_offset_raw(FILE *input)
+{
+	read_lli_byte(input);
+}
+
+void init_keyword_printing_funcs_raw(struct keyword_printing_funcs *funcs)
+{
+	funcs->skip_offset = &skip_offset_raw;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/keyword_printing_raw.h sleuthkit-2.04/src/searchtools/keyword_printing_raw.h
--- sleuthkit-2.04-orig/src/searchtools/keyword_printing_raw.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/keyword_printing_raw.h	2004-07-27 21:00:24.000000000 +0200
@@ -0,0 +1,3 @@
+#include <keyword_printing.h>
+
+void init_keyword_printing_funcs_raw(struct keyword_printing_funcs *funcs);
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/keyword_printing_raw_frag.c sleuthkit-2.04/src/searchtools/keyword_printing_raw_frag.c
--- sleuthkit-2.04-orig/src/searchtools/keyword_printing_raw_frag.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/keyword_printing_raw_frag.c	2004-07-27 21:00:23.000000000 +0200
@@ -0,0 +1,39 @@
+/* keyword_printing_raw_frag.c
+ * 
+ * Printing keyword lists.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_raw_frag.h>
+#include <keyword_printing.h>
+
+void skip_offset_raw_frag(FILE *input)
+{
+	int i;
+
+	for (i = 0; i < sizeof(struct frag_data); ++i)
+		fgetc(input);
+}
+
+void init_keyword_printing_funcs_raw_frag(struct keyword_printing_funcs *funcs)
+{
+	funcs->skip_offset = &skip_offset_raw_frag;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/keyword_printing_raw_frag.h sleuthkit-2.04/src/searchtools/keyword_printing_raw_frag.h
--- sleuthkit-2.04-orig/src/searchtools/keyword_printing_raw_frag.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/keyword_printing_raw_frag.h	2004-07-27 21:00:24.000000000 +0200
@@ -0,0 +1,3 @@
+#include <keyword_printing.h>
+
+void init_keyword_printing_funcs_raw_frag(struct keyword_printing_funcs *funcs);
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/lli_byte.c sleuthkit-2.04/src/searchtools/lli_byte.c
--- sleuthkit-2.04-orig/src/searchtools/lli_byte.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/lli_byte.c	2004-03-05 23:33:18.000000000 +0100
@@ -0,0 +1,202 @@
+/* lli_byte.c
+ * 
+ * Functions for lli (long long int) bytes.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <lli_byte.h>
+
+int get_lli_byte_length(long long int *n)
+{
+	long long int	l = *n;
+	int		count = 0;
+
+	if (!l)
+		return 1;
+	
+	while (l) {
+		l >>= 7;
+		++count;
+	}
+
+	return count;
+}
+
+int sget_lli_byte_length(unsigned char *p)
+{
+	int count = 0;
+
+	do {
+		++count;
+	} while (*(p++) & 0x80);
+
+	return count;
+}
+
+int write_lli_byte(FILE *output, long long int l)
+{
+	int		count = 0;
+
+	if (!l) {
+		fputc_unlocked(0, output);
+		return 1;
+	}
+
+	while (l) {
+		unsigned char	c = l & 0x7F;
+		l >>= 7;
+
+		if (l)
+			c |= 0x80;
+
+		fputc_unlocked(c, output);
+		++count;
+	}
+
+	return count;
+}
+
+int sprint_lli_byte(unsigned char *str, long long int *n)
+{
+	long long int	l = *n;
+	int		count = 0;
+
+	if (!l) {
+		*str = 0;
+		return 1;
+	}
+
+	while (l) {
+		*str = l & 0x7F;
+		l >>= 7;
+
+		if (l)
+			*str |= 0x80;
+
+		++count;
+		++str;
+	}
+
+	return count;
+}
+
+int write_raw_lli_byte(FILE *output, long long int *n)
+{
+	int i;
+	unsigned char *p = (unsigned char *) n;
+
+	for (i = 7; i >=0; --i)
+		fputc_unlocked(p[i], output);
+
+	return 8;
+}
+
+int sprint_raw_lli_byte(unsigned char *str, long long int *n)
+{
+	int i;
+	unsigned char *p = (unsigned char *) n;
+
+	for (i = 7; i >=0; --i)
+		*(str++) = p[i];
+
+	return 8;
+}
+	
+int sscan_lli_byte(unsigned char *str, long long int *n)
+{
+	int		count = 0;
+	unsigned char	*c = str;
+
+	*n = 0;
+	
+	while (*c & 0x80)
+		++c;
+	
+	if (count > 9) {
+		st_error(__FILE__, __LINE__, "sscan_lli_byte: Count is larger than 9: %d\n",
+				count);
+	}
+
+	while (c != str - 1) {
+		++count;
+
+		*n <<= 7;
+
+		*n += *c & 0x7F;
+
+		--c;
+	}
+
+	return count;
+}
+
+long long int read_lli_byte(FILE *input)
+{
+	long long int	l = 0;
+	unsigned char	buffer[10];
+	unsigned char	*c = buffer - 1;
+
+	do {
+		++c;
+
+		if (c > buffer + 9) {
+			st_error(__FILE__, __LINE__,
+					"Count is larger than 9: %d\n",
+					c - buffer);
+		}
+
+		*c = fgetc(input);
+	} while (*c & 0x80);
+	
+	while (c != buffer - 1) {
+		l <<= 7;
+
+		l += *c & 0x7F;
+
+		--c;
+	}
+
+	return l;
+}
+
+long long int read_raw_lli_byte(FILE *input)
+{
+	int i;
+	long long int n;
+	unsigned char *p = (unsigned char *) &n;
+
+	for (i = 7; i >=0; --i)
+		fscanf(input, "%c", &p[i]);
+
+	return n;
+}
+
+long long int sscan_raw_lli_byte(unsigned char *str)
+{
+	int i;
+	long long int n;
+	unsigned char *p = (unsigned char *) &n;
+
+	for (i = 7; i >= 0; --i)
+		p[i] = str[7 - i];
+
+	return n;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/lli_byte.h sleuthkit-2.04/src/searchtools/lli_byte.h
--- sleuthkit-2.04-orig/src/searchtools/lli_byte.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/lli_byte.h	2004-03-05 23:33:18.000000000 +0100
@@ -0,0 +1,37 @@
+/* lli_byte.h
+ * 
+ * Functions for lli (long long int) bytes.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <stdio.h>
+
+int get_lli_byte_length(long long int *n);
+int sget_lli_byte_length(unsigned char *str);
+
+int write_lli_byte(FILE *output, long long int n);
+int sprint_lli_byte(unsigned char *str, long long int *n);
+int write_raw_lli_byte(FILE *output, long long int *n);
+int sprint_raw_lli_byte(unsigned char *str, long long int *n);
+
+int sscan_lli_byte(unsigned char *str, long long int *n);
+long long int read_lli_byte(FILE *input);
+long long int read_raw_lli_byte(FILE *input);
+long long int sscan_raw_lli_byte(unsigned char *str);
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/print_config.c sleuthkit-2.04/src/searchtools/print_config.c
--- sleuthkit-2.04-orig/src/searchtools/print_config.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/print_config.c	2004-03-07 15:44:48.000000000 +0100
@@ -0,0 +1,81 @@
+/* print_config
+ * 
+ * Prints the information contained in the configuration file in a human
+ * readable format.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <lli_byte.h>
+#include <index_functions.h>
+#include <helpers.h>
+
+static void usage (char *prog)
+{
+	printf("Usage: %s index_directory\n", prog);
+	printf("Prints the configuration file found in the index_directory in a\n");
+	printf("human readable format.\n");
+	printf("\n");
+	printf("The options are:\n");
+	printf("\t-h\t\tDisplay this information.\n");
+	printf("\t-v\t\tVerbose output.\n");
+	printf("\n");
+	printf("Report bugs to <p.j.bakker@brainspark.nl>.\n");
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	char				ch;
+	struct index_configuration	config;
+	
+	while ((ch = getopt(argc, argv, "vh")) > 0) {
+		switch(ch) {
+		case '?':
+		case 'h':
+		default:
+			fprintf(stderr, "Unknown argument '%c'.\n", ch);
+			usage(argv[0]);
+			break;
+		case 'v':
+			++st_verbose;
+			break;
+		}
+	}
+
+	if (optind + 1 != argc)
+		usage(argv[0]);
+	
+	strncpy(index_dir, argv[optind], 255);
+	index_dir[255] = '\0';
+	
+	if (st_verbose)
+		printf("Using index_directory '%s'.\n", index_dir);
+	
+	config = read_config();
+
+	printf("Minimum length:\t\t%d\n", config.min_length);
+	printf("Maximum length:\t\t%d\n", config.max_length);
+	printf("Nr of raw_files:\t%d\n", config.raw_files);
+	printf("Nr of raw_frag_files:\t%d\n", config.raw_frag_files);
+		
+	return 1;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/print_header.c sleuthkit-2.04/src/searchtools/print_header.c
--- sleuthkit-2.04-orig/src/searchtools/print_header.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/print_header.c	2004-03-07 15:44:48.000000000 +0100
@@ -0,0 +1,110 @@
+/* print_header
+ * 
+ * Prints the information contained in the headers of the index files in a human
+ * readable format.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <lli_byte.h>
+#include <helpers.h>
+
+static void usage (char *prog)
+{
+	printf("Usage: %s [-d index_directory] [-f index_file]\n", prog);
+	printf("Prints the headers of the index file(s) found in the index_directory\n");
+	printf("in a human readable format.\n");
+	printf("\n");
+	printf("The options are:\n");
+	printf("\t-h\t\tDisplay this information.\n");
+	printf("\t-v\t\tVerbose output.\n");
+	printf("\n");
+	printf("Report bugs to <p.j.bakker@brainspark.nl>.\n");
+	exit(1);
+}
+
+void action(FILE *index_file, char *index_filename, void *ptr)
+{
+	struct index_header	header;
+
+	header = read_header(index_file);
+
+	printf("Header for %s\n", index_filename);
+	print_header(header);
+	printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+	char		ch;
+	enum WalkType	type = NoWalk;
+	int		set = 0;
+	char		index_name[256];
+	
+	while ((ch = getopt(argc, argv, "vhd:f:")) > 0) {
+		switch(ch) {
+		case '?':
+		case 'h':
+		default:
+			fprintf(stderr, "Unknown argument '%c'.\n", ch);
+			usage(argv[0]);
+			break;
+		case 'v':
+			++st_verbose;
+			break;
+		case 'd':
+			if (set && type == WalkDir)
+				st_error(__FILE__, __LINE__, "Already a directory defined.");
+
+			if (set && type == WalkFile)
+				st_error(__FILE__, __LINE__, "Already a file defined.");
+			
+			strncpy(index_name, optarg, 255);
+			index_name[255] = '\0';
+			set = 1;
+			type = WalkDir;
+			break;
+		case 'f':
+			if (set && type == WalkDir)
+				st_error(__FILE__, __LINE__, "Already a directory defined.");
+
+			if (set && type == WalkFile)
+				st_error(__FILE__, __LINE__, "Already a file defined.");
+			
+			strncpy(index_name, optarg, 255);
+			index_name[255] = '\0';
+			set = 1;
+			type = WalkFile;
+			break;
+
+		}
+	}
+
+	if (optind != argc)
+		usage(argv[0]);
+	
+	if (!set)
+		st_error(__FILE__, __LINE__, "No directory or file selected.");
+	
+	index_walk(type, index_name, action, 0);
+
+	return 1;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/print_keywords.c sleuthkit-2.04/src/searchtools/print_keywords.c
--- sleuthkit-2.04-orig/src/searchtools/print_keywords.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/print_keywords.c	2006-06-14 23:46:46.000000000 +0200
@@ -0,0 +1,369 @@
+/* print_keywords
+ * 
+ * Prints the keywords from multiple indexes.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <combining.h>
+#include <lli_byte.h>
+#include <helpers.h>
+#include <keyword_printing.h>
+
+#define MAX_FILES		1024
+#define MAX_DEPTH		BUFFER_OVERLAP
+
+int			no_count = 0;
+int			input_file_count = 0;
+
+FILE			*input_file[MAX_FILES];
+
+unsigned char		word[BUFFER_SIZE];
+unsigned char		index_chars[MAX_FILES][MAX_DEPTH][255];
+long long int		index_jumps[MAX_DEPTH][255];
+unsigned char		index_chars_index[MAX_FILES][MAX_DEPTH];
+unsigned char		index_chars_count[MAX_FILES][MAX_DEPTH];
+unsigned char		index_depth[MAX_FILES];
+unsigned char		children_count[MAX_FILES][MAX_DEPTH];
+long long int		offset_count[MAX_FILES][MAX_DEPTH];
+
+struct keyword_printing_funcs	funcs[MAX_FILES];
+
+typedef struct list_data {
+	int data[MAX_FILES];
+	int count;
+} list_data;
+
+void skip_single_node(FILE *input, int index, int depth)
+{
+	int i;
+
+	fscanf(input, "%c",
+			&children_count[index][depth]);
+	offset_count[index][depth] = read_lli_byte(input);
+	
+	for (i = 0; i < children_count[index][depth]; ++i) {
+		fscanf(input, "%c", &index_chars[index][depth][i]);
+		read_raw_lli_byte(input);
+	}
+	
+	index_chars_index[index][depth] = 0;
+	index_chars_count[index][depth] = children_count[index][depth];
+
+	for (i = 0; i < offset_count[index][depth]; ++i)
+		funcs[index].skip_offset(input);
+
+	if (depth) {
+		--children_count[index][depth - 1];
+		++index_chars_index[index][depth - 1];
+	}
+
+	index_depth[index] = depth + 1;		
+}
+
+void copy_node(FILE *from, int index, int depth)
+{
+	int i, count;
+
+	if (offset_count[index][depth]) {
+		if (no_count)
+			printf("%s\n", word);
+		else
+			printf("%-30s %lld\n", word, offset_count[index][depth]);
+	}
+
+	count = children_count[index][depth];
+	for (i = 0; i < count; ++i) {
+		word[depth] = index_chars[index][depth][index_chars_index[index][depth]];
+		skip_single_node(from, index, depth + 1);
+		copy_node(from, index, depth + 1);
+		word[depth] = '\0';
+	}
+}
+
+void clear_list(list_data *list)
+{
+	list->count = 0;
+}
+
+void add_to_list(list_data *list, int index)
+{
+	list->data[list->count] = index;
+	++list->count;
+}
+
+void remove_from_list(list_data *list, int index)
+{
+	int i;
+	
+	for (i = 0; i < list->count; ++i) {
+		if (list->data[i] == index) {
+			if (i != list->count - 1)
+				list->data[i] = list->data[list->count - 1];
+
+			--list->count;
+			return;
+		}
+	}
+	
+	fprintf(stderr, "remove_from_list: Index %d is not found count %d\n",
+		index,
+		list->count);
+	exit(1);
+}
+
+int get_list_count(list_data *list)
+{
+	return list->count;
+}
+
+int get_item(list_data *list, int index)
+{
+	if (index >= list->count) {
+		fprintf(stderr, "Index %d is too large for this depth/count %d\n",
+				index,
+				list->count);
+		exit(1);
+	}
+	return list->data[index];
+}
+
+void combine_input(list_data *list, int depth)
+{
+	int i;
+	list_data lowest_list;
+
+#ifdef DEBUG
+	char spaces[50];
+	for (i = 0; i < depth; ++i)
+		spaces[i] = ' ';
+	spaces[depth] = '\0';
+#endif
+//	printf("combine_input %d\n", depth);
+	do {
+		int minimum_index = 266;
+		clear_list(&lowest_list);	
+#ifdef DEBUG
+		printf("%scombine_input: List at level %d:", spaces, depth);
+		for (i = 0; i < list->count; ++i)
+			printf(" %d", list->data[i]);
+		printf("\n");
+#endif
+		
+			// First determine the nodes with the minimum index;
+			// 
+		for (i = 0; i < list->count; ++i) {
+			int index = list->data[i];
+		
+			if (index_depth[index] > depth) {
+				unsigned char c = index_chars[index][depth][index_chars_index[index][depth]];
+#ifdef DEBUG
+				printf("%scombine_input: index_char for index %d at depth %d is %c\n", spaces, index, depth, c);
+#endif
+				if (minimum_index > c) {
+					clear_list(&lowest_list);
+					add_to_list(&lowest_list, index);
+					minimum_index = c;
+				} else if (minimum_index == c) {
+					add_to_list(&lowest_list, index);
+				}
+#ifdef DEBUG
+			} else {
+				printf("%scombine_input: index_depth (%d) of index %d too low for %d\n", spaces, index_depth[index], index, depth);
+#endif
+			}
+		}
+#ifdef DEBUG
+		printf("%scombine_input: Lowest list at level %d for %c:", spaces, depth, minimum_index);
+		for (i = 0; i < lowest_list.count; ++i)
+			printf(" %d", lowest_list.data[i]);
+		printf("\n");
+#endif
+
+		if (lowest_list.count == 0) {
+			fprintf(stderr, "Count is 0... Should not happen.\n%d in list\n",
+					list->count);
+			exit(1);
+		}
+
+		word[depth] = minimum_index;
+
+//		printf("Current word: %s\n", word);
+		if (lowest_list.count == 1) {
+			int index = get_item(&lowest_list, 0);
+
+#ifdef DEBUG
+			printf("%scombine_input: Skipping for index %d index at depth %d\n", spaces, index, depth + 1);
+#endif
+			skip_single_node(input_file[index], index, depth + 1);
+
+			copy_node(input_file[index], index, depth + 1);
+		} else {
+			list_data	send_list;
+			long long int	offset_cnt = 0;
+
+			clear_list(&send_list);
+			
+			for (i = 0; i < get_list_count(&lowest_list); ++i) {
+				int index = lowest_list.data[i];
+
+#ifdef DEBUG
+				printf("%scombine_input: Skipping for index %d index at depth %d\n", spaces, index, depth + 1);
+#endif
+				skip_single_node(input_file[index], index, depth + 1);
+#ifdef DEBUG
+				printf("%scombine_input: Count at level %d for %c in %d: %lld\n", spaces, depth, minimum_index, index, offset_count[index][depth + 1]);
+#endif
+				offset_cnt += offset_count[index][depth + 1];
+				
+				if (children_count[index][depth + 1]) {
+					add_to_list(&send_list, index);
+				}
+			}
+
+			if (offset_cnt) {
+#ifdef DEBUG
+				printf("%s", spaces);
+#endif
+				if (no_count)
+					printf("%s\n", word);
+				else
+					printf("%-30s %lld\n", word, offset_cnt);
+			}
+			
+			if (send_list.count)
+				combine_input(&send_list, depth + 1);
+		}
+		word[depth] = '\0';
+
+		for (i = 0; i < get_list_count(&lowest_list); ++i) {
+			int index = get_item(&lowest_list, i);
+			
+#ifdef DEBUG
+			printf("%scombine_input: Removing from lowest_list for index %d index\n", spaces, index);
+#endif
+			if (index_chars_index[index][depth] >= index_chars_count[index][depth]) {
+				remove_from_list(list, index);
+			}
+		}
+#ifdef DEBUG
+		printf("%scombine_input: End of do at level %d\n", spaces, depth);
+#endif
+	} while(get_list_count(list));
+}	
+
+static void usage (char *prog)
+{
+	printf("Usage: %s <options> [-d index_dir] [-f index_file]\n", prog);
+	printf("Print all the keywords in the index_dir or index_file.\n");
+	printf("\n");
+	printf("\nThe options are:\n");
+	printf("\t-h\t\tDisplay this information.\n");
+	printf("\t-v\t\tVerbose output.\n");
+	printf("\t-c\t\tDo not display count of keywords.\n");
+	printf("\n");
+	printf("Report bugs to <p.j.bakker@brainspark.nl>.\n");
+	exit(1);
+}
+
+void action(FILE *index_file, char *name, void *ptr)
+{
+	struct index_header	header;
+	
+	if (st_verbose)
+		printf("Using '%s'.\n", name);
+
+	input_file[input_file_count] = safe_open(name, "r");
+		
+	header = read_header(input_file[input_file_count]);
+
+	funcs[input_file_count] = get_keyword_printing_funcs(header.type);
+		
+	index_depth[input_file_count] = 0;
+	skip_single_node(input_file[input_file_count], input_file_count, 0);
+		
+	++input_file_count;
+}
+
+int main(int argc, char **argv)
+{
+	char 		ch;
+	char		index_name[256];
+	int 		i;
+	list_data	list;
+	enum WalkType	type = NoWalk;
+	
+	while ((ch = getopt(argc, argv, "vhcd:f:")) > 0) {
+		switch (ch) {
+		case '?':
+		case 'h':
+		default:
+			fprintf(stderr, "Unknown argument '%c'.\n", ch);
+			usage(argv[0]);
+			break;
+		case 'v':
+			++st_verbose;
+			break;
+		case 'c':
+			++no_count;
+			break;
+		case 'd':
+			if (type == WalkDir)
+				st_error(__FILE__, __LINE__, "Already a directory defined.");
+			if (type == WalkFile)
+				st_error(__FILE__, __LINE__, "Already a file defined.");
+
+			strncpy(index_name, optarg, 255);
+			index_name[255] = '\0';
+			type = WalkDir;
+			break;
+		case 'f':
+			if (type == WalkDir)
+				st_error(__FILE__, __LINE__, "Already a directory defined.");
+			if (type == WalkFile)
+				st_error(__FILE__, __LINE__, "Already a file defined.");
+
+			strncpy(index_name, optarg, 255);
+			index_name[255] = '\0';
+			type = WalkFile;
+			break;
+		}
+	}
+
+	if (argc == 1 || optind != argc)
+		usage(argv[0]);
+
+	if (type == NoWalk)
+		st_error(__FILE__, __LINE__, "No directory or file selected");
+	
+	memset(word, 0, BUFFER_SIZE);
+
+	index_walk(type, index_name, action, 0);
+
+	for (i = 0; i < input_file_count; ++i)
+		list.data[i] = i;
+
+	list.count = input_file_count;
+
+	combine_input(&list, 0);
+
+	return 1;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/regression.c sleuthkit-2.04/src/searchtools/regression.c
--- sleuthkit-2.04-orig/src/searchtools/regression.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/regression.c	2004-03-03 15:41:02.000000000 +0100
@@ -0,0 +1,109 @@
+#include <st_common.h>
+#include <lli_byte.h>
+#include <helpers.h>
+
+void handle_lli_byte(FILE *output, long long int n)
+{
+	unsigned char buffer[20];
+	unsigned char *ptr = buffer;
+	
+	if (st_verbose)
+		printf("--------------\nHandling %lld\n\n", n);
+
+	rewind(output);
+
+	int length = get_lli_byte_length(&n);
+
+	if (st_verbose)
+		printf("Length start: %d\n", length);
+	
+	int wlength = write_lli_byte(output, n);
+
+	if (wlength != length)
+		st_error(__FILE__, __LINE__, "ERROR");
+	
+	int wrlength = write_raw_lli_byte(output, &n);
+
+	if (wrlength != 8)
+		st_error(__FILE__, __LINE__, "ERROR");
+
+	int plength = sprint_lli_byte(ptr, &n);
+	ptr += plength;
+
+	if (plength != length)
+		st_error(__FILE__, __LINE__, "ERROR");
+
+	int prlength = sprint_raw_lli_byte(ptr, &n);
+
+	if (prlength != 8)
+		st_error(__FILE__, __LINE__, "ERROR");
+
+	if (st_verbose) {
+		int i;
+		
+		printf("Chars: ");
+		for (i = 0; i < length; ++i)
+			printf("%d ", ptr[i]);
+		printf("\n");
+	}
+
+	rewind(output);
+	
+	if (st_verbose) {
+		unsigned char c;
+		
+		printf("Chars: ");
+		do {
+			c = fgetc(output);
+			printf("%d ", c);
+		} while (c & 0x80);
+		printf("\n");
+
+		rewind(output);
+	}
+
+	ptr = buffer;
+
+	long long int r = read_lli_byte(output);
+
+	if (r != n)
+		st_error(__FILE__, __LINE__, "ERROR %lld != %lld", n, r);
+
+	long long int rr = read_raw_lli_byte(output);
+
+	if (rr != n)
+		st_error(__FILE__, __LINE__, "ERROR %lld != %lld", n, rr);
+
+	long long int s;
+	int slength = sscan_lli_byte(ptr, &s);
+	ptr += slength;
+
+	if (slength != length)
+		st_error(__FILE__, __LINE__, "ERROR");
+
+	if (s != n)
+		st_error(__FILE__, __LINE__, "ERROR %lld != %lld", n, s);
+
+	long long int sr = sscan_raw_lli_byte(ptr);
+
+	if (sr != n)
+		st_error(__FILE__, __LINE__, "ERROR %lld != %lld", n, sr);
+
+	printf(" - SUCCESS\n");
+}
+
+int main()
+{
+	FILE *file = safe_open("regressionfile", "w+");
+
+	handle_lli_byte(file, 0);
+	handle_lli_byte(file, 1);
+	handle_lli_byte(file, 2);
+	handle_lli_byte(file, 127);
+	handle_lli_byte(file, 128);
+	handle_lli_byte(file, 129);
+	handle_lli_byte(file, 65535);
+	handle_lli_byte(file, 65536);
+
+	return 0;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/regression_tests/create_indexes.sh sleuthkit-2.04/src/searchtools/regression_tests/create_indexes.sh
--- sleuthkit-2.04-orig/src/searchtools/regression_tests/create_indexes.sh	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/regression_tests/create_indexes.sh	2006-06-14 23:52:39.000000000 +0200
@@ -0,0 +1,76 @@
+#!/bin/sh
+BINDIR=../../../bin
+TESTIMAGE=test_image2
+
+echo "Creating test indexes"
+echo " - Index 1a (All default)"
+rm -rf index_1a/*
+$BINDIR/indexer index_1a $TESTIMAGE 
+
+echo " - Index 2a (Maxmimum nodes)"
+rm -rf index_2a/*
+$BINDIR/indexer index_2a $TESTIMAGE -m 1 -n 100
+
+echo "Checking combiner"
+$BINDIR/combiner index_2a index_2a/raw_idx.* -o tsta
+
+DIFF="$( diff tsta.idx index_1a/raw_idx.000 )"
+
+if [ "X" != "X$DIFF" ];
+then
+	echo "ERROR!!!!!!"
+	echo "Combine not equal to single index"
+	exit 1
+fi
+
+echo "Checking print_keywords"
+rm -rf index_1a.kw index_2a.kw
+$BINDIR/print_keywords -d index_1a > index_1a.kw
+$BINDIR/print_keywords -d index_2a > index_2a.kw
+
+DIFF="$( diff index_1a.kw index_2a.kw )"
+
+if [ "X" != "X$DIFF" ];
+then
+        echo "ERROR!!!!!!"
+	echo "print_keywords not equal to single index"
+	exit 1
+fi
+
+TESTIMAGE=test_image
+
+echo "Creating test indexes"
+echo " - Index 1 (All default)"
+rm -rf index_1/*
+$BINDIR/indexer index_1 $TESTIMAGE
+
+echo " - Index 2 (Maxmimum 1Mb memory)"
+rm -rf index_2/*
+$BINDIR/indexer index_2 $TESTIMAGE -m 1
+
+echo "Checking combiner"
+$BINDIR/combiner index_2 index_2/raw_idx.* -o tst
+
+DIFF="$( diff tst.idx index_1/raw_idx.000 )"
+
+if [ "X" != "X$DIFF" ];
+then
+	echo "ERROR!!!!!!"
+	echo "Combine not equal to single index"
+	exit 1
+fi
+
+echo "Checking print_keywords"
+rm -rf index_1.kw index_2.kw
+$BINDIR/print_keywords -d index_1 > index_1.kw
+$BINDIR/print_keywords -d index_2 > index_2.kw
+
+DIFF="$( diff index_1.kw index_2.kw )"
+
+if [ "X" != "X$DIFF" ];
+then
+        echo "ERROR!!!!!!"
+	echo "print_keywords not equal to single index"
+	exit 1
+fi
+
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/regression_tests/make_image.sh sleuthkit-2.04/src/searchtools/regression_tests/make_image.sh
--- sleuthkit-2.04-orig/src/searchtools/regression_tests/make_image.sh	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/regression_tests/make_image.sh	2006-03-13 20:27:10.000000000 +0100
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+echo "Creating test image file: test_image"
+echo " - Getting 50Mb of zeros"
+dd if=/dev/zero of=test_image bs=1M count=50 || exit
+echo " - Creating a dummy ext2 filesystem"
+/sbin/mkfs.ext2 -F test_image || exit
+echo " - Mounting filesystem on mnt"
+if [ ! -d mnt ];
+then
+  mkdir mnt
+fi
+sudo mount -o loop test_image mnt || exit
+echo " - Filling up filesystem"
+for i in 1 2;
+do
+  mkdir mnt/test_$i
+  cp -a ../*.{c,h,o} mnt/test_$i
+  cp -a ../../fstools/* mnt/test_$i
+  cp -a ../../imgtools/* mnt/test_$i
+  cp -a ../../auxtools/* mnt/test_$i
+done
+echo " - Removing some entries"
+rm -r mnt/test_2/*.h
+echo " - Creating fragments"
+for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18;
+do
+  cat ../../srchtools/*.{c,o} >> mnt/test_2/catted_$i
+done
+sudo umount mnt
+echo "Creating test image file: test_image2"
+echo " - Getting 5Mb of zeros"
+dd if=/dev/zero of=test_image2 bs=1M count=5 || exit
+echo " - Creating a dummy ext2 filesystem"
+/sbin/mkfs.ext2 -F test_image2 || exit
+echo " - Mounting filesystem on mnt"
+if [ ! -d mnt ];
+then
+  mkdir mnt
+fi
+sudo mount -o loop test_image2 mnt || exit
+echo " - Filling up filesystem"
+for i in 1;
+do
+  mkdir mnt/test_$i
+  cp -a ../index_functions.h mnt/test_$i
+done
+sudo umount mnt
+echo "Done!"
+
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/searcher.c sleuthkit-2.04/src/searchtools/searcher.c
--- sleuthkit-2.04-orig/src/searchtools/searcher.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/searcher.c	2006-06-08 17:40:11.000000000 +0200
@@ -0,0 +1,414 @@
+/* searcher
+ * 
+ * Searches the image file using an index file for a word.
+ *
+ * Author:       Paul Bakker <p.j.bakker@brainspark.nl>
+ *
+ * Copyright 2003 Fox-IT Forensic IT Experts B.V.
+ *
+ * sleuthkit_searchtools 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.
+ *
+ * sleuthkit_searchtools 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 <st_common.h>
+#include <index_common.h>
+#include <combining.h>
+#include <lli_byte.h>
+#include <index_functions.h>
+#include <helpers.h>
+#include <index_raw.h>
+#include <index_raw_frag.h>
+#include <fstools/fs_types.h>
+
+int	insensitive = 0;
+int	whole_word = 0;
+int	no_folding = 0;
+int	err_to_out = 0;
+int	type_on_line = 0;
+
+struct search_data {
+	FS_INFO	*fs;
+	char	*search_word;
+	char	*orig_word;
+	int	orig_word_len;
+	int	count;
+};
+
+FILE *logfp;
+int goto_index_node(FILE *index_file, char *str)
+{ 
+	unsigned char children_count, c;
+	long long int offset;
+	int i;
+	
+	if (!*str)
+		return 1;
+
+	fscanf(index_file, "%c", &children_count);
+	read_lli_byte(index_file);
+	
+	for (i = 0; i < children_count; ++i) {
+		fscanf(index_file, "%c", &c);
+		offset = read_raw_lli_byte(index_file);
+
+		if (c > *str)
+			return 0;
+
+		if (c == *str) {
+			fseeko64(index_file, offset, SEEK_SET);
+			return goto_index_node(index_file, str + 1);
+		}
+	}
+
+	return 0;
+}
+
+static void usage (char *prog)
+{
+	fprintf(stderr, "Usage: %s <options> index_dir search_string image_file\n", prog);
+	fprintf(stderr, "Searches the image_file file for a search string, using an index directory.\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "The options are:\n");
+	fprintf(stderr, "\t-h\t\tDisplay this information.\n");
+	fprintf(stderr, "\t-v\t\tVerbose output.\n");
+	fprintf(stderr, "\t-c\t\tCase insensitive search. (Only on 'Default' ASCII alfabet)\n");
+	fprintf(stderr, "\t-w\t\tOnly matches whole words.\n");
+	fprintf(stderr, "\t-e\t\tPrint errors to stdout instead of stderr.\n");
+	fprintf(stderr, "\t-p\t\tPrint index type with each result.\n");
+//	fprintf(stderr, "\t-s\t\tStrict matching (No folding).\n");
+	fprintf(stderr, "\t-i <type>\tSpecify the image type.\n");
+	fprintf(stderr, "\t-o <type>\tSpecify the offset within the image.\n");
+	fprintf(stderr, "\t-t <fstype>\tImage file system type\n");
+	fs_print_types(stderr);
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Report bugs to <p.j.bakker@brainspark.nl>.\n");
+	exit(1);
+}
+
+void lower_string(char *string)
+{
+	char *ptr = string;
+	while ((*ptr = tolower(*ptr)))
+		++ptr;
+}
+
+void combine_node_offsets(struct combining_funcs *funcs, FILE *input, int recursive)
+{
+	unsigned char c;
+	long long int offset_count;
+
+	fscanf(input, "%c", &c);
+	offset_count = read_lli_byte(input);
+
+	fseeko64(input, c * 9, SEEK_CUR);
+
+	funcs->read_indexes(funcs, input, offset_count);
+
+	if (recursive) {
+		int i;
+
+		for (i = 0; i < c; ++i)
+			combine_node_offsets(funcs, input, recursive);
+	}
+}
+
+long long int	search_offset = 0;
+int		search_length = BUFFER_OVERLAP;
+unsigned char	*search_string = 0;
+
+u_int8_t search_word_frag(FS_INFO *fs, DADDR_T addr, char *buf, unsigned int size, int flags, void *ptr)
+{
+	if (!search_length)
+		return WALK_STOP;
+
+	if (search_offset < fs->block_size) {
+		int use_length = search_length;
+
+		if (search_offset + use_length > fs->block_size)
+			use_length = fs->block_size - search_offset;
+
+		fs_read_random(fs, (char *) search_string, use_length,
+				addr * fs->block_size + search_offset
+				);
+		
+		search_length -= use_length;
+		search_offset = 0;
+		search_string += use_length;
+
+		if (!search_length)
+			return WALK_STOP;
+		
+	} else
+		search_offset -= fs->block_size;
+
+	return WALK_CONT;
+}
+
+void get_image_word(enum IndexType type, FS_INFO *fs, void *ptr, unsigned char *string)
+{
+	long long int		offset;
+	struct frag_data	*index_data;
+	FS_INODE		*fs_inode;
+
+	switch(type) {
+	case Raw:
+		offset = *((long long int *) ptr);
+		fs_read_random(fs, (char *) string, BUFFER_OVERLAP, offset
+			);
+		break;
+	case RawFragment:
+		index_data = (struct frag_data *) ptr;
+		search_offset = index_data->offset;
+		search_length = BUFFER_OVERLAP;
+		search_string = string;
+		
+		fs_inode = fs->inode_lookup(fs, index_data->inode);
+
+		fs->file_walk(fs, fs_inode, 0, 0, FS_FLAG_FILE_AONLY | FS_FLAG_FILE_SLACK |
+				FS_FLAG_FILE_RECOVER | FS_FLAG_FILE_NOSPARSE,
+				search_word_frag, (char *) 0);
+		
+		fs_inode_free(fs_inode);
+		break;
+	default:
+		st_error(__FILE__, __LINE__, ERROR_UNHANDLED_INDEX_TYPE, type);
+	}
+}
+
+enum IndexType	last_match_type = Unknown;
+
+void print_match(enum IndexType type, void *data, unsigned char *string)
+{
+	switch(type) {
+	case Raw:
+		if (type_on_line) {
+			printf("raw      ");
+		} else if (last_match_type != Raw) {
+			last_match_type = Raw;
+			printf("Type: Raw\n");
+		}
+
+		printf("%12lld %s\n", *((long long int *) data), string);
+		break;
+	case RawFragment:
+		if (type_on_line) {
+			printf("raw_frag ");
+		} else if (last_match_type != RawFragment) {
+			last_match_type = RawFragment;
+			printf("Type: RawFragment\n");
+		}
+
+		printf("%8lld %12lld %s\n", ((struct frag_data *) data)->inode,
+				((struct frag_data *) data)->offset, string);
+		break;
+	default:
+		st_error(__FILE__, __LINE__, ERROR_UNHANDLED_INDEX_TYPE, type);
+	}
+}
+
+void search(FILE *index_file, char *index_filename, void *ptr)
+{
+	struct search_data	*search_data = (struct search_data *) ptr;
+	int			i, count = 0;
+	struct combining_funcs	funcs;
+	struct index_header 	header = read_header(index_file);
+
+	funcs = get_combining_funcs(header.type);
+
+	if (goto_index_node(index_file, (char *) search_data->search_word)) {
+		unsigned char 	string[BUFFER_OVERLAP + 1];
+		void		*ptr;
+		void		*data;
+		int		match;
+
+		clear_index_combine_list();
+
+		combine_node_offsets(&funcs, index_file, (whole_word)?0:1);
+
+		for (ptr = get_first_index_combine_list_item();
+				ptr; ptr = get_next_index_combine_list_item(ptr)) {
+			match = 1;
+			data = get_index_combine_list_offset(ptr);
+			get_image_word(header.type, search_data->fs, data, string);
+			string[BUFFER_OVERLAP] = '\0';
+
+			for (i = 0; i < BUFFER_OVERLAP; ++i) {
+				if (is_valid_char(string[i]) != '1') {
+					string[i]='\0';
+					i = BUFFER_OVERLAP;
+				}
+			}
+
+			if (!insensitive) {
+				for (i = 0; i < search_data->orig_word_len && match; ++i)
+					if (search_data->orig_word[i] != string[i])
+						match = 0;
+			} else if (no_folding) {
+				for (i = 0; i < search_data->orig_word_len && match; ++i)
+					if (tolower(search_data->orig_word[i]) !=
+							tolower(string[i]))
+						match = 0;
+			}
+
+			if (match) {
+				++count;
+				print_match(header.type, data, string);
+			}
+		}
+	} else if (st_verbose)
+		printf("   Node '%s' not found.\n", search_data->search_word);
+
+	search_data->count += count;
+}
+
+int main(int argc, char **argv)
+{
+	char				search_word[BUFFER_SIZE];
+	char				orig_word[BUFFER_SIZE];
+	int				orig_word_len;
+	
+	char				ch;
+	int				i;	
+	struct index_configuration	config;
+	struct search_data		data;
+	
+	char            *imgtype = NULL;
+	IMG_INFO        *img = 0;
+	SSIZE_T		imgoff = 0;
+	char            *fstype = NULL;
+	FS_INFO         *fs = 0;
+
+	logfp = stderr;
+
+	while ((ch = getopt(argc, argv, "vhi:cswt:epo:")) > 0) {
+		switch(ch) {
+		case '?':
+		case 'h':
+		default:
+			fprintf(logfp, "ERROR: Unknown argument '%c'.\n", ch);
+			usage(argv[0]);
+			break;
+		case 'v':
+			++st_verbose;
+			break;
+		case 'c':
+			++insensitive;
+			break;
+		case 'w':
+			++whole_word;
+			break;
+		case 's':
+			++no_folding;
+			break;
+		case 't':
+			fstype = optarg;
+			break;
+		case 'i':
+			imgtype = optarg;
+			break;
+		case 'o':
+			if ((imgoff = parse_offset(optarg)) == -1) {
+		                tsk_error_print(stderr);
+                		exit(1);
+            		}
+			break;
+		case 'e':
+			++err_to_out;
+			break;
+		case 'p':
+			++type_on_line;
+			break;
+		}
+	}
+
+	if (err_to_out)
+		logfp = stdout;
+
+	if (optind + 3 > argc) {
+		fprintf(stderr, "ERROR: Too few arguments.\n");
+		usage(argv[0]);
+	}
+	
+	if (st_verbose) {
+		int i;
+
+		printf("Using %d images:", argc - optind - 2);
+		for (i = optind + 2; i < argc; ++i)
+			printf(" %s", argv[i]);
+		printf("\n");
+	}
+
+	img = img_open(imgtype, argc - optind - 2,
+			(const char **) &argv[optind + 2]);
+	fs = fs_open(img, imgoff, fstype);
+
+	strncpy(index_dir, argv[optind], 255);
+	index_dir[255] = '\0';
+
+	if (st_verbose)
+		printf("Using index_dir '%s'.\n", index_dir);
+
+	config = read_config();
+
+	strncpy(search_word, argv[optind + 1], BUFFER_SIZE - 1);
+	search_word[strlen(search_word)] = '\0';
+
+	if (st_verbose)
+		printf("Searching for '%s'%s%s%s.\n", search_word,
+				(insensitive)?" (Case insensitive)":"",
+				(whole_word)?" (Only whole words)":"",
+				(no_folding)?" (No folding)":"");
+
+
+	if (strlen(search_word) < config.min_length) {
+		fprintf(logfp, "ERROR: Index has minimum word length %d. Search word has length %d.\n",
+				config.min_length,
+				strlen(search_word));
+		exit(1);
+	}
+
+	if (strlen(search_word) > config.max_length) {
+		fprintf(logfp, "ERROR: Index has maximum word length %d. Search word has length %d.\n",
+				config.max_length,
+				strlen(search_word));
+		exit(1);
+	}
+	
+	strncpy(orig_word, search_word, BUFFER_SIZE - 1);
+	orig_word_len = strlen(orig_word);
+	
+	for (i = 0; i < strlen(search_word); ++i) {
+		if (is_valid_char(search_word[i]) != '1') {
+			fprintf(logfp, "ERROR: Character '%c' not supported in this index.\n",
+					search_word[i]);
+			exit(1);
+		}
+		search_word[i] = mangle_char(search_word[i]);
+	}
+
+	int count = 0;
+	
+	data.fs = fs;
+	data.search_word = search_word;
+	data.orig_word = orig_word;
+	data.orig_word_len = orig_word_len;
+	data.count = 0;
+
+	index_walk(WalkDir, index_dir, search, &data);
+
+	if (!count && st_verbose)
+		printf("Did not find %s\n", search_word);
+
+	return 0;
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/st_common.c sleuthkit-2.04/src/searchtools/st_common.c
--- sleuthkit-2.04-orig/src/searchtools/st_common.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/st_common.c	2006-03-13 20:21:30.000000000 +0100
@@ -0,0 +1,9 @@
+#include <st_common.h>
+
+int	st_verbose = 0;
+int	st_quiet = 0;
+int	st_max_mem = 200;
+int	st_max_nodes = -1;
+int	st_human_readable = 0;
+
+char	index_dir[256];
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/st_common.h sleuthkit-2.04/src/searchtools/st_common.h
--- sleuthkit-2.04-orig/src/searchtools/st_common.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/st_common.h	2006-03-13 20:18:53.000000000 +0100
@@ -0,0 +1,21 @@
+#ifndef _H_ST_COMMON
+#define _H_ST_COMMON
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <st_error.h>
+
+extern int	st_verbose;
+extern int	st_quiet;
+extern int	st_max_mem;
+extern int	st_max_nodes;
+extern int	st_human_readable;
+
+extern char	index_dir[256];
+
+#endif
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/st_error.c sleuthkit-2.04/src/searchtools/st_error.c
--- sleuthkit-2.04-orig/src/searchtools/st_error.c	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/st_error.c	2004-02-19 17:09:00.000000000 +0100
@@ -0,0 +1,17 @@
+#include <st_error.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void st_error(char *filename, unsigned int line, char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+
+	fprintf(stderr, "%s:%d - ", filename, line);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");	
+
+	va_end(ap);
+	exit(1);
+}
diff -x CVS --unidirectional-new-file -r -u sleuthkit-2.04-orig/src/searchtools/st_error.h sleuthkit-2.04/src/searchtools/st_error.h
--- sleuthkit-2.04-orig/src/searchtools/st_error.h	1970-01-01 01:00:00.000000000 +0100
+++ sleuthkit-2.04/src/searchtools/st_error.h	2004-02-19 17:09:00.000000000 +0100
@@ -0,0 +1,3 @@
+#define ERROR_UNHANDLED_INDEX_TYPE	"Unhandled index type '%d'."
+
+void st_error(char *filename, unsigned int line, char *fmt, ...);
