aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorMartin Matuska <mm@FreeBSD.org>2016-05-11 10:10:11 +0000
committerMartin Matuska <mm@FreeBSD.org>2016-05-11 10:10:11 +0000
commit8c8f03ca5b7f1f0d07c23e7c354a45b7c95335f0 (patch)
tree6dbaec0a9141562742e447716c2fed6e53d97c6d /contrib
parent43341e7cf4b9eee3ead77da04edc14370fe5b166 (diff)
downloadsrc-8c8f03ca5b7f1f0d07c23e7c354a45b7c95335f0.tar.gz
src-8c8f03ca5b7f1f0d07c23e7c354a45b7c95335f0.zip
Keep full libarchive distribution in vendor branch (prep for 3.2.0 update)
Notes
Notes: svn path=/vendor/libarchive/dist/; revision=299422
Diffstat (limited to 'contrib')
-rw-r--r--contrib/README59
-rw-r--r--contrib/libarchive.1aix53.spec160
-rw-r--r--contrib/libarchive.spec147
-rw-r--r--contrib/libarchive_autodetect-st_lib_archive.m4154
-rw-r--r--contrib/psota-benchmark/results.txt136
-rwxr-xr-xcontrib/psota-benchmark/tcp.sh110
-rw-r--r--contrib/shar/Makefile14
-rw-r--r--contrib/shar/shar.1128
-rw-r--r--contrib/shar/shar.c314
-rw-r--r--contrib/shar/tree.c542
-rw-r--r--contrib/shar/tree.h115
-rw-r--r--contrib/shar/tree_config.h78
-rw-r--r--contrib/untar.c225
13 files changed, 2182 insertions, 0 deletions
diff --git a/contrib/README b/contrib/README
new file mode 100644
index 000000000000..8ad352a30204
--- /dev/null
+++ b/contrib/README
@@ -0,0 +1,59 @@
+Many people have graciously sent me configuration
+files, small programs that use libarchive, and other
+useful and interesting tidbits.
+
+I do not support or use any of these; but if you can use them, enjoy!
+
+======================================================================
+
+From: Andre Stechert <andre@splunk.com>
+
+libarchive_autodetect-st_lib_archive.m4
+
+M4 macros for use with autoconf to detect whether a suitable
+version of libarchive is installed on this system.
+
+
+======================================================================
+
+libarchive.spec
+
+An RPM ".spec" file for building libarchive on most systems.
+This apparently was originally developed by a group at pld-linux.org.
+Several people have sent me different versions of this file.
+
+======================================================================
+
+From: Robert Meier <rm1023@dcx.com>
+
+libarchive.1aix53.spec
+
+As above, for use on AIX5.3.
+
+======================================================================
+
+psota-benchmark
+
+Some scripts used by Jan Psota in benchmarking
+various tar implementations.
+
+I've edited his results slightly to correctly reflect that
+bsdtar does not support a "compare" operation.
+
+======================================================================
+
+shar
+
+A simple shar program written on top of libarchive.
+
+======================================================================
+
+untar.c
+
+A very simple and very portable standalone program that can
+extract basic ustar archives.
+This does not use libarchive and so can be used to extract
+the libarchive distribution on any system that has a C compiler
+but does not have a tar program.
+
+======================================================================
diff --git a/contrib/libarchive.1aix53.spec b/contrib/libarchive.1aix53.spec
new file mode 100644
index 000000000000..fe81d147e03e
--- /dev/null
+++ b/contrib/libarchive.1aix53.spec
@@ -0,0 +1,160 @@
+# $LastChangedRevision$, $LastChangedDate$
+Summary: Library to create and read several different archive formats
+Summary(pl): Biblioteka do tworzenia i odczytu różnych formatów archiwów
+Name: libarchive
+Version: 2.0a3
+Release: 1aix53
+License: BSD
+Group: Libraries
+Source0: http://people.freebsd.org/~kientzle/libarchive/src/%{name}-%{version}.tar.gz
+Patch: %{name}-0123457890.patch
+URL: http://people.freebsd.org/~kientzle/libarchive/
+Requires: glibc
+Requires: zlib
+Requires: bzip2
+BuildRequires: gcc
+BuildRequires: gcc-c++
+BuildRequires: gawk
+BuildRequires: zlib-devel
+BuildRequires: bzip2
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+
+%description
+Libarchive is a programming library that can create and read several
+different streaming archive formats, including most popular TAR
+variants and several CPIO formats. It can also write SHAR archives.
+
+%description -l pl
+Libarchive jest biblioteką służacą to tworzenia i odczytu wielu
+różnych strumieniowych formatów archiwów, włączając w to popularne
+odmiany TAR oraz wiele formatów CPIO. Biblioteka ta potrafi także
+zapisywać archiwa SHAR.
+
+%package devel
+Summary: Header files for libarchive library
+Summary(pl): Pliki nagłówkowe biblioteki libarchive
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+Header files for libarchive library.
+
+%description devel -l pl
+Pliki nagłówkowe biblioteki libarchive.
+
+%package static
+Summary: Static libarchive library
+Summary(pl): Statyczna biblioteka libarchive
+Group: Development/Libraries
+Requires: %{name}-devel = %{version}-%{release}
+
+%description static
+Static libarchive library.
+
+%description static -l pl
+Statyczna biblioteka libarchive.
+
+%package -n bsdtar
+Summary: bsdtar - tar(1) implementation based on libarchive
+Summary(pl): bsdtar - implementacja programu tar(1) oparta na libarchive
+Group: Applications/Archiving
+Requires: %{name} = %{version}-%{release}
+
+%description -n bsdtar
+bsdtar - tar(1) implementation based on libarchive.
+
+%description -n bsdtar -l pl
+bsdtar - implementacja programu tar(1), oparta na libarchive.
+
+%prep
+%setup -q
+%patch0 -p1
+
+%build
+# Specify paths to avoid use of vacpp
+# -maix64 - required to use large files with aix-5.3
+# -static - required for interoperability without copying libraries
+# -D_BSD - required to include definition of makedev
+# -X64 - required to assemble 64-bit COFF files
+mkdir -p %{buildroot}
+PATH=/opt/freeware/libexec:/opt/freeware/bin:/usr/local/bin:/usr/bin:/etc:/usr/sbin:/usr/ucb:/usr/bin/X11:/sbin:. \
+CPATH=/opt/freeware/include:/usr/local/include \
+LIBPATH=/opt/freeware/lib:/usr/local/lib:/usr/share/lib \
+LD_LIBRARY_PATH=/opt/freeware/lib:/usr/local/lib:/usr/share/lib \
+CFLAGS="$RPM_OPT_FLAGS -maix64 -static -D_BSD" \
+CXXFLAGS="$RPM_OPT_FLAGS -maix64 -static -D_BSD" \
+AR="ar -X64" \
+./configure \
+--prefix=%{_prefix} \
+--libexecdir=%{_libexecdir} \
+--mandir=%{_mandir} \
+--infodir=%{_infodir} \
+--enable-shared=yes \
+--enable-static=yes \
+| tee %{buildroot}/config.log
+make | tee %{buildroot}/make.log
+
+%install
+[ "%buildroot" != "/" ] && [ -d %buildroot ] && rm -rf %buildroot;
+make DESTDIR=%buildroot install
+# original install builds, but does install bsdtar
+cp .libs/%{name}.a %{buildroot}%{_libdir}
+cp bsdtar %{buildroot}%{_bindir}
+cp tar/bsdtar.1 %{buildroot}%{_mandir}/man1
+
+%clean
+rm -fr %buildroot
+
+%files
+%defattr(644,root,root,755)
+%{_libdir}/libarchive.a
+
+%files devel
+%defattr(644,root,root,755)
+%{_libdir}/libarchive.la
+%{_includedir}/*.h
+%doc %{_mandir}/man3/*
+%doc %{_mandir}/man5/*
+
+%files -n bsdtar
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_bindir}/bsdtar
+%doc %{_mandir}/man1/bsdtar.1*
+
+%define date %(echo `LC_ALL="C" date +"%a %b %d %Y"`)
+%changelog
+* %{date} PLD Team <feedback@pld-linux.org>
+All persons listed below can be reached at <cvs_login>@pld-linux.org
+
+$Log: libarchive.spec,v $
+Release 1aix53 2006/12/12 rm1023@dcx.com
+- tweak for aix-5.3
+- added libarchive-0123457890.patch for "0123457890" error
+- replaced libarchive-1.3.1.tar.gz with libarchive-2.0a3.tar.gz
+- removed obsolete -CVE-2006-5680.patch and -man_progname.patch
+
+Revision 1.6 2006/11/15 10:41:28 qboosh
+- BR: acl-devel,attr-devel
+- devel deps
+
+Revision 1.5 2006/11/08 22:22:25 twittner
+- up to 1.3.1
+- added BR: e2fsprogs-devel
+- added -CVE-2006-5680.patch against entering an infinite
+loop in corrupt archives
+- added bsdtar package (bsdtar is included now in libarchive
+sources)
+- rel. 0.1 for testing
+
+Revision 1.4 2005/12/15 18:26:36 twittner
+- up to 1.2.37
+- removed -shared.patch (no longer needed)
+
+Revision 1.3 2005/10/05 17:00:12 arekm
+- up to 1.02.034
+
+Revision 1.2 2005/07/27 20:17:21 qboosh
+- typo
+
+Revision 1.1 2005/07/27 08:36:03 adamg
+- new
diff --git a/contrib/libarchive.spec b/contrib/libarchive.spec
new file mode 100644
index 000000000000..b5b658a874b1
--- /dev/null
+++ b/contrib/libarchive.spec
@@ -0,0 +1,147 @@
+# $LastChangedRevision$, $LastChangedDate$
+Summary: Library to create and read several different archive formats
+Summary(pl): Biblioteka do tworzenia i odczytu różnych formatów archiwów
+Name: libarchive
+Version: 2.0a3
+Release: 1
+License: BSD
+Group: Libraries
+Source0: http://people.freebsd.org/~kientzle/libarchive/src/%{name}-%{version}.tar.gz
+Patch: %{name}-0123457890.patch
+URL: http://people.freebsd.org/~kientzle/libarchive/
+Requires: glibc
+Requires: zlib
+Requires: bzip2
+BuildRequires: gcc
+BuildRequires: gcc-c++
+BuildRequires: gawk
+BuildRequires: zlib-devel
+BuildRequires: bzip2
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+
+%description
+Libarchive is a programming library that can create and read several
+different streaming archive formats, including most popular TAR
+variants and several CPIO formats. It can also write SHAR archives.
+
+%description -l pl
+Libarchive jest biblioteką służacą to tworzenia i odczytu wielu
+różnych strumieniowych formatów archiwów, włączając w to popularne
+odmiany TAR oraz wiele formatów CPIO. Biblioteka ta potrafi także
+zapisywać archiwa SHAR.
+
+%package devel
+Summary: Header files for libarchive library
+Summary(pl): Pliki nagłówkowe biblioteki libarchive
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+Header files for libarchive library.
+
+%description devel -l pl
+Pliki nagłówkowe biblioteki libarchive.
+
+%package static
+Summary: Static libarchive library
+Summary(pl): Statyczna biblioteka libarchive
+Group: Development/Libraries
+Requires: %{name}-devel = %{version}-%{release}
+
+%description static
+Static libarchive library.
+
+%description static -l pl
+Statyczna biblioteka libarchive.
+
+%package -n bsdtar
+Summary: bsdtar - tar(1) implementation based on libarchive
+Summary(pl): bsdtar - implementacja programu tar(1) oparta na libarchive
+Group: Applications/Archiving
+Requires: %{name} = %{version}-%{release}
+
+%description -n bsdtar
+bsdtar - tar(1) implementation based on libarchive.
+
+%description -n bsdtar -l pl
+bsdtar - implementacja programu tar(1), oparta na libarchive.
+
+%prep
+%setup -q
+%patch0 -p1
+
+%build
+mkdir -p %{buildroot}
+./configure \
+--prefix=%{_prefix} \
+--libexecdir=%{_libexecdir} \
+--mandir=%{_mandir} \
+--infodir=%{_infodir} \
+--enable-shared=yes \
+--enable-static=yes \
+| tee %{buildroot}/config.log
+make | tee %{buildroot}/make.log
+
+%install
+[ "%buildroot" != "/" ] && [ -d %buildroot ] && rm -rf %buildroot;
+make DESTDIR=%buildroot install
+# original install builds, but does install bsdtar
+cp .libs/%{name}.a %{buildroot}%{_libdir}
+cp bsdtar %{buildroot}%{_bindir}
+cp tar/bsdtar.1 %{buildroot}%{_mandir}/man1
+
+%clean
+rm -fr %buildroot
+
+%files
+%defattr(644,root,root,755)
+%{_libdir}/libarchive.a
+
+%files devel
+%defattr(644,root,root,755)
+%{_libdir}/libarchive.la
+%{_includedir}/*.h
+%doc %{_mandir}/man3/*
+%doc %{_mandir}/man5/*
+
+%files -n bsdtar
+%defattr(644,root,root,755)
+%attr(755,root,root) %{_bindir}/bsdtar
+%doc %{_mandir}/man1/bsdtar.1*
+
+%define date %(echo `LC_ALL="C" date +"%a %b %d %Y"`)
+%changelog
+* %{date} PLD Team <feedback@pld-linux.org>
+All persons listed below can be reached at <cvs_login>@pld-linux.org
+
+$Log: libarchive.spec,v $
+Release 1 2006/12/12 rm1023@dcx.com
+- added libarchive-0123457890.patch for "0123457890" error
+- replaced libarchive-1.3.1.tar.gz with libarchive-2.0a3.tar.gz
+- removed obsolete -CVE-2006-5680.patch and -man_progname.patch
+
+Revision 1.6 2006/11/15 10:41:28 qboosh
+- BR: acl-devel,attr-devel
+- devel deps
+
+Revision 1.5 2006/11/08 22:22:25 twittner
+- up to 1.3.1
+- added BR: e2fsprogs-devel
+- added -CVE-2006-5680.patch against entering an infinite
+loop in corrupt archives
+- added bsdtar package (bsdtar is included now in libarchive
+sources)
+- rel. 0.1 for testing
+
+Revision 1.4 2005/12/15 18:26:36 twittner
+- up to 1.2.37
+- removed -shared.patch (no longer needed)
+
+Revision 1.3 2005/10/05 17:00:12 arekm
+- up to 1.02.034
+
+Revision 1.2 2005/07/27 20:17:21 qboosh
+- typo
+
+Revision 1.1 2005/07/27 08:36:03 adamg
+- new
diff --git a/contrib/libarchive_autodetect-st_lib_archive.m4 b/contrib/libarchive_autodetect-st_lib_archive.m4
new file mode 100644
index 000000000000..4419e888f240
--- /dev/null
+++ b/contrib/libarchive_autodetect-st_lib_archive.m4
@@ -0,0 +1,154 @@
+dnl
+dnl @synopsis ST_LIB_ARCHIVE([ENABLED-DEFAULT])
+dnl
+dnl This macro figures out what's necessary to link a program against an
+dnl instance of the BSD libarchive package by Tim Kientzle.
+dnl
+dnl See http://people.freebsd.org/~kientzle/libarchive/ for more info.
+dnl
+dnl It exports and substitutes the variables LIBARCHIVE_LIBS, LIBARCHIVE_LDFLAGS,
+dnl and LIBARCHIVE_CPPFLAGS to appropriate values for the identified instance of
+dnl libarchive. The values are AC_SUBST'd, so a user could, for example, simply
+dnl include @LIBARCHIVE_CPPFLAGS@ in the definition of AM_CPPFLAGS in a Makefile.am.
+dnl
+dnl ENABLED-DEFAULT is either "yes" or "no" and determines whether the default value
+dnl is --with-libarchive or --without-libarchive. It is not possible to specify a
+dnl default directory. More simply, any reasonable choice for a default should just
+dnl go into the auto-detect list.
+dnl
+dnl The macro defines the symbol HAVE_LIBARCHIVE if the library is found. You
+dnl should use autoheader to include a definition for this symbol in a config.h
+dnl file. Sample usage in a C/C++ source is as follows:
+dnl
+dnl #ifdef HAVE_LIBARCHIVE
+dnl #include <archive.h>
+dnl #endif /* HAVE_LIBARCHIVE */
+dnl
+dnl @category InstalledPackages
+dnl @author Andre Stechert <andre@splunk.com>
+dnl @version 2006-04-20
+dnl @license GPLWithACException
+
+AC_DEFUN([ST_LIB_ARCHIVE],
+[
+#
+# Handle input from the configurer and blend with the requirements from the maintainer.
+# We go through the trouble of creating a second set of variables other than the with_foo
+# variables in order to be sure that error/corner cases have been cleaned up.
+#
+# After this statement, three trusted variable are defined.
+#
+# st_lib_archive_ENABLED will be either "yes" or "no". its value determines whether
+# or not we bother with the rest of the checks and whether or not we export a
+# bunch of variables.
+#
+# st_lib_archive_LOCATION will be either "auto" or "defined". if it is "auto", then
+# we try a bunch of standard locations. if it is "defined", then we just try the value
+# provided in st_lib_archive_DIR.
+#
+# st_lib_archive_DIR will contain the string provided by the user, provided that it's
+# actually a directory.
+#
+AC_MSG_CHECKING([if libarchive is wanted])
+AC_ARG_WITH([libarchive],
+ AS_HELP_STRING([--with-libarchive=DIR], [libarchive installation directory]),
+ [if test "x$with_libarchive" = "xno" ; then
+ st_lib_archive_ENABLED=no
+ elif test "x$with_libarchive" = "xyes" ; then
+ st_lib_archive_ENABLED=yes
+ st_lib_archive_LOCATION=auto
+ else
+ st_lib_archive_ENABLED=yes
+ st_lib_archive_LOCATION=defined
+ if test -d "$with_libarchive" ; then
+ st_lib_archive_DIR="$with_libarchive"
+ else
+ AC_MSG_ERROR([$with_libarchive is not a directory])
+ fi
+ fi],
+ [if test "x$1" = "xno" ; then
+ st_lib_archive_ENABLED=no
+ elif test "x$1" = "xyes" ; then
+ st_lib_archive_ENABLED=yes
+ else
+ st_lib_archive_ENABLED=yes
+ fi])
+
+if test "$st_lib_archive_ENABLED" = "yes" ; then
+ AC_MSG_RESULT([yes])
+#
+# After this statement, one trusted variable is defined.
+#
+# st_lib_archive_LIB will be either "lib" or "lib64", depending on whether the configurer
+# specified 32, 64. The default is "lib".
+#
+ AC_MSG_CHECKING([whether to use lib or lib64])
+ AC_ARG_WITH([libarchive-bits],
+ AS_HELP_STRING([--with-libarchive-bits=32/64], [if 64, look in /lib64 on hybrid systems]),
+ [if test "x$with_libarchive_bits" = "x32" ; then
+ st_lib_archive_LIB=lib
+ elif test "x$with_libarchive_bits" = "x64" ; then
+ st_lib_archive_LIB=lib64
+ else
+ AC_MSG_ERROR([the argument must be either 32 or 64])
+ fi],
+ [st_lib_archive_LIB=lib])
+ AC_MSG_RESULT($st_lib_archive_LIB)
+#
+# Save the environment before verifying libarchive availability
+#
+ st_lib_archive_SAVECPPFLAGS="$CPPFLAGS"
+ st_lib_archive_SAVELDFLAGS="$LDFLAGS"
+ AC_LANG_SAVE
+ AC_LANG_C
+
+ if test "x$st_lib_archive_LOCATION" = "xdefined" ; then
+ CPPFLAGS="-I$st_lib_archive_DIR/include $st_lib_archive_SAVECPPFLAGS"
+ LDFLAGS="-L$st_lib_archive_DIR/$st_lib_archive_LIB $st_lib_archive_SAVELDFLAGS"
+ AC_CHECK_LIB(archive, archive_read_new, [st_lib_archive_found_lib=yes], [st_lib_archive_found_lib=no])
+ AC_CHECK_HEADER(archive.h, [st_lib_archive_found_hdr=yes], [st_lib_archive_found_hdr=no])
+ if test "x$st_lib_archive_found_lib" = "xyes" && test "x$st_lib_archive_found_hdr" = "xyes"; then
+ LIBARCHIVE_CPPFLAGS="-I$dir/include"
+ LIBARCHIVE_LDFLAGS="-L$dir/$st_lib_archive_LIB"
+ else
+ AC_MSG_ERROR([could not find libarchive in the requested location])
+ fi
+ else
+ #
+ # These are the common install directories for Linux, FreeBSD, Solaris, and Mac.
+ #
+ for dir in /usr /usr/local /usr/sfw /opt/csw /opt/local /sw
+ do
+ if test -d "$dir" ; then
+ CPPFLAGS="-I$dir/include $st_lib_archive_SAVECPPFLAGS"
+ LDFLAGS="-L$dir/$st_lib_archive_LIB $st_lib_archive_SAVELDFLAGS"
+ AC_CHECK_LIB(archive, archive_read_new, [st_lib_archive_found_lib=yes], [st_lib_archive_found_lib=no])
+ AC_CHECK_HEADER(archive.h, [st_lib_archive_found_hdr=yes], [st_lib_archive_found_hdr=no])
+ if test "x$st_lib_archive_found_lib" = "xyes" && test "x$st_lib_archive_found_hdr" = "xyes"; then
+ LIBARCHIVE_CPPFLAGS="-I$dir/include"
+ LIBARCHIVE_LDFLAGS="-L$dir/$st_lib_archive_LIB"
+ break
+ fi
+ fi
+ done
+ fi
+
+ if test "x$st_lib_archive_found_hdr" = "xyes" && test "x$st_lib_archive_found_lib" = "xyes" ; then
+ LIBARCHIVE_LIBS="-larchive"
+ AC_DEFINE([HAVE_LIBARCHIVE], [1], [Defined to 1 if libarchive is available for use.])
+ AC_SUBST(LIBARCHIVE_LIBS)
+ AC_SUBST(LIBARCHIVE_CPPFLAGS)
+ AC_SUBST(LIBARCHIVE_LDFLAGS)
+ fi
+
+#
+# Restore the environment now that we're done.
+#
+ AC_LANG_RESTORE
+ CPPFLAGS="$st_lib_archive_SAVECPPFLAGS"
+ LDFLAGS="$st_lib_archive_SAVELDFLAGS"
+else
+ AC_MSG_RESULT([no])
+fi
+AM_CONDITIONAL(LIBARCHIVE, test "x$st_lib_archive_found_lib" = "xyes" && test "x$st_lib_archive_found_hdr" = "xyes")
+])
diff --git a/contrib/psota-benchmark/results.txt b/contrib/psota-benchmark/results.txt
new file mode 100644
index 000000000000..2d364c5558dd
--- /dev/null
+++ b/contrib/psota-benchmark/results.txt
@@ -0,0 +1,136 @@
+ODP: [Bug-tar] GNU tar, star and BSD tar speed comparison +new script
+
+Jan Psota
+Thu, 25 Oct 2007 06:51:13 -0700
+
+Latest TCP script at the bottom (3180 bytes).
+4 tests: 64bit dual core Athlon tmpfs / disk (reiserfs) - 60MB/s,
+ 32bit Athlon tmpfs / disk (reiserfs) - 55MB/s
+Both machines were idle -- used for testing only.
+Tarball and extracted files were on different physical devices.
+Test data: linux 2.6.22/3 kernel sources for memory operations,
+for the other data average file size should bring enough info.
+
+2 x [...] processor means 1 processor with 2 cores (2 entries in cpuinfo).
+Archive format is set to pax (Joerg).
+Let's end with it. I only wanted to send You a new version of TCP script :-).
+
+--
+Jan Psota
+
+TCP, version 2007-10-25
+Linux 2.6.22-suspend2-r2 / Gentoo Base System release 2.0.0_rc5
+2012MB of memory, 2 x AMD Athlon(tm) 64 X2 Dual Core Processor 4200+ 2211.348
+512 KB 4426.24 bmips
+gcc (GCC) 4.2.2 (Gentoo 4.2.2 p1.0)
+CFLAGS="-O2 -march=k8 -pipe"
+
+bsdtar: bsdtar 2.3.4 - libarchive 2.3.4
+gnutar: tar (GNU tar) 1.19
+star: star: star 1.5a85 (x86_64-unknown-linux-gnu)
+
+best time of 5 repetitions,
+ src=linux-2.6.23, 291M in 23867 files, avg 13KB/file,
+ archive=/tmp/tcp.tar, extract to /tmp/tcptmp
+program operation real user system %CPU speed
+bsdtar create 0.764 0.232 0.532 99.96 370308 KB/s
+gnutar create 0.743 0.200 0.512 95.87 380775 KB/s
+star create 0.587 0.040 0.820 100.00 441247 KB/s
+
+bsdtar list 0.164 0.096 0.068 99.84 1579341 KB/s
+gnutar list 0.218 0.064 0.152 98.92 1188128 KB/s
+star list 0.359 0.044 0.240 79.09 721481 KB/s
+
+bsdtar extract 0.733 0.200 0.504 96.02 353358 KB/s
+gnutar extract 0.625 0.092 0.508 96.02 414419 KB/s
+star extract 0.875 0.096 0.980 100.00 296013 KB/s
+
+bsdtar compare 0.001 0.000 0.000 0.00 259012000 KB/s
+gnutar compare 0.719 0.288 0.400 95.66 360239 KB/s
+star compare 0.695 0.224 0.636 100.00 372679 KB/s
+
+[...]
+best time of 3 repetitions,
+ src=/home, 3.2G in 7447 files, avg 554KB/file,
+ archive=/var/tcp.tar, extract to /mnt/a/tcptmp
+program operation real user system %CPU speed
+bsdtar create 184.680 0.552 13.365 7.53 17958 KB/s
+gnutar create 159.240 0.256 12.417 7.95 20827 KB/s
+star create 181.779 0.140 14.789 8.21 18203 KB/s
+
+bsdtar list 0.053 0.032 0.016 91.41 62435471 KB/s
+gnutar list 56.535 0.136 3.764 6.89 58531 KB/s
+star list 56.652 0.080 5.236 9.38 58410 KB/s
+
+bsdtar extract 78.914 0.820 15.149 20.23 41932 KB/s
+gnutar extract 78.480 0.196 14.197 18.33 42164 KB/s
+star extract 79.439 0.132 12.973 16.49 41655 KB/s
+
+bsdtar compare 0.001 0.000 0.000 0.00 3309080000 KB/s
+gnutar compare 61.771 3.464 8.905 20.02 53570 KB/s
+star compare 57.561 1.728 9.897 20.19 57488 KB/s
+
+
+Linux 2.6.22-suspend2-smp / Gentoo Base System release 2.0.0_rc5
+504MB of memory, 1 x AMD Athlon(tm) Processor 1500.033 256 KB 3002.55 bmips
+gcc (GCC) 4.2.2 (Gentoo 4.2.2 p1.0)
+CFLAGS="-O2 -march=athlon-xp -mfpmath=sse -frename-registers -pipe"
+
+bsdtar: bsdtar 2.3.4 - libarchive 2.3.4
+gnutar: tar (GNU tar) 1.19
+star: star: star 1.5a85 (i686-pc-linux-gnu)
+
+best time of 3 repetitions,
+ src=/usr/src/linux-2.6.22-suspend2/drivers, 119M in 5900 files,
+ avg 21KB/file, archive=/tmp/tcp.tar, extract to /tmp/tcptmp
+program operation real user system %CPU speed
+bsdtar create 1.329 0.192 1.132 99.63 89784 KB/s
+gnutar create 1.223 0.124 1.092 99.46 97566 KB/s
+star create 1.848 0.036 1.708 94.36 61372 KB/s
+
+bsdtar list 0.167 0.060 0.108 100.00 679137 KB/s
+gnutar list 0.161 0.040 0.124 100.00 704447 KB/s
+star list 0.859 0.044 0.716 88.51 132032 KB/s
+
+bsdtar extract 1.186 0.172 1.012 99.87 95629 KB/s
+gnutar extract 1.064 0.056 1.004 99.63 106593 KB/s
+star extract 1.920 0.088 1.724 94.40 59070 KB/s
+
+bsdtar compare 0.002 0.000 0.000 0.00 56708000 KB/s
+gnutar compare 0.925 0.232 0.692 99.90 122611 KB/s
+star compare 1.569 0.376 1.096 93.79 72285 KB/s
+
+[...]
+best time of 3 repetitions,
+ src=/home/jasiu, 2.1G in 8416 files, avg 277KB/file,
+ archive=/home/j2/tcp.tar, extract to /mnt/a/tar/tcptmp
+program operation real user system %CPU speed
+bsdtar create 182.171 1.692 29.130 16.91 11584 KB/s
+gnutar create 174.999 0.632 27.450 16.04 12059 KB/s
+star create 180.004 0.360 41.795 23.41 11677 KB/s
+
+bsdtar list 0.214 0.076 0.136 99.04 9822294 KB/s
+gnutar list 0.210 0.076 0.136 100.00 10009385 KB/s
+star list 43.462 0.148 18.109 42.00 48363 KB/s
+
+bsdtar extract 94.912 4.476 31.574 37.98 22146 KB/s
+gnutar extract 94.657 0.396 29.462 31.54 22206 KB/s
+star extract 100.814 0.400 39.906 39.98 20849 KB/s
+
+bsdtar compare 0.003 0.000 0.004 100.00 700657000 KB/s
+gnutar compare 80.174 3.932 20.365 30.30 26217 KB/s
+star compare 73.911 8.341 27.670 48.72 28439 KB/s
+
+=============================================================
+
+Note by Tim Kientzle: The "bsdtar compare" results here are
+invalid since bsdtar does not support that operation.
+For the list numbers, note that libarchive automatically optimizes
+list operations on uncompressed tar archives on disk by using lseek()
+to skip over the bodies of entries. GNU tar added an option to
+provide the same feature.
+
+The biggest problem with these tests is that they only
+cover uncompressed archives stored on disk. The results for
+compressed archives and/or archives stored on tape are
+likely quite different.
diff --git a/contrib/psota-benchmark/tcp.sh b/contrib/psota-benchmark/tcp.sh
new file mode 100755
index 000000000000..3f630732be7c
--- /dev/null
+++ b/contrib/psota-benchmark/tcp.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+# tar comparison program
+# 2007-10-25 Jan Psota
+
+n=3 # number of repetitions
+TAR="bsdtar gnutar star" # Tape archivers to compare
+OPT=("" "--seek" "-no-fsync")
+pax="--format=pax" # comment out for defaults
+OPN=(create list extract compare) # operations
+version="2007-10-25"
+TIMEFORMAT=$'%R\t%U\t%S\t%P'
+LC_ALL=C
+
+test $# -ge 2 || {
+ echo -e "usage:\t$0 source_dir where_to_place_archive
+[where_to_extract_it]
+
+TCP, version $version
+TCP stands for Tar Comparison Program here.
+It currently compares: BSD tar (bsdtar), GNU tar (gnutar) and star in archive
+creation, listing, extraction and archive-to-extracted comparison.
+Tcp prints out best time of n=$n repetitions.
+
+Tcp creates temporary archive named tcp.tar with $pax and some native
+(--seek/-no-fsync) options and extracts it to [\$3]/tcptmp/.
+If unset, third argument defaults to [\$2].
+After normal exit tcp removes tarball and extracted files.
+Tcp does not check filesystems destination directories are on for free space,
+so make sure there is enough space (a bit more than source_dir uses) for both:
+archive and extracted files.
+Do not use white space in arguments.
+ Jan Psota, $version"
+ exit 0
+}
+src=$1
+dst=$2/tcp.tar
+dst_path=${3:-$2}/tcptmp
+test -e $dst -o -e /tmp/tcp \
+ && { echo "$dst or /tmp/tcp exists, exiting"; exit 1; }
+mkdir $dst_path || exit 2
+
+use_times ()
+{
+ awk -F"\t" -vN=$n -vL="`du -k $dst`" -vOFS="\t" -vORS="" '
+ { if (NF==4) { printf "\t%s\t%10.1d KB/s\n", $0, ($1+0>0 ?
+(L+0)/($1+0) : 0) } }' \
+ /tmp/tcp | sort | head -1
+ > /tmp/tcp
+}
+
+test -d $src || { echo "'$src' is not a directory"; exit 3; }
+
+# system information: type, release, memory, cpu(s), compiler and flags
+echo -e "TCP, version $version\n"`uname -sr`" / "`head -1 /etc/*-release`
+free -m | awk '/^Mem/ { printf "%dMB of memory, ", $2 }'
+test -e /proc/cpuinfo \
+ && awk -F: '/name|cache size|MHz|mips/ { if (!a) b=b $2 }
+ /^$/ { a++ } END { print a" x"b" bmips" }' /proc/cpuinfo
+test -e /etc/gentoo-release \
+ && gcc --version | head -1 && grep ^CFLAGS /etc/make.conf
+
+# tar versions
+t=
+echo
+for tar in $TAR; do
+ if which $tar &> /dev/null; then
+ t="$t $tar";
+ echo -ne "$tar:\t"; $tar --version | head -1;
+ fi
+done
+
+TAR="$t"
+
+echo -e "\nbest time of $n repetitions,\n"\
+" src=$src, "\
+`du -sh $src | awk '{print $1}'`" in "`find $src | wc -l`" files, "\
+"avg "$((`du -sk $src | awk '{print $1}'`/`find $src -type f | wc -l`))"KB/file,\n"\
+" archive=$dst, extract to $dst_path"
+
+echo -e "program\toperation\treal\tuser\tsystem\t%CPU\t speed"
+> /tmp/tcp
+let op_num=0
+for op in "cf $dst $pax -C $src ." "tf $dst" "xf $dst -C $dst_path" \
+ "f $dst -C $dst_path --diff"; do
+ let tar_num=0
+ for tar in $TAR; do
+ echo -en "$tar\t${OPN[op_num]}\t"
+ for ((i=1; i<=$n; i++)); do
+ echo $op | grep -q ^cf && rm -f $dst
+ echo $op | grep -q ^xf &&
+ { chmod -R u+w $dst_path
+ rm -rf $dst_path; mkdir $dst_path; }
+ sync
+ if echo $op | grep -q ^f; then # op == compare
+ time $tar $op ${OPT[$tar_num]} > /dev/null
+ else # op in (create | list | extract)
+ time $tar $op ${OPT[$tar_num]} > /dev/null \
+ || break 3
+ fi 2>> /tmp/tcp
+ done
+ use_times
+ let tar_num++
+ done
+ let op_num++
+ echo
+done
+rm -rf $dst_path $dst
+echo
+cat /tmp/tcp
+rm -f /tmp/tcp
diff --git a/contrib/shar/Makefile b/contrib/shar/Makefile
new file mode 100644
index 000000000000..3bd94d4192cb
--- /dev/null
+++ b/contrib/shar/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= shar
+SRCS= shar.c tree.c
+
+WARNS?= 6
+
+DPADD= ${LIBARCHIVE}
+LDADD= -larchive
+
+LINKS= ${BINDIR}/shar
+MLINKS= shar.1
+
+.include <bsd.prog.mk>
diff --git a/contrib/shar/shar.1 b/contrib/shar/shar.1
new file mode 100644
index 000000000000..e3152f299ebe
--- /dev/null
+++ b/contrib/shar/shar.1
@@ -0,0 +1,128 @@
+.\" 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.
+.\"
+.\" @(#)shar.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd April 17, 2008
+.Dt SHAR 1
+.Os
+.Sh NAME
+.Nm shar
+.Nd create a shell archive of files
+.Sh SYNOPSIS
+.Nm
+.Op Fl br
+.Op Fl o Ar archive-file
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+command writes a
+.Xr sh 1
+shell script which will recreate the file hierarchy specified by the command
+line operands.
+.Pp
+The
+.Nm
+command is normally used for distributing files by
+.Xr ftp 1
+or
+.Xr mail 1 .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl b
+Use an alternative binary format. Content of files will be uuencoded.
+This option should be used to archive binary files correctly.
+In this mode also file permissions will be stored to the archive.
+uudecode(1) is needed to extract archives created with this option.
+.It Fl o Ar archive-file
+Redirect output to
+.Ar archive-file .
+.It Fl r
+If
+.Ar file
+given on command line is a directory the entire subtree will be archived.
+Symbolic links given on command line are followed. Other symbolic links will
+be archived as such.
+.El
+.Sh EXAMPLES
+To create a shell archive of the program
+.Xr ls 1
+and mail it to Rick:
+.Bd -literal -offset indent
+cd ls
+shar -r . \&| mail -s "ls source" rick
+.Ed
+.Pp
+To recreate the program directory:
+.Bd -literal -offset indent
+mkdir ls
+cd ls
+\&...
+<delete header lines and examine mailed archive>
+\&...
+sh archive
+.Ed
+.Sh SEE ALSO
+.Xr compress 1 ,
+.Xr mail 1 ,
+.Xr tar 1 ,
+.Xr uuencode 1 ,
+.Xr uuencode 5
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
+This is a re-implementation based on the libarchive(3) library.
+.Sh BUGS
+The
+.Nm
+command makes no provisions for hard links.
+.Pp
+Files containing magic characters or files without a newline ('\\n') as the
+last character are not handled correctly with the default format. Use the -b
+option for binary files.
+.Pp
+It is easy to insert trojan horses into
+.Nm
+files.
+It is strongly recommended that all shell archive files be examined
+before running them through
+.Xr sh 1 .
+Archives produced using this implementation of
+.Nm
+may be easily examined with the command:
+.Bd -literal -offset indent
+egrep -v '^[X#]' shar.file
+.Ed
diff --git a/contrib/shar/shar.c b/contrib/shar/shar.c
new file mode 100644
index 000000000000..6d5c206e2a51
--- /dev/null
+++ b/contrib/shar/shar.c
@@ -0,0 +1,314 @@
+/*-
+ * Copyright (c) 2008 Jaakko Heinonen
+ * 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
+ * in this position and unchanged.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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 <sys/cdefs.h>
+#ifdef __FBSDID
+__FBSDID("$FreeBSD$");
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <archive.h>
+#include <archive_entry.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "tree.h"
+
+/* command line options */
+static int b_opt; /* use alternative shar binary format */
+static int r_opt; /* recurse into subdirectories */
+static char *o_arg; /* output file name */
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: shar [-br] [-o filename] file ...\n");
+ exit(EX_USAGE);
+}
+
+/*
+ * Initialize archive structure and create a shar archive.
+ */
+static struct archive *
+shar_create(void)
+{
+ struct archive *a;
+
+ if ((a = archive_write_new()) == NULL)
+ errx(EXIT_FAILURE, "%s", archive_error_string(a));
+
+ if (b_opt)
+ archive_write_set_format_shar_dump(a);
+ else
+ archive_write_set_format_shar(a);
+ archive_write_set_compression_none(a);
+
+ if (archive_write_open_filename(a, o_arg) != ARCHIVE_OK)
+ errx(EX_CANTCREAT, "%s", archive_error_string(a));
+
+ return (a);
+}
+
+/* buffer for file data */
+static char buffer[32768];
+
+/*
+ * Write file data to an archive entry.
+ */
+static int
+shar_write_entry_data(struct archive *a, const int fd)
+{
+ ssize_t bytes_read, bytes_written;
+
+ assert(a != NULL);
+ assert(fd >= 0);
+
+ bytes_read = read(fd, buffer, sizeof(buffer));
+ while (bytes_read != 0) {
+ if (bytes_read < 0) {
+ archive_set_error(a, errno, "Read failed");
+ return (ARCHIVE_WARN);
+ }
+ bytes_written = archive_write_data(a, buffer, bytes_read);
+ if (bytes_written < 0)
+ return (ARCHIVE_WARN);
+ bytes_read = read(fd, buffer, sizeof(buffer));
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Write a file to the archive. We have special handling for symbolic links.
+ */
+static int
+shar_write_entry(struct archive *a, const char *pathname, const char *accpath,
+ const struct stat *st)
+{
+ struct archive_entry *entry;
+ int fd = -1;
+ int ret = ARCHIVE_OK;
+
+ assert(a != NULL);
+ assert(pathname != NULL);
+ assert(accpath != NULL);
+ assert(st != NULL);
+
+ entry = archive_entry_new();
+
+ if (S_ISREG(st->st_mode) && st->st_size > 0) {
+ /* regular file */
+ if ((fd = open(accpath, O_RDONLY)) == -1) {
+ warn("%s", accpath);
+ ret = ARCHIVE_WARN;
+ goto out;
+ }
+ } else if (S_ISLNK(st->st_mode)) {
+ /* symbolic link */
+ char lnkbuff[PATH_MAX + 1];
+ int lnklen;
+ if ((lnklen = readlink(accpath, lnkbuff, PATH_MAX)) == -1) {
+ warn("%s", accpath);
+ ret = ARCHIVE_WARN;
+ goto out;
+ }
+ lnkbuff[lnklen] = '\0';
+ archive_entry_set_symlink(entry, lnkbuff);
+ }
+ archive_entry_copy_stat(entry, st);
+ archive_entry_set_pathname(entry, pathname);
+ if (!S_ISREG(st->st_mode) || st->st_size == 0)
+ archive_entry_set_size(entry, 0);
+ if (archive_write_header(a, entry) != ARCHIVE_OK) {
+ warnx("%s: %s", pathname, archive_error_string(a));
+ ret = ARCHIVE_WARN;
+ goto out;
+ }
+ if (fd >= 0) {
+ if ((ret = shar_write_entry_data(a, fd)) != ARCHIVE_OK)
+ warnx("%s: %s", accpath, archive_error_string(a));
+ }
+out:
+ archive_entry_free(entry);
+ if (fd >= 0)
+ close(fd);
+
+ return (ret);
+}
+
+/*
+ * Write singe path to the archive. The path can be a regular file, directory
+ * or device. Symbolic links are followed.
+ */
+static int
+shar_write_path(struct archive *a, const char *pathname)
+{
+ struct stat st;
+
+ assert(a != NULL);
+ assert(pathname != NULL);
+
+ if ((stat(pathname, &st)) == -1) {
+ warn("%s", pathname);
+ return (ARCHIVE_WARN);
+ }
+
+ return (shar_write_entry(a, pathname, pathname, &st));
+}
+
+/*
+ * Write tree to the archive. If pathname is a symbolic link it will be
+ * followed. Other symbolic links are stored as such to the archive.
+ */
+static int
+shar_write_tree(struct archive *a, const char *pathname)
+{
+ struct tree *t;
+ const struct stat *lst, *st;
+ int error = 0;
+ int tree_ret;
+ int first;
+
+ assert(a != NULL);
+ assert(pathname != NULL);
+
+ t = tree_open(pathname);
+ for (first = 1; (tree_ret = tree_next(t)); first = 0) {
+ if (tree_ret == TREE_ERROR_DIR) {
+ warnx("%s: %s", tree_current_path(t),
+ strerror(tree_errno(t)));
+ error = 1;
+ continue;
+ } else if (tree_ret != TREE_REGULAR)
+ continue;
+ if ((lst = tree_current_lstat(t)) == NULL) {
+ warn("%s", tree_current_path(t));
+ error = 1;
+ continue;
+ }
+ /*
+ * If the symlink was given on command line then
+ * follow it rather than write it as symlink.
+ */
+ if (first && S_ISLNK(lst->st_mode)) {
+ if ((st = tree_current_stat(t)) == NULL) {
+ warn("%s", tree_current_path(t));
+ error = 1;
+ continue;
+ }
+ } else
+ st = lst;
+
+ if (shar_write_entry(a, tree_current_path(t),
+ tree_current_access_path(t), st) != ARCHIVE_OK)
+ error = 1;
+
+ tree_descend(t);
+ }
+
+ tree_close(t);
+
+ return ((error != 0) ? ARCHIVE_WARN : ARCHIVE_OK);
+}
+
+/*
+ * Create a shar archive and write files/trees into it.
+ */
+static int
+shar_write(char **fn, size_t nfn)
+{
+ struct archive *a;
+ size_t i;
+ int error = 0;
+
+ assert(fn != NULL);
+ assert(nfn > 0);
+
+ a = shar_create();
+
+ for (i = 0; i < nfn; i++) {
+ if (r_opt) {
+ if (shar_write_tree(a, fn[i]) != ARCHIVE_OK)
+ error = 1;
+ } else {
+ if (shar_write_path(a, fn[i]) != ARCHIVE_OK)
+ error = 1;
+ }
+ }
+
+ if (archive_write_free(a) != ARCHIVE_OK)
+ errx(EXIT_FAILURE, "%s", archive_error_string(a));
+
+ if (error != 0)
+ warnx("Error exit delayed from previous errors.");
+
+ return (error);
+}
+
+int
+main(int argc, char **argv)
+{
+ int opt;
+
+ while ((opt = getopt(argc, argv, "bro:")) != -1) {
+ switch (opt) {
+ case 'b':
+ b_opt = 1;
+ break;
+ case 'o':
+ o_arg = optarg;
+ break;
+ case 'r':
+ r_opt = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if(argc < 1)
+ usage();
+
+ if (shar_write(argv, argc) != 0)
+ exit(EXIT_FAILURE);
+ else
+ exit(EXIT_SUCCESS);
+ /* NOTREACHED */
+}
+
diff --git a/contrib/shar/tree.c b/contrib/shar/tree.c
new file mode 100644
index 000000000000..d5a04abf5f4b
--- /dev/null
+++ b/contrib/shar/tree.c
@@ -0,0 +1,542 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ */
+
+/*-
+ * This is a new directory-walking system that addresses a number
+ * of problems I've had with fts(3). In particular, it has no
+ * pathname-length limits (other than the size of 'int'), handles
+ * deep logical traversals, uses considerably less memory, and has
+ * an opaque interface (easier to modify in the future).
+ *
+ * Internally, it keeps a single list of "tree_entry" items that
+ * represent filesystem objects that require further attention.
+ * Non-directories are not kept in memory: they are pulled from
+ * readdir(), returned to the client, then freed as soon as possible.
+ * Any directory entry to be traversed gets pushed onto the stack.
+ *
+ * There is surprisingly little information that needs to be kept for
+ * each item on the stack. Just the name, depth (represented here as the
+ * string length of the parent directory's pathname), and some markers
+ * indicating how to get back to the parent (via chdir("..") for a
+ * regular dir or via fchdir(2) for a symlink).
+ */
+#include "tree_config.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "tree.h"
+
+/*
+ * TODO:
+ * 1) Loop checking.
+ * 3) Arbitrary logical traversals by closing/reopening intermediate fds.
+ */
+
+struct tree_entry {
+ struct tree_entry *next;
+ struct tree_entry *parent;
+ char *name;
+ size_t dirname_length;
+ dev_t dev;
+ ino_t ino;
+ int fd;
+ int flags;
+};
+
+/* Definitions for tree_entry.flags bitmap. */
+#define isDir 1 /* This entry is a regular directory. */
+#define isDirLink 2 /* This entry is a symbolic link to a directory. */
+#define needsPreVisit 4 /* This entry needs to be previsited. */
+#define needsPostVisit 8 /* This entry needs to be postvisited. */
+
+/*
+ * Local data for this package.
+ */
+struct tree {
+ struct tree_entry *stack;
+ struct tree_entry *current;
+ DIR *d;
+ int initialDirFd;
+ int flags;
+ int visit_type;
+ int tree_errno; /* Error code from last failed operation. */
+
+ char *buff;
+ const char *basename;
+ size_t buff_length;
+ size_t path_length;
+ size_t dirname_length;
+
+ int depth;
+ int openCount;
+ int maxOpenCount;
+
+ struct stat lst;
+ struct stat st;
+};
+
+/* Definitions for tree.flags bitmap. */
+#define needsReturn 8 /* Marks first entry as not having been returned yet. */
+#define hasStat 16 /* The st entry is set. */
+#define hasLstat 32 /* The lst entry is set. */
+
+
+#ifdef HAVE_DIRENT_D_NAMLEN
+/* BSD extension; avoids need for a strlen() call. */
+#define D_NAMELEN(dp) (dp)->d_namlen
+#else
+#define D_NAMELEN(dp) (strlen((dp)->d_name))
+#endif
+
+#if 0
+#include <stdio.h>
+void
+tree_dump(struct tree *t, FILE *out)
+{
+ struct tree_entry *te;
+
+ fprintf(out, "\tdepth: %d\n", t->depth);
+ fprintf(out, "\tbuff: %s\n", t->buff);
+ fprintf(out, "\tpwd: "); fflush(stdout); system("pwd");
+ fprintf(out, "\taccess: %s\n", t->basename);
+ fprintf(out, "\tstack:\n");
+ for (te = t->stack; te != NULL; te = te->next) {
+ fprintf(out, "\t\tte->name: %s%s%s\n", te->name,
+ te->flags & needsPreVisit ? "" : " *",
+ t->current == te ? " (current)" : "");
+ }
+}
+#endif
+
+/*
+ * Add a directory path to the current stack.
+ */
+static void
+tree_push(struct tree *t, const char *path)
+{
+ struct tree_entry *te;
+
+ te = malloc(sizeof(*te));
+ memset(te, 0, sizeof(*te));
+ te->next = t->stack;
+ t->stack = te;
+ te->fd = -1;
+ te->name = strdup(path);
+ te->flags = needsPreVisit | needsPostVisit;
+ te->dirname_length = t->dirname_length;
+}
+
+/*
+ * Append a name to the current path.
+ */
+static void
+tree_append(struct tree *t, const char *name, size_t name_length)
+{
+ char *p;
+
+ if (t->buff != NULL)
+ t->buff[t->dirname_length] = '\0';
+ /* Strip trailing '/' from name, unless entire name is "/". */
+ while (name_length > 1 && name[name_length - 1] == '/')
+ name_length--;
+
+ /* Resize pathname buffer as needed. */
+ while (name_length + 1 + t->dirname_length >= t->buff_length) {
+ t->buff_length *= 2;
+ if (t->buff_length < 1024)
+ t->buff_length = 1024;
+ t->buff = realloc(t->buff, t->buff_length);
+ }
+ p = t->buff + t->dirname_length;
+ t->path_length = t->dirname_length + name_length;
+ /* Add a separating '/' if it's needed. */
+ if (t->dirname_length > 0 && p[-1] != '/') {
+ *p++ = '/';
+ t->path_length ++;
+ }
+ strncpy(p, name, name_length);
+ p[name_length] = '\0';
+ t->basename = p;
+}
+
+/*
+ * Open a directory tree for traversal.
+ */
+struct tree *
+tree_open(const char *path)
+{
+ struct tree *t;
+
+ t = malloc(sizeof(*t));
+ memset(t, 0, sizeof(*t));
+ tree_append(t, path, strlen(path));
+ t->initialDirFd = open(".", O_RDONLY);
+ /*
+ * During most of the traversal, items are set up and then
+ * returned immediately from tree_next(). That doesn't work
+ * for the very first entry, so we set a flag for this special
+ * case.
+ */
+ t->flags = needsReturn;
+ return (t);
+}
+
+/*
+ * We've finished a directory; ascend back to the parent.
+ */
+static void
+tree_ascend(struct tree *t)
+{
+ struct tree_entry *te;
+
+ te = t->stack;
+ t->depth--;
+ if (te->flags & isDirLink) {
+ fchdir(te->fd);
+ close(te->fd);
+ t->openCount--;
+ } else {
+ chdir("..");
+ }
+}
+
+/*
+ * Pop the working stack.
+ */
+static void
+tree_pop(struct tree *t)
+{
+ struct tree_entry *te;
+
+ t->buff[t->dirname_length] = '\0';
+ if (t->stack == t->current && t->current != NULL)
+ t->current = t->current->parent;
+ te = t->stack;
+ t->stack = te->next;
+ t->dirname_length = te->dirname_length;
+ t->basename = t->buff + t->dirname_length;
+ /* Special case: starting dir doesn't skip leading '/'. */
+ if (t->dirname_length > 0)
+ t->basename++;
+ free(te->name);
+ free(te);
+}
+
+/*
+ * Get the next item in the tree traversal.
+ */
+int
+tree_next(struct tree *t)
+{
+ struct dirent *de = NULL;
+
+ /* Handle the startup case by returning the initial entry. */
+ if (t->flags & needsReturn) {
+ t->flags &= ~needsReturn;
+ return (t->visit_type = TREE_REGULAR);
+ }
+
+ while (t->stack != NULL) {
+ /* If there's an open dir, get the next entry from there. */
+ while (t->d != NULL) {
+ de = readdir(t->d);
+ if (de == NULL) {
+ closedir(t->d);
+ t->d = NULL;
+ } else if (de->d_name[0] == '.'
+ && de->d_name[1] == '\0') {
+ /* Skip '.' */
+ } else if (de->d_name[0] == '.'
+ && de->d_name[1] == '.'
+ && de->d_name[2] == '\0') {
+ /* Skip '..' */
+ } else {
+ /*
+ * Append the path to the current path
+ * and return it.
+ */
+ tree_append(t, de->d_name, D_NAMELEN(de));
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ return (t->visit_type = TREE_REGULAR);
+ }
+ }
+
+ /* If the current dir needs to be visited, set it up. */
+ if (t->stack->flags & needsPreVisit) {
+ t->current = t->stack;
+ tree_append(t, t->stack->name, strlen(t->stack->name));
+ t->stack->flags &= ~needsPreVisit;
+ /* If it is a link, set up fd for the ascent. */
+ if (t->stack->flags & isDirLink) {
+ t->stack->fd = open(".", O_RDONLY);
+ t->openCount++;
+ if (t->openCount > t->maxOpenCount)
+ t->maxOpenCount = t->openCount;
+ }
+ t->dirname_length = t->path_length;
+ if (chdir(t->stack->name) != 0) {
+ /* chdir() failed; return error */
+ tree_pop(t);
+ t->tree_errno = errno;
+ return (t->visit_type = TREE_ERROR_DIR);
+ }
+ t->depth++;
+ t->d = opendir(".");
+ if (t->d == NULL) {
+ tree_ascend(t); /* Undo "chdir" */
+ tree_pop(t);
+ t->tree_errno = errno;
+ return (t->visit_type = TREE_ERROR_DIR);
+ }
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ t->basename = ".";
+ return (t->visit_type = TREE_POSTDESCENT);
+ }
+
+ /* We've done everything necessary for the top stack entry. */
+ if (t->stack->flags & needsPostVisit) {
+ tree_ascend(t);
+ tree_pop(t);
+ t->flags &= ~hasLstat;
+ t->flags &= ~hasStat;
+ return (t->visit_type = TREE_POSTASCENT);
+ }
+ }
+ return (t->visit_type = 0);
+}
+
+/*
+ * Return error code.
+ */
+int
+tree_errno(struct tree *t)
+{
+ return (t->tree_errno);
+}
+
+/*
+ * Called by the client to mark the directory just returned from
+ * tree_next() as needing to be visited.
+ */
+void
+tree_descend(struct tree *t)
+{
+ if (t->visit_type != TREE_REGULAR)
+ return;
+
+ if (tree_current_is_physical_dir(t)) {
+ tree_push(t, t->basename);
+ t->stack->flags |= isDir;
+ } else if (tree_current_is_dir(t)) {
+ tree_push(t, t->basename);
+ t->stack->flags |= isDirLink;
+ }
+}
+
+/*
+ * Get the stat() data for the entry just returned from tree_next().
+ */
+const struct stat *
+tree_current_stat(struct tree *t)
+{
+ if (!(t->flags & hasStat)) {
+ if (stat(t->basename, &t->st) != 0)
+ return NULL;
+ t->flags |= hasStat;
+ }
+ return (&t->st);
+}
+
+/*
+ * Get the lstat() data for the entry just returned from tree_next().
+ */
+const struct stat *
+tree_current_lstat(struct tree *t)
+{
+ if (!(t->flags & hasLstat)) {
+ if (lstat(t->basename, &t->lst) != 0)
+ return NULL;
+ t->flags |= hasLstat;
+ }
+ return (&t->lst);
+}
+
+/*
+ * Test whether current entry is a dir or link to a dir.
+ */
+int
+tree_current_is_dir(struct tree *t)
+{
+ const struct stat *st;
+
+ /*
+ * If we already have lstat() info, then try some
+ * cheap tests to determine if this is a dir.
+ */
+ if (t->flags & hasLstat) {
+ /* If lstat() says it's a dir, it must be a dir. */
+ if (S_ISDIR(tree_current_lstat(t)->st_mode))
+ return 1;
+ /* Not a dir; might be a link to a dir. */
+ /* If it's not a link, then it's not a link to a dir. */
+ if (!S_ISLNK(tree_current_lstat(t)->st_mode))
+ return 0;
+ /*
+ * It's a link, but we don't know what it's a link to,
+ * so we'll have to use stat().
+ */
+ }
+
+ st = tree_current_stat(t);
+ /* If we can't stat it, it's not a dir. */
+ if (st == NULL)
+ return 0;
+ /* Use the definitive test. Hopefully this is cached. */
+ return (S_ISDIR(st->st_mode));
+}
+
+/*
+ * Test whether current entry is a physical directory. Usually, we
+ * already have at least one of stat() or lstat() in memory, so we
+ * use tricks to try to avoid an extra trip to the disk.
+ */
+int
+tree_current_is_physical_dir(struct tree *t)
+{
+ const struct stat *st;
+
+ /*
+ * If stat() says it isn't a dir, then it's not a dir.
+ * If stat() data is cached, this check is free, so do it first.
+ */
+ if ((t->flags & hasStat)
+ && (!S_ISDIR(tree_current_stat(t)->st_mode)))
+ return 0;
+
+ /*
+ * Either stat() said it was a dir (in which case, we have
+ * to determine whether it's really a link to a dir) or
+ * stat() info wasn't available. So we use lstat(), which
+ * hopefully is already cached.
+ */
+
+ st = tree_current_lstat(t);
+ /* If we can't stat it, it's not a dir. */
+ if (st == NULL)
+ return 0;
+ /* Use the definitive test. Hopefully this is cached. */
+ return (S_ISDIR(st->st_mode));
+}
+
+/*
+ * Test whether current entry is a symbolic link.
+ */
+int
+tree_current_is_physical_link(struct tree *t)
+{
+ const struct stat *st = tree_current_lstat(t);
+ if (st == NULL)
+ return 0;
+ return (S_ISLNK(st->st_mode));
+}
+
+/*
+ * Return the access path for the entry just returned from tree_next().
+ */
+const char *
+tree_current_access_path(struct tree *t)
+{
+ return (t->basename);
+}
+
+/*
+ * Return the full path for the entry just returned from tree_next().
+ */
+const char *
+tree_current_path(struct tree *t)
+{
+ return (t->buff);
+}
+
+/*
+ * Return the length of the path for the entry just returned from tree_next().
+ */
+size_t
+tree_current_pathlen(struct tree *t)
+{
+ return (t->path_length);
+}
+
+/*
+ * Return the nesting depth of the entry just returned from tree_next().
+ */
+int
+tree_current_depth(struct tree *t)
+{
+ return (t->depth);
+}
+
+/*
+ * Terminate the traversal and release any resources.
+ */
+void
+tree_close(struct tree *t)
+{
+ /* Release anything remaining in the stack. */
+ while (t->stack != NULL)
+ tree_pop(t);
+ if (t->buff)
+ free(t->buff);
+ /* chdir() back to where we started. */
+ if (t->initialDirFd >= 0) {
+ fchdir(t->initialDirFd);
+ close(t->initialDirFd);
+ t->initialDirFd = -1;
+ }
+ free(t);
+}
diff --git a/contrib/shar/tree.h b/contrib/shar/tree.h
new file mode 100644
index 000000000000..ff38f5346c1c
--- /dev/null
+++ b/contrib/shar/tree.h
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+
+/*-
+ * A set of routines for traversing directory trees.
+ * Similar in concept to the fts library, but with a few
+ * important differences:
+ * * Uses less memory. In particular, fts stores an entire directory
+ * in memory at a time. This package only keeps enough subdirectory
+ * information in memory to track the traversal. Information
+ * about non-directories is discarded as soon as possible.
+ * * Supports very deep logical traversals. The fts package
+ * uses "non-chdir" approach for logical traversals. This
+ * package does use a chdir approach for logical traversals
+ * and can therefore handle pathnames much longer than
+ * PATH_MAX.
+ * * Supports deep physical traversals "out of the box."
+ * Due to the memory optimizations above, there's no need to
+ * limit dir names to 32k.
+ */
+
+#include <sys/stat.h>
+#include <stdio.h>
+
+struct tree;
+
+/* Initiate/terminate a tree traversal. */
+struct tree *tree_open(const char * /* pathname */);
+void tree_close(struct tree *);
+
+/*
+ * tree_next() returns Zero if there is no next entry, non-zero if there is.
+ * Note that directories are potentially visited three times. The first
+ * time as "regular" file. If tree_descend() is invoked at that time,
+ * the directory is added to a work list and will be visited two more
+ * times: once just after descending into the directory and again
+ * just after ascending back to the parent.
+ *
+ * TREE_ERROR is returned if the descent failed (because the
+ * directory couldn't be opened, for instance). This is returned
+ * instead of TREE_PREVISIT/TREE_POSTVISIT.
+ */
+#define TREE_REGULAR 1
+#define TREE_POSTDESCENT 2
+#define TREE_POSTASCENT 3
+#define TREE_ERROR_DIR -1
+int tree_next(struct tree *);
+
+int tree_errno(struct tree *);
+
+/*
+ * Request that current entry be visited. If you invoke it on every
+ * directory, you'll get a physical traversal. This is ignored if the
+ * current entry isn't a directory or a link to a directory. So, if
+ * you invoke this on every returned path, you'll get a full logical
+ * traversal.
+ */
+void tree_descend(struct tree *);
+
+/*
+ * Return information about the current entry.
+ */
+
+int tree_current_depth(struct tree *);
+/*
+ * The current full pathname, length of the full pathname,
+ * and a name that can be used to access the file.
+ * Because tree does use chdir extensively, the access path is
+ * almost never the same as the full current path.
+ */
+const char *tree_current_path(struct tree *);
+size_t tree_current_pathlen(struct tree *);
+const char *tree_current_access_path(struct tree *);
+/*
+ * Request the lstat() or stat() data for the current path. Since the
+ * tree package needs to do some of this anyway, and caches the
+ * results, you should take advantage of it here if you need it rather
+ * than make a redundant stat() or lstat() call of your own.
+ */
+const struct stat *tree_current_stat(struct tree *);
+const struct stat *tree_current_lstat(struct tree *);
+/* The following tests may use mechanisms much faster than stat()/lstat(). */
+/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */
+int tree_current_is_physical_dir(struct tree *);
+/* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */
+int tree_current_is_physical_link(struct tree *);
+/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */
+int tree_current_is_dir(struct tree *);
+
+/* For testing/debugging: Dump the internal status to the given filehandle. */
+void tree_dump(struct tree *, FILE *);
diff --git a/contrib/shar/tree_config.h b/contrib/shar/tree_config.h
new file mode 100644
index 000000000000..8dfd90baf685
--- /dev/null
+++ b/contrib/shar/tree_config.h
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``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(S) 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.
+ *
+ * $FreeBSD$
+ */
+#ifndef TREE_CONFIG_H_INCLUDED
+#define TREE_CONFIG_H_INCLUDED
+
+#if defined(PLATFORM_CONFIG_H)
+/*
+ * Use hand-built config.h in environments that need it.
+ */
+#include PLATFORM_CONFIG_H
+#elif defined(HAVE_CONFIG_H)
+/*
+ * Most POSIX platforms use the 'configure' script to build config.h
+ */
+#include "../config.h"
+#elif defined(__FreeBSD__)
+/*
+ * Built-in definitions for FreeBSD.
+ */
+#define HAVE_DIRENT_D_NAMLEN 1
+#define HAVE_DIRENT_H 1
+#define HAVE_ERRNO_H 1
+#define HAVE_FCNTL_H 1
+#define HAVE_LIBARCHIVE 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRING_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_UNISTD_H 1
+#else
+/*
+ * Warn if there's no platform configuration.
+ */
+#error Oops: No config.h and no built-in configuration in bsdtar_platform.h.
+#endif /* !HAVE_CONFIG_H */
+
+/* No non-FreeBSD platform will have __FBSDID, so just define it here. */
+#ifdef __FreeBSD__
+#include <sys/cdefs.h> /* For __FBSDID */
+#else
+/* Just leaving this macro replacement empty leads to a dangling semicolon. */
+#define __FBSDID(a) struct _undefined_hack
+#endif
+
+#ifdef HAVE_LIBARCHIVE
+/* If we're using the platform libarchive, include system headers. */
+#include <archive.h>
+#include <archive_entry.h>
+#else
+/* Otherwise, include user headers. */
+#include "archive.h"
+#include "archive_entry.h"
+#endif
+
+#endif /* !TREE_CONFIG_H_INCLUDED */
diff --git a/contrib/untar.c b/contrib/untar.c
new file mode 100644
index 000000000000..c4cc2bf9bea2
--- /dev/null
+++ b/contrib/untar.c
@@ -0,0 +1,225 @@
+/*
+ * "untar" is an extremely simple tar extractor:
+ * * A single C source file, so it should be easy to compile
+ * and run on any system with a C compiler.
+ * * Extremely portable standard C. The only non-ANSI function
+ * used is mkdir().
+ * * Reads basic ustar tar archives.
+ * * Does not require libarchive or any other special library.
+ *
+ * To compile: cc -o untar untar.c
+ *
+ * Usage: untar <archive>
+ *
+ * In particular, this program should be sufficient to extract the
+ * distribution for libarchive, allowing people to bootstrap
+ * libarchive on systems that do not already have a tar program.
+ *
+ * To unpack libarchive-x.y.z.tar.gz:
+ * * gunzip libarchive-x.y.z.tar.gz
+ * * untar libarchive-x.y.z.tar
+ *
+ * Written by Tim Kientzle, March 2009.
+ *
+ * Released into the public domain.
+ */
+
+/* These are all highly standard and portable headers. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* This is for mkdir(); this may need to be changed for some platforms. */
+#include <sys/stat.h> /* For mkdir() */
+
+/* Parse an octal number, ignoring leading and trailing nonsense. */
+static int
+parseoct(const char *p, size_t n)
+{
+ int i = 0;
+
+ while ((*p < '0' || *p > '7') && n > 0) {
+ ++p;
+ --n;
+ }
+ while (*p >= '0' && *p <= '7' && n > 0) {
+ i *= 8;
+ i += *p - '0';
+ ++p;
+ --n;
+ }
+ return (i);
+}
+
+/* Returns true if this is 512 zero bytes. */
+static int
+is_end_of_archive(const char *p)
+{
+ int n;
+ for (n = 511; n >= 0; --n)
+ if (p[n] != '\0')
+ return (0);
+ return (1);
+}
+
+/* Create a directory, including parent directories as necessary. */
+static void
+create_dir(char *pathname, int mode)
+{
+ char *p;
+ int r;
+
+ /* Strip trailing '/' */
+ if (pathname[strlen(pathname) - 1] == '/')
+ pathname[strlen(pathname) - 1] = '\0';
+
+ /* Try creating the directory. */
+ r = mkdir(pathname, mode);
+
+ if (r != 0) {
+ /* On failure, try creating parent directory. */
+ p = strrchr(pathname, '/');
+ if (p != NULL) {
+ *p = '\0';
+ create_dir(pathname, 0755);
+ *p = '/';
+ r = mkdir(pathname, mode);
+ }
+ }
+ if (r != 0)
+ fprintf(stderr, "Could not create directory %s\n", pathname);
+}
+
+/* Create a file, including parent directory as necessary. */
+static FILE *
+create_file(char *pathname, int mode)
+{
+ FILE *f;
+ f = fopen(pathname, "w+");
+ if (f == NULL) {
+ /* Try creating parent dir and then creating file. */
+ char *p = strrchr(pathname, '/');
+ if (p != NULL) {
+ *p = '\0';
+ create_dir(pathname, 0755);
+ *p = '/';
+ f = fopen(pathname, "w+");
+ }
+ }
+ return (f);
+}
+
+/* Verify the tar checksum. */
+static int
+verify_checksum(const char *p)
+{
+ int n, u = 0;
+ for (n = 0; n < 512; ++n) {
+ if (n < 148 || n > 155)
+ /* Standard tar checksum adds unsigned bytes. */
+ u += ((unsigned char *)p)[n];
+ else
+ u += 0x20;
+
+ }
+ return (u == parseoct(p + 148, 8));
+}
+
+/* Extract a tar archive. */
+static void
+untar(FILE *a, const char *path)
+{
+ char buff[512];
+ FILE *f = NULL;
+ size_t bytes_read;
+ int filesize;
+
+ printf("Extracting from %s\n", path);
+ for (;;) {
+ bytes_read = fread(buff, 1, 512, a);
+ if (bytes_read < 512) {
+ fprintf(stderr,
+ "Short read on %s: expected 512, got %d\n",
+ path, (int)bytes_read);
+ return;
+ }
+ if (is_end_of_archive(buff)) {
+ printf("End of %s\n", path);
+ return;
+ }
+ if (!verify_checksum(buff)) {
+ fprintf(stderr, "Checksum failure\n");
+ return;
+ }
+ filesize = parseoct(buff + 124, 12);
+ switch (buff[156]) {
+ case '1':
+ printf(" Ignoring hardlink %s\n", buff);
+ break;
+ case '2':
+ printf(" Ignoring symlink %s\n", buff);
+ break;
+ case '3':
+ printf(" Ignoring character device %s\n", buff);
+ break;
+ case '4':
+ printf(" Ignoring block device %s\n", buff);
+ break;
+ case '5':
+ printf(" Extracting dir %s\n", buff);
+ create_dir(buff, parseoct(buff + 100, 8));
+ filesize = 0;
+ break;
+ case '6':
+ printf(" Ignoring FIFO %s\n", buff);
+ break;
+ default:
+ printf(" Extracting file %s\n", buff);
+ f = create_file(buff, parseoct(buff + 100, 8));
+ break;
+ }
+ while (filesize > 0) {
+ bytes_read = fread(buff, 1, 512, a);
+ if (bytes_read < 512) {
+ fprintf(stderr,
+ "Short read on %s: Expected 512, got %d\n",
+ path, (int)bytes_read);
+ return;
+ }
+ if (filesize < 512)
+ bytes_read = filesize;
+ if (f != NULL) {
+ if (fwrite(buff, 1, bytes_read, f)
+ != bytes_read)
+ {
+ fprintf(stderr, "Failed write\n");
+ fclose(f);
+ f = NULL;
+ }
+ }
+ filesize -= bytes_read;
+ }
+ if (f != NULL) {
+ fclose(f);
+ f = NULL;
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ FILE *a;
+
+ ++argv; /* Skip program name */
+ for ( ;*argv != NULL; ++argv) {
+ a = fopen(*argv, "r");
+ if (a == NULL)
+ fprintf(stderr, "Unable to open %s\n", *argv);
+ else {
+ untar(a, *argv);
+ fclose(a);
+ }
+ }
+ return (0);
+}