Add libtar to TWRP instead of using busybox tar
Add proper mkdosfs tool
Add fuse to TWRP
Add experimental exfat-fuse to TWRP
Convert all system() functions to use new Exec_Cmd function
diff --git a/libtar/Android.mk b/libtar/Android.mk
new file mode 100644
index 0000000..b73a41f
--- /dev/null
+++ b/libtar/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtar
+LOCAL_MODULE_TAGS := eng
+LOCAL_MODULES_TAGS = optional
+LOCAL_CFLAGS = 
+LOCAL_SRC_FILES = append.c block.c decode.c encode.c extract.c handle.c output.c util.c wrapper.c basename.c strmode.c libtar_hash.c libtar_list.c dirname.c
+LOCAL_C_INCLUDES += $(LOCAL_PATH) \
+					external/zlib
+LOCAL_SHARED_LIBRARIES += libz libc
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/libtar/COPYRIGHT b/libtar/COPYRIGHT
new file mode 100644
index 0000000..2471ec0
--- /dev/null
+++ b/libtar/COPYRIGHT
@@ -0,0 +1,35 @@
+Copyright (c) 1998-2003  University of Illinois Board of Trustees
+Copyright (c) 1998-2003  Mark D. Roth
+All rights reserved.
+
+Developed by: Campus Information Technologies and Educational Services,
+              University of Illinois at Urbana-Champaign
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+``Software''), to deal with the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimers.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimers in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the names of Campus Information Technologies and Educational
+  Services, University of Illinois at Urbana-Champaign, nor the names
+  of its contributors may be used to endorse or promote products derived
+  from this Software without specific prior written permission.
+
+THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+
diff --git a/libtar/ChangeLog b/libtar/ChangeLog
new file mode 100644
index 0000000..cde7675
--- /dev/null
+++ b/libtar/ChangeLog
@@ -0,0 +1,15 @@
+2002-12-09	added list_empty() and hash_empty() functions
+
+2002-09-12	fixed list_iterate function to return -1 if it gets
+		an invalid argument
+
+		include <config.h> and <compat.h> from source files, not
+		from header file, since header file is sometimes
+		installed as part of a user-visible API
+		(those APIs should eventually be redesigned without the
+		listhash code being publicly visible, but for now we
+		need to accomodate this)
+
+2002-07-07	modified list iterate function to return int
+		(returns -1 if plugin function returns -1)
+
diff --git a/libtar/ChangeLog-1.0.x b/libtar/ChangeLog-1.0.x
new file mode 100644
index 0000000..23b06b3
--- /dev/null
+++ b/libtar/ChangeLog-1.0.x
@@ -0,0 +1,141 @@
+libtar 1.0.2 - 6/21/00
+------------
+
+- tar_set_file_perms() now calls chown() only if the effective user ID is 0
+  (workaround for IRIX and HP-UX, which allow file giveaways)
+
+- tar_set_file_perms() now calls chmod() or lchmod() after chown()
+  (this fixes a problem with extracting setuid files under Linux)
+
+- removed calls to fchown() and fchmod() from tar_extract_regfile()
+
+- fixed bugs in th_read() which didn't set errno properly
+
+- removed various unused variables
+
+----------------------------------------------------------------------
+
+libtar 1.0.1 - 4/1/00
+------------
+
+- removed libgen.h include from dirname and basename compat code
+
+- added lib/fnmatch.c compatability module from OpenBSD
+
+- fixed several objdirs bugs in libtar/Makefile.in
+
+- misc Makefile changes (added $CPPFLAGS support, added -o flag to compile
+  commands, use $CFLAGS on link line, etc)
+
+- removed "inline" keyword from all source files to prevent portability
+  problems
+
+- updated README
+
+----------------------------------------------------------------------
+
+libtar 1.0 - 1/2/00
+----------
+
+- various portability fixes
+
+- "make install" now runs mkencap and epkg if they're available
+
+- libmisc is now integrated into libtar
+
+----------------------------------------------------------------------
+
+libtar 0.5.6 beta - 12/16/99
+-----------------
+
+- changed API to allow better error reporting via errno
+
+- added manpages to document libtar API
+
+- replaced symbolic_mode() call with strmode() compatibility code
+
+----------------------------------------------------------------------
+
+libtar 0.5.5 beta - 11/16/99
+-----------------
+
+- fixed conditional expression in extract.c to check if we're overwriting
+  a pre-existing file
+
+- many improvements to libtar.c driver program (better error checking,
+  added -C and -v options, etc)
+
+- changed API to include list of canned file types, instead of passing
+  function pointers to tar_open()
+
+- fixed tar_set_file_perms() to not complain about chown() if not root
+  and not to call utime() on a symlink
+
+- added hash code for extracting hard links in other directory paths
+
+- fixed tar_extract_glob() to only print filenames if TAR_VERBOSE option
+  is set
+
+- replaced GNU basename(), dirname(), and strdup() compatibility code
+  with OpenBSD versions
+
+- configure performs super-anal checking of basename() and dirname()
+
+----------------------------------------------------------------------
+
+libtar 0.5.4 beta - 11/13/99
+-----------------
+
+- portability fix: use ranlib instead of ar -s
+
+- misc fixes in append.c, extract.c, and wrapper.c to do error checking
+
+- fixed a bug in tar_append_file() in append.c which added some garbage
+  characters to encoded symlink names (wasn't NULL-terminating the result
+  of readlink())
+
+- fixed a bug in symbolic_mode() in output.c concerning setuid and setgid
+  bit displaying
+
+- fixed tar_extract_all() in wrapper.c to only call print_long_ls() if
+  the TAR_VERBOSE option is set
+
+- added libtar_version constant string to handle.c for external configure
+  scripts to detect what version of libtar is installed
+
+----------------------------------------------------------------------
+
+libtar 0.5.3 beta - 09/27/99
+-----------------
+
+- fixed mk_dirs_for_file() to avoid broken dirname() implementations
+
+- misc portability fixes
+
+- merged old "compat" and "libds" directories into new "misc" directory
+  and cleaned up Makefiles
+
+----------------------------------------------------------------------
+
+libtar 0.5.2 beta - 09/10/99
+-----------------
+
+- use calloc() instead of malloc() in tar_open() to fix a bounds-checking
+  bug in tar_extract_all()
+
+- fix tar_extract_all() to properly honor the prefix argument
+
+----------------------------------------------------------------------
+
+libtar 0.5.1 beta - 08/27/99
+-----------------
+
+- misc portability fixes
+
+----------------------------------------------------------------------
+
+libtar 0.5 beta - 07/05/99
+---------------
+
+- first public release
+
diff --git a/libtar/INSTALL b/libtar/INSTALL
new file mode 100644
index 0000000..50dbe43
--- /dev/null
+++ b/libtar/INSTALL
@@ -0,0 +1,183 @@
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes awhile.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/libtar/README b/libtar/README
new file mode 100644
index 0000000..91f466c
--- /dev/null
+++ b/libtar/README
@@ -0,0 +1,121 @@
+libtar - C library for manipulating tar files
+======
+
+libtar is a library for manipulating tar files from within C programs.
+Here are some of its features:
+
+  * Handles both POSIX tar file format and the GNU extensions.
+  * API provides functions for easy use, such as tar_extract_all().
+  * Also provides functions for more granular use, such as 
+    tar_append_regfile().
+
+
+Installation
+------------
+
+To build libtar, you should be able to simply run these commands:
+
+   ./configure
+   make
+   make install
+
+
+Encap Package Support
+---------------------
+
+To build this software as an Encap package, you can pass the
+--enable-encap option to configure.  This will be automatically
+enabled if the epkg or mkencap programs are detected on the system,
+but can be overridden by the --disable-encap option.
+
+When building an Encap package, the configure script will automatically
+adjust the installation prefix to use an appropriate Encap package
+directory.  It does this using a heuristic algorithm which examines the
+values of the ${ENCAP_SOURCE} and ${ENCAP_TARGET} environment variables
+and the argument to configure's --prefix option.
+
+If mkencap was detected on the system, it will be automatically run during
+"make install".  By default, epkg will also be run, but this can be
+inhibited with the --disable-epkg-install configure option.
+
+For information on the Encap package management system, see the WSG
+Encap Archive:
+
+   http://www.encap.org/
+
+
+zlib Support
+------------
+
+The configure script will attempt to find the zlib library on your system
+for use with the libtar driver program.  The zlib package is available from:
+
+   http://www.gzip.org/zlib/
+
+If zlib is installed on your system, but you do not wish to use it,
+specify the --without-zlib option when you invoke configure.
+
+
+More Information
+----------------
+
+For documentation of the libtar API, see the enclosed manpages.  For more
+information on the libtar package, see:
+
+   http://www-dev.cites.uiuc.edu/libtar/
+
+Source code for the latest version of libtar will be available there, as
+well as Encap binary distributions for many common platforms.
+
+
+Supported Platforms
+-------------------
+
+I develop and test libtar on the following platforms:
+
+   AIX 4.3.3 and 5.1
+   HP-UX 11.00
+   IRIX 6.5
+   RedHat Linux 7.2
+   Solaris 8 and 9
+
+It should also build on the following platforms, but I do not actively
+support them:
+
+   AIX 3.2.5
+   AIX 4.2.1
+   Cygwin
+   FreeBSD
+   HP-UX 10.20
+   Linux/libc5
+   OpenBSD
+   Solaris 2.5
+   Solaris 2.6
+   Solaris 7
+
+If you successfully build libtar on another platform, please email me a
+patch and/or configuration information.
+
+
+Compatibility Code
+------------------
+
+libtar depends on some library calls which are not available or not
+usable on some platforms.  To accomodate these systems, I've included
+a version of these calls in the compat subdirectory.
+
+I've slightly modified these functions for integration into this source
+tree, but the functionality has not been modified from the original
+source.  Please note that while this code should work for you, I didn't
+write it, so please don't send me bug reports on it.
+
+
+Author
+------
+
+Feedback and bug reports are welcome.
+
+Mark D. Roth <roth@uiuc.edu>
+Campus Information Technologies and Educational Services
+University of Illinois at Urbana-Champaign
+
diff --git a/libtar/TODO b/libtar/TODO
new file mode 100644
index 0000000..f2f859f
--- /dev/null
+++ b/libtar/TODO
@@ -0,0 +1,21 @@
+Functionality:
+--------------
+
+* add list mode to allow nodes to be inserted in any arbitrary location
+* add "*_hash_iterate()" function
+* add flags argument to *_list_del() that allows the listptr to be set
+  to the previous or next element
+* add a generic pointer type to replace *_listptr_t and *_hashptr_t ???
+
+
+Code Cleanup:
+-------------
+
+* rename functions:
+	*_list_next	=> *_listptr_next()
+	*_list_prev	=> *_listptr_prev()
+	*_hash_next	=> *_hashptr_next()
+* start using "*_list_t" and "*_hash_t" instead of "*_list_t *" and
+  "*_hash_t *" ?
+* add prefixes to structure member field names
+
diff --git a/libtar/append.c b/libtar/append.c
new file mode 100644
index 0000000..05024b9
--- /dev/null
+++ b/libtar/append.c
@@ -0,0 +1,256 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  append.c - libtar code to append files to a tar archive
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+
+struct tar_dev
+{
+	dev_t td_dev;
+	libtar_hash_t *td_h;
+};
+typedef struct tar_dev tar_dev_t;
+
+struct tar_ino
+{
+	ino_t ti_ino;
+	char ti_name[MAXPATHLEN];
+};
+typedef struct tar_ino tar_ino_t;
+
+
+/* free memory associated with a tar_dev_t */
+void
+tar_dev_free(tar_dev_t *tdp)
+{
+	libtar_hash_free(tdp->td_h, free);
+	free(tdp);
+}
+
+
+/* appends a file to the tar archive */
+int
+tar_append_file(TAR *t, char *realname, char *savename)
+{
+	struct stat s;
+	int i;
+	libtar_hashptr_t hp;
+	tar_dev_t *td = NULL;
+	tar_ino_t *ti = NULL;
+	char path[MAXPATHLEN];
+
+#ifdef DEBUG
+	printf("==> tar_append_file(TAR=0x%lx (\"%s\"), realname=\"%s\", "
+	       "savename=\"%s\")\n", t, t->pathname, realname,
+	       (savename ? savename : "[NULL]"));
+#endif
+
+	if (lstat(realname, &s) != 0)
+	{
+#ifdef DEBUG
+		perror("lstat()");
+#endif
+		return -1;
+	}
+
+	/* set header block */
+#ifdef DEBUG
+	puts("    tar_append_file(): setting header block...");
+#endif
+	memset(&(t->th_buf), 0, sizeof(struct tar_header));
+	th_set_from_stat(t, &s);
+
+	/* set the header path */
+#ifdef DEBUG
+	puts("    tar_append_file(): setting header path...");
+#endif
+	th_set_path(t, (savename ? savename : realname));
+
+	/* check if it's a hardlink */
+#ifdef DEBUG
+	puts("    tar_append_file(): checking inode cache for hardlink...");
+#endif
+	libtar_hashptr_reset(&hp);
+	if (libtar_hash_getkey(t->h, &hp, &(s.st_dev),
+			       (libtar_matchfunc_t)dev_match) != 0)
+		td = (tar_dev_t *)libtar_hashptr_data(&hp);
+	else
+	{
+#ifdef DEBUG
+		printf("+++ adding hash for device (0x%lx, 0x%lx)...\n",
+		       major(s.st_dev), minor(s.st_dev));
+#endif
+		td = (tar_dev_t *)calloc(1, sizeof(tar_dev_t));
+		td->td_dev = s.st_dev;
+		td->td_h = libtar_hash_new(256, (libtar_hashfunc_t)ino_hash);
+		if (td->td_h == NULL)
+			return -1;
+		if (libtar_hash_add(t->h, td) == -1)
+			return -1;
+	}
+	libtar_hashptr_reset(&hp);
+	if (libtar_hash_getkey(td->td_h, &hp, &(s.st_ino),
+			       (libtar_matchfunc_t)ino_match) != 0)
+	{
+		ti = (tar_ino_t *)libtar_hashptr_data(&hp);
+#ifdef DEBUG
+		printf("    tar_append_file(): encoding hard link \"%s\" "
+		       "to \"%s\"...\n", realname, ti->ti_name);
+#endif
+		t->th_buf.typeflag = LNKTYPE;
+		th_set_link(t, ti->ti_name);
+	}
+	else
+	{
+#ifdef DEBUG
+		printf("+++ adding entry: device (0x%lx,0x%lx), inode %ld "
+		       "(\"%s\")...\n", major(s.st_dev), minor(s.st_dev),
+		       s.st_ino, realname);
+#endif
+		ti = (tar_ino_t *)calloc(1, sizeof(tar_ino_t));
+		if (ti == NULL)
+			return -1;
+		ti->ti_ino = s.st_ino;
+		snprintf(ti->ti_name, sizeof(ti->ti_name), "%s",
+			 savename ? savename : realname);
+		libtar_hash_add(td->td_h, ti);
+	}
+
+	/* check if it's a symlink */
+	if (TH_ISSYM(t))
+	{
+		i = readlink(realname, path, sizeof(path));
+		if (i == -1)
+			return -1;
+		if (i >= MAXPATHLEN)
+			i = MAXPATHLEN - 1;
+		path[i] = '\0';
+#ifdef DEBUG
+		printf("    tar_append_file(): encoding symlink \"%s\" -> "
+		       "\"%s\"...\n", realname, path);
+#endif
+		th_set_link(t, path);
+	}
+
+	/* print file info */
+	if (t->options & TAR_VERBOSE)
+		th_print_long_ls(t);
+
+#ifdef DEBUG
+	puts("    tar_append_file(): writing header");
+#endif
+	/* write header */
+	if (th_write(t) != 0)
+	{
+#ifdef DEBUG
+		printf("t->fd = %d\n", t->fd);
+#endif
+		return -1;
+	}
+#ifdef DEBUG
+	puts("    tar_append_file(): back from th_write()");
+#endif
+
+	/* if it's a regular file, write the contents as well */
+	if (TH_ISREG(t) && tar_append_regfile(t, realname) != 0)
+		return -1;
+
+	return 0;
+}
+
+
+/* write EOF indicator */
+int
+tar_append_eof(TAR *t)
+{
+	int i, j;
+	char block[T_BLOCKSIZE];
+
+	memset(&block, 0, T_BLOCKSIZE);
+	for (j = 0; j < 2; j++)
+	{
+		i = tar_block_write(t, &block);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* add file contents to a tarchive */
+int
+tar_append_regfile(TAR *t, char *realname)
+{
+	char block[T_BLOCKSIZE];
+	int filefd;
+	int i, j;
+	size_t size;
+
+	filefd = open(realname, O_RDONLY);
+	if (filefd == -1)
+	{
+#ifdef DEBUG
+		perror("open()");
+#endif
+		return -1;
+	}
+
+	size = th_get_size(t);
+	for (i = size; i > T_BLOCKSIZE; i -= T_BLOCKSIZE)
+	{
+		j = read(filefd, &block, T_BLOCKSIZE);
+		if (j != T_BLOCKSIZE)
+		{
+			if (j != -1)
+				errno = EINVAL;
+			return -1;
+		}
+		if (tar_block_write(t, &block) == -1)
+			return -1;
+	}
+
+	if (i > 0)
+	{
+		j = read(filefd, &block, i);
+		if (j == -1)
+			return -1;
+		memset(&(block[i]), 0, T_BLOCKSIZE - i);
+		if (tar_block_write(t, &block) == -1)
+			return -1;
+	}
+
+	close(filefd);
+
+	return 0;
+}
+
+
diff --git a/libtar/basename.c b/libtar/basename.c
new file mode 100644
index 0000000..2ac1e13
--- /dev/null
+++ b/libtar/basename.c
@@ -0,0 +1,74 @@
+/*	$OpenBSD: basename.c,v 1.4 1999/05/30 17:10:30 espie Exp $	*/
+
+/*
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$OpenBSD: basename.c,v 1.4 1999/05/30 17:10:30 espie Exp $";
+#endif /* not lint */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+
+char *
+openbsd_basename(path)
+	const char *path;
+{
+	static char bname[MAXPATHLEN];
+	register const char *endp, *startp;
+
+	/* Empty or NULL string gets treated as "." */
+	if (path == NULL || *path == '\0') {
+		(void)strcpy(bname, ".");
+		return(bname);
+	}
+
+	/* Strip trailing slashes */
+	endp = path + strlen(path) - 1;
+	while (endp > path && *endp == '/')
+		endp--;
+
+	/* All slashes becomes "/" */
+	if (endp == path && *endp == '/') {
+		(void)strcpy(bname, "/");
+		return(bname);
+	}
+
+	/* Find the start of the base */
+	startp = endp;
+	while (startp > path && *(startp - 1) != '/')
+		startp--;
+
+	if (endp - startp + 1 > sizeof(bname)) {
+		errno = ENAMETOOLONG;
+		return(NULL);
+	}
+	(void)strncpy(bname, startp, endp - startp + 1);
+	bname[endp - startp + 1] = '\0';
+	return(bname);
+}
diff --git a/libtar/block.c b/libtar/block.c
new file mode 100644
index 0000000..89e5e3d
--- /dev/null
+++ b/libtar/block.c
@@ -0,0 +1,383 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  block.c - libtar code to handle tar archive header blocks
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+#define BIT_ISSET(bitmask, bit) ((bitmask) & (bit))
+
+
+/* read a header block */
+int
+th_read_internal(TAR *t)
+{
+	int i;
+	int num_zero_blocks = 0;
+
+#ifdef DEBUG
+	printf("==> th_read_internal(TAR=\"%s\")\n", t->pathname);
+#endif
+
+	while ((i = tar_block_read(t, &(t->th_buf))) == T_BLOCKSIZE)
+	{
+		/* two all-zero blocks mark EOF */
+		if (t->th_buf.name[0] == '\0')
+		{
+			num_zero_blocks++;
+			if (!BIT_ISSET(t->options, TAR_IGNORE_EOT)
+			    && num_zero_blocks >= 2)
+				return 0;	/* EOF */
+			else
+				continue;
+		}
+
+		/* verify magic and version */
+		if (BIT_ISSET(t->options, TAR_CHECK_MAGIC)
+		    && strncmp(t->th_buf.magic, TMAGIC, TMAGLEN - 1) != 0)
+		{
+#ifdef DEBUG
+			puts("!!! unknown magic value in tar header");
+#endif
+			return -2;
+		}
+
+		if (BIT_ISSET(t->options, TAR_CHECK_VERSION)
+		    && strncmp(t->th_buf.version, TVERSION, TVERSLEN) != 0)
+		{
+#ifdef DEBUG
+			puts("!!! unknown version value in tar header");
+#endif
+			return -2;
+		}
+
+		/* check chksum */
+		if (!BIT_ISSET(t->options, TAR_IGNORE_CRC)
+		    && !th_crc_ok(t))
+		{
+#ifdef DEBUG
+			puts("!!! tar header checksum error");
+#endif
+			return -2;
+		}
+
+		break;
+	}
+
+#ifdef DEBUG
+	printf("<== th_read_internal(): returning %d\n", i);
+#endif
+	return i;
+}
+
+
+/* wrapper function for th_read_internal() to handle GNU extensions */
+int
+th_read(TAR *t)
+{
+	int i, j;
+	size_t sz;
+	char *ptr;
+
+#ifdef DEBUG
+	printf("==> th_read(t=0x%lx)\n", t);
+#endif
+
+	if (t->th_buf.gnu_longname != NULL)
+		free(t->th_buf.gnu_longname);
+	if (t->th_buf.gnu_longlink != NULL)
+		free(t->th_buf.gnu_longlink);
+	memset(&(t->th_buf), 0, sizeof(struct tar_header));
+
+	i = th_read_internal(t);
+	if (i == 0)
+		return 1;
+	else if (i != T_BLOCKSIZE)
+	{
+		if (i != -1)
+			errno = EINVAL;
+		return -1;
+	}
+
+	/* check for GNU long link extention */
+	if (TH_ISLONGLINK(t))
+	{
+		sz = th_get_size(t);
+		j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+#ifdef DEBUG
+		printf("    th_read(): GNU long linkname detected "
+		       "(%ld bytes, %d blocks)\n", sz, j);
+#endif
+		t->th_buf.gnu_longlink = (char *)malloc(j * T_BLOCKSIZE);
+		if (t->th_buf.gnu_longlink == NULL)
+			return -1;
+
+		for (ptr = t->th_buf.gnu_longlink; j > 0;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+#ifdef DEBUG
+			printf("    th_read(): reading long linkname "
+			       "(%d blocks left, ptr == %ld)\n", j, ptr);
+#endif
+			i = tar_block_read(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+#ifdef DEBUG
+			printf("    th_read(): read block == \"%s\"\n", ptr);
+#endif
+		}
+#ifdef DEBUG
+		printf("    th_read(): t->th_buf.gnu_longlink == \"%s\"\n",
+		       t->th_buf.gnu_longlink);
+#endif
+
+		i = th_read_internal(t);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+
+	/* check for GNU long name extention */
+	if (TH_ISLONGNAME(t))
+	{
+		sz = th_get_size(t);
+		j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0);
+#ifdef DEBUG
+		printf("    th_read(): GNU long filename detected "
+		       "(%ld bytes, %d blocks)\n", sz, j);
+#endif
+		t->th_buf.gnu_longname = (char *)malloc(j * T_BLOCKSIZE);
+		if (t->th_buf.gnu_longname == NULL)
+			return -1;
+
+		for (ptr = t->th_buf.gnu_longname; j > 0;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+#ifdef DEBUG
+			printf("    th_read(): reading long filename "
+			       "(%d blocks left, ptr == %ld)\n", j, ptr);
+#endif
+			i = tar_block_read(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+#ifdef DEBUG
+			printf("    th_read(): read block == \"%s\"\n", ptr);
+#endif
+		}
+#ifdef DEBUG
+		printf("    th_read(): t->th_buf.gnu_longname == \"%s\"\n",
+		       t->th_buf.gnu_longname);
+#endif
+
+		i = th_read_internal(t);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+
+#if 0
+	/*
+	** work-around for old archive files with broken typeflag fields
+	** NOTE: I fixed this in the TH_IS*() macros instead
+	*/
+
+	/*
+	** (directories are signified with a trailing '/')
+	*/
+	if (t->th_buf.typeflag == AREGTYPE
+	    && t->th_buf.name[strlen(t->th_buf.name) - 1] == '/')
+		t->th_buf.typeflag = DIRTYPE;
+
+	/*
+	** fallback to using mode bits
+	*/
+	if (t->th_buf.typeflag == AREGTYPE)
+	{
+		mode = (mode_t)oct_to_int(t->th_buf.mode);
+
+		if (S_ISREG(mode))
+			t->th_buf.typeflag = REGTYPE;
+		else if (S_ISDIR(mode))
+			t->th_buf.typeflag = DIRTYPE;
+		else if (S_ISFIFO(mode))
+			t->th_buf.typeflag = FIFOTYPE;
+		else if (S_ISCHR(mode))
+			t->th_buf.typeflag = CHRTYPE;
+		else if (S_ISBLK(mode))
+			t->th_buf.typeflag = BLKTYPE;
+		else if (S_ISLNK(mode))
+			t->th_buf.typeflag = SYMTYPE;
+	}
+#endif
+
+	return 0;
+}
+
+
+/* write a header block */
+int
+th_write(TAR *t)
+{
+	int i, j;
+	char type2;
+	size_t sz, sz2;
+	char *ptr;
+	char buf[T_BLOCKSIZE];
+
+#ifdef DEBUG
+	printf("==> th_write(TAR=\"%s\")\n", t->pathname);
+	th_print(t);
+#endif
+
+	if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
+	{
+#ifdef DEBUG
+		printf("th_write(): using gnu_longlink (\"%s\")\n",
+		       t->th_buf.gnu_longlink);
+#endif
+		/* save old size and type */
+		type2 = t->th_buf.typeflag;
+		sz2 = th_get_size(t);
+
+		/* write out initial header block with fake size and type */
+		t->th_buf.typeflag = GNU_LONGLINK_TYPE;
+		sz = strlen(t->th_buf.gnu_longlink);
+		th_set_size(t, sz);
+		th_finish(t);
+		i = tar_block_write(t, &(t->th_buf));
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* write out extra blocks containing long name */
+		for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
+		     ptr = t->th_buf.gnu_longlink; j > 1;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+			i = tar_block_write(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+		}
+		memset(buf, 0, T_BLOCKSIZE);
+		strncpy(buf, ptr, T_BLOCKSIZE);
+		i = tar_block_write(t, &buf);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* reset type and size to original values */
+		t->th_buf.typeflag = type2;
+		th_set_size(t, sz2);
+	}
+
+	if ((t->options & TAR_GNU) && t->th_buf.gnu_longname != NULL)
+	{
+#ifdef DEBUG
+		printf("th_write(): using gnu_longname (\"%s\")\n",
+		       t->th_buf.gnu_longname);
+#endif
+		/* save old size and type */
+		type2 = t->th_buf.typeflag;
+		sz2 = th_get_size(t);
+
+		/* write out initial header block with fake size and type */
+		t->th_buf.typeflag = GNU_LONGNAME_TYPE;
+		sz = strlen(t->th_buf.gnu_longname);
+		th_set_size(t, sz);
+		th_finish(t);
+		i = tar_block_write(t, &(t->th_buf));
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* write out extra blocks containing long name */
+		for (j = (sz / T_BLOCKSIZE) + (sz % T_BLOCKSIZE ? 1 : 0),
+		     ptr = t->th_buf.gnu_longname; j > 1;
+		     j--, ptr += T_BLOCKSIZE)
+		{
+			i = tar_block_write(t, ptr);
+			if (i != T_BLOCKSIZE)
+			{
+				if (i != -1)
+					errno = EINVAL;
+				return -1;
+			}
+		}
+		memset(buf, 0, T_BLOCKSIZE);
+		strncpy(buf, ptr, T_BLOCKSIZE);
+		i = tar_block_write(t, &buf);
+		if (i != T_BLOCKSIZE)
+		{
+			if (i != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* reset type and size to original values */
+		t->th_buf.typeflag = type2;
+		th_set_size(t, sz2);
+	}
+
+	th_finish(t);
+
+#ifdef DEBUG
+	/* print tar header */
+	th_print(t);
+#endif
+
+	i = tar_block_write(t, &(t->th_buf));
+	if (i != T_BLOCKSIZE)
+	{
+		if (i != -1)
+			errno = EINVAL;
+		return -1;
+	}
+
+#ifdef DEBUG
+	puts("th_write(): returning 0");
+#endif
+	return 0;
+}
+
+
diff --git a/libtar/compat.h b/libtar/compat.h
new file mode 100644
index 0000000..70ac2f4
--- /dev/null
+++ b/libtar/compat.h
@@ -0,0 +1,256 @@
+/* prototypes for borrowed "compatibility" code */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifdef HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+
+
+#if defined(NEED_BASENAME) && !defined(HAVE_BASENAME)
+
+# ifdef basename
+#  undef basename		/* fix glibc brokenness */
+# endif
+
+char *openbsd_basename(const char *);
+# define basename openbsd_basename
+
+#endif /* NEED_BASENAME && ! HAVE_BASENAME */
+
+
+#if defined(NEED_DIRNAME) && !defined(HAVE_DIRNAME)
+
+char *openbsd_dirname(const char *);
+# define dirname openbsd_dirname
+
+#endif /* NEED_DIRNAME && ! HAVE_DIRNAME */
+
+
+#ifdef NEED_FNMATCH
+# ifndef HAVE_FNMATCH
+
+#  define FNM_NOMATCH	1	/* Match failed. */
+
+#  define FNM_NOESCAPE	0x01	/* Disable backslash escaping. */
+#  define FNM_PATHNAME	0x02	/* Slash must be matched by slash. */
+#  define FNM_PERIOD	0x04	/* Period must be matched by period. */
+
+#  define FNM_LEADING_DIR 0x08	/* Ignore /<tail> after Imatch. */
+#  define FNM_CASEFOLD	0x10	/* Case insensitive search. */
+#  define FNM_IGNORECASE FNM_CASEFOLD
+#  define FNM_FILE_NAME FNM_PATHNAME
+
+int openbsd_fnmatch(const char *, const char *, int);
+#  define fnmatch openbsd_fnmatch
+
+# else /* HAVE_FNMATCH */
+
+#  ifdef HAVE_FNMATCH_H
+#   include <fnmatch.h>
+#  endif
+
+# endif /* ! HAVE_FNMATCH */
+#endif /* NEED_FNMATCH */
+
+
+#ifdef NEED_GETHOSTBYNAME_R
+
+# include <netdb.h>
+
+# if GETHOSTBYNAME_R_NUM_ARGS != 6
+
+int compat_gethostbyname_r(const char *, struct hostent *,
+			   char *, size_t, struct hostent **, int *);
+
+#  define gethostbyname_r compat_gethostbyname_r
+
+# endif /* GETHOSTBYNAME_R_NUM_ARGS != 6 */
+
+#endif /* NEED_GETHOSTBYNAME_R */
+
+
+#if defined(NEED_GETHOSTNAME) && !defined(HAVE_GETHOSTNAME)
+
+int gethostname(char *, size_t);
+
+#endif /* NEED_GETHOSTNAME && ! HAVE_GETHOSTNAME */
+
+
+#ifdef NEED_GETSERVBYNAME_R
+
+# include <netdb.h>
+
+# if GETSERVBYNAME_R_NUM_ARGS != 6
+
+int compat_getservbyname_r(const char *, const char *, struct servent *,
+			   char *, size_t, struct servent **);
+
+#  define getservbyname_r compat_getservbyname_r
+
+# endif /* GETSERVBYNAME_R_NUM_ARGS != 6 */
+
+#endif /* NEED_GETSERVBYNAME_R */
+
+
+
+#ifdef NEED_GLOB
+# ifndef HAVE_GLOB
+
+typedef struct {
+	int gl_pathc;		/* Count of total paths so far. */
+	int gl_matchc;		/* Count of paths matching pattern. */
+	int gl_offs;		/* Reserved at beginning of gl_pathv. */
+	int gl_flags;		/* Copy of flags parameter to glob. */
+	char **gl_pathv;	/* List of paths matching pattern. */
+				/* Copy of errfunc parameter to glob. */
+	int (*gl_errfunc)(const char *, int);
+
+	/*
+	 * Alternate filesystem access methods for glob; replacement
+	 * versions of closedir(3), readdir(3), opendir(3), stat(2)
+	 * and lstat(2).
+	 */
+	void (*gl_closedir)(void *);
+	struct dirent *(*gl_readdir)(void *);
+	void *(*gl_opendir)(const char *);
+	int (*gl_lstat)(const char *, struct stat *);
+	int (*gl_stat)(const char *, struct stat *);
+} glob_t;
+
+/* Flags */
+#  define GLOB_APPEND	0x0001	/* Append to output from previous call. */
+#  define GLOB_DOOFFS	0x0002	/* Use gl_offs. */
+#  define GLOB_ERR	0x0004	/* Return on error. */
+#  define GLOB_MARK	0x0008	/* Append / to matching directories. */
+#  define GLOB_NOCHECK	0x0010	/* Return pattern itself if nothing matches. */
+#  define GLOB_NOSORT	0x0020	/* Don't sort. */
+
+#  define GLOB_ALTDIRFUNC 0x0040 /* Use alternately specified directory funcs. */
+#  define GLOB_BRACE	0x0080	/* Expand braces ala csh. */
+#  define GLOB_MAGCHAR	0x0100	/* Pattern had globbing characters. */
+#  define GLOB_NOMAGIC	0x0200	/* GLOB_NOCHECK without magic chars (csh). */
+#  define GLOB_QUOTE	0x0400	/* Quote special chars with \. */
+#  define GLOB_TILDE	0x0800	/* Expand tilde names from the passwd file. */
+#  define GLOB_NOESCAPE	0x1000	/* Disable backslash escaping. */
+
+/* Error values returned by glob(3) */
+#  define GLOB_NOSPACE	(-1)	/* Malloc call failed. */
+#  define GLOB_ABORTED	(-2)	/* Unignored error. */
+#  define GLOB_NOMATCH	(-3)	/* No match and GLOB_NOCHECK not set. */
+#  define GLOB_NOSYS	(-4)	/* Function not supported. */
+#  define GLOB_ABEND	GLOB_ABORTED
+
+int openbsd_glob(const char *, int, int (*)(const char *, int), glob_t *);
+void openbsd_globfree(glob_t *);
+#  define glob openbsd_glob
+#  define globfree openbsd_globfree
+
+# else /* HAVE_GLOB */
+
+#  ifdef HAVE_GLOB_H
+#   include <glob.h>
+#  endif
+
+# endif /* ! HAVE_GLOB */
+#endif /* NEED_GLOB */
+
+
+#if defined(NEED_INET_ATON) && !defined(HAVE_INET_ATON)
+
+int inet_aton(const char *, struct in_addr *);
+
+#endif /* NEED_INET_ATON && ! HAVE_INET_ATON */
+
+
+#ifdef NEED_MAKEDEV
+
+# ifdef MAJOR_IN_MKDEV
+#  include <sys/mkdev.h>
+# else
+#  ifdef MAJOR_IN_SYSMACROS
+#   include <sys/sysmacros.h>
+#  endif
+# endif
+
+/*
+** On most systems makedev() has two args.
+** Some weird systems, like QNX6, have makedev() functions that expect
+** an extra first argument for "node", which can be 0 for a local
+** machine.
+*/
+
+# ifdef MAKEDEV_THREE_ARGS
+#  define compat_makedev(maj, min)	makedev(0, maj, min)
+# else
+#  define compat_makedev		makedev
+# endif
+
+#endif /* NEED_MAKEDEV */
+
+
+#if defined(NEED_SNPRINTF) && !defined(HAVE_SNPRINTF)
+
+int mutt_snprintf(char *, size_t, const char *, ...);
+int mutt_vsnprintf(char *, size_t, const char *, va_list);
+#define snprintf mutt_snprintf
+#define vsnprintf mutt_vsnprintf
+
+#endif /* NEED_SNPRINTF && ! HAVE_SNPRINTF */
+
+
+#if defined(NEED_STRLCAT) && !defined(HAVE_STRLCAT)
+
+size_t strlcat(char *, const char *, size_t);
+
+#endif /* NEED_STRLCAT && ! HAVE_STRLCAT */
+
+
+#if defined(NEED_STRLCPY) && !defined(HAVE_STRLCPY)
+
+size_t strlcpy(char *, const char *, size_t);
+
+#endif /* NEED_STRLCPY && ! HAVE_STRLCPY */
+
+
+#if defined(NEED_STRDUP) && !defined(HAVE_STRDUP)
+
+char *openbsd_strdup(const char *);
+# define strdup openbsd_strdup
+
+#endif /* NEED_STRDUP && ! HAVE_STRDUP */
+
+
+#if defined(NEED_STRMODE) && !defined(HAVE_STRMODE)
+
+void strmode(register mode_t, register char *);
+
+#endif /* NEED_STRMODE && ! HAVE_STRMODE */
+
+
+#if defined(NEED_STRRSTR) && !defined(HAVE_STRRSTR)
+
+char *strrstr(char *, char *);
+
+#endif /* NEED_STRRSTR && ! HAVE_STRRSTR */
+
+
+#ifdef NEED_STRSEP
+
+# ifdef HAVE_STRSEP
+#  define _LINUX_SOURCE_COMPAT		/* needed on AIX 4.3.3 */
+# else
+
+char *strsep(register char **, register const char *);
+
+# endif
+
+#endif /* NEED_STRSEP */
+
+
diff --git a/libtar/config.h b/libtar/config.h
new file mode 100644
index 0000000..c658020
--- /dev/null
+++ b/libtar/config.h
@@ -0,0 +1,188 @@
+/* config.h.  Generated by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if your system has a working basename */
+/* #undef HAVE_BASENAME */
+
+/* Define to 1 if you have the <ctype.h> header file. */
+/* #undef HAVE_CTYPE_H */
+
+/* Define to 1 if the system has the type `dev_t'. */
+#define HAVE_DEV_T 1
+
+/* Define if your system has a working dirname */
+/* #undef HAVE_DIRNAME */
+
+/* Define to 1 if your system has a working POSIX `fnmatch' function. */
+#define HAVE_FNMATCH 1
+
+/* Define to 1 if you have the <fnmatch.h> header file. */
+#define HAVE_FNMATCH_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#define HAVE_LIBGEN_H 1
+
+/* Define to 1 if you have the `z' library (-lz). */
+#define HAVE_LIBZ 1
+
+/* Define to 1 if the system has the type `major_t'. */
+/* #undef HAVE_MAJOR_T */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if the system has the type `minor_t'. */
+/* #undef HAVE_MINOR_T */
+
+/* Define to 1 if the system has the type `nlink_t'. */
+#define HAVE_NLINK_T 1
+
+/* Define if your system has a working snprintf */
+#define HAVE_SNPRINTF 1
+
+/* Define to 1 if the system has the type `socklen_t'. */
+#define HAVE_SOCKLEN_T 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the strdup function */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the strlcpy function */
+/* #undef HAVE_STRLCPY */
+
+/* Define if you have the strmode function */
+/* #undef HAVE_STRMODE */
+
+/* Define if you have the strsep function */
+#define HAVE_STRSEP 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if the system has the type `uint64_t'. */
+#define HAVE_UINT64_T 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+   */
+/* #undef MAJOR_IN_MKDEV */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+   <sysmacros.h>. */
+/* #undef MAJOR_IN_SYSMACROS */
+
+/* Define as 1 if makedev expects three arguments */
+/* #undef MAKEDEV_THREE_ARGS */
+
+/* Define if you want to use the basename function */
+#define NEED_BASENAME 1
+
+/* Define if you want to use the dirname function */
+#define NEED_DIRNAME 1
+
+/* Define if you want to use the fnmatch function */
+#define NEED_FNMATCH 1
+
+/* Define if you want to use the makedev function */
+#define NEED_MAKEDEV 1
+
+/* Define if you want to use the snprintf function */
+#define NEED_SNPRINTF 1
+
+/* Define if you want to use the strdup function */
+#define NEED_STRDUP 1
+
+/* Define if you want to use the strlcpy function */
+#define NEED_STRLCPY 1
+
+/* Define if you want to use the strmode function */
+#define NEED_STRMODE 1
+
+/* Define if you want to use the strsep function */
+#define NEED_STRSEP 1
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libtar"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libtar 1.2.11"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libtar"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.2.11"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
+#ifndef _ALL_SOURCE
+/* # undef _ALL_SOURCE */
+#endif
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `unsigned long' if not defined in system header files. */
+/* #undef dev_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define to `unsigned int' if not defined in system header files. */
+#define major_t unsigned int
+
+/* Define to `unsigned int' if not defined in system header files. */
+#define minor_t unsigned int
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `unsigned short' if not defined in system header files. */
+/* #undef nlink_t */
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `unsigned long' if not defined in system header files. */
+/* #undef socklen_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Define to `long long' if not defined in system header files. */
+/* #undef uint64_t */
diff --git a/libtar/decode.c b/libtar/decode.c
new file mode 100644
index 0000000..383306d
--- /dev/null
+++ b/libtar/decode.c
@@ -0,0 +1,122 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  decode.c - libtar code to decode tar header blocks
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+
+/* determine full path name */
+char *
+th_get_pathname(TAR *t)
+{
+	char filename[MAXPATHLEN];
+
+	if (t->th_buf.gnu_longname) {
+		printf("returning gnu longname\n");
+		return t->th_buf.gnu_longname;
+	}
+
+	if (t->th_buf.prefix[0] != '\0')
+	{
+		snprintf(filename, sizeof(filename), "%.155s/%.100s",
+			 t->th_buf.prefix, t->th_buf.name);
+		return strdup(filename);
+	}
+
+	snprintf(filename, sizeof(filename), "%.100s", t->th_buf.name);
+	return strdup(filename);
+}
+
+
+uid_t
+th_get_uid(TAR *t)
+{
+	int uid;
+	struct passwd *pw;
+
+	pw = getpwnam(t->th_buf.uname);
+	if (pw != NULL)
+		return pw->pw_uid;
+
+	/* if the password entry doesn't exist */
+	sscanf(t->th_buf.uid, "%o", &uid);
+	return uid;
+}
+
+
+gid_t
+th_get_gid(TAR *t)
+{
+	int gid;
+	struct group *gr;
+
+	gr = getgrnam(t->th_buf.gname);
+	if (gr != NULL)
+		return gr->gr_gid;
+
+	/* if the group entry doesn't exist */
+	sscanf(t->th_buf.gid, "%o", &gid);
+	return gid;
+}
+
+
+mode_t
+th_get_mode(TAR *t)
+{
+	mode_t mode;
+
+	mode = (mode_t)oct_to_int(t->th_buf.mode);
+	if (! (mode & S_IFMT))
+	{
+		switch (t->th_buf.typeflag)
+		{
+		case SYMTYPE:
+			mode |= S_IFLNK;
+			break;
+		case CHRTYPE:
+			mode |= S_IFCHR;
+			break;
+		case BLKTYPE:
+			mode |= S_IFBLK;
+			break;
+		case DIRTYPE:
+			mode |= S_IFDIR;
+			break;
+		case FIFOTYPE:
+			mode |= S_IFIFO;
+			break;
+		case AREGTYPE:
+			if (t->th_buf.name[strlen(t->th_buf.name) - 1] == '/')
+			{
+				mode |= S_IFDIR;
+				break;
+			}
+			/* FALLTHROUGH */
+		case LNKTYPE:
+		case REGTYPE:
+		default:
+			mode |= S_IFREG;
+		}
+	}
+
+	return mode;
+}
+
+
diff --git a/libtar/dirname.c b/libtar/dirname.c
new file mode 100644
index 0000000..986db4a
--- /dev/null
+++ b/libtar/dirname.c
@@ -0,0 +1,77 @@
+/*	$OpenBSD: dirname.c,v 1.4 1999/05/30 17:10:30 espie Exp $	*/
+
+/*
+ * Copyright (c) 1997 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$OpenBSD: dirname.c,v 1.4 1999/05/30 17:10:30 espie Exp $";
+#endif /* not lint */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+
+char *
+openbsd_dirname(path)
+	const char *path;
+{
+	static char bname[MAXPATHLEN];
+	register const char *endp;
+
+	/* Empty or NULL string gets treated as "." */
+	if (path == NULL || *path == '\0') {
+		(void)strcpy(bname, ".");
+		return(bname);
+	}
+
+	/* Strip trailing slashes */
+	endp = path + strlen(path) - 1;
+	while (endp > path && *endp == '/')
+		endp--;
+
+	/* Find the start of the dir */
+	while (endp > path && *endp != '/')
+		endp--;
+
+	/* Either the dir is "/" or there are no slashes */
+	if (endp == path) {
+		(void)strcpy(bname, *endp == '/' ? "/" : ".");
+		return(bname);
+	} else {
+		do {
+			endp--;
+		} while (endp > path && *endp == '/');
+	}
+
+	if (endp - path + 1 > sizeof(bname)) {
+		errno = ENAMETOOLONG;
+		return(NULL);
+	}
+	(void)strncpy(bname, path, endp - path + 1);
+	bname[endp - path + 1] = '\0';
+	return(bname);
+}
diff --git a/libtar/encode.c b/libtar/encode.c
new file mode 100644
index 0000000..662eff5
--- /dev/null
+++ b/libtar/encode.c
@@ -0,0 +1,213 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  encode.c - libtar code to encode tar header blocks
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+
+/* magic, version, and checksum */
+void
+th_finish(TAR *t)
+{
+	if (t->options & TAR_GNU)
+	{
+		/* we're aiming for this result, but must do it in
+		 * two calls to avoid FORTIFY segfaults on some Linux
+		 * systems:
+		 *      strncpy(t->th_buf.magic, "ustar  ", 8);
+		 */
+		strncpy(t->th_buf.magic, "ustar ", 6);
+		strncpy(t->th_buf.version, " ", 2);
+	}
+	else
+	{
+		strncpy(t->th_buf.version, TVERSION, TVERSLEN);
+		strncpy(t->th_buf.magic, TMAGIC, TMAGLEN);
+	}
+
+	int_to_oct(th_crc_calc(t), t->th_buf.chksum, 8);
+}
+
+
+/* map a file mode to a typeflag */
+void
+th_set_type(TAR *t, mode_t mode)
+{
+	if (S_ISLNK(mode))
+		t->th_buf.typeflag = SYMTYPE;
+	if (S_ISREG(mode))
+		t->th_buf.typeflag = REGTYPE;
+	if (S_ISDIR(mode))
+		t->th_buf.typeflag = DIRTYPE;
+	if (S_ISCHR(mode))
+		t->th_buf.typeflag = CHRTYPE;
+	if (S_ISBLK(mode))
+		t->th_buf.typeflag = BLKTYPE;
+	if (S_ISFIFO(mode) || S_ISSOCK(mode))
+		t->th_buf.typeflag = FIFOTYPE;
+}
+
+
+/* encode file path */
+void
+th_set_path(TAR *t, char *pathname)
+{
+	char suffix[2] = "";
+	char *tmp;
+
+#ifdef DEBUG
+	printf("in th_set_path(th, pathname=\"%s\")\n", pathname);
+#endif
+
+	if (t->th_buf.gnu_longname != NULL)
+		free(t->th_buf.gnu_longname);
+	t->th_buf.gnu_longname = NULL;
+
+	if (pathname[strlen(pathname) - 1] != '/' && TH_ISDIR(t))
+		strcpy(suffix, "/");
+
+	if (strlen(pathname) > T_NAMELEN-1 && (t->options & TAR_GNU))
+	{
+		/* GNU-style long name */
+		t->th_buf.gnu_longname = strdup(pathname);
+		strncpy(t->th_buf.name, t->th_buf.gnu_longname, T_NAMELEN);
+	}
+	else if (strlen(pathname) > T_NAMELEN)
+	{
+		/* POSIX-style prefix field */
+		tmp = strchr(&(pathname[strlen(pathname) - T_NAMELEN - 1]), '/');
+		if (tmp == NULL)
+		{
+			printf("!!! '/' not found in \"%s\"\n", pathname);
+			return;
+		}
+		snprintf(t->th_buf.name, 100, "%s%s", &(tmp[1]), suffix);
+		snprintf(t->th_buf.prefix,
+			 ((tmp - pathname + 1) <
+			  155 ? (tmp - pathname + 1) : 155), "%s", pathname);
+	}
+	else
+		/* classic tar format */
+		snprintf(t->th_buf.name, 100, "%s%s", pathname, suffix);
+
+#ifdef DEBUG
+	puts("returning from th_set_path()...");
+#endif
+}
+
+
+/* encode link path */
+void
+th_set_link(TAR *t, char *linkname)
+{
+#ifdef DEBUG
+	printf("==> th_set_link(th, linkname=\"%s\")\n", linkname);
+#endif
+
+	if (strlen(linkname) > T_NAMELEN-1 && (t->options & TAR_GNU))
+	{
+		/* GNU longlink format */
+		t->th_buf.gnu_longlink = strdup(linkname);
+		strcpy(t->th_buf.linkname, "././@LongLink");
+	}
+	else
+	{
+		/* classic tar format */
+		strlcpy(t->th_buf.linkname, linkname,
+			sizeof(t->th_buf.linkname));
+		if (t->th_buf.gnu_longlink != NULL)
+			free(t->th_buf.gnu_longlink);
+		t->th_buf.gnu_longlink = NULL;
+	}
+}
+
+
+/* encode device info */
+void
+th_set_device(TAR *t, dev_t device)
+{
+#ifdef DEBUG
+	printf("th_set_device(): major = %d, minor = %d\n",
+	       major(device), minor(device));
+#endif
+	int_to_oct(major(device), t->th_buf.devmajor, 8);
+	int_to_oct(minor(device), t->th_buf.devminor, 8);
+}
+
+
+/* encode user info */
+void
+th_set_user(TAR *t, uid_t uid)
+{
+	struct passwd *pw;
+
+	pw = getpwuid(uid);
+	if (pw != NULL)
+		strlcpy(t->th_buf.uname, pw->pw_name, sizeof(t->th_buf.uname));
+
+	int_to_oct(uid, t->th_buf.uid, 8);
+}
+
+
+/* encode group info */
+void
+th_set_group(TAR *t, gid_t gid)
+{
+	struct group *gr;
+
+	gr = getgrgid(gid);
+	if (gr != NULL)
+		strlcpy(t->th_buf.gname, gr->gr_name, sizeof(t->th_buf.gname));
+
+	int_to_oct(gid, t->th_buf.gid, 8);
+}
+
+
+/* encode file mode */
+void
+th_set_mode(TAR *t, mode_t fmode)
+{
+	if (S_ISSOCK(fmode))
+	{
+		fmode &= ~S_IFSOCK;
+		fmode |= S_IFIFO;
+	}
+	int_to_oct(fmode, (t)->th_buf.mode, 8);
+}
+
+
+void
+th_set_from_stat(TAR *t, struct stat *s)
+{
+	th_set_type(t, s->st_mode);
+	if (S_ISCHR(s->st_mode) || S_ISBLK(s->st_mode))
+		th_set_device(t, s->st_rdev);
+	th_set_user(t, s->st_uid);
+	th_set_group(t, s->st_gid);
+	th_set_mode(t, s->st_mode);
+	th_set_mtime(t, s->st_mtime);
+	if (S_ISREG(s->st_mode))
+		th_set_size(t, s->st_size);
+	else
+		th_set_size(t, 0);
+}
+
+
diff --git a/libtar/extract.c b/libtar/extract.c
new file mode 100644
index 0000000..5544287
--- /dev/null
+++ b/libtar/extract.c
@@ -0,0 +1,551 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  extract.c - libtar code to extract a file from a tar archive
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <utime.h>
+
+#define DEBUG
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#define DEBUG
+
+static int
+tar_set_file_perms(TAR *t, char *realname)
+{
+	mode_t mode;
+	uid_t uid;
+	gid_t gid;
+	struct utimbuf ut;
+	char *filename;
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+	uid = th_get_uid(t);
+	gid = th_get_gid(t);
+	ut.modtime = ut.actime = th_get_mtime(t);
+
+	/* change owner/group */
+	if (geteuid() == 0)
+#ifdef HAVE_LCHOWN
+		if (lchown(filename, uid, gid) == -1)
+		{
+# ifdef DEBUG
+			fprintf(stderr, "lchown(\"%s\", %d, %d): %s\n",
+				filename, uid, gid, strerror(errno));
+# endif
+#else /* ! HAVE_LCHOWN */
+		if (!TH_ISSYM(t) && chown(filename, uid, gid) == -1)
+		{
+# ifdef DEBUG
+			fprintf(stderr, "chown(\"%s\", %d, %d): %s\n",
+				filename, uid, gid, strerror(errno));
+# endif
+#endif /* HAVE_LCHOWN */
+			return -1;
+		}
+
+	/* change access/modification time */
+	if (!TH_ISSYM(t) && utime(filename, &ut) == -1)
+	{
+#ifdef DEBUG
+		perror("utime()");
+#endif
+		return -1;
+	}
+
+	/* change permissions */
+	if (!TH_ISSYM(t) && chmod(filename, mode) == -1)
+	{
+#ifdef DEBUG
+		perror("chmod()");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* switchboard */
+int
+tar_extract_file(TAR *t, char *realname)
+{
+	int i;
+	char *lnp;
+	int pathname_len;
+	int realname_len;
+	
+	if (t->options & TAR_NOOVERWRITE)
+	{
+		struct stat s;
+
+		if (lstat(realname, &s) == 0 || errno != ENOENT)
+		{
+			errno = EEXIST;
+			return -1;
+		}
+	}
+
+	if (TH_ISDIR(t))
+	{
+		i = tar_extract_dir(t, realname);
+		if (i == 1)
+			i = 0;
+	}
+	else if (TH_ISLNK(t)) {
+		printf("link\n");
+		i = tar_extract_hardlink(t, realname);
+	}
+	else if (TH_ISSYM(t)) {
+		printf("sym\n");
+		i = tar_extract_symlink(t, realname);
+	}
+	else if (TH_ISCHR(t)) {
+		printf("chr\n");
+		i = tar_extract_chardev(t, realname);
+	}
+	else if (TH_ISBLK(t)) {
+		printf("blk\n");
+		i = tar_extract_blockdev(t, realname);
+	}
+	else if (TH_ISFIFO(t)) {
+		printf("fifo\n");
+		i = tar_extract_fifo(t, realname);
+	}
+	else /* if (TH_ISREG(t)) */ {
+		printf("reg\n");
+		i = tar_extract_regfile(t, realname);
+	}
+
+	if (i != 0) {
+		printf("here i: %d\n", i);
+		return i;
+	}
+
+	i = tar_set_file_perms(t, realname);
+	if (i != 0) {
+		printf("i: %d\n", i);
+		return i;
+	}
+/*
+	pathname_len = strlen(th_get_pathname(t)) + 1;
+	realname_len = strlen(realname) + 1;
+	lnp = (char *)calloc(1, pathname_len + realname_len);
+	if (lnp == NULL)
+		return -1;
+	strcpy(&lnp[0], th_get_pathname(t));
+	strcpy(&lnp[pathname_len], realname);
+#ifdef DEBUG
+	printf("tar_extract_file(): calling libtar_hash_add(): key=\"%s\", "
+	       "value=\"%s\"\n", th_get_pathname(t), realname);
+#endif
+	if (libtar_hash_add(t->h, lnp) != 0)
+		return -1;
+	free(lnp);
+*/
+	return 0;
+}
+
+
+/* extract regular file */
+int
+tar_extract_regfile(TAR *t, char *realname)
+{
+	mode_t mode;
+	size_t size;
+	uid_t uid;
+	gid_t gid;
+	int fdout;
+	int i, k;
+	char buf[T_BLOCKSIZE];
+	char *filename;
+
+	fflush(NULL);
+#ifdef DEBUG
+	printf("==> tar_extract_regfile(t=0x%lx, realname=\"%s\")\n", t,
+	       realname);
+#endif
+
+	if (!TH_ISREG(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+	size = th_get_size(t);
+	uid = th_get_uid(t);
+	gid = th_get_gid(t);
+
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (mode %04o, uid %d, gid %d, %d bytes)\n",
+	       filename, mode, uid, gid, size);
+#endif
+	fdout = open(filename, O_WRONLY | O_CREAT | O_TRUNC
+#ifdef O_BINARY
+		     | O_BINARY
+#endif
+		    , 0666);
+	if (fdout == -1)
+	{
+#ifdef DEBUG
+		perror("open()");
+#endif
+		return -1;
+	}
+
+#if 0
+	/* change the owner.  (will only work if run as root) */
+	if (fchown(fdout, uid, gid) == -1 && errno != EPERM)
+	{
+#ifdef DEBUG
+		perror("fchown()");
+#endif
+		return -1;
+	}
+
+	/* make sure the mode isn't inheritted from a file we're overwriting */
+	if (fchmod(fdout, mode & 07777) == -1)
+	{
+#ifdef DEBUG
+		perror("fchmod()");
+#endif
+		return -1;
+	}
+#endif
+
+	/* extract the file */
+	for (i = size; i > 0; i -= T_BLOCKSIZE)
+	{
+		k = tar_block_read(t, buf);
+		if (k != T_BLOCKSIZE)
+		{
+			if (k != -1)
+				errno = EINVAL;
+			return -1;
+		}
+
+		/* write block to output file */
+		if (write(fdout, buf,
+			  ((i > T_BLOCKSIZE) ? T_BLOCKSIZE : i)) == -1)
+			return -1;
+	}
+
+	/* close output file */
+	if (close(fdout) == -1)
+		return -1;
+
+#ifdef DEBUG
+	printf("### done extracting %s\n", filename);
+#endif
+
+	return 0;
+}
+
+
+/* skip regfile */
+int
+tar_skip_regfile(TAR *t)
+{
+	int i, k;
+	size_t size;
+	char buf[T_BLOCKSIZE];
+
+	if (!TH_ISREG(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	size = th_get_size(t);
+	for (i = size; i > 0; i -= T_BLOCKSIZE)
+	{
+		k = tar_block_read(t, buf);
+		if (k != T_BLOCKSIZE)
+		{
+			if (k != -1)
+				errno = EINVAL;
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* hardlink */
+int
+tar_extract_hardlink(TAR * t, char *realname)
+{
+	char *filename;
+	char *linktgt = NULL;
+	char *lnp;
+	libtar_hashptr_t hp;
+
+	if (!TH_ISLNK(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+	libtar_hashptr_reset(&hp);
+	if (libtar_hash_getkey(t->h, &hp, th_get_linkname(t),
+			       (libtar_matchfunc_t)libtar_str_match) != 0)
+	{
+		lnp = (char *)libtar_hashptr_data(&hp);
+		linktgt = &lnp[strlen(lnp) + 1];
+	}
+	else
+		linktgt = th_get_linkname(t);
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (link to %s)\n", filename, linktgt);
+#endif
+	if (link(linktgt, filename) == -1)
+	{
+#ifdef DEBUG
+		perror("link()");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* symlink */
+int
+tar_extract_symlink(TAR *t, char *realname)
+{
+	char *filename;
+	
+	if (!TH_ISSYM(t))
+	{
+		printf("not a sym\n");
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	printf("file: %s\n", filename);
+	if (mkdirhier(dirname(filename)) == -1) {
+		printf("mkdirhier\n");
+		return -1;
+	}
+
+	if (unlink(filename) == -1 && errno != ENOENT) {
+		printf("unlink\n");
+		return -1;
+	}
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (symlink to %s)\n",
+	       filename, th_get_linkname(t));
+#endif
+	if (symlink(th_get_linkname(t), filename) == -1)
+	{
+#ifdef DEBUG
+		perror("symlink()");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* character device */
+int
+tar_extract_chardev(TAR *t, char *realname)
+{
+	mode_t mode;
+	unsigned long devmaj, devmin;
+	char *filename;
+
+	if (!TH_ISCHR(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+	devmaj = th_get_devmajor(t);
+	devmin = th_get_devminor(t);
+
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (character device %ld,%ld)\n",
+	       filename, devmaj, devmin);
+#endif
+	if (mknod(filename, mode | S_IFCHR,
+		  compat_makedev(devmaj, devmin)) == -1)
+	{
+#ifdef DEBUG
+		perror("mknod()");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* block device */
+int
+tar_extract_blockdev(TAR *t, char *realname)
+{
+	mode_t mode;
+	unsigned long devmaj, devmin;
+	char *filename;
+
+	if (!TH_ISBLK(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+	devmaj = th_get_devmajor(t);
+	devmin = th_get_devminor(t);
+
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (block device %ld,%ld)\n",
+	       filename, devmaj, devmin);
+#endif
+	if (mknod(filename, mode | S_IFBLK,
+		  compat_makedev(devmaj, devmin)) == -1)
+	{
+#ifdef DEBUG
+		perror("mknod()");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* directory */
+int
+tar_extract_dir(TAR *t, char *realname)
+{
+	mode_t mode;
+	char *filename;
+	printf("filename: %s\n", filename);
+	if (!TH_ISDIR(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (mode %04o, directory)\n", filename,
+	       mode);
+#endif
+	if (mkdir(filename, mode) == -1)
+	{
+		if (errno == EEXIST)
+		{
+			if (chmod(filename, mode) == -1)
+			{
+#ifdef DEBUG
+				perror("chmod()");
+#endif
+				return -1;
+			}
+			else
+			{
+#ifdef DEBUG
+				puts("  *** using existing directory");
+#endif
+				return 1;
+			}
+		}
+		else
+		{
+#ifdef DEBUG
+			perror("mkdir()");
+#endif
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* FIFO */
+int
+tar_extract_fifo(TAR *t, char *realname)
+{
+	mode_t mode;
+	char *filename;
+
+	if (!TH_ISFIFO(t))
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	filename = (realname ? realname : th_get_pathname(t));
+	mode = th_get_mode(t);
+
+	if (mkdirhier(dirname(filename)) == -1)
+		return -1;
+
+#ifdef DEBUG
+	printf("  ==> extracting: %s (fifo)\n", filename);
+#endif
+	if (mkfifo(filename, mode) == -1)
+	{
+#ifdef DEBUG
+		perror("mkfifo()");
+#endif
+		return -1;
+	}
+
+	return 0;
+}
+
+
diff --git a/libtar/fnmatch.c b/libtar/fnmatch.c
new file mode 100644
index 0000000..fe75f0e
--- /dev/null
+++ b/libtar/fnmatch.c
@@ -0,0 +1,237 @@
+/*	$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $	*/
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)fnmatch.c	8.2 (Berkeley) 4/16/94";
+#else
+static char rcsid[] = "$OpenBSD: fnmatch.c,v 1.6 1998/03/19 00:29:59 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+#ifdef HAVE_CTYPE_H
+# include <ctype.h>
+#endif
+
+#include <compat.h>
+
+
+#define	EOS	'\0'
+
+#define	RANGE_MATCH	1
+#define	RANGE_NOMATCH	0
+#define	RANGE_ERROR	(-1)
+
+#ifdef NO_IBM_COMPILER_HORKAGE
+static int rangematch (const char *, char, int, char **);
+#else
+static int rangematch ();
+#endif
+
+int
+fnmatch(pattern, string, flags)
+	const char *pattern, *string;
+	int flags;
+{
+	const char *stringstart;
+	char *newp;
+	char c, test;
+
+	for (stringstart = string;;)
+		switch (c = *pattern++) {
+		case EOS:
+			if ((flags & FNM_LEADING_DIR) && *string == '/')
+				return (0);
+			return (*string == EOS ? 0 : FNM_NOMATCH);
+		case '?':
+			if (*string == EOS)
+				return (FNM_NOMATCH);
+			if (*string == '/' && (flags & FNM_PATHNAME))
+				return (FNM_NOMATCH);
+			if (*string == '.' && (flags & FNM_PERIOD) &&
+			    (string == stringstart ||
+			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+				return (FNM_NOMATCH);
+			++string;
+			break;
+		case '*':
+			c = *pattern;
+			/* Collapse multiple stars. */
+			while (c == '*')
+				c = *++pattern;
+
+			if (*string == '.' && (flags & FNM_PERIOD) &&
+			    (string == stringstart ||
+			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+				return (FNM_NOMATCH);
+
+			/* Optimize for pattern with * at end or before /. */
+			if (c == EOS) {
+				if (flags & FNM_PATHNAME)
+					return ((flags & FNM_LEADING_DIR) ||
+					    strchr(string, '/') == NULL ?
+					    0 : FNM_NOMATCH);
+				else
+					return (0);
+			} else if (c == '/' && (flags & FNM_PATHNAME)) {
+				if ((string = strchr(string, '/')) == NULL)
+					return (FNM_NOMATCH);
+				break;
+			}
+
+			/* General case, use recursion. */
+			while ((test = *string) != EOS) {
+				if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
+					return (0);
+				if (test == '/' && (flags & FNM_PATHNAME))
+					break;
+				++string;
+			}
+			return (FNM_NOMATCH);
+		case '[':
+			if (*string == EOS)
+				return (FNM_NOMATCH);
+			if (*string == '/' && (flags & FNM_PATHNAME))
+				return (FNM_NOMATCH);
+			if (*string == '.' && (flags & FNM_PERIOD) &&
+			    (string == stringstart ||
+			    ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+				return (FNM_NOMATCH);
+
+			switch (rangematch(pattern, *string, flags, &newp)) {
+			case RANGE_ERROR:
+				/* not a good range, treat as normal text */
+				goto normal;
+			case RANGE_MATCH:
+				pattern = newp;
+				break;
+			case RANGE_NOMATCH:
+				return (FNM_NOMATCH);
+			}
+			++string;
+			break;
+		case '\\':
+			if (!(flags & FNM_NOESCAPE)) {
+				if ((c = *pattern++) == EOS) {
+					c = '\\';
+					--pattern;
+				}
+			}
+			/* FALLTHROUGH */
+		default:
+		normal:
+			if (c != *string && !((flags & FNM_CASEFOLD) &&
+				 (tolower((unsigned char)c) ==
+				 tolower((unsigned char)*string))))
+				return (FNM_NOMATCH);
+			++string;
+			break;
+		}
+	/* NOTREACHED */
+}
+
+static int
+rangematch(pattern, test, flags, newp)
+	const char *pattern;
+	char test;
+	int flags;
+	char **newp;
+{
+	int negate, ok;
+	char c, c2;
+
+	/*
+	 * A bracket expression starting with an unquoted circumflex
+	 * character produces unspecified results (IEEE 1003.2-1992,
+	 * 3.13.2).  This implementation treats it like '!', for
+	 * consistency with the regular expression syntax.
+	 * J.T. Conklin (conklin@ngai.kaleida.com)
+	 */
+	if ((negate = (*pattern == '!' || *pattern == '^')))
+		++pattern;
+
+	if (flags & FNM_CASEFOLD)
+		test = tolower((unsigned char)test);
+
+	/*
+	 * A right bracket shall lose its special meaning and represent
+	 * itself in a bracket expression if it occurs first in the list.
+	 * -- POSIX.2 2.8.3.2
+	 */
+	ok = 0;
+	c = *pattern++;
+	do {
+		if (c == '\\' && !(flags & FNM_NOESCAPE))
+			c = *pattern++;
+		if (c == EOS)
+			return (RANGE_ERROR);
+		if (c == '/' && (flags & FNM_PATHNAME))
+			return (RANGE_NOMATCH);
+		if ((flags & FNM_CASEFOLD))
+			c = tolower((unsigned char)c);
+		if (*pattern == '-'
+		    && (c2 = *(pattern+1)) != EOS && c2 != ']') {
+			pattern += 2;
+			if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+				c2 = *pattern++;
+			if (c2 == EOS)
+				return (RANGE_ERROR);
+			if (flags & FNM_CASEFOLD)
+				c2 = tolower((unsigned char)c2);
+			if (c <= test && test <= c2)
+				ok = 1;
+		} else if (c == test)
+			ok = 1;
+	} while ((c = *pattern++) != ']');
+
+	*newp = (char *)pattern;
+	return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
+}
diff --git a/libtar/gethostbyname_r.c b/libtar/gethostbyname_r.c
new file mode 100644
index 0000000..5264b84
--- /dev/null
+++ b/libtar/gethostbyname_r.c
@@ -0,0 +1,41 @@
+/*
+**  Copyright 2002 University of Illinois Board of Trustees
+**  Copyright 2002 Mark D. Roth
+**  All rights reserved.
+**
+**  gethostbyname_r.c - gethostbyname_r() function for compatibility library
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+
+int
+compat_gethostbyname_r(const char *name, struct hostent *hp,
+		       char *buf, size_t buflen,
+		       struct hostent **hpp, int *herr)
+{
+#if GETHOSTBYNAME_R_NUM_ARGS == 5
+	*hpp = gethostbyname_r(name, hp, buf, buflen, herr);
+
+	if (*hpp == NULL)
+		return -1;
+	return 0;
+#elif GETHOSTBYNAME_R_NUM_ARGS == 3
+	struct hostent_data hdata;
+
+	if (gethostbyname_r(name, hp, &hdata) == -1)
+		return -1;
+	*hpp = hp;
+	return 0;
+#endif /* GETHOSTBYNAME_R_NUM_ARGS == 5 */
+}
+
+
diff --git a/libtar/gethostname.c b/libtar/gethostname.c
new file mode 100644
index 0000000..1abaae1
--- /dev/null
+++ b/libtar/gethostname.c
@@ -0,0 +1,36 @@
+/* gethostname.c: minimal substitute for missing gethostname() function
+ * created 2000-Mar-02 jmk
+ * requires SVR4 uname() and -lc
+ *
+ * by Jim Knoble <jmknoble@pobox.com>
+ * Copyright ? 2000 Jim Knoble
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This software is provided "as is", without warranty of any kind,
+ * express or implied, including but not limited to the warranties of
+ * merchantability, fitness for a particular purpose and
+ * noninfringement. In no event shall the author(s) be liable for any
+ * claim, damages or other liability, whether in an action of contract,
+ * tort or otherwise, arising from, out of or in connection with the
+ * software or the use or other dealings in the software.
+ */
+
+#include <string.h>
+#include <sys/utsname.h>
+
+int gethostname(char *name, size_t len)
+{
+   struct utsname u;
+   int status = uname(&u);
+   if (-1 != status) {
+      strncpy(name, u.nodename, len);
+      name[len - 1] = '\0';
+   }
+   return(status);
+}
+
diff --git a/libtar/getservbyname_r.c b/libtar/getservbyname_r.c
new file mode 100644
index 0000000..e386bc9
--- /dev/null
+++ b/libtar/getservbyname_r.c
@@ -0,0 +1,41 @@
+/*
+**  Copyright 2002 University of Illinois Board of Trustees
+**  Copyright 2002 Mark D. Roth
+**  All rights reserved.
+**
+**  getservbyname_r.c - getservbyname_r() function for compatibility library
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+
+int
+compat_getservbyname_r(const char *name, const char *proto,
+		       struct servent *sp, char *buf, size_t buflen,
+		       struct servent **spp)
+{
+#if GETSERVBYNAME_R_NUM_ARGS == 5
+	*spp = getservbyname_r(name, proto, sp, buf, buflen);
+
+	if (*spp == NULL)
+		return -1;
+	return 0;
+#elif GETSERVBYNAME_R_NUM_ARGS == 4
+	struct servent_data sdata;
+
+	if (getservbyname_r(name, proto, sp, &sdata) == -1)
+		return -1;
+	*spp = sp;
+	return 0;
+#endif /* GETSERVBYNAME_R_NUM_ARGS == 5 */
+}
+
+
diff --git a/libtar/glob.c b/libtar/glob.c
new file mode 100644
index 0000000..9ee235a
--- /dev/null
+++ b/libtar/glob.c
@@ -0,0 +1,873 @@
+/*
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)glob.c	8.3 (Berkeley) 10/13/93";
+#else
+static char rcsid[] = "$OpenBSD: glob.c,v 1.8 1998/08/14 21:39:30 deraadt Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * glob(3) -- a superset of the one defined in POSIX 1003.2.
+ *
+ * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
+ *
+ * Optional extra services, controlled by flags not defined by POSIX:
+ *
+ * GLOB_QUOTE:
+ *	Escaping convention: \ inhibits any special meaning the following
+ *	character might have (except \ at end of string is retained).
+ * GLOB_MAGCHAR:
+ *	Set in gl_flags if pattern contained a globbing character.
+ * GLOB_NOMAGIC:
+ *	Same as GLOB_NOCHECK, but it will only append pattern if it did
+ *	not contain any magic characters.  [Used in csh style globbing]
+ * GLOB_ALTDIRFUNC:
+ *	Use alternately specified directory access functions.
+ * GLOB_TILDE:
+ *	expand ~user/foo to the /home/dir/of/user/foo
+ * GLOB_BRACE:
+ *	expand {1,2}{a,b} to 1a 1b 2a 2b
+ * gl_matchc:
+ *	Number of matches in the current invocation of glob.
+ */
+
+#include <config.h>
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <string.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <compat.h>
+
+
+#define	DOLLAR		'$'
+#define	DOT		'.'
+#define	EOS		'\0'
+#define	LBRACKET	'['
+#define	NOT		'!'
+#define	QUESTION	'?'
+#define	QUOTE		'\\'
+#define	RANGE		'-'
+#define	RBRACKET	']'
+#define	SEP		'/'
+#define	STAR		'*'
+#define	TILDE		'~'
+#define	UNDERSCORE	'_'
+#define	LBRACE		'{'
+#define	RBRACE		'}'
+#define	SLASH		'/'
+#define	COMMA		','
+
+#ifndef DEBUG
+
+#define	M_QUOTE		0x8000
+#define	M_PROTECT	0x4000
+#define	M_MASK		0xffff
+#define	M_ASCII		0x00ff
+
+typedef u_short Char;
+
+#else
+
+#define	M_QUOTE		0x80
+#define	M_PROTECT	0x40
+#define	M_MASK		0xff
+#define	M_ASCII		0x7f
+
+typedef char Char;
+
+#endif
+
+
+#define	CHAR(c)		((Char)((c)&M_ASCII))
+#define	META(c)		((Char)((c)|M_QUOTE))
+#define	M_ALL		META('*')
+#define	M_END		META(']')
+#define	M_NOT		META('!')
+#define	M_ONE		META('?')
+#define	M_RNG		META('-')
+#define	M_SET		META('[')
+#define	ismeta(c)	(((c)&M_QUOTE) != 0)
+
+
+static int	 compare (const void *, const void *);
+static void	 g_Ctoc (const Char *, char *);
+static int	 g_lstat (Char *, struct stat *, glob_t *);
+static DIR	*g_opendir (Char *, glob_t *);
+static Char	*g_strchr (Char *, int);
+#ifdef notdef
+static Char	*g_strcat (Char *, const Char *);
+#endif
+static int	 g_stat (Char *, struct stat *, glob_t *);
+static int	 glob0 (const Char *, glob_t *);
+static int	 glob1 (Char *, glob_t *);
+static int	 glob2 (Char *, Char *, Char *, glob_t *);
+static int	 glob3 (Char *, Char *, Char *, Char *, glob_t *);
+static int	 globextend (const Char *, glob_t *);
+static const Char *	globtilde (const Char *, Char *, size_t, glob_t *);
+static int	 globexp1 (const Char *, glob_t *);
+static int	 globexp2 (const Char *, const Char *, glob_t *, int *);
+static int	 match (Char *, Char *, Char *);
+#ifdef DEBUG
+static void	 qprintf (const char *, Char *);
+#endif
+
+int
+openbsd_glob(pattern, flags, errfunc, pglob)
+	const char *pattern;
+	int flags, (*errfunc) __P((const char *, int));
+	glob_t *pglob;
+{
+	const u_char *patnext;
+	int c;
+	Char *bufnext, *bufend, patbuf[MAXPATHLEN+1];
+
+	patnext = (u_char *) pattern;
+	if (!(flags & GLOB_APPEND)) {
+		pglob->gl_pathc = 0;
+		pglob->gl_pathv = NULL;
+		if (!(flags & GLOB_DOOFFS))
+			pglob->gl_offs = 0;
+	}
+	pglob->gl_flags = flags & ~GLOB_MAGCHAR;
+	pglob->gl_errfunc = errfunc;
+	pglob->gl_matchc = 0;
+
+	bufnext = patbuf;
+	bufend = bufnext + MAXPATHLEN;
+	if (flags & GLOB_NOESCAPE)
+	    while (bufnext < bufend && (c = *patnext++) != EOS)
+		    *bufnext++ = c;
+	else {
+		/* Protect the quoted characters. */
+		while (bufnext < bufend && (c = *patnext++) != EOS)
+			if (c == QUOTE) {
+				if ((c = *patnext++) == EOS) {
+					c = QUOTE;
+					--patnext;
+				}
+				*bufnext++ = c | M_PROTECT;
+			}
+			else
+				*bufnext++ = c;
+	}
+	*bufnext = EOS;
+
+	if (flags & GLOB_BRACE)
+		return globexp1(patbuf, pglob);
+	else
+		return glob0(patbuf, pglob);
+}
+
+/*
+ * Expand recursively a glob {} pattern. When there is no more expansion
+ * invoke the standard globbing routine to glob the rest of the magic
+ * characters
+ */
+static int globexp1(pattern, pglob)
+	const Char *pattern;
+	glob_t *pglob;
+{
+	const Char* ptr = pattern;
+	int rv;
+
+	/* Protect a single {}, for find(1), like csh */
+	if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
+		return glob0(pattern, pglob);
+
+	while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL)
+		if (!globexp2(ptr, pattern, pglob, &rv))
+			return rv;
+
+	return glob0(pattern, pglob);
+}
+
+
+/*
+ * Recursive brace globbing helper. Tries to expand a single brace.
+ * If it succeeds then it invokes globexp1 with the new pattern.
+ * If it fails then it tries to glob the rest of the pattern and returns.
+ */
+static int globexp2(ptr, pattern, pglob, rv)
+	const Char *ptr, *pattern;
+	glob_t *pglob;
+	int *rv;
+{
+	int     i;
+	Char   *lm, *ls;
+	const Char *pe, *pm, *pl;
+	Char    patbuf[MAXPATHLEN + 1];
+
+	/* copy part up to the brace */
+	for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
+		continue;
+	ls = lm;
+
+	/* Find the balanced brace */
+	for (i = 0, pe = ++ptr; *pe; pe++)
+		if (*pe == LBRACKET) {
+			/* Ignore everything between [] */
+			for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
+				continue;
+			if (*pe == EOS) {
+				/*
+				 * We could not find a matching RBRACKET.
+				 * Ignore and just look for RBRACE
+				 */
+				pe = pm;
+			}
+		}
+		else if (*pe == LBRACE)
+			i++;
+		else if (*pe == RBRACE) {
+			if (i == 0)
+				break;
+			i--;
+		}
+
+	/* Non matching braces; just glob the pattern */
+	if (i != 0 || *pe == EOS) {
+		*rv = glob0(patbuf, pglob);
+		return 0;
+	}
+
+	for (i = 0, pl = pm = ptr; pm <= pe; pm++)
+		switch (*pm) {
+		case LBRACKET:
+			/* Ignore everything between [] */
+			for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
+				continue;
+			if (*pm == EOS) {
+				/*
+				 * We could not find a matching RBRACKET.
+				 * Ignore and just look for RBRACE
+				 */
+				pm = pl;
+			}
+			break;
+
+		case LBRACE:
+			i++;
+			break;
+
+		case RBRACE:
+			if (i) {
+			    i--;
+			    break;
+			}
+			/* FALLTHROUGH */
+		case COMMA:
+			if (i && *pm == COMMA)
+				break;
+			else {
+				/* Append the current string */
+				for (lm = ls; (pl < pm); *lm++ = *pl++)
+					continue;
+				/*
+				 * Append the rest of the pattern after the
+				 * closing brace
+				 */
+				for (pl = pe + 1; (*lm++ = *pl++) != EOS;)
+					continue;
+
+				/* Expand the current pattern */
+#ifdef DEBUG
+				qprintf("globexp2:", patbuf);
+#endif
+				*rv = globexp1(patbuf, pglob);
+
+				/* move after the comma, to the next string */
+				pl = pm + 1;
+			}
+			break;
+
+		default:
+			break;
+		}
+	*rv = 0;
+	return 0;
+}
+
+
+
+/*
+ * expand tilde from the passwd file.
+ */
+static const Char *
+globtilde(pattern, patbuf, patbuf_len, pglob)
+	const Char *pattern;
+	Char *patbuf;
+	size_t patbuf_len;
+	glob_t *pglob;
+{
+	struct passwd *pwd;
+	char *h;
+	const Char *p;
+	Char *b, *eb;
+
+	if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE))
+		return pattern;
+
+	/* Copy up to the end of the string or / */
+	eb = &patbuf[patbuf_len - 1];
+	for (p = pattern + 1, h = (char *) patbuf;
+	    h < (char *)eb && *p && *p != SLASH; *h++ = *p++)
+		continue;
+
+	*h = EOS;
+
+	if (((char *) patbuf)[0] == EOS) {
+		/*
+		 * handle a plain ~ or ~/ by expanding $HOME
+		 * first and then trying the password file
+		 */
+#ifdef HAVE_ISSETUGID
+		if (issetugid() != 0 || (h = getenv("HOME")) == NULL) {
+#endif
+			if ((pwd = getpwuid(getuid())) == NULL)
+				return pattern;
+			else
+				h = pwd->pw_dir;
+#ifdef HAVE_ISSETUGID
+		}
+#endif
+	}
+	else {
+		/*
+		 * Expand a ~user
+		 */
+		if ((pwd = getpwnam((char*) patbuf)) == NULL)
+			return pattern;
+		else
+			h = pwd->pw_dir;
+	}
+
+	/* Copy the home directory */
+	for (b = patbuf; b < eb && *h; *b++ = *h++)
+		continue;
+
+	/* Append the rest of the pattern */
+	while (b < eb && (*b++ = *p++) != EOS)
+		continue;
+	*b = EOS;
+
+	return patbuf;
+}
+
+
+/*
+ * The main glob() routine: compiles the pattern (optionally processing
+ * quotes), calls glob1() to do the real pattern matching, and finally
+ * sorts the list (unless unsorted operation is requested).  Returns 0
+ * if things went well, nonzero if errors occurred.  It is not an error
+ * to find no matches.
+ */
+static int
+glob0(pattern, pglob)
+	const Char *pattern;
+	glob_t *pglob;
+{
+	const Char *qpatnext;
+	int c, err, oldpathc;
+	Char *bufnext, patbuf[MAXPATHLEN+1];
+
+	qpatnext = globtilde(pattern, patbuf, sizeof(patbuf) / sizeof(Char),
+	    pglob);
+	oldpathc = pglob->gl_pathc;
+	bufnext = patbuf;
+
+	/* We don't need to check for buffer overflow any more. */
+	while ((c = *qpatnext++) != EOS) {
+		switch (c) {
+		case LBRACKET:
+			c = *qpatnext;
+			if (c == NOT)
+				++qpatnext;
+			if (*qpatnext == EOS ||
+			    g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) {
+				*bufnext++ = LBRACKET;
+				if (c == NOT)
+					--qpatnext;
+				break;
+			}
+			*bufnext++ = M_SET;
+			if (c == NOT)
+				*bufnext++ = M_NOT;
+			c = *qpatnext++;
+			do {
+				*bufnext++ = CHAR(c);
+				if (*qpatnext == RANGE &&
+				    (c = qpatnext[1]) != RBRACKET) {
+					*bufnext++ = M_RNG;
+					*bufnext++ = CHAR(c);
+					qpatnext += 2;
+				}
+			} while ((c = *qpatnext++) != RBRACKET);
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			*bufnext++ = M_END;
+			break;
+		case QUESTION:
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			*bufnext++ = M_ONE;
+			break;
+		case STAR:
+			pglob->gl_flags |= GLOB_MAGCHAR;
+			/* collapse adjacent stars to one,
+			 * to avoid exponential behavior
+			 */
+			if (bufnext == patbuf || bufnext[-1] != M_ALL)
+			    *bufnext++ = M_ALL;
+			break;
+		default:
+			*bufnext++ = CHAR(c);
+			break;
+		}
+	}
+	*bufnext = EOS;
+#ifdef DEBUG
+	qprintf("glob0:", patbuf);
+#endif
+
+	if ((err = glob1(patbuf, pglob)) != 0)
+		return(err);
+
+	/*
+	 * If there was no match we are going to append the pattern
+	 * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified
+	 * and the pattern did not contain any magic characters
+	 * GLOB_NOMAGIC is there just for compatibility with csh.
+	 */
+	if (pglob->gl_pathc == oldpathc) {
+		if ((pglob->gl_flags & GLOB_NOCHECK) ||
+		    ((pglob->gl_flags & GLOB_NOMAGIC) &&
+		    !(pglob->gl_flags & GLOB_MAGCHAR)))
+			return(globextend(pattern, pglob));
+		else
+			return(GLOB_NOMATCH);
+	}
+	if (!(pglob->gl_flags & GLOB_NOSORT))
+		qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
+		    pglob->gl_pathc - oldpathc, sizeof(char *), compare);
+	return(0);
+}
+
+static int
+compare(p, q)
+	const void *p, *q;
+{
+	return(strcmp(*(char **)p, *(char **)q));
+}
+
+static int
+glob1(pattern, pglob)
+	Char *pattern;
+	glob_t *pglob;
+{
+	Char pathbuf[MAXPATHLEN+1];
+
+	/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
+	if (*pattern == EOS)
+		return(0);
+	return(glob2(pathbuf, pathbuf, pattern, pglob));
+}
+
+/*
+ * The functions glob2 and glob3 are mutually recursive; there is one level
+ * of recursion for each segment in the pattern that contains one or more
+ * meta characters.
+ */
+static int
+glob2(pathbuf, pathend, pattern, pglob)
+	Char *pathbuf, *pathend, *pattern;
+	glob_t *pglob;
+{
+	struct stat sb;
+	Char *p, *q;
+	int anymeta;
+
+	/*
+	 * Loop over pattern segments until end of pattern or until
+	 * segment with meta character found.
+	 */
+	for (anymeta = 0;;) {
+		if (*pattern == EOS) {		/* End of pattern? */
+			*pathend = EOS;
+			if (g_lstat(pathbuf, &sb, pglob))
+				return(0);
+
+			if (((pglob->gl_flags & GLOB_MARK) &&
+			    pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
+			    || (S_ISLNK(sb.st_mode) &&
+			    (g_stat(pathbuf, &sb, pglob) == 0) &&
+			    S_ISDIR(sb.st_mode)))) {
+				*pathend++ = SEP;
+				*pathend = EOS;
+			}
+			++pglob->gl_matchc;
+			return(globextend(pathbuf, pglob));
+		}
+
+		/* Find end of next segment, copy tentatively to pathend. */
+		q = pathend;
+		p = pattern;
+		while (*p != EOS && *p != SEP) {
+			if (ismeta(*p))
+				anymeta = 1;
+			*q++ = *p++;
+		}
+
+		if (!anymeta) {		/* No expansion, do next segment. */
+			pathend = q;
+			pattern = p;
+			while (*pattern == SEP)
+				*pathend++ = *pattern++;
+		} else			/* Need expansion, recurse. */
+			return(glob3(pathbuf, pathend, pattern, p, pglob));
+	}
+	/* NOTREACHED */
+}
+
+static int
+glob3(pathbuf, pathend, pattern, restpattern, pglob)
+	Char *pathbuf, *pathend, *pattern, *restpattern;
+	glob_t *pglob;
+{
+	register struct dirent *dp;
+	DIR *dirp;
+	int err;
+	char buf[MAXPATHLEN];
+
+	/*
+	 * The readdirfunc declaration can't be prototyped, because it is
+	 * assigned, below, to two functions which are prototyped in glob.h
+	 * and dirent.h as taking pointers to differently typed opaque
+	 * structures.
+	 */
+	struct dirent *(*readdirfunc)();
+
+	*pathend = EOS;
+	errno = 0;
+
+	if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
+		/* TODO: don't call for ENOENT or ENOTDIR? */
+		if (pglob->gl_errfunc) {
+			g_Ctoc(pathbuf, buf);
+			if (pglob->gl_errfunc(buf, errno) ||
+			    pglob->gl_flags & GLOB_ERR)
+				return (GLOB_ABORTED);
+		}
+		return(0);
+	}
+
+	err = 0;
+
+	/* Search directory for matching names. */
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		readdirfunc = pglob->gl_readdir;
+	else
+		readdirfunc = readdir;
+	while ((dp = (*readdirfunc)(dirp))) {
+		register u_char *sc;
+		register Char *dc;
+
+		/* Initial DOT must be matched literally. */
+		if (dp->d_name[0] == DOT && *pattern != DOT)
+			continue;
+		for (sc = (u_char *) dp->d_name, dc = pathend;
+		     (*dc++ = *sc++) != EOS;)
+			continue;
+		if (!match(pathend, pattern, restpattern)) {
+			*pathend = EOS;
+			continue;
+		}
+		err = glob2(pathbuf, --dc, restpattern, pglob);
+		if (err)
+			break;
+	}
+
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		(*pglob->gl_closedir)(dirp);
+	else
+		closedir(dirp);
+	return(err);
+}
+
+
+/*
+ * Extend the gl_pathv member of a glob_t structure to accomodate a new item,
+ * add the new item, and update gl_pathc.
+ *
+ * This assumes the BSD realloc, which only copies the block when its size
+ * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
+ * behavior.
+ *
+ * Return 0 if new item added, error code if memory couldn't be allocated.
+ *
+ * Invariant of the glob_t structure:
+ *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
+ *	gl_pathv points to (gl_offs + gl_pathc + 1) items.
+ */
+static int
+globextend(path, pglob)
+	const Char *path;
+	glob_t *pglob;
+{
+	register char **pathv;
+	register int i;
+	u_int newsize;
+	char *copy;
+	const Char *p;
+
+	newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
+	pathv = pglob->gl_pathv ?
+		    realloc((char *)pglob->gl_pathv, newsize) :
+		    malloc(newsize);
+	if (pathv == NULL) {
+		if (pglob->gl_pathv)
+			free(pglob->gl_pathv);
+		return(GLOB_NOSPACE);
+	}
+
+	if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
+		/* first time around -- clear initial gl_offs items */
+		pathv += pglob->gl_offs;
+		for (i = pglob->gl_offs; --i >= 0; )
+			*--pathv = NULL;
+	}
+	pglob->gl_pathv = pathv;
+
+	for (p = path; *p++;)
+		continue;
+	if ((copy = malloc(p - path)) != NULL) {
+		g_Ctoc(path, copy);
+		pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
+	}
+	pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+	return(copy == NULL ? GLOB_NOSPACE : 0);
+}
+
+
+/*
+ * pattern matching function for filenames.  Each occurrence of the *
+ * pattern causes a recursion level.
+ */
+static int
+match(name, pat, patend)
+	register Char *name, *pat, *patend;
+{
+	int ok, negate_range;
+	Char c, k;
+
+	while (pat < patend) {
+		c = *pat++;
+		switch (c & M_MASK) {
+		case M_ALL:
+			if (pat == patend)
+				return(1);
+			do
+			    if (match(name, pat, patend))
+				    return(1);
+			while (*name++ != EOS);
+			return(0);
+		case M_ONE:
+			if (*name++ == EOS)
+				return(0);
+			break;
+		case M_SET:
+			ok = 0;
+			if ((k = *name++) == EOS)
+				return(0);
+			if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
+				++pat;
+			while (((c = *pat++) & M_MASK) != M_END)
+				if ((*pat & M_MASK) == M_RNG) {
+					if (c <= k && k <= pat[1])
+						ok = 1;
+					pat += 2;
+				} else if (c == k)
+					ok = 1;
+			if (ok == negate_range)
+				return(0);
+			break;
+		default:
+			if (*name++ != c)
+				return(0);
+			break;
+		}
+	}
+	return(*name == EOS);
+}
+
+/* Free allocated data belonging to a glob_t structure. */
+void
+openbsd_globfree(pglob)
+	glob_t *pglob;
+{
+	register int i;
+	register char **pp;
+
+	if (pglob->gl_pathv != NULL) {
+		pp = pglob->gl_pathv + pglob->gl_offs;
+		for (i = pglob->gl_pathc; i--; ++pp)
+			if (*pp)
+				free(*pp);
+		free(pglob->gl_pathv);
+	}
+}
+
+static DIR *
+g_opendir(str, pglob)
+	register Char *str;
+	glob_t *pglob;
+{
+	char buf[MAXPATHLEN];
+
+	if (!*str)
+		strcpy(buf, ".");
+	else
+		g_Ctoc(str, buf);
+
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return((*pglob->gl_opendir)(buf));
+
+	return(opendir(buf));
+}
+
+static int
+g_lstat(fn, sb, pglob)
+	register Char *fn;
+	struct stat *sb;
+	glob_t *pglob;
+{
+	char buf[MAXPATHLEN];
+
+	g_Ctoc(fn, buf);
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return((*pglob->gl_lstat)(buf, sb));
+	return(lstat(buf, sb));
+}
+
+static int
+g_stat(fn, sb, pglob)
+	register Char *fn;
+	struct stat *sb;
+	glob_t *pglob;
+{
+	char buf[MAXPATHLEN];
+
+	g_Ctoc(fn, buf);
+	if (pglob->gl_flags & GLOB_ALTDIRFUNC)
+		return((*pglob->gl_stat)(buf, sb));
+	return(stat(buf, sb));
+}
+
+static Char *
+g_strchr(str, ch)
+	Char *str;
+	int ch;
+{
+	do {
+		if (*str == ch)
+			return (str);
+	} while (*str++);
+	return (NULL);
+}
+
+#ifdef notdef
+static Char *
+g_strcat(dst, src)
+	Char *dst;
+	const Char* src;
+{
+	Char *sdst = dst;
+
+	while (*dst++)
+		continue;
+	--dst;
+	while((*dst++ = *src++) != EOS)
+	    continue;
+
+	return (sdst);
+}
+#endif
+
+static void
+g_Ctoc(str, buf)
+	register const Char *str;
+	char *buf;
+{
+	register char *dc;
+
+	for (dc = buf; (*dc++ = *str++) != EOS;)
+		continue;
+}
+
+#ifdef DEBUG
+static void
+qprintf(str, s)
+	const char *str;
+	register Char *s;
+{
+	register Char *p;
+
+	(void)printf("%s:\n", str);
+	for (p = s; *p; p++)
+		(void)printf("%c", CHAR(*p));
+	(void)printf("\n");
+	for (p = s; *p; p++)
+		(void)printf("%c", *p & M_PROTECT ? '"' : ' ');
+	(void)printf("\n");
+	for (p = s; *p; p++)
+		(void)printf("%c", ismeta(*p) ? '_' : ' ');
+	(void)printf("\n");
+}
+#endif
diff --git a/libtar/handle.c b/libtar/handle.c
new file mode 100644
index 0000000..ae974b9
--- /dev/null
+++ b/libtar/handle.c
@@ -0,0 +1,129 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  handle.c - libtar code for initializing a TAR handle
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+
+const char libtar_version[] = PACKAGE_VERSION;
+
+static tartype_t default_type = { open, close, read, write };
+
+
+static int
+tar_init(TAR **t, char *pathname, tartype_t *type,
+	 int oflags, int mode, int options)
+{
+	if ((oflags & O_ACCMODE) == O_RDWR)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	*t = (TAR *)calloc(1, sizeof(TAR));
+	if (*t == NULL)
+		return -1;
+
+	(*t)->pathname = pathname;
+	(*t)->options = options;
+	(*t)->type = (type ? type : &default_type);
+	(*t)->oflags = oflags;
+
+	if ((oflags & O_ACCMODE) == O_RDONLY)
+		(*t)->h = libtar_hash_new(256,
+					  (libtar_hashfunc_t)path_hashfunc);
+	else
+		(*t)->h = libtar_hash_new(16, (libtar_hashfunc_t)dev_hash);
+	if ((*t)->h == NULL)
+	{
+		free(*t);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/* open a new tarfile handle */
+int
+tar_open(TAR **t, char *pathname, tartype_t *type,
+	 int oflags, int mode, int options)
+{
+	if (tar_init(t, pathname, type, oflags, mode, options) == -1)
+		return -1;
+
+	if ((options & TAR_NOOVERWRITE) && (oflags & O_CREAT))
+		oflags |= O_EXCL;
+
+#ifdef O_BINARY
+	oflags |= O_BINARY;
+#endif
+
+	(*t)->fd = (*((*t)->type->openfunc))(pathname, oflags, mode);
+	if ((*t)->fd == -1)
+	{
+		free(*t);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int
+tar_fdopen(TAR **t, int fd, char *pathname, tartype_t *type,
+	   int oflags, int mode, int options)
+{
+	if (tar_init(t, pathname, type, oflags, mode, options) == -1)
+		return -1;
+
+	(*t)->fd = fd;
+	return 0;
+}
+
+
+int
+tar_fd(TAR *t)
+{
+	return t->fd;
+}
+
+
+/* close tarfile handle */
+int
+tar_close(TAR *t)
+{
+	int i;
+
+	i = (*(t->type->closefunc))(t->fd);
+
+	if (t->h != NULL)
+		libtar_hash_free(t->h, ((t->oflags & O_ACCMODE) == O_RDONLY
+					? free
+					: (libtar_freefunc_t)tar_dev_free));
+	free(t);
+
+	return i;
+}
+
+
diff --git a/libtar/inet_aton.c b/libtar/inet_aton.c
new file mode 100644
index 0000000..a935d5a
--- /dev/null
+++ b/libtar/inet_aton.c
@@ -0,0 +1,27 @@
+/*
+**  Copyright 2002 University of Illinois Board of Trustees
+**  Copyright 2002 Mark D. Roth
+**  All rights reserved.
+**
+**  inet_aton.c - inet_aton() function for compatibility library
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+
+int
+inet_aton(const char *cp, struct in_addr *inp)
+{
+	inp->s_addr = inet_addr(cp);
+	if (inp->s_addr == -1)
+		return 0;
+	return 1;
+}
+
+
diff --git a/libtar/internal.h b/libtar/internal.h
new file mode 100644
index 0000000..23243d2
--- /dev/null
+++ b/libtar/internal.h
@@ -0,0 +1,17 @@
+/*
+**  Copyright 2002-2003 University of Illinois Board of Trustees
+**  Copyright 2002-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  internal.h - internal header file for libtar
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+#include <compat.h>
+
+#include <libtar.h>
+
diff --git a/libtar/libtar.h b/libtar/libtar.h
new file mode 100644
index 0000000..7a8f332
--- /dev/null
+++ b/libtar/libtar.h
@@ -0,0 +1,300 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  libtar.h - header file for libtar library
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#ifndef LIBTAR_H
+#define LIBTAR_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "tar.h"
+
+#include "libtar_listhash.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+/* useful constants */
+#define T_BLOCKSIZE		512
+#define T_NAMELEN		100
+#define T_PREFIXLEN		155
+#define T_MAXPATHLEN		(T_NAMELEN + T_PREFIXLEN)
+
+/* GNU extensions for typeflag */
+#define GNU_LONGNAME_TYPE	'L'
+#define GNU_LONGLINK_TYPE	'K'
+
+/* our version of the tar header structure */
+struct tar_header
+{
+	char name[100];
+	char mode[8];
+	char uid[8];
+	char gid[8];
+	char size[12];
+	char mtime[12];
+	char chksum[8];
+	char typeflag;
+	char linkname[100];
+	char magic[6];
+	char version[2];
+	char uname[32];
+	char gname[32];
+	char devmajor[8];
+	char devminor[8];
+	char prefix[155];
+	char padding[12];
+	char *gnu_longname;
+	char *gnu_longlink;
+};
+
+
+/***** handle.c ************************************************************/
+
+typedef int (*openfunc_t)(const char *, int, ...);
+typedef int (*closefunc_t)(int);
+typedef ssize_t (*readfunc_t)(int, void *, size_t);
+typedef ssize_t (*writefunc_t)(int, const void *, size_t);
+
+typedef struct
+{
+	openfunc_t openfunc;
+	closefunc_t closefunc;
+	readfunc_t readfunc;
+	writefunc_t writefunc;
+}
+tartype_t;
+
+typedef struct
+{
+	tartype_t *type;
+	char *pathname;
+	long fd;
+	int oflags;
+	int options;
+	struct tar_header th_buf;
+	libtar_hash_t *h;
+}
+TAR;
+
+/* constant values for the TAR options field */
+#define TAR_GNU			 1	/* use GNU extensions */
+#define TAR_VERBOSE		 2	/* output file info to stdout */
+#define TAR_NOOVERWRITE		 4	/* don't overwrite existing files */
+#define TAR_IGNORE_EOT		 8	/* ignore double zero blocks as EOF */
+#define TAR_CHECK_MAGIC		16	/* check magic in file header */
+#define TAR_CHECK_VERSION	32	/* check version in file header */
+#define TAR_IGNORE_CRC		64	/* ignore CRC in file header */
+
+/* this is obsolete - it's here for backwards-compatibility only */
+#define TAR_IGNORE_MAGIC	0
+
+extern const char libtar_version[];
+
+
+/* open a new tarfile handle */
+int tar_open(TAR **t, char *pathname, tartype_t *type,
+	     int oflags, int mode, int options);
+
+/* make a tarfile handle out of a previously-opened descriptor */
+int tar_fdopen(TAR **t, int fd, char *pathname, tartype_t *type,
+	       int oflags, int mode, int options);
+
+/* returns the descriptor associated with t */
+int tar_fd(TAR *t);
+
+/* close tarfile handle */
+int tar_close(TAR *t);
+
+
+/***** append.c ************************************************************/
+
+/* forward declaration to appease the compiler */
+struct tar_dev;
+
+/* cleanup function */
+void tar_dev_free(struct tar_dev *tdp);
+
+/* Appends a file to the tar archive.
+ * Arguments:
+ *    t        = TAR handle to append to
+ *    realname = path of file to append
+ *    savename = name to save the file under in the archive
+ */
+int tar_append_file(TAR *t, char *realname, char *savename);
+
+/* write EOF indicator */
+int tar_append_eof(TAR *t);
+
+/* add file contents to a tarchive */
+int tar_append_regfile(TAR *t, char *realname);
+
+
+/***** block.c *************************************************************/
+
+/* macros for reading/writing tarchive blocks */
+#define tar_block_read(t, buf) \
+	(*((t)->type->readfunc))((t)->fd, (char *)(buf), T_BLOCKSIZE)
+#define tar_block_write(t, buf) \
+	(*((t)->type->writefunc))((t)->fd, (char *)(buf), T_BLOCKSIZE)
+
+/* read/write a header block */
+int th_read(TAR *t);
+int th_write(TAR *t);
+
+
+/***** decode.c ************************************************************/
+
+/* determine file type */
+#define TH_ISREG(t)	((t)->th_buf.typeflag == REGTYPE \
+			 || (t)->th_buf.typeflag == AREGTYPE \
+			 || (t)->th_buf.typeflag == CONTTYPE \
+			 || (S_ISREG((mode_t)oct_to_int((t)->th_buf.mode)) \
+			     && (t)->th_buf.typeflag != LNKTYPE))
+#define TH_ISLNK(t)	((t)->th_buf.typeflag == LNKTYPE)
+#define TH_ISSYM(t)	((t)->th_buf.typeflag == SYMTYPE \
+			 || S_ISLNK((mode_t)oct_to_int((t)->th_buf.mode)))
+#define TH_ISCHR(t)	((t)->th_buf.typeflag == CHRTYPE \
+			 || S_ISCHR((mode_t)oct_to_int((t)->th_buf.mode)))
+#define TH_ISBLK(t)	((t)->th_buf.typeflag == BLKTYPE \
+			 || S_ISBLK((mode_t)oct_to_int((t)->th_buf.mode)))
+#define TH_ISDIR(t)	((t)->th_buf.typeflag == DIRTYPE \
+			 || S_ISDIR((mode_t)oct_to_int((t)->th_buf.mode)) \
+			 || ((t)->th_buf.typeflag == AREGTYPE \
+			     && ((t)->th_buf.name[strlen((t)->th_buf.name) - 1] == '/')))
+#define TH_ISFIFO(t)	((t)->th_buf.typeflag == FIFOTYPE \
+			 || S_ISFIFO((mode_t)oct_to_int((t)->th_buf.mode)))
+#define TH_ISLONGNAME(t)	((t)->th_buf.typeflag == GNU_LONGNAME_TYPE)
+#define TH_ISLONGLINK(t)	((t)->th_buf.typeflag == GNU_LONGLINK_TYPE)
+
+/* decode tar header info */
+#define th_get_crc(t) oct_to_int((t)->th_buf.chksum)
+#define th_get_size(t) oct_to_int((t)->th_buf.size)
+#define th_get_mtime(t) oct_to_int((t)->th_buf.mtime)
+#define th_get_devmajor(t) oct_to_int((t)->th_buf.devmajor)
+#define th_get_devminor(t) oct_to_int((t)->th_buf.devminor)
+#define th_get_linkname(t) ((t)->th_buf.gnu_longlink \
+                            ? (t)->th_buf.gnu_longlink \
+                            : (t)->th_buf.linkname)
+char *th_get_pathname(TAR *t);
+mode_t th_get_mode(TAR *t);
+uid_t th_get_uid(TAR *t);
+gid_t th_get_gid(TAR *t);
+
+
+/***** encode.c ************************************************************/
+
+/* encode file info in th_header */
+void th_set_type(TAR *t, mode_t mode);
+void th_set_path(TAR *t, char *pathname);
+void th_set_link(TAR *t, char *linkname);
+void th_set_device(TAR *t, dev_t device);
+void th_set_user(TAR *t, uid_t uid);
+void th_set_group(TAR *t, gid_t gid);
+void th_set_mode(TAR *t, mode_t fmode);
+#define th_set_mtime(t, fmtime) \
+	int_to_oct_nonull((fmtime), (t)->th_buf.mtime, 12)
+#define th_set_size(t, fsize) \
+	int_to_oct_nonull((fsize), (t)->th_buf.size, 12)
+
+/* encode everything at once (except the pathname and linkname) */
+void th_set_from_stat(TAR *t, struct stat *s);
+
+/* encode magic, version, and crc - must be done after everything else is set */
+void th_finish(TAR *t);
+
+
+/***** extract.c ***********************************************************/
+
+/* sequentially extract next file from t */
+int tar_extract_file(TAR *t, char *realname);
+
+/* extract different file types */
+int tar_extract_dir(TAR *t, char *realname);
+int tar_extract_hardlink(TAR *t, char *realname);
+int tar_extract_symlink(TAR *t, char *realname);
+int tar_extract_chardev(TAR *t, char *realname);
+int tar_extract_blockdev(TAR *t, char *realname);
+int tar_extract_fifo(TAR *t, char *realname);
+
+/* for regfiles, we need to extract the content blocks as well */
+int tar_extract_regfile(TAR *t, char *realname);
+int tar_skip_regfile(TAR *t);
+
+
+/***** output.c ************************************************************/
+
+/* print the tar header */
+void th_print(TAR *t);
+
+/* print "ls -l"-like output for the file described by th */
+void th_print_long_ls(TAR *t);
+
+
+/***** util.c *************************************************************/
+
+/* hashing function for pathnames */
+int path_hashfunc(char *key, int numbuckets);
+
+/* matching function for dev_t's */
+int dev_match(dev_t *dev1, dev_t *dev2);
+
+/* matching function for ino_t's */
+int ino_match(ino_t *ino1, ino_t *ino2);
+
+/* hashing function for dev_t's */
+int dev_hash(dev_t *dev);
+
+/* hashing function for ino_t's */
+int ino_hash(ino_t *inode);
+
+/* create any necessary dirs */
+int mkdirhier(char *path);
+
+/* calculate header checksum */
+int th_crc_calc(TAR *t);
+
+/* calculate a signed header checksum */
+int th_signed_crc_calc(TAR *t);
+
+/* compare checksums in a forgiving way */
+#define th_crc_ok(t) (th_get_crc(t) == th_crc_calc(t) || th_get_crc(t) == th_signed_crc_calc(t))
+
+/* string-octal to integer conversion */
+int oct_to_int(char *oct);
+
+/* integer to NULL-terminated string-octal conversion */
+#define int_to_oct(num, oct, octlen) \
+	snprintf((oct), (octlen), "%*lo ", (octlen) - 2, (unsigned long)(num))
+
+/* integer to string-octal conversion, no NULL */
+void int_to_oct_nonull(int num, char *oct, size_t octlen);
+
+
+/***** wrapper.c **********************************************************/
+
+/* extract groups of files */
+int tar_extract_glob(TAR *t, char *globname, char *prefix);
+int tar_extract_all(TAR *t, char *prefix);
+
+/* add a whole tree of files */
+int tar_append_tree(TAR *t, char *realdir, char *savedir);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ! LIBTAR_H */
+
diff --git a/libtar/libtar_hash.c b/libtar/libtar_hash.c
new file mode 100644
index 0000000..796ebbe
--- /dev/null
+++ b/libtar/libtar_hash.c
@@ -0,0 +1,344 @@
+/* listhash/libtar_hash.c.  Generated from hash.c.in by configure. */
+
+/*
+**  Copyright 1998-2002 University of Illinois Board of Trustees
+**  Copyright 1998-2002 Mark D. Roth
+**  All rights reserved. 
+**
+**  libtar_hash.c - hash table routines
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+#include <compat.h>
+
+#include <libtar_listhash.h>
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+#endif
+
+
+/*
+** libtar_hashptr_reset() - reset a hash pointer
+*/
+void
+libtar_hashptr_reset(libtar_hashptr_t *hp)
+{
+	libtar_listptr_reset(&(hp->node));
+	hp->bucket = -1;
+}
+
+
+/*
+** libtar_hashptr_data() - retrieve the data being pointed to
+*/
+void *
+libtar_hashptr_data(libtar_hashptr_t *hp)
+{
+	return libtar_listptr_data(&(hp->node));
+}
+
+
+/*
+** libtar_str_hashfunc() - default hash function, optimized for
+**				      7-bit strings
+*/
+unsigned int
+libtar_str_hashfunc(char *key, unsigned int num_buckets)
+{
+#if 0
+	register unsigned result = 0;
+	register int i;
+
+	if (key == NULL)
+		return 0;
+
+	for (i = 0; *key != '\0' && i < 32; i++)
+		result = result * 33U + *key++;
+
+	return (result % num_buckets);
+#else
+	if (key == NULL)
+		return 0;
+
+	return (key[0] % num_buckets);
+#endif
+}
+
+
+/*
+** libtar_hash_nents() - return number of elements from hash
+*/
+unsigned int
+libtar_hash_nents(libtar_hash_t *h)
+{
+	return h->nents;
+}
+
+
+/*
+** libtar_hash_new() - create a new hash
+*/
+libtar_hash_t *
+libtar_hash_new(int num, libtar_hashfunc_t hashfunc)
+{
+	libtar_hash_t *hash;
+
+	hash = (libtar_hash_t *)calloc(1, sizeof(libtar_hash_t));
+	if (hash == NULL)
+		return NULL;
+	hash->numbuckets = num;
+	if (hashfunc != NULL)
+		hash->hashfunc = hashfunc;
+	else
+		hash->hashfunc = (libtar_hashfunc_t)libtar_str_hashfunc;
+
+	hash->table = (libtar_list_t **)calloc(num, sizeof(libtar_list_t *));
+	if (hash->table == NULL)
+	{
+		free(hash);
+		return NULL;
+	}
+
+	return hash;
+}
+
+
+/*
+** libtar_hash_next() - get next element in hash
+** returns:
+**	1			data found
+**	0			end of list
+*/
+int
+libtar_hash_next(libtar_hash_t *h,
+			    libtar_hashptr_t *hp)
+{
+#ifdef DS_DEBUG
+	printf("==> libtar_hash_next(h=0x%lx, hp={%d,0x%lx})\n",
+	       h, hp->bucket, hp->node);
+#endif
+
+	if (hp->bucket >= 0 && hp->node != NULL &&
+	    libtar_list_next(h->table[hp->bucket], &(hp->node)) != 0)
+	{
+#ifdef DS_DEBUG
+		printf("    libtar_hash_next(): found additional "
+		       "data in current bucket (%d), returing 1\n",
+		       hp->bucket);
+#endif
+		return 1;
+	}
+
+#ifdef DS_DEBUG
+	printf("    libtar_hash_next(): done with bucket %d\n",
+	       hp->bucket);
+#endif
+
+	for (hp->bucket++; hp->bucket < h->numbuckets; hp->bucket++)
+	{
+#ifdef DS_DEBUG
+		printf("    libtar_hash_next(): "
+		       "checking bucket %d\n", hp->bucket);
+#endif
+		hp->node = NULL;
+		if (h->table[hp->bucket] != NULL &&
+		    libtar_list_next(h->table[hp->bucket],
+		    				&(hp->node)) != 0)
+		{
+#ifdef DS_DEBUG
+			printf("    libtar_hash_next(): "
+			       "found data in bucket %d, returing 1\n",
+			       hp->bucket);
+#endif
+			return 1;
+		}
+	}
+
+	if (hp->bucket == h->numbuckets)
+	{
+#ifdef DS_DEBUG
+		printf("    libtar_hash_next(): hash pointer "
+		       "wrapped to 0\n");
+#endif
+		hp->bucket = -1;
+		hp->node = NULL;
+	}
+
+#ifdef DS_DEBUG
+	printf("<== libtar_hash_next(): no more data, "
+	       "returning 0\n");
+#endif
+	return 0;
+}
+
+
+/*
+** libtar_hash_del() - delete an entry from the hash
+** returns:
+**	0			success
+**	-1 (and sets errno)	failure
+*/
+int
+libtar_hash_del(libtar_hash_t *h,
+			   libtar_hashptr_t *hp)
+{
+	if (hp->bucket < 0
+	    || hp->bucket >= h->numbuckets
+	    || h->table[hp->bucket] == NULL
+	    || hp->node == NULL)
+	{
+		errno = EINVAL;
+		return -1;
+	}
+
+	libtar_list_del(h->table[hp->bucket], &(hp->node));
+	h->nents--;
+	return 0;
+}
+
+
+/*
+** libtar_hash_empty() - empty the hash
+*/
+void
+libtar_hash_empty(libtar_hash_t *h, libtar_freefunc_t freefunc)
+{
+	int i;
+
+	for (i = 0; i < h->numbuckets; i++)
+		if (h->table[i] != NULL)
+			libtar_list_empty(h->table[i], freefunc);
+
+	h->nents = 0;
+}
+
+
+/*
+** libtar_hash_free() - delete all of the nodes in the hash
+*/
+void
+libtar_hash_free(libtar_hash_t *h, libtar_freefunc_t freefunc)
+{
+	int i;
+
+	for (i = 0; i < h->numbuckets; i++)
+		if (h->table[i] != NULL)
+			libtar_list_free(h->table[i], freefunc);
+
+	free(h->table);
+	free(h);
+}
+
+
+/*
+** libtar_hash_search() - iterative search for an element in a hash
+** returns:
+**	1			match found
+**	0			no match
+*/
+int
+libtar_hash_search(libtar_hash_t *h,
+			      libtar_hashptr_t *hp, void *data,
+			      libtar_matchfunc_t matchfunc)
+{
+	while (libtar_hash_next(h, hp) != 0)
+		if ((*matchfunc)(data, libtar_listptr_data(&(hp->node))) != 0)
+			return 1;
+
+	return 0;
+}
+
+
+/*
+** libtar_hash_getkey() - hash-based search for an element in a hash
+** returns:
+**	1			match found
+**	0			no match
+*/
+int
+libtar_hash_getkey(libtar_hash_t *h,
+			      libtar_hashptr_t *hp, void *key,
+			      libtar_matchfunc_t matchfunc)
+{
+#ifdef DS_DEBUG
+	printf("==> libtar_hash_getkey(h=0x%lx, hp={%d,0x%lx}, "
+	       "key=0x%lx, matchfunc=0x%lx)\n",
+	       h, hp->bucket, hp->node, key, matchfunc);
+#endif
+
+	if (hp->bucket == -1)
+	{
+		hp->bucket = (*(h->hashfunc))(key, h->numbuckets);
+#ifdef DS_DEBUG
+		printf("    libtar_hash_getkey(): hp->bucket "
+		       "set to %d\n", hp->bucket);
+#endif
+	}
+
+	if (h->table[hp->bucket] == NULL)
+	{
+#ifdef DS_DEBUG
+		printf("    libtar_hash_getkey(): no list "
+		       "for bucket %d, returning 0\n", hp->bucket);
+#endif
+		hp->bucket = -1;
+		return 0;
+	}
+
+#ifdef DS_DEBUG
+	printf("<== libtar_hash_getkey(): "
+	       "returning libtar_list_search()\n");
+#endif
+	return libtar_list_search(h->table[hp->bucket], &(hp->node),
+					     key, matchfunc);
+}
+
+
+/*
+** libtar_hash_add() - add an element to the hash
+** returns:
+**	0			success
+**	-1 (and sets errno)	failure
+*/
+int
+libtar_hash_add(libtar_hash_t *h, void *data)
+{
+	int bucket, i;
+
+#ifdef DS_DEBUG
+	printf("==> libtar_hash_add(h=0x%lx, data=0x%lx)\n",
+	       h, data);
+#endif
+
+	bucket = (*(h->hashfunc))(data, h->numbuckets);
+#ifdef DS_DEBUG
+	printf("    libtar_hash_add(): inserting in bucket %d\n",
+	       bucket);
+#endif
+	if (h->table[bucket] == NULL)
+	{
+#ifdef DS_DEBUG
+		printf("    libtar_hash_add(): creating new list\n");
+#endif
+		h->table[bucket] = libtar_list_new(LIST_QUEUE, NULL);
+	}
+
+#ifdef DS_DEBUG
+	printf("<== libtar_hash_add(): "
+	       "returning libtar_list_add()\n");
+#endif
+	i = libtar_list_add(h->table[bucket], data);
+	if (i == 0)
+		h->nents++;
+	return i;
+}
+
+
diff --git a/libtar/libtar_list.c b/libtar/libtar_list.c
new file mode 100644
index 0000000..2c5febb
--- /dev/null
+++ b/libtar/libtar_list.c
@@ -0,0 +1,458 @@
+/* listhash/libtar_list.c.  Generated from list.c.in by configure. */
+
+/*
+**  Copyright 1998-2002 University of Illinois Board of Trustees
+**  Copyright 1998-2002 Mark D. Roth
+**  All rights reserved.
+**
+**  libtar_list.c - linked list routines
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <config.h>
+#include <compat.h>
+
+#include <libtar_listhash.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/param.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+# include <stdlib.h>
+#endif
+
+
+/*
+** libtar_listptr_reset() - reset a list pointer
+*/
+void
+libtar_listptr_reset(libtar_listptr_t *lp)
+{
+	*lp = NULL;
+}
+
+
+/*
+** libtar_listptr_data() - retrieve the data pointed to by lp
+*/
+void *
+libtar_listptr_data(libtar_listptr_t *lp)
+{
+	return (*lp)->data;
+}
+
+
+/*
+** libtar_list_new() - create a new, empty list
+*/
+libtar_list_t *
+libtar_list_new(int flags, libtar_cmpfunc_t cmpfunc)
+{
+	libtar_list_t *newlist;
+
+#ifdef DS_DEBUG
+	printf("in libtar_list_new(%d, 0x%lx)\n", flags, cmpfunc);
+#endif
+
+	if (flags != LIST_USERFUNC
+	    && flags != LIST_STACK
+	    && flags != LIST_QUEUE)
+	{
+		errno = EINVAL;
+		return NULL;
+	}
+
+	newlist = (libtar_list_t *)calloc(1, sizeof(libtar_list_t));
+	if (cmpfunc != NULL)
+		newlist->cmpfunc = cmpfunc;
+	else
+		newlist->cmpfunc = (libtar_cmpfunc_t)strcmp;
+	newlist->flags = flags;
+
+	return newlist;
+}
+
+
+/*
+** libtar_list_iterate() - call a function for every element
+**				      in a list
+*/
+int
+libtar_list_iterate(libtar_list_t *l,
+			       libtar_iterate_func_t plugin,
+			       void *state)
+{
+	libtar_listptr_t n;
+
+	if (l == NULL)
+		return -1;
+
+	for (n = l->first; n != NULL; n = n->next)
+	{
+		if ((*plugin)(n->data, state) == -1)
+			return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+** libtar_list_empty() - empty the list
+*/
+void
+libtar_list_empty(libtar_list_t *l, libtar_freefunc_t freefunc)
+{
+	libtar_listptr_t n;
+
+	for (n = l->first; n != NULL; n = l->first)
+	{
+		l->first = n->next;
+		if (freefunc != NULL)
+			(*freefunc)(n->data);
+		free(n);
+	}
+
+	l->nents = 0;
+}
+
+
+/*
+** libtar_list_free() - remove and free() the whole list
+*/
+void
+libtar_list_free(libtar_list_t *l, libtar_freefunc_t freefunc)
+{
+	libtar_list_empty(l, freefunc);
+	free(l);
+}
+
+
+/*
+** libtar_list_nents() - return number of elements in the list
+*/
+unsigned int
+libtar_list_nents(libtar_list_t *l)
+{
+	return l->nents;
+}
+
+
+/*
+** libtar_list_add() - adds an element to the list
+** returns:
+**	0			success
+**	-1 (and sets errno)	failure
+*/
+int
+libtar_list_add(libtar_list_t *l, void *data)
+{
+	libtar_listptr_t n, m;
+
+#ifdef DS_DEBUG
+	printf("==> libtar_list_add(\"%s\")\n", (char *)data);
+#endif
+
+	n = (libtar_listptr_t)malloc(sizeof(struct libtar_node));
+	if (n == NULL)
+		return -1;
+	n->data = data;
+	l->nents++;
+
+#ifdef DS_DEBUG
+	printf("    libtar_list_add(): allocated data\n");
+#endif
+
+	/* if the list is empty */
+	if (l->first == NULL)
+	{
+		l->last = l->first = n;
+		n->next = n->prev = NULL;
+#ifdef DS_DEBUG
+		printf("<== libtar_list_add(): list was empty; "
+		       "added first element and returning 0\n");
+#endif
+		return 0;
+	}
+
+#ifdef DS_DEBUG
+	printf("    libtar_list_add(): list not empty\n");
+#endif
+
+	if (l->flags == LIST_STACK)
+	{
+		n->prev = NULL;
+		n->next = l->first;
+		if (l->first != NULL)
+			l->first->prev = n;
+		l->first = n;
+#ifdef DS_DEBUG
+		printf("<== libtar_list_add(): LIST_STACK set; "
+		       "added in front\n");
+#endif
+		return 0;
+	}
+
+	if (l->flags == LIST_QUEUE)
+	{
+		n->prev = l->last;
+		n->next = NULL;
+		if (l->last != NULL)
+			l->last->next = n;
+		l->last = n;
+#ifdef DS_DEBUG
+		printf("<== libtar_list_add(): LIST_QUEUE set; "
+		       "added at end\n");
+#endif
+		return 0;
+	}
+
+	for (m = l->first; m != NULL; m = m->next)
+		if ((*(l->cmpfunc))(data, m->data) < 0)
+		{
+			/*
+			** if we find one that's bigger,
+			** insert data before it
+			*/
+#ifdef DS_DEBUG
+			printf("    libtar_list_add(): gotcha..."
+			       "inserting data\n");
+#endif
+			if (m == l->first)
+			{
+				l->first = n;
+				n->prev = NULL;
+				m->prev = n;
+				n->next = m;
+#ifdef DS_DEBUG
+				printf("<== libtar_list_add(): "
+				       "added first, returning 0\n");
+#endif
+				return 0;
+			}
+			m->prev->next = n;
+			n->prev = m->prev;
+			m->prev = n;
+			n->next = m;
+#ifdef DS_DEBUG
+			printf("<== libtar_list_add(): added middle,"
+			       " returning 0\n");
+#endif
+			return 0;
+		}
+
+#ifdef DS_DEBUG
+	printf("    libtar_list_add(): new data larger than current "
+	       "list elements\n");
+#endif
+
+	/* if we get here, data is bigger than everything in the list */
+	l->last->next = n;
+	n->prev = l->last;
+	l->last = n;
+	n->next = NULL;
+#ifdef DS_DEBUG
+	printf("<== libtar_list_add(): added end, returning 0\n");
+#endif
+	return 0;
+}
+
+
+/*
+** libtar_list_del() - remove the element pointed to by n
+**				  from the list l
+*/
+void
+libtar_list_del(libtar_list_t *l, libtar_listptr_t *n)
+{
+	libtar_listptr_t m;
+
+#ifdef DS_DEBUG
+	printf("==> libtar_list_del()\n");
+#endif
+
+	l->nents--;
+
+	m = (*n)->next;
+
+	if ((*n)->prev)
+		(*n)->prev->next = (*n)->next;
+	else
+		l->first = (*n)->next;
+	if ((*n)->next)
+		(*n)->next->prev = (*n)->prev;
+	else
+		l->last = (*n)->prev;
+
+	free(*n);
+	*n = m;
+}
+
+
+/*
+** libtar_list_next() - get the next element in the list
+** returns:
+**	1			success
+**	0			end of list
+*/
+int
+libtar_list_next(libtar_list_t *l,
+			    libtar_listptr_t *n)
+{
+	if (*n == NULL)
+		*n = l->first;
+	else
+		*n = (*n)->next;
+
+	return (*n != NULL ? 1 : 0);
+}
+
+
+/*
+** libtar_list_prev() - get the previous element in the list
+** returns:
+**	1			success
+**	0			end of list
+*/
+int
+libtar_list_prev(libtar_list_t *l,
+			    libtar_listptr_t *n)
+{
+	if (*n == NULL)
+		*n = l->last;
+	else
+		*n = (*n)->prev;
+
+	return (*n != NULL ? 1 : 0);
+}
+
+
+/*
+** libtar_str_match() - string matching function
+** returns:
+**	1			match
+**	0			no match
+*/
+int
+libtar_str_match(char *check, char *data)
+{
+	return !strcmp(check, data);
+}
+
+
+/*
+** libtar_list_add_str() - splits string str into delim-delimited
+**				      elements and adds them to list l
+** returns:
+**	0			success
+**	-1 (and sets errno)	failure
+*/
+int
+libtar_list_add_str(libtar_list_t *l,
+			       char *str, char *delim)
+{
+	char tmp[10240];
+	char *tokp, *nextp = tmp;
+
+	strlcpy(tmp, str, sizeof(tmp));
+	while ((tokp = strsep(&nextp, delim)) != NULL)
+	{
+		if (*tokp == '\0')
+			continue;
+		if (libtar_list_add(l, strdup(tokp)))
+			return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+** libtar_list_search() - find an entry in a list
+** returns:
+**	1			match found
+**	0			no match
+*/
+int
+libtar_list_search(libtar_list_t *l,
+			      libtar_listptr_t *n, void *data,
+			      libtar_matchfunc_t matchfunc)
+{
+#ifdef DS_DEBUG
+	printf("==> libtar_list_search(l=0x%lx, n=0x%lx, \"%s\")\n",
+	       l, n, (char *)data);
+#endif
+
+	if (matchfunc == NULL)
+		matchfunc = (libtar_matchfunc_t)libtar_str_match;
+
+	if (*n == NULL)
+		*n = l->first;
+	else
+		*n = (*n)->next;
+
+	for (; *n != NULL; *n = (*n)->next)
+	{
+#ifdef DS_DEBUG
+		printf("checking against \"%s\"\n", (char *)(*n)->data);
+#endif
+		if ((*(matchfunc))(data, (*n)->data) != 0)
+			return 1;
+	}
+
+#ifdef DS_DEBUG
+	printf("no matches found\n");
+#endif
+	return 0;
+}
+
+
+/*
+** libtar_list_dup() - copy an existing list
+*/
+libtar_list_t *
+libtar_list_dup(libtar_list_t *l)
+{
+	libtar_list_t *newlist;
+	libtar_listptr_t n;
+
+	newlist = libtar_list_new(l->flags, l->cmpfunc);
+	for (n = l->first; n != NULL; n = n->next)
+		libtar_list_add(newlist, n->data);
+
+#ifdef DS_DEBUG
+	printf("returning from libtar_list_dup()\n");
+#endif
+	return newlist;
+}
+
+
+/*
+** libtar_list_merge() - merge two lists into a new list
+*/
+libtar_list_t *
+libtar_list_merge(libtar_cmpfunc_t cmpfunc, int flags,
+			     libtar_list_t *list1,
+			     libtar_list_t *list2)
+{
+	libtar_list_t *newlist;
+	libtar_listptr_t n;
+
+	newlist = libtar_list_new(flags, cmpfunc);
+
+	n = NULL;
+	while (libtar_list_next(list1, &n) != 0)
+		libtar_list_add(newlist, n->data);
+	n = NULL;
+	while (libtar_list_next(list2, &n) != 0)
+		libtar_list_add(newlist, n->data);
+
+	return newlist;
+}
+
+
diff --git a/libtar/libtar_listhash.h b/libtar/libtar_listhash.h
new file mode 100644
index 0000000..fa33cfd
--- /dev/null
+++ b/libtar/libtar_listhash.h
@@ -0,0 +1,196 @@
+/* listhash/libtar_listhash.h.  Generated from listhash.h.in by configure. */
+
+/*
+**  Copyright 1998-2002 University of Illinois Board of Trustees
+**  Copyright 1998-2002 Mark D. Roth
+**  All rights reserved.
+**
+**  libtar_listhash.h - header file for listhash module
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#ifndef libtar_LISTHASH_H
+#define libtar_LISTHASH_H
+
+
+/***** list.c **********************************************************/
+
+/*
+** Comparison function (used to determine order of elements in a list)
+** returns less than, equal to, or greater than 0
+** if data1 is less than, equal to, or greater than data2
+*/
+typedef int (*libtar_cmpfunc_t)(void *, void *);
+
+/*
+** Free function (for freeing allocated memory in each element)
+*/
+typedef void (*libtar_freefunc_t)(void *);
+
+/*
+** Plugin function for libtar_list_iterate()
+*/
+typedef int (*libtar_iterate_func_t)(void *, void *);
+
+/*
+** Matching function (used to find elements in a list)
+** first argument is the data to search for
+** second argument is the list element it's being compared to
+** returns 0 if no match is found, non-zero otherwise
+*/
+typedef int (*libtar_matchfunc_t)(void *, void *);
+
+
+struct libtar_node
+{
+	void *data;
+	struct libtar_node *next;
+	struct libtar_node *prev;
+};
+typedef struct libtar_node *libtar_listptr_t;
+
+struct libtar_list
+{
+	libtar_listptr_t first;
+	libtar_listptr_t last;
+	libtar_cmpfunc_t cmpfunc;
+	int flags;
+	unsigned int nents;
+};
+typedef struct libtar_list libtar_list_t;
+
+
+/* values for flags */
+#define LIST_USERFUNC	0	/* use cmpfunc() to order */
+#define LIST_STACK	1	/* new elements go in front */
+#define LIST_QUEUE	2	/* new elements go at the end */
+
+
+/* reset a list pointer */
+void libtar_listptr_reset(libtar_listptr_t *);
+
+/* retrieve the data being pointed to */
+void *libtar_listptr_data(libtar_listptr_t *);
+
+/* creates a new, empty list */
+libtar_list_t *libtar_list_new(int, libtar_cmpfunc_t);
+
+/* call a function for every element in a list */
+int libtar_list_iterate(libtar_list_t *,
+				   libtar_iterate_func_t, void *);
+
+/* empty the list */
+void libtar_list_empty(libtar_list_t *,
+				  libtar_freefunc_t);
+
+/* remove and free() the entire list */
+void libtar_list_free(libtar_list_t *,
+				 libtar_freefunc_t);
+
+/* add elements */
+int libtar_list_add(libtar_list_t *, void *);
+
+/* removes an element from the list - returns -1 on error */
+void libtar_list_del(libtar_list_t *,
+				libtar_listptr_t *);
+
+/* returns 1 when valid data is returned, or 0 at end of list */
+int libtar_list_next(libtar_list_t *,
+				libtar_listptr_t *);
+
+/* returns 1 when valid data is returned, or 0 at end of list */
+int libtar_list_prev(libtar_list_t *,
+				libtar_listptr_t *);
+
+/* return 1 if the data matches a list entry, 0 otherwise */
+int libtar_list_search(libtar_list_t *,
+				  libtar_listptr_t *, void *,
+				  libtar_matchfunc_t);
+
+/* return number of elements from list */
+unsigned int libtar_list_nents(libtar_list_t *);
+
+/* adds elements from a string delimited by delim */
+int libtar_list_add_str(libtar_list_t *, char *, char *);
+
+/* string matching function */
+int libtar_str_match(char *, char *);
+
+
+/***** hash.c **********************************************************/
+
+/*
+** Hashing function (determines which bucket the given key hashes into)
+** first argument is the key to hash
+** second argument is the total number of buckets
+** returns the bucket number
+*/
+typedef unsigned int (*libtar_hashfunc_t)(void *, unsigned int);
+
+
+struct libtar_hashptr
+{
+	int bucket;
+	libtar_listptr_t node;
+};
+typedef struct libtar_hashptr libtar_hashptr_t;
+
+struct libtar_hash
+{
+	int numbuckets;
+	libtar_list_t **table;
+	libtar_hashfunc_t hashfunc;
+	unsigned int nents;
+};
+typedef struct libtar_hash libtar_hash_t;
+
+
+/* reset a hash pointer */
+void libtar_hashptr_reset(libtar_hashptr_t *);
+
+/* retrieve the data being pointed to */
+void *libtar_hashptr_data(libtar_hashptr_t *);
+
+/* default hash function, optimized for 7-bit strings */
+unsigned int libtar_str_hashfunc(char *, unsigned int);
+
+/* return number of elements from hash */
+unsigned int libtar_hash_nents(libtar_hash_t *);
+
+/* create a new hash */
+libtar_hash_t *libtar_hash_new(int, libtar_hashfunc_t);
+
+/* empty the hash */
+void libtar_hash_empty(libtar_hash_t *,
+				  libtar_freefunc_t);
+
+/* delete all the libtar_nodes of the hash and clean up */
+void libtar_hash_free(libtar_hash_t *,
+				 libtar_freefunc_t);
+
+/* returns 1 when valid data is returned, or 0 at end of list */
+int libtar_hash_next(libtar_hash_t *,
+				libtar_hashptr_t *);
+
+/* return 1 if the data matches a list entry, 0 otherwise */
+int libtar_hash_search(libtar_hash_t *,
+				  libtar_hashptr_t *, void *,
+				  libtar_matchfunc_t);
+
+/* return 1 if the key matches a list entry, 0 otherwise */
+int libtar_hash_getkey(libtar_hash_t *,
+				  libtar_hashptr_t *, void *,
+				  libtar_matchfunc_t);
+
+/* inserting data */
+int libtar_hash_add(libtar_hash_t *, void *);
+
+/* delete an entry */
+int libtar_hash_del(libtar_hash_t *,
+			       libtar_hashptr_t *);
+
+#endif /* ! libtar_LISTHASH_H */
+
diff --git a/libtar/output.c b/libtar/output.c
new file mode 100644
index 0000000..a2db929
--- /dev/null
+++ b/libtar/output.c
@@ -0,0 +1,134 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  output.c - libtar code to print out tar header blocks
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/param.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+
+#ifndef _POSIX_LOGIN_NAME_MAX
+# define _POSIX_LOGIN_NAME_MAX	9
+#endif
+
+
+void
+th_print(TAR *t)
+{
+	puts("\nPrinting tar header:");
+	printf("  name     = \"%.100s\"\n", t->th_buf.name);
+	printf("  mode     = \"%.8s\"\n", t->th_buf.mode);
+	printf("  uid      = \"%.8s\"\n", t->th_buf.uid);
+	printf("  gid      = \"%.8s\"\n", t->th_buf.gid);
+	printf("  size     = \"%.12s\"\n", t->th_buf.size);
+	printf("  mtime    = \"%.12s\"\n", t->th_buf.mtime);
+	printf("  chksum   = \"%.8s\"\n", t->th_buf.chksum);
+	printf("  typeflag = \'%c\'\n", t->th_buf.typeflag);
+	printf("  linkname = \"%.100s\"\n", t->th_buf.linkname);
+	printf("  magic    = \"%.6s\"\n", t->th_buf.magic);
+	/*printf("  version  = \"%.2s\"\n", t->th_buf.version); */
+	printf("  version[0] = \'%c\',version[1] = \'%c\'\n",
+	       t->th_buf.version[0], t->th_buf.version[1]);
+	printf("  uname    = \"%.32s\"\n", t->th_buf.uname);
+	printf("  gname    = \"%.32s\"\n", t->th_buf.gname);
+	printf("  devmajor = \"%.8s\"\n", t->th_buf.devmajor);
+	printf("  devminor = \"%.8s\"\n", t->th_buf.devminor);
+	printf("  prefix   = \"%.155s\"\n", t->th_buf.prefix);
+	printf("  padding  = \"%.12s\"\n", t->th_buf.padding);
+	printf("  gnu_longname = \"%s\"\n",
+	       (t->th_buf.gnu_longname ? t->th_buf.gnu_longname : "[NULL]"));
+	printf("  gnu_longlink = \"%s\"\n",
+	       (t->th_buf.gnu_longlink ? t->th_buf.gnu_longlink : "[NULL]"));
+}
+
+
+void
+th_print_long_ls(TAR *t)
+{
+	char modestring[12];
+	struct passwd *pw;
+	struct group *gr;
+	uid_t uid;
+	gid_t gid;
+	char username[_POSIX_LOGIN_NAME_MAX];
+	char groupname[_POSIX_LOGIN_NAME_MAX];
+	time_t mtime;
+	struct tm *mtm;
+
+#ifdef HAVE_STRFTIME
+	char timebuf[18];
+#else
+	const char *months[] = {
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+	};
+#endif
+
+	uid = th_get_uid(t);
+	pw = getpwuid(uid);
+	if (pw == NULL)
+		snprintf(username, sizeof(username), "%d", uid);
+	else
+		strlcpy(username, pw->pw_name, sizeof(username));
+
+	gid = th_get_gid(t);
+	gr = getgrgid(gid);
+	if (gr == NULL)
+		snprintf(groupname, sizeof(groupname), "%d", gid);
+	else
+		strlcpy(groupname, gr->gr_name, sizeof(groupname));
+
+	strmode(th_get_mode(t), modestring);
+	printf("%.10s %-8.8s %-8.8s ", modestring, username, groupname);
+
+	if (TH_ISCHR(t) || TH_ISBLK(t))
+		printf(" %3d, %3d ", th_get_devmajor(t), th_get_devminor(t));
+	else
+		printf("%9ld ", (long)th_get_size(t));
+
+	mtime = th_get_mtime(t);
+	mtm = localtime(&mtime);
+#ifdef HAVE_STRFTIME
+	strftime(timebuf, sizeof(timebuf), "%h %e %H:%M %Y", mtm);
+	printf("%s", timebuf);
+#else
+	printf("%.3s %2d %2d:%02d %4d",
+	       months[mtm->tm_mon],
+	       mtm->tm_mday, mtm->tm_hour, mtm->tm_min, mtm->tm_year + 1900);
+#endif
+
+	printf(" %s", th_get_pathname(t));
+
+	if (TH_ISSYM(t) || TH_ISLNK(t))
+	{
+		if (TH_ISSYM(t))
+			printf(" -> ");
+		else
+			printf(" link to ");
+		if ((t->options & TAR_GNU) && t->th_buf.gnu_longlink != NULL)
+			printf("%s", t->th_buf.gnu_longlink);
+		else
+			printf("%.100s", t->th_buf.linkname);
+	}
+
+	putchar('\n');
+}
+
+
diff --git a/libtar/snprintf.c b/libtar/snprintf.c
new file mode 100644
index 0000000..8c228d4
--- /dev/null
+++ b/libtar/snprintf.c
@@ -0,0 +1,761 @@
+/**************************************************************
+ * Original:
+ * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
+ * A bombproof version of doprnt (dopr) included.
+ * Sigh.  This sort of thing is always nasty do deal with.  Note that
+ * the version here does not include floating point...
+ *
+ * snprintf() is used instead of sprintf() as it does limit checks
+ * for string length.  This covers a nasty loophole.
+ *
+ * The other functions are there to prevent NULL pointers from
+ * causing nast effects.
+ *
+ * More Recently:
+ *  Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
+ *  This was ugly.  It is still ugly.  I opted out of floating point
+ *  numbers, but the formatter understands just about everything
+ *  from the normal C string format, at least as far as I can tell from
+ *  the Solaris 2.5 printf(3S) man page.
+ *
+ *  Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
+ *    Ok, added some minimal floating point support, which means this
+ *    probably requires libm on most operating systems.  Don't yet
+ *    support the exponent (e,E) and sigfig (g,G).  Also, fmtint()
+ *    was pretty badly broken, it just wasn't being exercised in ways
+ *    which showed it, so that's been fixed.  Also, formated the code
+ *    to mutt conventions, and removed dead code left over from the
+ *    original.  Also, there is now a builtin-test, just compile with:
+ *           gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
+ *    and run snprintf for results.
+ * 
+ *  Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
+ *    The PGP code was using unsigned hexadecimal formats. 
+ *    Unfortunately, unsigned formats simply didn't work.
+ *
+ *  Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
+ *    The original code assumed that both snprintf() and vsnprintf() were
+ *    missing.  Some systems only have snprintf() but not vsnprintf(), so
+ *    the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ *  Chris Frey <cdfrey@foursquare.net> 2012/09/05
+ *    Removed varargs.h and all dependency on it.
+ *
+ **************************************************************/
+
+#include <config.h>
+
+#if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
+
+#include <string.h>
+# include <ctype.h>
+#include <sys/types.h>
+
+#include <stdarg.h>
+#define VA_LOCAL_DECL   va_list ap
+#define VA_START(f)     va_start(ap, f)
+#define VA_SHIFT(v,t)  ;   /* no-op for ANSI */
+#define VA_END          va_end(ap)
+
+/*int snprintf (char *str, size_t count, const char *fmt, ...);*/
+/*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
+
+static void dopr (char *buffer, size_t maxlen, const char *format, 
+                  va_list args);
+static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+		    char *value, int flags, int min, int max);
+static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
+		    long value, int base, int min, int max, int flags);
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+		   long double fvalue, int min, int max, int flags);
+static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c );
+
+/*
+ * dopr(): poor man's version of doprintf
+ */
+
+/* format read states */
+#define DP_S_DEFAULT 0
+#define DP_S_FLAGS   1
+#define DP_S_MIN     2
+#define DP_S_DOT     3
+#define DP_S_MAX     4
+#define DP_S_MOD     5
+#define DP_S_CONV    6
+#define DP_S_DONE    7
+
+/* format flags - Bits */
+#define DP_F_MINUS 	(1 << 0)
+#define DP_F_PLUS  	(1 << 1)
+#define DP_F_SPACE 	(1 << 2)
+#define DP_F_NUM   	(1 << 3)
+#define DP_F_ZERO  	(1 << 4)
+#define DP_F_UP    	(1 << 5)
+#define DP_F_UNSIGNED 	(1 << 6)
+
+/* Conversion Flags */
+#define DP_C_SHORT   1
+#define DP_C_LONG    2
+#define DP_C_LDOUBLE 3
+
+#define char_to_int(p) (p - '0')
+#define MAX(p,q) ((p >= q) ? p : q)
+
+static void dopr (char *buffer, size_t maxlen, const char *format, va_list args)
+{
+  char ch;
+  long value;
+  long double fvalue;
+  char *strvalue;
+  int min;
+  int max;
+  int state;
+  int flags;
+  int cflags;
+  size_t currlen;
+  
+  state = DP_S_DEFAULT;
+  currlen = flags = cflags = min = 0;
+  max = -1;
+  ch = *format++;
+
+  while (state != DP_S_DONE)
+  {
+    if ((ch == '\0') || (currlen >= maxlen)) 
+      state = DP_S_DONE;
+
+    switch(state) 
+    {
+    case DP_S_DEFAULT:
+      if (ch == '%') 
+	state = DP_S_FLAGS;
+      else 
+	dopr_outch (buffer, &currlen, maxlen, ch);
+      ch = *format++;
+      break;
+    case DP_S_FLAGS:
+      switch (ch) 
+      {
+      case '-':
+	flags |= DP_F_MINUS;
+        ch = *format++;
+	break;
+      case '+':
+	flags |= DP_F_PLUS;
+        ch = *format++;
+	break;
+      case ' ':
+	flags |= DP_F_SPACE;
+        ch = *format++;
+	break;
+      case '#':
+	flags |= DP_F_NUM;
+        ch = *format++;
+	break;
+      case '0':
+	flags |= DP_F_ZERO;
+        ch = *format++;
+	break;
+      default:
+	state = DP_S_MIN;
+	break;
+      }
+      break;
+    case DP_S_MIN:
+      if (isdigit((unsigned char)ch)) 
+      {
+	min = 10*min + char_to_int (ch);
+	ch = *format++;
+      } 
+      else if (ch == '*') 
+      {
+	min = va_arg (args, int);
+	ch = *format++;
+	state = DP_S_DOT;
+      } 
+      else 
+	state = DP_S_DOT;
+      break;
+    case DP_S_DOT:
+      if (ch == '.') 
+      {
+	state = DP_S_MAX;
+	ch = *format++;
+      } 
+      else 
+	state = DP_S_MOD;
+      break;
+    case DP_S_MAX:
+      if (isdigit((unsigned char)ch)) 
+      {
+	if (max < 0)
+	  max = 0;
+	max = 10*max + char_to_int (ch);
+	ch = *format++;
+      } 
+      else if (ch == '*') 
+      {
+	max = va_arg (args, int);
+	ch = *format++;
+	state = DP_S_MOD;
+      } 
+      else 
+	state = DP_S_MOD;
+      break;
+    case DP_S_MOD:
+      /* Currently, we don't support Long Long, bummer */
+      switch (ch) 
+      {
+      case 'h':
+	cflags = DP_C_SHORT;
+	ch = *format++;
+	break;
+      case 'l':
+	cflags = DP_C_LONG;
+	ch = *format++;
+	break;
+      case 'L':
+	cflags = DP_C_LDOUBLE;
+	ch = *format++;
+	break;
+      default:
+	break;
+      }
+      state = DP_S_CONV;
+      break;
+    case DP_S_CONV:
+      switch (ch) 
+      {
+      case 'd':
+      case 'i':
+	if (cflags == DP_C_SHORT) 
+	  value = va_arg (args, int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, long int);
+	else
+	  value = va_arg (args, int);
+	fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+	break;
+      case 'o':
+	flags |= DP_F_UNSIGNED;
+	if (cflags == DP_C_SHORT)
+	  value = va_arg (args, unsigned int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, unsigned long int);
+	else
+	  value = va_arg (args, unsigned int);
+	fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
+	break;
+      case 'u':
+	flags |= DP_F_UNSIGNED;
+	if (cflags == DP_C_SHORT)
+	  value = va_arg (args, unsigned int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, unsigned long int);
+	else
+	  value = va_arg (args, unsigned int);
+	fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
+	break;
+      case 'X':
+	flags |= DP_F_UP;
+      case 'x':
+	flags |= DP_F_UNSIGNED;
+	if (cflags == DP_C_SHORT)
+	  value = va_arg (args, unsigned int);
+	else if (cflags == DP_C_LONG)
+	  value = va_arg (args, unsigned long int);
+	else
+	  value = va_arg (args, unsigned int);
+	fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
+	break;
+      case 'f':
+	if (cflags == DP_C_LDOUBLE)
+	  fvalue = va_arg (args, long double);
+	else
+	  fvalue = va_arg (args, double);
+	/* um, floating point? */
+	fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
+	break;
+      case 'E':
+	flags |= DP_F_UP;
+      case 'e':
+	if (cflags == DP_C_LDOUBLE)
+	  fvalue = va_arg (args, long double);
+	else
+	  fvalue = va_arg (args, double);
+	break;
+      case 'G':
+	flags |= DP_F_UP;
+      case 'g':
+	if (cflags == DP_C_LDOUBLE)
+	  fvalue = va_arg (args, long double);
+	else
+	  fvalue = va_arg (args, double);
+	break;
+      case 'c':
+	dopr_outch (buffer, &currlen, maxlen, va_arg (args, int));
+	break;
+      case 's':
+	strvalue = va_arg (args, char *);
+	if (max < 0) 
+	  max = maxlen; /* ie, no max */
+	fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
+	break;
+      case 'p':
+	strvalue = va_arg (args, void *);
+	fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
+	break;
+      case 'n':
+	if (cflags == DP_C_SHORT) 
+	{
+	  short int *num;
+	  num = va_arg (args, short int *);
+	  *num = currlen;
+        } 
+	else if (cflags == DP_C_LONG) 
+	{
+	  long int *num;
+	  num = va_arg (args, long int *);
+	  *num = currlen;
+        } 
+	else 
+	{
+	  int *num;
+	  num = va_arg (args, int *);
+	  *num = currlen;
+        }
+	break;
+      case '%':
+	dopr_outch (buffer, &currlen, maxlen, ch);
+	break;
+      case 'w':
+	/* not supported yet, treat as next char */
+	ch = *format++;
+	break;
+      default:
+	/* Unknown, skip */
+	break;
+      }
+      ch = *format++;
+      state = DP_S_DEFAULT;
+      flags = cflags = min = 0;
+      max = -1;
+      break;
+    case DP_S_DONE:
+      break;
+    default:
+      /* hmm? */
+      break; /* some picky compilers need this */
+    }
+  }
+  if (currlen < maxlen - 1) 
+    buffer[currlen] = '\0';
+  else 
+    buffer[maxlen - 1] = '\0';
+}
+
+static void fmtstr (char *buffer, size_t *currlen, size_t maxlen,
+		    char *value, int flags, int min, int max)
+{
+  int padlen, strln;     /* amount to pad */
+  int cnt = 0;
+  
+  if (value == 0)
+  {
+    value = "<NULL>";
+  }
+
+  for (strln = 0; value[strln]; ++strln); /* strlen */
+  padlen = min - strln;
+  if (padlen < 0) 
+    padlen = 0;
+  if (flags & DP_F_MINUS) 
+    padlen = -padlen; /* Left Justify */
+
+  while ((padlen > 0) && (cnt < max)) 
+  {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    --padlen;
+    ++cnt;
+  }
+  while (*value && (cnt < max)) 
+  {
+    dopr_outch (buffer, currlen, maxlen, *value++);
+    ++cnt;
+  }
+  while ((padlen < 0) && (cnt < max)) 
+  {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    ++padlen;
+    ++cnt;
+  }
+}
+
+/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */
+
+static void fmtint (char *buffer, size_t *currlen, size_t maxlen,
+		    long value, int base, int min, int max, int flags)
+{
+  int signvalue = 0;
+  unsigned long uvalue;
+  char convert[20];
+  int place = 0;
+  int spadlen = 0; /* amount to space pad */
+  int zpadlen = 0; /* amount to zero pad */
+  int caps = 0;
+  
+  if (max < 0)
+    max = 0;
+
+  uvalue = value;
+
+  if(!(flags & DP_F_UNSIGNED))
+  {
+    if( value < 0 ) {
+      signvalue = '-';
+      uvalue = -value;
+    }
+    else
+      if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
+	signvalue = '+';
+    else
+      if (flags & DP_F_SPACE)
+	signvalue = ' ';
+  }
+  
+  if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+
+  do {
+    convert[place++] =
+      (caps? "0123456789ABCDEF":"0123456789abcdef")
+      [uvalue % (unsigned)base  ];
+    uvalue = (uvalue / (unsigned)base );
+  } while(uvalue && (place < 20));
+  if (place == 20) place--;
+  convert[place] = 0;
+
+  zpadlen = max - place;
+  spadlen = min - MAX (max, place) - (signvalue ? 1 : 0);
+  if (zpadlen < 0) zpadlen = 0;
+  if (spadlen < 0) spadlen = 0;
+  if (flags & DP_F_ZERO)
+  {
+    zpadlen = MAX(zpadlen, spadlen);
+    spadlen = 0;
+  }
+  if (flags & DP_F_MINUS) 
+    spadlen = -spadlen; /* Left Justifty */
+
+#ifdef DEBUG_SNPRINTF
+  dprint (1, (debugfile, "zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
+      zpadlen, spadlen, min, max, place));
+#endif
+
+  /* Spaces */
+  while (spadlen > 0) 
+  {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    --spadlen;
+  }
+
+  /* Sign */
+  if (signvalue) 
+    dopr_outch (buffer, currlen, maxlen, signvalue);
+
+  /* Zeros */
+  if (zpadlen > 0) 
+  {
+    while (zpadlen > 0)
+    {
+      dopr_outch (buffer, currlen, maxlen, '0');
+      --zpadlen;
+    }
+  }
+
+  /* Digits */
+  while (place > 0) 
+    dopr_outch (buffer, currlen, maxlen, convert[--place]);
+  
+  /* Left Justified spaces */
+  while (spadlen < 0) {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    ++spadlen;
+  }
+}
+
+static long double abs_val (long double value)
+{
+  long double result = value;
+
+  if (value < 0)
+    result = -value;
+
+  return result;
+}
+
+static long double pow10_ (int exp)
+{
+  long double result = 1;
+
+  while (exp)
+  {
+    result *= 10;
+    exp--;
+  }
+  
+  return result;
+}
+
+static long round_ (long double value)
+{
+  long intpart;
+
+  intpart = value;
+  value = value - intpart;
+  if (value >= 0.5)
+    intpart++;
+
+  return intpart;
+}
+
+static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
+		   long double fvalue, int min, int max, int flags)
+{
+  int signvalue = 0;
+  long double ufvalue;
+  char iconvert[20];
+  char fconvert[20];
+  int iplace = 0;
+  int fplace = 0;
+  int padlen = 0; /* amount to pad */
+  int zpadlen = 0; 
+  int caps = 0;
+  long intpart;
+  long fracpart;
+  
+  /* 
+   * AIX manpage says the default is 0, but Solaris says the default
+   * is 6, and sprintf on AIX defaults to 6
+   */
+  if (max < 0)
+    max = 6;
+
+  ufvalue = abs_val (fvalue);
+
+  if (fvalue < 0)
+    signvalue = '-';
+  else
+    if (flags & DP_F_PLUS)  /* Do a sign (+/i) */
+      signvalue = '+';
+    else
+      if (flags & DP_F_SPACE)
+	signvalue = ' ';
+
+#if 0
+  if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
+#endif
+
+  intpart = ufvalue;
+
+  /* 
+   * Sorry, we only support 9 digits past the decimal because of our 
+   * conversion method
+   */
+  if (max > 9)
+    max = 9;
+
+  /* We "cheat" by converting the fractional part to integer by
+   * multiplying by a factor of 10
+   */
+  fracpart = round_ ((pow10_ (max)) * (ufvalue - intpart));
+
+  if (fracpart >= pow10_ (max))
+  {
+    intpart++;
+    fracpart -= pow10_ (max);
+  }
+
+#ifdef DEBUG_SNPRINTF
+  dprint (1, (debugfile, "fmtfp: %f =? %d.%d\n", fvalue, intpart, fracpart));
+#endif
+
+  /* Convert integer part */
+  do {
+    iconvert[iplace++] =
+      (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
+    intpart = (intpart / 10);
+  } while(intpart && (iplace < 20));
+  if (iplace == 20) iplace--;
+  iconvert[iplace] = 0;
+
+  /* Convert fractional part */
+  do {
+    fconvert[fplace++] =
+      (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
+    fracpart = (fracpart / 10);
+  } while(fracpart && (fplace < 20));
+  if (fplace == 20) fplace--;
+  fconvert[fplace] = 0;
+
+  /* -1 for decimal point, another -1 if we are printing a sign */
+  padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
+  zpadlen = max - fplace;
+  if (zpadlen < 0)
+    zpadlen = 0;
+  if (padlen < 0) 
+    padlen = 0;
+  if (flags & DP_F_MINUS) 
+    padlen = -padlen; /* Left Justifty */
+
+  if ((flags & DP_F_ZERO) && (padlen > 0)) 
+  {
+    if (signvalue) 
+    {
+      dopr_outch (buffer, currlen, maxlen, signvalue);
+      --padlen;
+      signvalue = 0;
+    }
+    while (padlen > 0)
+    {
+      dopr_outch (buffer, currlen, maxlen, '0');
+      --padlen;
+    }
+  }
+  while (padlen > 0)
+  {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    --padlen;
+  }
+  if (signvalue) 
+    dopr_outch (buffer, currlen, maxlen, signvalue);
+
+  while (iplace > 0) 
+    dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);
+
+  /*
+   * Decimal point.  This should probably use locale to find the correct
+   * char to print out.
+   */
+  dopr_outch (buffer, currlen, maxlen, '.');
+
+  while (fplace > 0) 
+    dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
+
+  while (zpadlen > 0)
+  {
+    dopr_outch (buffer, currlen, maxlen, '0');
+    --zpadlen;
+  }
+
+  while (padlen < 0) 
+  {
+    dopr_outch (buffer, currlen, maxlen, ' ');
+    ++padlen;
+  }
+}
+
+static void dopr_outch (char *buffer, size_t *currlen, size_t maxlen, char c)
+{
+  if (*currlen < maxlen)
+    buffer[(*currlen)++] = c;
+}
+#endif /* !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) */
+
+#ifndef HAVE_VSNPRINTF
+int mutt_vsnprintf (char *str, size_t count, const char *fmt, va_list args)
+{
+  str[0] = 0;
+  dopr(str, count, fmt, args);
+  return(strlen(str));
+}
+#endif /* !HAVE_VSNPRINTF */
+
+#ifndef HAVE_SNPRINTF
+int mutt_snprintf (char *str,size_t count,const char *fmt,...)
+{
+  VA_LOCAL_DECL;
+
+  VA_START (fmt);
+  VA_SHIFT (str, char *);
+  VA_SHIFT (count, size_t );
+  VA_SHIFT (fmt, char *);
+  (void) mutt_vsnprintf(str, count, fmt, ap);
+  VA_END;
+  return(strlen(str));
+}
+
+#ifdef TEST_SNPRINTF
+#ifndef LONG_STRING
+#define LONG_STRING 1024
+#endif
+int main (void)
+{
+  char buf1[LONG_STRING];
+  char buf2[LONG_STRING];
+  char *fp_fmt[] = {
+    "%-1.5f",
+    "%1.5f",
+    "%123.9f",
+    "%10.5f",
+    "% 10.5f",
+    "%+22.9f",
+    "%+4.9f",
+    "%01.3f",
+    "%4f",
+    "%3.1f",
+    "%3.2f",
+    NULL
+  };
+  double fp_nums[] = { -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, 
+    0.9996, 1.996, 4.136, 0};
+  char *int_fmt[] = {
+    "%-1.5d",
+    "%1.5d",
+    "%123.9d",
+    "%5.5d",
+    "%10.5d",
+    "% 10.5d",
+    "%+22.33d",
+    "%01.3d",
+    "%4d",
+    NULL
+  };
+  long int_nums[] = { -1, 134, 91340, 341, 0203, 0};
+  int x, y;
+  int fail = 0;
+  int num = 0;
+
+  printf ("Testing snprintf format codes against system sprintf...\n");
+
+  for (x = 0; fp_fmt[x] != NULL ; x++)
+    for (y = 0; fp_nums[y] != 0 ; y++)
+    {
+      mutt_snprintf (buf1, sizeof (buf1), fp_fmt[x], fp_nums[y]);
+      sprintf (buf2, fp_fmt[x], fp_nums[y]);
+      if (strcmp (buf1, buf2))
+      {
+	printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
+	    fp_fmt[x], buf1, buf2);
+	fail++;
+      }
+      num++;
+    }
+
+  for (x = 0; int_fmt[x] != NULL ; x++)
+    for (y = 0; int_nums[y] != 0 ; y++)
+    {
+      mutt_snprintf (buf1, sizeof (buf1), int_fmt[x], int_nums[y]);
+      sprintf (buf2, int_fmt[x], int_nums[y]);
+      if (strcmp (buf1, buf2))
+      {
+	printf("snprintf doesn't match Format: %s\n\tsnprintf = %s\n\tsprintf  = %s\n", 
+	    int_fmt[x], buf1, buf2);
+	fail++;
+      }
+      num++;
+    }
+  printf ("%d tests failed out of %d.\n", fail, num);
+}
+#endif /* SNPRINTF_TEST */
+
+#endif /* !HAVE_SNPRINTF */
diff --git a/libtar/strdup.c b/libtar/strdup.c
new file mode 100644
index 0000000..75e9af5
--- /dev/null
+++ b/libtar/strdup.c
@@ -0,0 +1,62 @@
+/*	$OpenBSD: strdup.c,v 1.3 1997/08/20 04:18:52 millert Exp $	*/
+
+/*
+ * Copyright (c) 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)strdup.c	8.1 (Berkeley) 6/4/93";
+#else
+static char *rcsid = "$OpenBSD: strdup.c,v 1.3 1997/08/20 04:18:52 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+char *
+openbsd_strdup(str)
+	const char *str;
+{
+	size_t siz;
+	char *copy;
+
+	siz = strlen(str) + 1;
+	if ((copy = malloc(siz)) == NULL)
+		return(NULL);
+	(void)memcpy(copy, str, siz);
+	return(copy);
+}
diff --git a/libtar/strlcat.c b/libtar/strlcat.c
new file mode 100644
index 0000000..3912539
--- /dev/null
+++ b/libtar/strlcat.c
@@ -0,0 +1,72 @@
+/*	$OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcat.c,v 1.5 2001/01/13 16:17:24 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Appends src to string dst of size siz (unlike strncat, siz is the
+ * full size of dst, not space left).  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
+ * Returns strlen(initial dst) + strlen(src); if retval >= siz,
+ * truncation occurred.
+ */
+size_t strlcat(dst, src, siz)
+	char *dst;
+	const char *src;
+	size_t siz;
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+	size_t dlen;
+
+	/* Find the end of dst and adjust bytes left but don't go past end */
+	while (n-- != 0 && *d != '\0')
+		d++;
+	dlen = d - dst;
+	n = siz - dlen;
+
+	if (n == 0)
+		return(dlen + strlen(s));
+	while (*s != '\0') {
+		if (n != 1) {
+			*d++ = *s;
+			n--;
+		}
+		s++;
+	}
+	*d = '\0';
+
+	return(dlen + (s - src));	/* count does not include NUL */
+}
diff --git a/libtar/strlcpy.c b/libtar/strlcpy.c
new file mode 100644
index 0000000..300a28b
--- /dev/null
+++ b/libtar/strlcpy.c
@@ -0,0 +1,68 @@
+/*	$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $	*/
+
+/*
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <string.h>
+
+/*
+ * Copy src to string dst of size siz.  At most siz-1 characters
+ * will be copied.  Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+size_t strlcpy(dst, src, siz)
+	char *dst;
+	const char *src;
+	size_t siz;
+{
+	register char *d = dst;
+	register const char *s = src;
+	register size_t n = siz;
+
+	/* Copy as many bytes as will fit */
+	if (n != 0 && --n != 0) {
+		do {
+			if ((*d++ = *s++) == 0)
+				break;
+		} while (--n != 0);
+	}
+
+	/* Not enough room in dst, add NUL and traverse rest of src */
+	if (n == 0) {
+		if (siz != 0)
+			*d = '\0';		/* NUL-terminate dst */
+		while (*s++)
+			;
+	}
+
+	return(s - src - 1);	/* count does not include NUL */
+}
diff --git a/libtar/strmode.c b/libtar/strmode.c
new file mode 100644
index 0000000..5e7f15e
--- /dev/null
+++ b/libtar/strmode.c
@@ -0,0 +1,152 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: strmode.c,v 1.3 1997/06/13 13:57:20 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+void
+strmode(mode, p)
+	register mode_t mode;
+	register char *p;
+{
+	 /* print type */
+	switch (mode & S_IFMT) {
+	case S_IFDIR:			/* directory */
+		*p++ = 'd';
+		break;
+	case S_IFCHR:			/* character special */
+		*p++ = 'c';
+		break;
+	case S_IFBLK:			/* block special */
+		*p++ = 'b';
+		break;
+	case S_IFREG:			/* regular */
+		*p++ = '-';
+		break;
+	case S_IFLNK:			/* symbolic link */
+		*p++ = 'l';
+		break;
+	case S_IFSOCK:			/* socket */
+		*p++ = 's';
+		break;
+#ifdef S_IFIFO
+	case S_IFIFO:			/* fifo */
+		*p++ = 'p';
+		break;
+#endif
+#ifdef S_IFWHT
+	case S_IFWHT:			/* whiteout */
+		*p++ = 'w';
+		break;
+#endif
+	default:			/* unknown */
+		*p++ = '?';
+		break;
+	}
+	/* usr */
+	if (mode & S_IRUSR)
+		*p++ = 'r';
+	else
+		*p++ = '-';
+	if (mode & S_IWUSR)
+		*p++ = 'w';
+	else
+		*p++ = '-';
+	switch (mode & (S_IXUSR | S_ISUID)) {
+	case 0:
+		*p++ = '-';
+		break;
+	case S_IXUSR:
+		*p++ = 'x';
+		break;
+	case S_ISUID:
+		*p++ = 'S';
+		break;
+	case S_IXUSR | S_ISUID:
+		*p++ = 's';
+		break;
+	}
+	/* group */
+	if (mode & S_IRGRP)
+		*p++ = 'r';
+	else
+		*p++ = '-';
+	if (mode & S_IWGRP)
+		*p++ = 'w';
+	else
+		*p++ = '-';
+	switch (mode & (S_IXGRP | S_ISGID)) {
+	case 0:
+		*p++ = '-';
+		break;
+	case S_IXGRP:
+		*p++ = 'x';
+		break;
+	case S_ISGID:
+		*p++ = 'S';
+		break;
+	case S_IXGRP | S_ISGID:
+		*p++ = 's';
+		break;
+	}
+	/* other */
+	if (mode & S_IROTH)
+		*p++ = 'r';
+	else
+		*p++ = '-';
+	if (mode & S_IWOTH)
+		*p++ = 'w';
+	else
+		*p++ = '-';
+	switch (mode & (S_IXOTH | S_ISVTX)) {
+	case 0:
+		*p++ = '-';
+		break;
+	case S_IXOTH:
+		*p++ = 'x';
+		break;
+	case S_ISVTX:
+		*p++ = 'T';
+		break;
+	case S_IXOTH | S_ISVTX:
+		*p++ = 't';
+		break;
+	}
+	*p++ = ' ';		/* will be a '+' if ACL's implemented */
+	*p = '\0';
+}
diff --git a/libtar/strrstr.c b/libtar/strrstr.c
new file mode 100644
index 0000000..097ef91
--- /dev/null
+++ b/libtar/strrstr.c
@@ -0,0 +1,40 @@
+/*
+**  Copyright 1998-2002 University of Illinois Board of Trustees
+**  Copyright 1998-2002 Mark D. Roth
+**  All rights reserved.
+**
+**  strrstr.c - strrstr() function for compatibility library
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <string.h>
+
+
+/*
+** find the last occurrance of find in string
+*/
+char *
+strrstr(char *string, char *find)
+{
+	size_t stringlen, findlen;
+	char *cp;
+
+	findlen = strlen(find);
+	stringlen = strlen(string);
+	if (findlen > stringlen)
+		return NULL;
+
+	for (cp = string + stringlen - findlen; cp >= string; cp--)
+		if (strncmp(cp, find, findlen) == 0)
+			return cp;
+
+	return NULL;
+}
+
+
diff --git a/libtar/strsep.c b/libtar/strsep.c
new file mode 100644
index 0000000..3132962
--- /dev/null
+++ b/libtar/strsep.c
@@ -0,0 +1,87 @@
+/*	$OpenBSD: strsep.c,v 1.3 1997/08/20 04:28:14 millert Exp $	*/
+
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)strsep.c	8.1 (Berkeley) 6/4/93";
+#else
+static char *rcsid = "$OpenBSD: strsep.c,v 1.3 1997/08/20 04:28:14 millert Exp $";
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#ifndef HAVE_STRSEP
+/*
+ * Get next token from string *stringp, where tokens are possibly-empty
+ * strings separated by characters from delim.  
+ *
+ * Writes NULs into the string at *stringp to end tokens.
+ * delim need not remain constant from call to call.
+ * On return, *stringp points past the last NUL written (if there might
+ * be further tokens), or is NULL (if there are definitely no more tokens).
+ *
+ * If *stringp is NULL, strsep returns NULL.
+ */
+char *
+strsep(stringp, delim)
+	register char **stringp;
+	register const char *delim;
+{
+	register char *s;
+	register const char *spanp;
+	register int c, sc;
+	char *tok;
+
+	if ((s = *stringp) == NULL)
+		return (NULL);
+	for (tok = s;;) {
+		c = *s++;
+		spanp = delim;
+		do {
+			if ((sc = *spanp++) == c) {
+				if (c == 0)
+					s = NULL;
+				else
+					s[-1] = 0;
+				*stringp = s;
+				return (tok);
+			}
+		} while (sc != 0);
+	}
+	/* NOTREACHED */
+}
+#endif /* ! HAVE_STRSEP */
diff --git a/libtar/tar.h b/libtar/tar.h
new file mode 100644
index 0000000..ddfef75
--- /dev/null
+++ b/libtar/tar.h
@@ -0,0 +1,108 @@
+/* Extended tar format from POSIX.1.
+   Copyright (C) 1992, 1996 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Written by David J. MacKenzie.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef	_TAR_H
+#define	_TAR_H	1
+
+/* A tar archive consists of 512-byte blocks.
+   Each file in the archive has a header block followed by 0+ data blocks.
+   Two blocks of NUL bytes indicate the end of the archive.  */
+
+/* The fields of header blocks:
+   All strings are stored as ISO 646 (approximately ASCII) strings.
+
+   Fields are numeric unless otherwise noted below; numbers are ISO 646
+   representations of octal numbers, with leading zeros as needed.
+
+   linkname is only valid when typeflag==LNKTYPE.  It doesn't use prefix;
+   files that are links to pathnames >100 chars long can not be stored
+   in a tar archive.
+
+   If typeflag=={LNKTYPE,SYMTYPE,DIRTYPE} then size must be 0.
+
+   devmajor and devminor are only valid for typeflag=={BLKTYPE,CHRTYPE}.
+
+   chksum contains the sum of all 512 bytes in the header block,
+   treating each byte as an 8-bit unsigned value and treating the
+   8 bytes of chksum as blank characters.
+
+   uname and gname are used in preference to uid and gid, if those
+   names exist locally.
+
+   Field Name	Byte Offset	Length in Bytes	Field Type
+   name		0		100		NUL-terminated if NUL fits
+   mode		100		8
+   uid		108		8
+   gid		116		8
+   size		124		12
+   mtime	136		12
+   chksum	148		8
+   typeflag	156		1		see below
+   linkname	157		100		NUL-terminated if NUL fits
+   magic	257		6		must be TMAGIC (NUL term.)
+   version	263		2		must be TVERSION
+   uname	265		32		NUL-terminated
+   gname	297		32		NUL-terminated
+   devmajor	329		8
+   devminor	337		8
+   prefix	345		155		NUL-terminated if NUL fits
+
+   If the first character of prefix is '\0', the file name is name;
+   otherwise, it is prefix/name.  Files whose pathnames don't fit in that
+   length can not be stored in a tar archive.  */
+
+/* The bits in mode: */
+#define TSUID	04000
+#define TSGID	02000
+#define TSVTX	01000
+#define TUREAD	00400
+#define TUWRITE	00200
+#define TUEXEC	00100
+#define TGREAD	00040
+#define TGWRITE	00020
+#define TGEXEC	00010
+#define TOREAD	00004
+#define TOWRITE	00002
+#define TOEXEC	00001
+
+/* The values for typeflag:
+   Values 'A'-'Z' are reserved for custom implementations.
+   All other values are reserved for future POSIX.1 revisions.  */
+
+#define REGTYPE		'0'	/* Regular file (preferred code).  */
+#define AREGTYPE	'\0'	/* Regular file (alternate code).  */
+#define LNKTYPE		'1'	/* Hard link.  */
+#define SYMTYPE		'2'	/* Symbolic link (hard if not supported).  */
+#define CHRTYPE		'3'	/* Character special.  */
+#define BLKTYPE		'4'	/* Block special.  */
+#define DIRTYPE		'5'	/* Directory.  */
+#define FIFOTYPE	'6'	/* Named pipe.  */
+#define CONTTYPE	'7'	/* Contiguous file */
+ /* (regular file if not supported).  */
+
+/* Contents of magic field and its length.  */
+#define TMAGIC	"ustar"
+#define TMAGLEN	6
+
+/* Contents of the version field and its length.  */
+#define TVERSION	"00"
+#define TVERSLEN	2
+
+#endif /* tar.h */
diff --git a/libtar/util.c b/libtar/util.c
new file mode 100644
index 0000000..31e8315
--- /dev/null
+++ b/libtar/util.c
@@ -0,0 +1,165 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  util.c - miscellaneous utility code for libtar
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#include <internal.h>
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+
+/* hashing function for pathnames */
+int
+path_hashfunc(char *key, int numbuckets)
+{
+	char buf[MAXPATHLEN];
+	char *p;
+
+	strcpy(buf, key);
+	p = basename(buf);
+
+	return (((unsigned int)p[0]) % numbuckets);
+}
+
+
+/* matching function for dev_t's */
+int
+dev_match(dev_t *dev1, dev_t *dev2)
+{
+	return !memcmp(dev1, dev2, sizeof(dev_t));
+}
+
+
+/* matching function for ino_t's */
+int
+ino_match(ino_t *ino1, ino_t *ino2)
+{
+	return !memcmp(ino1, ino2, sizeof(ino_t));
+}
+
+
+/* hashing function for dev_t's */
+int
+dev_hash(dev_t *dev)
+{
+	return *dev % 16;
+}
+
+
+/* hashing function for ino_t's */
+int
+ino_hash(ino_t *inode)
+{
+	return *inode % 256;
+}
+
+
+/*
+** mkdirhier() - create all directories in a given path
+** returns:
+**	0			success
+**	1			all directories already exist
+**	-1 (and sets errno)	error
+*/
+int
+mkdirhier(char *path)
+{
+	char src[MAXPATHLEN], dst[MAXPATHLEN] = "";
+	char *dirp, *nextp = src;
+	int retval = 1;
+
+	if (strlcpy(src, path, sizeof(src)) > sizeof(src))
+	{
+		errno = ENAMETOOLONG;
+		return -1;
+	}
+
+	if (path[0] == '/')
+		strcpy(dst, "/");
+
+	while ((dirp = strsep(&nextp, "/")) != NULL)
+	{
+		if (*dirp == '\0')
+			continue;
+
+		if (dst[0] != '\0')
+			strcat(dst, "/");
+		strcat(dst, dirp);
+
+		if (mkdir(dst, 0777) == -1)
+		{
+			if (errno != EEXIST)
+				return -1;
+		}
+		else
+			retval = 0;
+	}
+
+	return retval;
+}
+
+
+/* calculate header checksum */
+int
+th_crc_calc(TAR *t)
+{
+	int i, sum = 0;
+
+	for (i = 0; i < T_BLOCKSIZE; i++)
+		sum += ((unsigned char *)(&(t->th_buf)))[i];
+	for (i = 0; i < 8; i++)
+		sum += (' ' - (unsigned char)t->th_buf.chksum[i]);
+
+	return sum;
+}
+
+
+/* calculate a signed header checksum */
+int
+th_signed_crc_calc(TAR *t)
+{
+	int i, sum = 0;
+
+	for (i = 0; i < T_BLOCKSIZE; i++)
+		sum += ((signed char *)(&(t->th_buf)))[i];
+	for (i = 0; i < 8; i++)
+		sum += (' ' - (signed char)t->th_buf.chksum[i]);
+
+	return sum;
+}
+
+
+/* string-octal to integer conversion */
+int
+oct_to_int(char *oct)
+{
+	int i;
+
+	sscanf(oct, "%o", &i);
+
+	return i;
+}
+
+
+/* integer to string-octal conversion, no NULL */
+void
+int_to_oct_nonull(int num, char *oct, size_t octlen)
+{
+	snprintf(oct, octlen, "%*lo", octlen - 1, (unsigned long)num);
+	oct[octlen - 1] = ' ';
+}
+
+
diff --git a/libtar/wrapper.c b/libtar/wrapper.c
new file mode 100644
index 0000000..7cd8ed1
--- /dev/null
+++ b/libtar/wrapper.c
@@ -0,0 +1,155 @@
+/*
+**  Copyright 1998-2003 University of Illinois Board of Trustees
+**  Copyright 1998-2003 Mark D. Roth
+**  All rights reserved.
+**
+**  wrapper.c - libtar high-level wrapper code
+**
+**  Mark D. Roth <roth@uiuc.edu>
+**  Campus Information Technologies and Educational Services
+**  University of Illinois at Urbana-Champaign
+*/
+
+#define DEBUG
+#include <internal.h>
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <errno.h>
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+int
+tar_extract_glob(TAR *t, char *globname, char *prefix)
+{
+	char *filename;
+	char buf[MAXPATHLEN];
+	int i;
+
+	while ((i = th_read(t)) == 0)
+	{
+		filename = th_get_pathname(t);
+		if (fnmatch(globname, filename, FNM_PATHNAME | FNM_PERIOD))
+		{
+			if (TH_ISREG(t) && tar_skip_regfile(t))
+				return -1;
+			continue;
+		}
+		if (t->options & TAR_VERBOSE)
+			th_print_long_ls(t);
+		if (prefix != NULL)
+			snprintf(buf, sizeof(buf), "%s/%s", prefix, filename);
+		else
+			strlcpy(buf, filename, sizeof(buf));
+		if (tar_extract_file(t, filename) != 0)
+			return -1;
+	}
+
+	return (i == 1 ? 0 : -1);
+}
+
+
+int
+tar_extract_all(TAR *t, char *prefix)
+{
+	char *filename;
+	char buf[MAXPATHLEN];
+	int i;
+	printf("prefix: %s\n", prefix);
+#ifdef DEBUG
+	printf("==> tar_extract_all(TAR *t, \"%s\")\n",
+	       (prefix ? prefix : "(null)"));
+#endif
+	while ((i = th_read(t)) == 0)
+	{
+#ifdef DEBUG
+		puts("    tar_extract_all(): calling th_get_pathname()");
+#endif
+		filename = th_get_pathname(t);
+		if (t->options & TAR_VERBOSE)
+			th_print_long_ls(t);
+		if (prefix != NULL)
+			snprintf(buf, sizeof(buf), "%s/%s", prefix, filename);
+		else
+			strlcpy(buf, filename, sizeof(buf));
+#ifdef DEBUG
+		printf("    tar_extract_all(): calling tar_extract_file(t, "
+		       "\"%s\")\n", buf);
+#endif
+		printf("filename: %s\n", filename);
+		/*
+		if (strcmp(filename, "/") == 0) {
+			printf("skipping /\n");
+			continue;
+		}
+		*/
+		if (tar_extract_file(t, buf) != 0)  
+			return -1;
+	}
+	return (i == 1 ? 0 : -1);
+}
+
+
+int
+tar_append_tree(TAR *t, char *realdir, char *savedir)
+{
+	char realpath[MAXPATHLEN];
+	char savepath[MAXPATHLEN];
+	struct dirent *dent;
+	DIR *dp;
+	struct stat s;
+
+#ifdef DEBUG
+	printf("==> tar_append_tree(0x%lx, \"%s\", \"%s\")\n",
+	       t, realdir, (savedir ? savedir : "[NULL]"));
+#endif
+
+	if (tar_append_file(t, realdir, savedir) != 0)
+		return -1;
+
+#ifdef DEBUG
+	puts("    tar_append_tree(): done with tar_append_file()...");
+#endif
+
+	dp = opendir(realdir);
+	if (dp == NULL)
+	{
+		if (errno == ENOTDIR)
+			return 0;
+		return -1;
+	}
+	while ((dent = readdir(dp)) != NULL)
+	{
+		if (strcmp(dent->d_name, ".") == 0 ||
+		    strcmp(dent->d_name, "..") == 0)
+			continue;
+
+		snprintf(realpath, MAXPATHLEN, "%s/%s", realdir,
+			 dent->d_name);
+		if (savedir)
+			snprintf(savepath, MAXPATHLEN, "%s/%s", savedir,
+				 dent->d_name);
+
+		if (lstat(realpath, &s) != 0)
+			return -1;
+
+		if (S_ISDIR(s.st_mode))
+		{
+			if (tar_append_tree(t, realpath,
+					    (savedir ? savepath : NULL)) != 0)
+				return -1;
+			continue;
+		}
+
+		if (tar_append_file(t, realpath,
+				    (savedir ? savepath : NULL)) != 0)
+			return -1;
+	}
+
+	closedir(dp);
+
+	return 0;
+}