]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Include 'examples' and 'contrib' dirs from libarchive portable distro.
authorTim Kientzle <kientzle@gmail.com>
Wed, 30 Apr 2008 22:11:33 +0000 (18:11 -0400)
committerTim Kientzle <kientzle@gmail.com>
Wed, 30 Apr 2008 22:11:33 +0000 (18:11 -0400)
SVN-Revision: 8

contrib/README [new file with mode: 0644]
contrib/libarchive.1aix53.spec [new file with mode: 0644]
contrib/libarchive.spec [new file with mode: 0644]
contrib/libarchive_autodetect-st_lib_archive.m4 [new file with mode: 0644]
examples/minitar/Makefile [new file with mode: 0644]
examples/minitar/README [new file with mode: 0644]
examples/minitar/minitar.c [new file with mode: 0644]
examples/minitar/tree.c [new file with mode: 0644]
examples/minitar/tree.h [new file with mode: 0644]
examples/tarfilter.c [new file with mode: 0644]
examples/untar.c [new file with mode: 0644]

diff --git a/contrib/README b/contrib/README
new file mode 100644 (file)
index 0000000..2eb0114
--- /dev/null
@@ -0,0 +1,32 @@
+Many people have graciously sent me configuration
+files and other useful tidbits for use with libarchive.
+
+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.
+
+======================================================================
diff --git a/contrib/libarchive.1aix53.spec b/contrib/libarchive.1aix53.spec
new file mode 100644 (file)
index 0000000..5ea3a4a
--- /dev/null
@@ -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 agains entering in 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 (file)
index 0000000..8f116ac
--- /dev/null
@@ -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 agains entering in 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 (file)
index 0000000..4419e88
--- /dev/null
@@ -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/examples/minitar/Makefile b/examples/minitar/Makefile
new file mode 100644 (file)
index 0000000..485eb8d
--- /dev/null
@@ -0,0 +1,28 @@
+CFLAGS=                                \
+       -DNO_BZIP2_CREATE       \
+       -DNO_BZIP2_EXTRACT      \
+       -DNO_COMPRESS_EXTRACT   \
+       -DNO_CPIO_EXTRACT       \
+       -DNO_CREATE             \
+       -DNO_GZIP_CREATE        \
+       -DNO_GZIP_EXTRACT       \
+
+# Omit 'tree.o' if you're not including create support
+#OBJS= minitar.o tree.o
+OBJS= minitar.o
+
+all: minitar
+
+minitar: $(OBJS)
+       cc -o minitar -static $> -larchive -lz -lbz2
+       strip minitar
+       ls -l minitar
+
+minitar.o: minitar.c
+
+tree.o: tree.c
+
+clean::
+       rm -f *.o
+       rm -f minitar
+       rm -f *~
diff --git a/examples/minitar/README b/examples/minitar/README
new file mode 100644 (file)
index 0000000..83f646c
--- /dev/null
@@ -0,0 +1,12 @@
+"minitar" is a minimal example of a program that uses libarchive to
+read/write various archive formats.  It's a more ambitious version of
+'untar.c' that includes compile-time options to enable/disable various
+features, including non-tar formats, archive creation, and automatic
+decompression support.
+
+I use this as a test bed to check for "link pollution," ensuring that
+a program using libarchive does not pull in unnecessary code.
+
+The "minitar" program is also a good starting point for anyone who
+wants to use libarchive for their own purposes, as it demonstrates
+basic usage of the library.
diff --git a/examples/minitar/minitar.c b/examples/minitar/minitar.c
new file mode 100644 (file)
index 0000000..c943916
--- /dev/null
@@ -0,0 +1,433 @@
+/*-
+ * Copyright (c) 2003-2004 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
+ *    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.
+ */
+
+/*-
+ * This is a compact "tar" program whose primary goal is small size.
+ * Statically linked, it can be under 64k.  This serves a number
+ * of goals:
+ *   o a testbed for libarchive (to check for link pollution),
+ *   o a useful tool for space-constrained systems (boot floppies, etc),
+ *   o a place to experiment with new implementation ideas for bsdtar,
+ *   o a small program to demonstrate libarchive usage.
+ *
+ * Use the following macros to control what features get incorporated:
+ *   NO_BZIP2 - Implies NO_BZIP2_CREATE and NO_BZIP2_EXTRACT
+ *   NO_BZIP2_CREATE - Suppress bzip2 compression support.
+ *   NO_BZIP2_EXTRACT - Suppress bzip2 auto-detection and decompression.
+ *   NO_COMPRESS_EXTRACT - Suppress compress(1) auto-detect and decompression.
+ *   NO_COMPRESS - Implies NO_COMPRESS_EXTRACT
+ *   NO_CREATE - Suppress all archive creation support.
+ *   NO_CPIO_EXTRACT - Suppress auto-detect and dearchiving of cpio archives.
+ *   NO_GZIP - Implies NO_GZIP_CREATE and NO_GZIP_EXTRACT
+ *   NO_GZIP_CREATE - Suppress gzip compression support.
+ *   NO_GZIP_EXTRACT - Suppress gzip auto-detection and decompression.
+ *   NO_TAR_EXTRACT - Suppress tar extraction
+ *
+ * With all of the above options (except NO_TAR_EXTRACT), you get a
+ * very small program that can recognize and extract essentially any
+ * uncompressed tar archive.  On FreeBSD 5.1, this minimal program is
+ * under 64k, statically linked.  Without any of the above options,
+ * you get a static executable of about 180k with a lot of very
+ * sophisticated modern features.
+ *
+ * Compare this to over 60k for: main(){printf("hello, world!");}
+ */
+
+#include <sys/types.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stat.h>
+
+#include <archive.h>
+#include <archive_entry.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef NO_CREATE
+#include "tree.h"
+#endif
+
+/*
+ * NO_CREATE implies NO_BZIP2_CREATE and NO_GZIP_CREATE.
+ */
+#ifdef NO_CREATE
+#undef NO_BZIP2_CREATE
+#define NO_BZIP2_CREATE
+#undef NO_GZIP_CREATE
+#define NO_GZIP_CREATE
+#endif
+
+/*
+ * The combination of NO_GZIP_CREATE and NO_GZIP_EXTRACT is
+ * equivalent to NO_GZIP.
+ */
+#ifdef NO_GZIP_CREATE
+#ifdef NO_GZIP_EXTRACT
+#undef NO_GZIP
+#define NO_GZIP
+#endif
+#endif
+
+#ifdef NO_GZIP
+#undef NO_GZIP_EXTRACT
+#define NO_GZIP_EXTRACT
+#undef NO_GZIP_CREATE
+#define NO_GZIP_CREATE
+#endif
+
+/*
+ * The combination of NO_BZIP2_CREATE and NO_BZIP2_EXTRACT is
+ * equivalent to NO_BZIP2.
+ */
+#ifdef NO_BZIP2_CREATE
+#ifdef NO_BZIP2_EXTRACT
+#undef NO_BZIP2
+#define NO_BZIP2
+#endif
+#endif
+
+#ifdef NO_BZIP2
+#undef NO_BZIP2_EXTRACT
+#define NO_BZIP2_EXTRACT
+#undef NO_BZIP2_CREATE
+#define NO_BZIP2_CREATE
+#endif
+
+/*
+ * NO_COMPRESS_EXTRACT and NO_COMPRESS are equivalent.
+ */
+#ifdef NO_COMPRESS_EXTRACT
+#undef NO_COMPRESS
+#define NO_COMPRESS
+#endif
+
+#ifdef NO_COMPRESS
+#undef NO_COMPRESS_EXTRACT
+#define NO_COMPRESS_EXTRACT
+#endif
+
+
+#ifndef NO_CREATE
+static void    create(const char *filename, int compress, const char **argv);
+#endif
+static void    errmsg(const char *);
+static void    extract(const char *filename, int do_extract, int flags);
+static int     copy_data(struct archive *, struct archive *);
+static void    msg(const char *);
+static void    usage(void);
+
+static int verbose = 0;
+
+int
+main(int argc, const char **argv)
+{
+       const char *filename = NULL;
+       int compress, flags, mode, opt;
+
+       (void)argc;
+       mode = 'x';
+       verbose = 0;
+       compress = '\0';
+       flags = ARCHIVE_EXTRACT_TIME;
+
+       /* Among other sins, getopt(3) pulls in printf(3). */
+       while (*++argv != NULL && **argv == '-') {
+               const char *p = *argv + 1;
+
+               while ((opt = *p++) != '\0') {
+                       switch (opt) {
+#ifndef NO_CREATE
+                       case 'c':
+                               mode = opt;
+                               break;
+#endif
+                       case 'f':
+                               if (*p != '\0')
+                                       filename = p;
+                               else
+                                       filename = *++argv;
+                               p += strlen(p);
+                               break;
+#ifndef NO_BZIP2_CREATE
+                       case 'j':
+                               compress = opt;
+                               break;
+#endif
+                       case 'p':
+                               flags |= ARCHIVE_EXTRACT_PERM;
+                               flags |= ARCHIVE_EXTRACT_ACL;
+                               flags |= ARCHIVE_EXTRACT_FFLAGS;
+                               break;
+                       case 't':
+                               mode = opt;
+                               break;
+                       case 'v':
+                               verbose++;
+                               break;
+                       case 'x':
+                               mode = opt;
+                               break;
+#ifndef NO_BZIP2_CREATE
+                       case 'y':
+                               compress = opt;
+                               break;
+#endif
+#ifndef NO_GZIP_CREATE
+                       case 'z':
+                               compress = opt;
+                               break;
+#endif
+                       default:
+                               usage();
+                       }
+               }
+       }
+
+       switch (mode) {
+#ifndef NO_CREATE
+       case 'c':
+               create(filename, compress, argv);
+               break;
+#endif
+       case 't':
+               extract(filename, 0, flags);
+               break;
+       case 'x':
+               extract(filename, 1, flags);
+               break;
+       }
+
+       return (0);
+}
+
+
+#ifndef NO_CREATE
+static char buff[16384];
+
+static void
+create(const char *filename, int compress, const char **argv)
+{
+       struct archive *a;
+       struct archive_entry *entry;
+       ssize_t len;
+       int fd;
+
+       a = archive_write_new();
+       switch (compress) {
+#ifndef NO_BZIP2_CREATE
+       case 'j': case 'y':
+               archive_write_set_compression_bzip2(a);
+               break;
+#endif
+#ifndef NO_GZIP_CREATE
+       case 'z':
+               archive_write_set_compression_gzip(a);
+               break;
+#endif
+       default:
+               archive_write_set_compression_none(a);
+               break;
+       }
+       archive_write_set_format_ustar(a);
+       if (strcmp(filename, "-") == 0)
+               filename = NULL;
+       archive_write_open_file(a, filename);
+       while (*argv != NULL) {
+               struct tree *t = tree_open(*argv);
+               while (tree_next(t)) {
+                       entry = archive_entry_new();
+                       archive_entry_copy_stat(entry, tree_current_stat(t));
+                       archive_entry_set_pathname(entry, tree_current_path(t));
+                       if (verbose) {
+                               msg("a ");
+                               msg(tree_current_path(t));
+                       }
+                       archive_write_header(a, entry);
+                       fd = open(tree_current_access_path(t), O_RDONLY);
+                       len = read(fd, buff, sizeof(buff));
+                       while (len > 0) {
+                               archive_write_data(a, buff, len);
+                               len = read(fd, buff, sizeof(buff));
+                       }
+                       close(fd);
+                       archive_entry_free(entry);
+                       if (verbose)
+                               msg("\n");
+               }
+               argv++;
+       }
+       archive_write_close(a);
+       archive_write_finish(a);
+}
+#endif
+
+static void
+extract(const char *filename, int do_extract, int flags)
+{
+       struct archive *a;
+       struct archive *ext;
+       struct archive_entry *entry;
+       int r;
+
+       a = archive_read_new();
+       ext = archive_write_disk_new();
+       archive_write_disk_set_options(ext, flags);
+#ifndef NO_BZIP2_EXTRACT
+       archive_read_support_compression_bzip2(a);
+#endif
+#ifndef NO_GZIP_EXTRACT
+       archive_read_support_compression_gzip(a);
+#endif
+#ifndef NO_COMPRESS_EXTRACT
+       archive_read_support_compression_compress(a);
+#endif
+#ifndef NO_TAR_EXTRACT
+       archive_read_support_format_tar(a);
+#endif
+#ifndef NO_CPIO_EXTRACT
+       archive_read_support_format_cpio(a);
+#endif
+       if (filename != NULL && strcmp(filename, "-") == 0)
+               filename = NULL;
+       if ((r = archive_read_open_file(a, filename, 10240))) {
+               errmsg(archive_error_string(a));
+               errmsg("\n");
+               exit(r);
+       }
+       for (;;) {
+               r = archive_read_next_header(a, &entry);
+               if (r == ARCHIVE_EOF)
+                       break;
+               if (r != ARCHIVE_OK) {
+                       errmsg(archive_error_string(a));
+                       errmsg("\n");
+                       exit(1);
+               }
+               if (verbose && do_extract)
+                       msg("x ");
+               if (verbose || !do_extract)
+                       msg(archive_entry_pathname(entry));
+               if (do_extract) {
+                       r = archive_write_header(ext, entry);
+                       if (r != ARCHIVE_OK)
+                               errmsg(archive_error_string(a));
+                       else
+                               copy_data(a, ext);
+               }
+               if (verbose || !do_extract)
+                       msg("\n");
+       }
+       archive_read_close(a);
+       archive_read_finish(a);
+       exit(0);
+}
+
+static int
+copy_data(struct archive *ar, struct archive *aw)
+{
+       int r;
+       const void *buff;
+       size_t size;
+       off_t offset;
+
+       for (;;) {
+               r = archive_read_data_block(ar, &buff, &size, &offset);
+               if (r == ARCHIVE_EOF) {
+                       errmsg(archive_error_string(ar));
+                       return (ARCHIVE_OK);
+               }
+               if (r != ARCHIVE_OK)
+                       return (r);
+               r = archive_write_data_block(aw, buff, size, offset);
+               if (r != ARCHIVE_OK) {
+                       errmsg(archive_error_string(ar));
+                       return (r);
+               }
+       }
+}
+
+static void
+msg(const char *m)
+{
+       write(1, m, strlen(m));
+}
+
+static void
+errmsg(const char *m)
+{
+       write(2, m, strlen(m));
+}
+
+static void
+usage(void)
+{
+/* Many program options depend on compile options. */
+       const char *m = "Usage: minitar [-"
+#ifndef NO_CREATE
+           "c"
+#endif
+#ifndef        NO_BZIP2
+           "j"
+#endif
+           "tvx"
+#ifndef NO_BZIP2
+           "y"
+#endif
+#ifndef NO_GZIP
+           "z"
+#endif
+           "] [-f file] [file]\n";
+
+       errmsg(m);
+       exit(1);
+}
+
+#if 0
+/*
+ * These override functions in libc (which are called by libarchive).
+ * The libc functions are pretty large; this bit of subterfuge
+ * reduces the size of the executable by about 70%.
+ */
+struct passwd *getpwnam(const char *);
+struct group *getgrnam(const char *);
+
+struct passwd *
+getpwnam(const char *login)
+{
+        (void)login;
+        return (NULL);
+}
+
+struct group *
+getgrnam(const char *name)
+{
+        (void)name;
+        return (NULL);
+}
+#endif
diff --git a/examples/minitar/tree.c b/examples/minitar/tree.c
new file mode 100644 (file)
index 0000000..8af0b4d
--- /dev/null
@@ -0,0 +1,423 @@
+/*-
+ * Copyright (c) 2003-2004 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
+ *    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.
+ */
+
+/*-
+ * There is 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 <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tree.h"
+
+/*
+ * TODO:
+ *    1) Loop checking.
+ *    3) Arbitrary logical traversals by closing/reopening intermediate fds.
+ */
+
+struct tree_entry {
+       struct tree_entry *next;
+       char *name;
+       size_t dirname_length;
+       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 needsTraversal 4 /* This entry hasn't yet been traversed. */
+
+/*
+ * Local data for this package.
+ */
+struct tree {
+       struct tree_entry       *stack;
+       DIR     *d;
+       int      initialDirFd;
+       int      flags;
+
+       char    *buff;
+       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. */
+
+
+#define HAVE_DIRENT_D_NAMLEN 1
+#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
+static void
+dumpStack(struct tree *t)
+{
+       struct tree_entry *te;
+
+       printf("\tbuff: %s\n", t->buff);
+       printf("\tpwd: "); fflush(stdout); system("pwd");
+       printf("\tstack:\n");
+       for (te = t->stack; te != NULL; te = te->next) {
+               printf("\t\tte->name: %s %s\n", te->name, te->flags & needsTraversal ? "" : "*");
+       }
+}
+#endif
+
+/*
+ * Add a directory path to the current stack.
+ */
+static void
+tree_add(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 = needsTraversal;
+       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)
+{
+       if (t->buff != NULL)
+               t->buff[t->dirname_length] = '\0';
+
+       /* 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);
+       }
+       t->basename = t->buff + t->dirname_length;
+       t->path_length = t->dirname_length + name_length;
+       if (t->dirname_length > 0) {
+               *t->basename++ = '/';
+               t->path_length ++;
+       }
+       strcpy(t->basename, name);
+}
+
+/*
+ * 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;
+
+       te = t->stack;
+       t->stack = te->next;
+       t->dirname_length = te->dirname_length;
+       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 (1);
+       }
+
+       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 (1);
+                       }
+               }
+
+               /* If the current dir needs to be traversed, set it up. */
+               if (t->stack->flags & needsTraversal) {
+                       tree_append(t, t->stack->name, strlen(t->stack->name));
+                       t->stack->flags &= ~needsTraversal;
+                       /* 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;
+                       }
+                       if (chdir(t->stack->name) == 0) {
+                               t->depth++;
+                               t->dirname_length = t->path_length;
+                               t->d = opendir(".");
+                       } else
+                               tree_pop(t);
+                       continue;
+               }
+
+               /* We've done everything necessary for the top stack entry. */
+               tree_ascend(t);
+               tree_pop(t);
+       }
+       return (0);
+}
+
+/*
+ * Called by the client to mark the directory just returned from
+ * tree_next() as needing to be visited.
+ */
+void
+tree_descend(struct tree *t)
+{
+       const struct stat *s = tree_current_lstat(t);
+
+       if (S_ISDIR(s->st_mode)) {
+               tree_add(t, t->basename);
+               t->stack->flags |= isDir;
+       }
+
+       if (S_ISLNK(s->st_mode) && S_ISDIR(tree_current_stat(t)->st_mode)) {
+               tree_add(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)) {
+               stat(t->basename, &t->st);
+               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)) {
+               lstat(t->basename, &t->lst);
+               t->flags |= hasLstat;
+       }
+       return (&t->lst);
+}
+
+/*
+ * 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);
+}
+
+
+#if 0
+/* Main function for testing. */
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+       size_t max_path_len = 0;
+       int max_depth = 0;
+
+       system("pwd");
+       while (*++argv) {
+               struct tree *t = tree_open(*argv);
+               while (tree_next(t)) {
+                       size_t path_len = tree_current_pathlen(t);
+                       int depth = tree_current_depth(t);
+                       if (path_len > max_path_len)
+                               max_path_len = path_len;
+                       if (depth > max_depth)
+                               max_depth = depth;
+                       printf("%s\n", tree_current_path(t));
+                       if (S_ISDIR(tree_current_lstat(t)->st_mode))
+                               tree_descend(t); /* Descend into every dir. */
+               }
+               tree_close(t);
+               printf("Max path length: %d\n", max_path_len);
+               printf("Max depth: %d\n", max_depth);
+               printf("Final open count: %d\n", t->openCount);
+               printf("Max open count: %d\n", t->maxOpenCount);
+               fflush(stdout);
+               system("pwd");
+       }
+       return (0);
+}
+#endif
diff --git a/examples/minitar/tree.h b/examples/minitar/tree.h
new file mode 100644 (file)
index 0000000..554e6c2
--- /dev/null
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2003-2004 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
+ *    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.
+ */
+
+/*-
+ * 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>
+
+struct tree;
+
+struct tree *tree_open(const char *);
+/* Returns TRUE if there is a next entry.  Zero if there is no next entry. */
+int tree_next(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,
+ * you should take advantage of it here if you need it.
+ */
+const struct stat *tree_current_stat(struct tree *);
+const struct stat *tree_current_lstat(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 *);
+void tree_close(struct tree *);
diff --git a/examples/tarfilter.c b/examples/tarfilter.c
new file mode 100644 (file)
index 0000000..b7e08cf
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is in the public domain.
+ *
+ * Feel free to use it as you wish.
+ */
+
+/*
+ * This example program reads an archive from stdin (which can be in
+ * any format recognized by libarchive) and writes certain entries to
+ * an uncompressed ustar archive on stdout.  This is a template for
+ * many kinds of archive manipulation: converting formats, resetting
+ * ownership, inserting entries, removing entries, etc.
+ *
+ * To compile:
+ * gcc -Wall -o tarfilter tarfilter.c -larchive -lz -lbz2
+ */
+
+#include <sys/stat.h>
+#include <archive.h>
+#include <archive_entry.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void
+die(char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       fprintf(stderr, "\n");
+       exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+       char buff[8192];
+       ssize_t len;
+       int r;
+       mode_t m;
+       struct archive *ina;
+       struct archive *outa;
+       struct archive_entry *entry;
+
+       /* Read an archive from stdin, with automatic format detection. */
+       ina = archive_read_new();
+       if (ina == NULL)
+               die("Couldn't create archive reader.");
+       if (archive_read_support_compression_all(ina) != ARCHIVE_OK)
+               die("Couldn't enable decompression");
+       if (archive_read_support_format_all(ina) != ARCHIVE_OK)
+               die("Couldn't enable read formats");
+       if (archive_read_open_fd(ina, 0, 10240) != ARCHIVE_OK)
+               die("Couldn't open input archive");
+
+       /* Write an uncompressed ustar archive to stdout. */
+       outa = archive_write_new();
+       if (outa == NULL)
+               die("Couldn't create archive writer.");
+       if (archive_write_set_compression_none(outa) != ARCHIVE_OK)
+               die("Couldn't enable compression");
+       if (archive_write_set_format_ustar(outa) != ARCHIVE_OK)
+               die("Couldn't set output format");
+       if (archive_write_open_fd(outa, 1) != ARCHIVE_OK)
+               die("Couldn't open output archive");
+
+       /* Examine each entry in the input archive. */
+       while ((r = archive_read_next_header(ina, &entry)) == ARCHIVE_OK) {
+               fprintf(stderr, "%s: ", archive_entry_pathname(entry));
+
+               /* Skip anything that isn't a regular file. */
+               if (!S_ISREG(archive_entry_mode(entry))) {
+                       fprintf(stderr, "skipped\n");
+                       continue;
+               }
+
+               /* Make everything owned by root/wheel. */
+               archive_entry_set_uid(entry, 0);
+               archive_entry_set_uname(entry, "root");
+               archive_entry_set_gid(entry, 0);
+               archive_entry_set_gname(entry, "wheel");
+
+               /* Make everything permission 0744, strip SUID, etc. */
+               m = archive_entry_mode(entry);
+               archive_entry_set_mode(entry, (m & ~07777) | 0744);
+
+               /* Copy input entries to output archive. */
+               if (archive_write_header(outa, entry) != ARCHIVE_OK)
+                       die("Error writing output archive");
+               if (archive_entry_size(entry) > 0) {
+                       len = archive_read_data(ina, buff, sizeof(buff));
+                       while (len > 0) {
+                               if (archive_write_data(outa, buff, len) != len)
+                                       die("Error writing output archive");
+                               len = archive_read_data(ina, buff, sizeof(buff));
+                       }
+                       if (len < 0)
+                               die("Error reading input archive");
+               }
+               fprintf(stderr, "copied\n");
+       }
+       if (r != ARCHIVE_EOF)
+               die("Error reading archive");
+       /* Close the archives.  */
+       if (archive_read_finish(ina) != ARCHIVE_OK)
+               die("Error closing input archive");
+       if (archive_write_finish(outa) != ARCHIVE_OK)
+               die("Error closing output archive");
+       return (0);
+}
diff --git a/examples/untar.c b/examples/untar.c
new file mode 100644 (file)
index 0000000..88f6dc2
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * This file is in the public domain.
+ * Use it as you wish.
+ */
+
+/*
+ * This is a compact tar extraction program whose primary goal is
+ * small size.  Statically linked, it can be under 64k, depending on
+ * how cleanly factored your system libraries are.  Note that this
+ * uses the standard libarchive, without any special recompilation.
+ * The only functional concession is that this program uses the
+ * uid/gid from the archive instead of doing uname/gname lookups.
+ * (Call archive_write_disk_set_standard_lookup() to enable
+ * uname/gname lookups, but be aware that this can add 500k or more to
+ * a static executable, depending on the system libraries.)
+ *
+ * To build:
+ * gcc -static -Wall -o untar untar.c -larchive
+ * strip untar
+ *
+ * For fun, statically compile the following simple hello.c program
+ * and compare the size.  (On my system, the result is 89k, untar is
+ * 69k.)
+ *
+ * #include <stdio.h>
+ * int main(int argc, char **argv) {
+ *    printf("hello, world\n");
+ *    return(0);
+ * }
+ */
+
+#include <sys/types.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/stat.h>
+
+#include <archive.h>
+#include <archive_entry.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void    errmsg(const char *);
+static void    extract(const char *filename, int do_extract, int flags);
+static int     copy_data(struct archive *, struct archive *);
+static void    msg(const char *);
+static void    usage(void);
+
+static int verbose = 0;
+
+int
+main(int argc, const char **argv)
+{
+       const char *filename = NULL;
+       int compress, flags, mode, opt;
+
+       (void)argc;
+       mode = 'x';
+       verbose = 0;
+       compress = '\0';
+       flags = ARCHIVE_EXTRACT_TIME;
+
+       /* Among other sins, getopt(3) pulls in printf(3). */
+       while (*++argv != NULL && **argv == '-') {
+               const char *p = *argv + 1;
+
+               while ((opt = *p++) != '\0') {
+                       switch (opt) {
+                       case 'f':
+                               if (*p != '\0')
+                                       filename = p;
+                               else
+                                       filename = *++argv;
+                               p += strlen(p);
+                               break;
+                       case 'p':
+                               flags |= ARCHIVE_EXTRACT_PERM;
+                               flags |= ARCHIVE_EXTRACT_ACL;
+                               flags |= ARCHIVE_EXTRACT_FFLAGS;
+                               break;
+                       case 't':
+                               mode = opt;
+                               break;
+                       case 'v':
+                               verbose++;
+                               break;
+                       case 'x':
+                               mode = opt;
+                               break;
+                       default:
+                               usage();
+                       }
+               }
+       }
+
+       switch (mode) {
+       case 't':
+               extract(filename, 0, flags);
+               break;
+       case 'x':
+               extract(filename, 1, flags);
+               break;
+       }
+
+       return (0);
+}
+
+
+static void
+extract(const char *filename, int do_extract, int flags)
+{
+       struct archive *a;
+       struct archive *ext;
+       struct archive_entry *entry;
+       int r;
+
+       a = archive_read_new();
+       ext = archive_write_disk_new();
+       archive_write_disk_set_options(ext, flags);
+       /*
+        * Note: archive_write_disk_set_standard_lookup() is useful
+        * here, but it requires library routines that can add 500k or
+        * more to a static executable.
+        */
+       archive_read_support_format_tar(a);
+       /*
+        * On my system, enabling other archive formats adds 20k-30k
+        * each.  Enabling gzip decompression adds about 20k.
+        * Enabling bzip2 is more expensive because the libbz2 library
+        * isn't very well factored.
+        */
+       if (filename != NULL && strcmp(filename, "-") == 0)
+               filename = NULL;
+       if ((r = archive_read_open_file(a, filename, 10240))) {
+               errmsg(archive_error_string(a));
+               errmsg("\n");
+               exit(r);
+       }
+       for (;;) {
+               r = archive_read_next_header(a, &entry);
+               if (r == ARCHIVE_EOF)
+                       break;
+               if (r != ARCHIVE_OK) {
+                       errmsg(archive_error_string(a));
+                       errmsg("\n");
+                       exit(1);
+               }
+               if (verbose && do_extract)
+                       msg("x ");
+               if (verbose || !do_extract)
+                       msg(archive_entry_pathname(entry));
+               if (do_extract) {
+                       r = archive_write_header(ext, entry);
+                       if (r != ARCHIVE_OK)
+                               errmsg(archive_error_string(a));
+                       else
+                               copy_data(a, ext);
+               }
+               if (verbose || !do_extract)
+                       msg("\n");
+       }
+       archive_read_close(a);
+       archive_read_finish(a);
+       exit(0);
+}
+
+static int
+copy_data(struct archive *ar, struct archive *aw)
+{
+       int r;
+       const void *buff;
+       size_t size;
+       off_t offset;
+
+       for (;;) {
+               r = archive_read_data_block(ar, &buff, &size, &offset);
+               if (r == ARCHIVE_EOF) {
+                       errmsg(archive_error_string(ar));
+                       return (ARCHIVE_OK);
+               }
+               if (r != ARCHIVE_OK)
+                       return (r);
+               r = archive_write_data_block(aw, buff, size, offset);
+               if (r != ARCHIVE_OK) {
+                       errmsg(archive_error_string(ar));
+                       return (r);
+               }
+       }
+}
+
+static void
+msg(const char *m)
+{
+       write(1, m, strlen(m));
+}
+
+static void
+errmsg(const char *m)
+{
+       write(2, m, strlen(m));
+}
+
+static void
+usage(void)
+{
+       const char *m = "Usage: untar [-tvx] [-f file] [file]\n";
+       errmsg(m);
+       exit(1);
+}