From 296578554d9e5bcd024d1ddce7b5314ff18a109f Mon Sep 17 00:00:00 2001 From: mm Date: Tue, 20 Dec 2011 22:47:56 +0000 Subject: Vendor import of libarchive (release/2.8, r3824) Obtained from: http://libarchive.googlecode.com/svn/release/2.8 --- contrib/README | 32 ++ contrib/libarchive.1aix53.spec | 160 +++++++ contrib/libarchive.spec | 147 +++++++ contrib/libarchive_autodetect-st_lib_archive.m4 | 154 +++++++ contrib/psota-benchmark/results.txt | 122 ++++++ contrib/psota-benchmark/tcp.sh | 104 +++++ contrib/shar/Makefile | 14 + contrib/shar/shar.1 | 128 ++++++ contrib/shar/shar.c | 314 ++++++++++++++ contrib/shar/tree.c | 542 ++++++++++++++++++++++++ contrib/shar/tree.h | 115 +++++ contrib/shar/tree_config.h | 78 ++++ contrib/untar.c | 225 ++++++++++ 13 files changed, 2135 insertions(+) create mode 100644 contrib/README create mode 100644 contrib/libarchive.1aix53.spec create mode 100644 contrib/libarchive.spec create mode 100644 contrib/libarchive_autodetect-st_lib_archive.m4 create mode 100644 contrib/psota-benchmark/results.txt create mode 100644 contrib/psota-benchmark/tcp.sh create mode 100644 contrib/shar/Makefile create mode 100644 contrib/shar/shar.1 create mode 100644 contrib/shar/shar.c create mode 100644 contrib/shar/tree.c create mode 100644 contrib/shar/tree.h create mode 100644 contrib/shar/tree_config.h create mode 100644 contrib/untar.c (limited to 'contrib') diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..2eb0114f --- /dev/null +++ b/contrib/README @@ -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 + +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 + +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 index 0000000..1c3bb7d --- /dev/null +++ b/contrib/libarchive.1aix53.spec @@ -0,0 +1,160 @@ +# $LastChangedRevision: 8 $, $LastChangedDate: 2008-05-01 00:11:33 +0200 (ĹĄt , 01 mĂĄj 2008) $ +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 +All persons listed below can be reached at @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 index 0000000..1b81f6d --- /dev/null +++ b/contrib/libarchive.spec @@ -0,0 +1,147 @@ +# $LastChangedRevision: 8 $, $LastChangedDate: 2008-05-01 00:11:33 +0200 (ĹĄt , 01 mĂĄj 2008) $ +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 +All persons listed below can be reached at @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 index 0000000..4419e88 --- /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 +dnl #endif /* HAVE_LIBARCHIVE */ +dnl +dnl @category InstalledPackages +dnl @author Andre Stechert +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 0000000..8197b28 --- /dev/null +++ b/contrib/psota-benchmark/results.txt @@ -0,0 +1,122 @@ +ODP: [Bug-tar] GNU tar, star and BSD tar speed comparision +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 diff --git a/contrib/psota-benchmark/tcp.sh b/contrib/psota-benchmark/tcp.sh new file mode 100644 index 0000000..6c0e4b5 --- /dev/null +++ b/contrib/psota-benchmark/tcp.sh @@ -0,0 +1,104 @@ +#!/bin/sh +# tar comparision program +# 2007-10-25 Jan Psota + +n=3 # number of repetitions +TAR=(bsdtar gnutar star) # TApeArchivers 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 Comparision Program here. +It currently compares: BSD tar (bsdtar), GNU tar (gnutar) and star in archive +creation, listing, extraction and archive-to-extracted comparision. +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 +echo +for tar in [EMAIL PROTECTED]; do echo -ne "$tar:\t"; $tar --version | head -1; +done + +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 [EMAIL PROTECTED]; 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 0000000..3bd94d4 --- /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 diff --git a/contrib/shar/shar.1 b/contrib/shar/shar.1 new file mode 100644 index 0000000..e3152f2 --- /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 +\&... + +\&... +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 0000000..12c8425 --- /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 +#ifdef __FBSDID +__FBSDID("$FreeBSD$"); +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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_finish(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 0000000..d5a04ab --- /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 +#endif +#ifdef HAVE_DIRENT_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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 +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 0000000..ff38f53 --- /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 +#include + +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 0000000..8dfd90b --- /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 /* 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 +#include +#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 0000000..dc092d3 --- /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 + * + * 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 +#include +#include + +/* This is for mkdir(); this may need to be changed for some platforms. */ +#include /* 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') { + ++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, 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, 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); +} -- cgit v1.1