Merge from vendor branch LIBARCHIVE:
authorPeter Avalos <pavalos@dragonflybsd.org>
Sun, 25 Mar 2007 22:19:51 +0000 (22:19 +0000)
committerPeter Avalos <pavalos@dragonflybsd.org>
Sun, 25 Mar 2007 22:19:51 +0000 (22:19 +0000)
Import libarchive and bsdtar version 2.0.25

73 files changed:
contrib/libarchive-2.0/COPYING [new file with mode: 0644]
contrib/libarchive-2.0/NEWS [new file with mode: 0644]
contrib/libarchive-2.0/README [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive.h.in [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_check_magic.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_entry.3 [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_entry.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_entry.h [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_platform.h [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_private.h [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read.3 [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_data_into_buffer.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_data_into_fd.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_extract.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_open_fd.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_open_file.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_open_filename.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_open_memory.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_private.h [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_compression_all.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_compression_bzip2.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_compression_compress.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_compression_gzip.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_compression_none.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_format_all.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_format_cpio.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_format_empty.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_format_iso9660.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_format_tar.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_read_support_format_zip.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_string.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_string.h [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_string_sprintf.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_util.3 [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_util.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_virtual.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write.3 [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_disk.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_disk_private.h [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_disk_set_standard_lookup.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_open_fd.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_open_file.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_open_filename.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_open_memory.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_private.h [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_set_compression_bzip2.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_set_compression_gzip.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_set_compression_none.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_set_format.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_set_format_by_name.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_set_format_cpio.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_set_format_pax.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_set_format_shar.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/archive_write_set_format_ustar.c [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/config_freebsd.h [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/config_windows.h [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/libarchive-formats.5 [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/libarchive.3 [new file with mode: 0644]
contrib/libarchive-2.0/libarchive/tar.5 [new file with mode: 0644]
contrib/libarchive-2.0/tar/bsdtar.1 [new file with mode: 0644]
contrib/libarchive-2.0/tar/bsdtar.c [new file with mode: 0644]
contrib/libarchive-2.0/tar/bsdtar.h [new file with mode: 0644]
contrib/libarchive-2.0/tar/bsdtar_platform.h [new file with mode: 0644]
contrib/libarchive-2.0/tar/getdate.y [new file with mode: 0644]
contrib/libarchive-2.0/tar/matching.c [new file with mode: 0644]
contrib/libarchive-2.0/tar/read.c [new file with mode: 0644]
contrib/libarchive-2.0/tar/tree.c [new file with mode: 0644]
contrib/libarchive-2.0/tar/tree.h [new file with mode: 0644]
contrib/libarchive-2.0/tar/util.c [new file with mode: 0644]
contrib/libarchive-2.0/tar/write.c [new file with mode: 0644]
contrib/libarchive-2.0/version [new file with mode: 0644]

diff --git a/contrib/libarchive-2.0/COPYING b/contrib/libarchive-2.0/COPYING
new file mode 100644 (file)
index 0000000..6128d17
--- /dev/null
@@ -0,0 +1,26 @@
+All of the C source code and documentation in this package is subject
+to the following:
+
+Copyright (c) 2003-2006 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.
diff --git a/contrib/libarchive-2.0/NEWS b/contrib/libarchive-2.0/NEWS
new file mode 100644 (file)
index 0000000..d5c76a3
--- /dev/null
@@ -0,0 +1,148 @@
+
+Mar 12, 2007: libarchive 2.0.25 released
+Mar 12, 2007: Fix broken --unlink flag.
+
+Mar 11, 2007: libarchive 2.0.24 released
+Mar 10, 2007: Correct an ACL blunder that causes any ACL with an entry
+    that refers to a non-existent user or group to not be restored correctly.
+    The fix both makes the parser more tolerant (so that archives created
+    with the buggy ACLs can be read now) and corrects the ACL formatter.
+Mar 10, 2007: More work on test portability to Linux.
+
+Mar 10, 2007: libarchive 2.0.22 released
+Mar 10, 2007: Header cleanups; added linux/fs.h, removed
+    some unnecessary headers, added #include guards in bsdtar.
+    If you see any obvious compile failures from this, let me know.
+Mar 10, 2007: Work on bsdtar test scripts: not yet robust enough
+    to enable as part of "make check", but getting better.
+Mar 10, 2007: libarchive now returns ARCHIVE_FAILED when
+    a header write fails in a way that only affects this item.
+    Less bad than ARCHIVE_FATAL, but worse than ARCHIVE_WARN.
+
+Mar 07, 2007: libarchive 2.0.21 released
+Mar 07, 2007: Add some ACL tests (only for the system-independent
+    portion of the ACL support for now).
+Mar 07, 2007: tar's ability to read ACLs off disk got
+    turned off for FreeBSD; re-enable it.  (ACL restores and
+    libarchive support for storing/reading ACLs from pax
+    archives was unaffected.)
+
+Mar 02, 2007: libarchive 2.0.20 released
+Mar 2, 2007:  It's not perfect, but it's pretty good.
+    Libarchive 2.0 is officially out of beta.
+
+Feb 28, 2007: libarchive 2.0b17 released
+Feb 27, 2007: Make the GID restore checks more robust by checking
+    whether the current user has too few or too many privileges.
+
+Feb 26, 2007: libarchive 2.0b15 released
+Feb 26, 2007: Don't lose symlinks when extracting from ISOs.
+    Thanks to Diego "Flameeyes" Pettenò for telling me about the
+    broken testcase on Gentoo that (finally!) led me to the cause
+    of this long-standing bug.
+
+Feb 26, 2007: libarchive 2.0b14 released
+Feb 26, 2007: Fix a broken test on platforms that lack lchmod().
+
+Feb 25, 2007: libarchive 2.0b13 released
+Feb 25, 2007: Empty archives were being written as empty files,
+    without a proper end-of-archive marker.  Fixed.
+
+Feb 23, 2007: libarchive 2.0b12 released
+Feb 22, 2007: Basic security checks added: _EXTRACT_SECURE_NODOTDOT
+    and _EXTRACT_SECURE_SYMLINK.  These checks used to be in bsdtar,
+    but they belong down in libarchive where they can be used by
+    other tools and where they can be better optimized.
+
+Feb 11, 2007: libarchive 2.0b11 released
+Feb 10, 2007: Fixed a bunch of errors in libarchive's handling
+    of EXTRACT_PERM and EXTRACT_OWNER, especially relating
+    to SUID and SGID bits.
+
+Jan 31, 2007: libarchive 2.0b9 released
+Jan 31, 2007: Added read support for "empty" archives as a
+    distinct archive format.  Bsdtar uses this to handle, e.g.,
+    "touch foo.tar; tar -rf foo.tar"
+
+Jan 22, 2007: libarchive 2.0b6 released
+Jan 22, 2007: archive_write_disk API is now in place.  It provides
+    a finer-grained interface than archive_read_extract.  In particular,
+    you can use it to create objects on disk without having an archive
+    around (just feed it archive_entry objects describing what you
+    want to create), you can override the uname/gname-to-uid/gid lookups
+    (minitar uses this to avoid getpwXXX() and getgrXXX() bloat).
+
+Jan 09, 2007: libarchive 2.0a3 released
+Jan 9, 2007: archive_extract is now much better; it handles the
+    most common cases with a minimal number of system calls.
+    Some features still need a lot of testing, especially corner
+    cases involving objects that already exist on disk.   I expect
+    the next round of API overhaul will simplify building test cases.
+Jan 9, 2007: a number of fixes thanks to Colin Percival, especially
+    corrections to the skip() framework and handling of large files.
+Jan 9, 2007: Fixes for large ISOs.  The code should correctly handle
+    very large ISOs with entries up to 4G.   Thanks to Robert Sciuk
+    for pointing out these issues.
+
+Sep 05, 2006: libarchive 1.3.1 released
+Sep 5, 2006: Bump version to 1.3 for new I/O wrappers.
+Sep 4, 2006: New memory and FILE read/write wrappers.
+Sep 4, 2006: libarchive test harness is now minimally functional;
+    it's located a few minor bugs in error-handling logic
+
+Aug 17, 2006: libarchive 1.2.54 released
+Aug 17, 2006: Outline ABI changes for libarchive 2.0; these
+   are protected behind #ifdef's until I think I've found everything
+   that needs to change.
+Aug 17, 2006: Fix error-handling in archive_read/write_close()
+   They weren't returning any errors before.
+Aug 17, 2006: Fix recursive-add logic to not trigger if it's not set
+   Fixes a bug adding files when writing archive to pipe or when
+   using archive_write_open() directly.
+Jul 2006: New "skip" handling improves performance extracting
+   single files from large uncompressed archives.
+
+Mar 21, 2006: 1.2.52 released
+Mar 21, 2006: Fix -p on platforms that don't have platform-specific
+   extended attribute code.
+Mar 20, 2006: Add NEWS file; fill in some older history from other
+   files.  I'll try to keep this file up-to-date from now on.
+
+OLDER NEWS SUMMARIES
+
+Mar 19, 2006: libarchive 1.2.51 released
+Mar 18, 2006: Many fixes to extended attribute support, including a redesign
+   of the storage format to simplify debugging.
+Mar 12, 2006: Remove 'tp' support; it was a fun idea, but not worth
+   spending much time on.
+Mar 11, 2006: Incorporated Jaakko Heinonen's still-experimental support
+   for extended attributes (Currently Linux-only.).
+Mar 11, 2006: Reorganized distribution package: There is now one tar.gz
+   file that builds both libarchive and bsdtar.
+Feb 13, 2006: Minor bug fixes: correctly read cpio device entries, write
+   Pax attribute entry names.
+Nov 7, 2005: Experimental 'tp' format support in libarchive.  Feedback
+   appreciated; this is not enabled by archive_read_support_format_all()
+   yet as I'm not quite content with the format detection heuristics.
+Nov 7, 2005: Some more portability improvements thanks to Darin Broady,
+   minor bugfixes.
+Oct 12, 2005: Use GNU libtool to build shared libraries on many systems.
+Aug 9, 2005: Correctly detect that MacOS X does not have POSIX ACLs.
+Apr 17, 2005: Kees Zeelenberg has ported libarchive and bsdtar to Windows:
+   http://gnuwin32.sourceforge.net/
+Apr 11, 2005: Extended Zip/Zip64 support thanks to Dan Nelson.  -L/-h
+   fix from Jaakko Heinonen.
+Mar 12, 2005: archive_read_extract can now handle very long
+   pathnames (I've tested with pathnames up to 1MB).
+Mar 12, 2005: Marcus Geiger has written an article about libarchive
+   http://xsnil.antbear.org/2005/02/05/archive-mit-libarchive-verarbeiten/
+   including examples of using it from Objective-C.  His MoinX
+   http://moinx.antbear.org/ desktop Wiki uses
+   libarchive for archiving and restoring Wiki pages.
+Jan 22, 2005: Preliminary ZIP extraction support,
+    new directory-walking code for bsdtar.
+Jan 16, 2005: ISO9660 extraction code added; manpage corrections.
+May 22, 2004: Many gtar-compatible long options have been added; almost
+    all FreeBSD ports extract correctly with bsdtar.
+May 18, 2004: bsdtar can read Solaris, HP-UX, Unixware, star, gtar,
+    and pdtar archives.
diff --git a/contrib/libarchive-2.0/README b/contrib/libarchive-2.0/README
new file mode 100644 (file)
index 0000000..872649b
--- /dev/null
@@ -0,0 +1,114 @@
+README for libarchive bundle.
+
+This distribution bundle includes the following components:
+
+   * libarchive: a library for reading and writing streaming archives
+   * tar: the 'bsdtar' program is a full-featured 'tar'
+          replacement built on libarchive
+   * minitar: a compact sample demonstrating use of libarchive
+
+The top-level directory contains the following information files:
+   * AUTHORS - who wrote all of this
+   * COPYING - what you can do with this
+   * ChangeLog - highlights of recent changes
+   * INSTALL - installation instructions
+   * README - this file
+   * configure - configuration script, see INSTALL for details.
+
+The following files in the top-level directory are used by the
+'configure' script:
+
+   * Makefile.am, aclocal.m4, configure.ac
+       - used to build this distribution, only needed by maintainers
+   * Makefile.in, config.h.in
+       - templates used by configure script
+   * config.aux/* - auxiliary scripts used by build system
+
+Guide to Documentation installed by this system:
+ * bsdtar.1 explains the use of the bsdtar program
+ * libarchive.3 gives an overview of the library as a whole
+ * archive_read.3 and archive_write.3 provide detailed calling
+   sequences for the read and write APIs
+ * archive_entry.3 details the "struct archive_entry" utility class
+ * libarchive-formats.5 documents the file formats supported by the library
+ * tar.5 provides some detailed information about a variety of different
+   "tar" formats.
+
+You should also read the copious comments in "archive.h" and the source
+code for the sample "bsdtar" program for more details.  Please let me know
+about any errors or omissions you find.
+
+Currently, the library automatically detects and reads the following:
+  * gzip compression
+  * bzip2 compression
+  * compress/LZW compression
+  * GNU tar format (including GNU long filenames, long link names, and
+    sparse files)
+  * Solaris 9 extended tar format (including ACLs)
+  * Old V7 tar archives
+  * POSIX ustar
+  * POSIX pax interchange format
+  * POSIX octet-oriented cpio
+  * SVR4 ASCII cpio
+  * Binary cpio (big-endian or little-endian)
+  * ISO9660 CD-ROM images (with optional Rockridge extensions)
+  * ZIP archives (with uncompressed or "deflate" compressed entries)
+  * Experimental support for Unix 4th Edition "tp" archives
+
+The library can write:
+  * gzip compression
+  * bzip2 compression
+  * POSIX ustar
+  * POSIX pax interchange format
+  * "restricted" pax format, which will create ustar archives except for
+    entries that require pax extensions (for long filenames, ACLs, etc).
+  * POSIX octet-oriented cpio
+  * shar archives
+
+Notes about the library architecture:
+
+ * This is a heavily stream-oriented system.  There is no direct
+   support for in-place modification or random access and no intention
+   of ever adding such support.  Adding such support would require
+   sacrificing a lot of other features, so don't bother asking.
+
+ * The library is designed to be extended with new compression and
+   archive formats.  The only requirement is that the format be
+   readable or writable as a stream and that each archive entry be
+   independent.
+
+ * On read, compression and format are always detected automatically.
+
+ * I've attempted to minimize static link pollution.  If you don't
+   explicitly invoke a particular feature (such as support for a
+   particular compression or format), it won't get pulled in.
+   In particular, if you don't explicitly enable a particular
+   compression or decompression support, you won't need to link
+   against the corresponding compression or decompression libraries.
+   This also reduces the size of statically-linked binaries in
+   environments where that matters.
+
+ * On read, the library accepts whatever blocks you hand it.
+   Your read callback is free to pass the library a byte at a time
+   or mmap the entire archive and give it to the library at once.
+   On write, the library always produces correctly-blocked
+   output.
+
+ * The object-style approach allows you to have multiple archive streams
+   open at once.  bsdtar uses this in its "@archive" extension.
+
+ * The archive itself is read/written using callback functions.
+   You can read an archive directly from an in-memory buffer or
+   write it to a socket, if you wish.  There are some utility
+   functions to provide easy-to-use "open file," etc, capabilities.
+
+ * The read/write APIs are designed to allow individual entries
+   to be read or written to any data source:  You can create
+   a block of data in memory and add it to a tar archive without
+   first writing a temporary file.  You can also read an entry from
+   an archive and write the data directly to a socket.  If you want
+   to read/write entries to disk, there are convenience functions to
+   make this especially easy.
+
+ * Note: "pax interchange format" is really an extended tar format,
+   despite what the name says.
diff --git a/contrib/libarchive-2.0/libarchive/archive.h.in b/contrib/libarchive-2.0/libarchive/archive.h.in
new file mode 100644 (file)
index 0000000..a9ba787
--- /dev/null
@@ -0,0 +1,496 @@
+/*-
+ * 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: src/lib/libarchive/archive.h.in,v 1.40 2007/03/11 10:29:52 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_H_INCLUDED
+#define        ARCHIVE_H_INCLUDED
+
+/*
+ * This header file corresponds to:
+ *   Library version @VERSION@
+ *   Shared library version @SHLIB_MAJOR@
+ */
+
+#include <sys/types.h>  /* Linux requires this for off_t */
+@ARCHIVE_H_INCLUDE_INTTYPES_H@
+#include <stdio.h> /* For FILE * */
+#ifndef _WIN32
+#include <unistd.h>  /* For ssize_t and size_t */
+#else
+typedef long ssize_t;
+typedef unsigned int uid_t;
+typedef unsigned int gid_t;
+typedef unsigned short mode_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * If ARCHIVE_API_VERSION != archive_api_version(), then the library you
+ * were linked with is using an incompatible API to the one you were
+ * compiled with.  This is almost certainly a fatal problem.
+ *
+ * ARCHIVE_API_FEATURE is incremented with each significant feature
+ * addition, so you can test (at compile or run time) if a particular
+ * feature is implemented.  It's no big deal if ARCHIVE_API_FEATURE !=
+ * archive_api_feature(), as long as both are high enough to include
+ * the features you're relying on.  Specific values of FEATURE are
+ * documented here:
+ *
+ *    1 - Version tests are available.
+ *    2 - archive_{read,write}_close available separately from _finish.
+ *    3 - open_memory, open_memory2, open_FILE, open_fd available
+ *    5 - archive_write_disk interface available
+ */
+#define        ARCHIVE_API_VERSION     @ARCHIVE_API_MAJOR@
+int            archive_api_version(void);
+#define        ARCHIVE_API_FEATURE     @ARCHIVE_API_MINOR@
+int            archive_api_feature(void);
+/* Textual name/version of the library. */
+#define        ARCHIVE_LIBRARY_VERSION "libarchive @VERSION@"
+const char *   archive_version(void);
+
+#define        ARCHIVE_BYTES_PER_RECORD          512
+#define        ARCHIVE_DEFAULT_BYTES_PER_BLOCK 10240
+
+/* Declare our basic types. */
+struct archive;
+struct archive_entry;
+
+/*
+ * Error codes: Use archive_errno() and archive_error_string()
+ * to retrieve details.  Unless specified otherwise, all functions
+ * that return 'int' use these codes.
+ */
+#define        ARCHIVE_EOF       1     /* Found end of archive. */
+#define        ARCHIVE_OK        0     /* Operation was successful. */
+#define        ARCHIVE_RETRY   (-10)   /* Retry might succeed. */
+#define        ARCHIVE_WARN    (-20)   /* Partial success. */
+/* For example, if write_header "fails", then you can't push data. */
+#define        ARCHIVE_FAILED  (-25)   /* Current operation cannot complete. */
+#define        ARCHIVE_FATAL   (-30)   /* No more operations are possible. */
+
+/*
+ * As far as possible, archive_errno returns standard platform errno codes.
+ * Of course, the details vary by platform, so the actual definitions
+ * here are stored in "archive_platform.h".  The symbols are listed here
+ * for reference; as a rule, clients should not need to know the exact
+ * platform-dependent error code.
+ */
+/* Unrecognized or invalid file format. */
+/* #define     ARCHIVE_ERRNO_FILE_FORMAT */
+/* Illegal usage of the library. */
+/* #define     ARCHIVE_ERRNO_PROGRAMMER_ERROR */
+/* Unknown or unclassified error. */
+/* #define     ARCHIVE_ERRNO_MISC */
+
+/*
+ * Callbacks are invoked to automatically read/skip/write/open/close the
+ * archive. You can provide your own for complex tasks (like breaking
+ * archives across multiple tapes) or use standard ones built into the
+ * library.
+ */
+
+/* Returns pointer and size of next block of data from archive. */
+typedef ssize_t        archive_read_callback(struct archive *, void *_client_data,
+                   const void **_buffer);
+/* Skips at most request bytes from archive and returns the skipped amount */
+#if ARCHIVE_API_VERSION < 2
+typedef ssize_t        archive_skip_callback(struct archive *, void *_client_data,
+                   size_t request);
+#else
+typedef off_t  archive_skip_callback(struct archive *, void *_client_data,
+                   off_t request);
+#endif
+/* Returns size actually written, zero on EOF, -1 on error. */
+typedef ssize_t        archive_write_callback(struct archive *, void *_client_data,
+                   const void *_buffer, size_t _length);
+typedef int    archive_open_callback(struct archive *, void *_client_data);
+typedef int    archive_close_callback(struct archive *, void *_client_data);
+
+/*
+ * Codes for archive_compression.
+ */
+#define        ARCHIVE_COMPRESSION_NONE        0
+#define        ARCHIVE_COMPRESSION_GZIP        1
+#define        ARCHIVE_COMPRESSION_BZIP2       2
+#define        ARCHIVE_COMPRESSION_COMPRESS    3
+
+/*
+ * Codes returned by archive_format.
+ *
+ * Top 16 bits identifies the format family (e.g., "tar"); lower
+ * 16 bits indicate the variant.  This is updated by read_next_header.
+ * Note that the lower 16 bits will often vary from entry to entry.
+ * In some cases, this variation occurs as libarchive learns more about
+ * the archive (for example, later entries might utilize extensions that
+ * weren't necessary earlier in the archive; in this case, libarchive
+ * will change the format code to indicate the extended format that
+ * was used).  In other cases, it's because different tools have
+ * modified the archive and so different parts of the archive
+ * actually have slightly different formts.  (Both tar and cpio store
+ * format codes in each entry, so it is quite possible for each
+ * entry to be in a different format.)
+ */
+#define        ARCHIVE_FORMAT_BASE_MASK                0xff0000
+#define        ARCHIVE_FORMAT_CPIO                     0x10000
+#define        ARCHIVE_FORMAT_CPIO_POSIX               (ARCHIVE_FORMAT_CPIO | 1)
+#define        ARCHIVE_FORMAT_CPIO_BIN_LE              (ARCHIVE_FORMAT_CPIO | 2)
+#define        ARCHIVE_FORMAT_CPIO_BIN_BE              (ARCHIVE_FORMAT_CPIO | 3)
+#define        ARCHIVE_FORMAT_CPIO_SVR4_NOCRC          (ARCHIVE_FORMAT_CPIO | 4)
+#define        ARCHIVE_FORMAT_CPIO_SVR4_CRC            (ARCHIVE_FORMAT_CPIO | 5)
+#define        ARCHIVE_FORMAT_SHAR                     0x20000
+#define        ARCHIVE_FORMAT_SHAR_BASE                (ARCHIVE_FORMAT_SHAR | 1)
+#define        ARCHIVE_FORMAT_SHAR_DUMP                (ARCHIVE_FORMAT_SHAR | 2)
+#define        ARCHIVE_FORMAT_TAR                      0x30000
+#define        ARCHIVE_FORMAT_TAR_USTAR                (ARCHIVE_FORMAT_TAR | 1)
+#define        ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE      (ARCHIVE_FORMAT_TAR | 2)
+#define        ARCHIVE_FORMAT_TAR_PAX_RESTRICTED       (ARCHIVE_FORMAT_TAR | 3)
+#define        ARCHIVE_FORMAT_TAR_GNUTAR               (ARCHIVE_FORMAT_TAR | 4)
+#define        ARCHIVE_FORMAT_ISO9660                  0x40000
+#define        ARCHIVE_FORMAT_ISO9660_ROCKRIDGE        (ARCHIVE_FORMAT_ISO9660 | 1)
+#define        ARCHIVE_FORMAT_ZIP                      0x50000
+#define        ARCHIVE_FORMAT_EMPTY                    0x60000
+
+/*-
+ * Basic outline for reading an archive:
+ *   1) Ask archive_read_new for an archive reader object.
+ *   2) Update any global properties as appropriate.
+ *      In particular, you'll certainly want to call appropriate
+ *      archive_read_support_XXX functions.
+ *   3) Call archive_read_open_XXX to open the archive
+ *   4) Repeatedly call archive_read_next_header to get information about
+ *      successive archive entries.  Call archive_read_data to extract
+ *      data for entries of interest.
+ *   5) Call archive_read_finish to end processing.
+ */
+struct archive *archive_read_new(void);
+
+/*
+ * The archive_read_support_XXX calls enable auto-detect for this
+ * archive handle.  They also link in the necessary support code.
+ * For example, if you don't want bzlib linked in, don't invoke
+ * support_compression_bzip2().  The "all" functions provide the
+ * obvious shorthand.
+ */
+int             archive_read_support_compression_all(struct archive *);
+int             archive_read_support_compression_bzip2(struct archive *);
+int             archive_read_support_compression_compress(struct archive *);
+int             archive_read_support_compression_gzip(struct archive *);
+int             archive_read_support_compression_none(struct archive *);
+
+int             archive_read_support_format_all(struct archive *);
+int             archive_read_support_format_cpio(struct archive *);
+int             archive_read_support_format_empty(struct archive *);
+int             archive_read_support_format_gnutar(struct archive *);
+int             archive_read_support_format_iso9660(struct archive *);
+int             archive_read_support_format_tar(struct archive *);
+int             archive_read_support_format_zip(struct archive *);
+
+
+/* Open the archive using callbacks for archive I/O. */
+int             archive_read_open(struct archive *, void *_client_data,
+                    archive_open_callback *, archive_read_callback *,
+                    archive_close_callback *);
+int             archive_read_open2(struct archive *, void *_client_data,
+                    archive_open_callback *, archive_read_callback *,
+                    archive_skip_callback *, archive_close_callback *);
+
+/*
+ * A variety of shortcuts that invoke archive_read_open() with
+ * canned callbacks suitable for common situations.  The ones that
+ * accept a block size handle tape blocking correctly.
+ */
+/* Use this if you know the filename.  Note: NULL indicates stdin. */
+int             archive_read_open_filename(struct archive *,
+                    const char *_filename, size_t _block_size);
+/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */
+int             archive_read_open_file(struct archive *,
+                    const char *_filename, size_t _block_size);
+/* Read an archive that's stored in memory. */
+int             archive_read_open_memory(struct archive *,
+                    void * buff, size_t size);
+/* A more involved version that is only used for internal testing. */
+int            archive_read_open_memory2(struct archive *a, void *buff,
+                    size_t size, size_t read_size);
+/* Read an archive that's already open, using the file descriptor. */
+int             archive_read_open_fd(struct archive *, int _fd,
+                    size_t _block_size);
+/* Read an archive that's already open, using a FILE *. */
+/* Note: DO NOT use this with tape drives. */
+int             archive_read_open_FILE(struct archive *, FILE *_file);
+
+/* Parses and returns next entry header. */
+int             archive_read_next_header(struct archive *,
+                    struct archive_entry **);
+
+/*
+ * Retrieve the byte offset in UNCOMPRESSED data where last-read
+ * header started.
+ */
+int64_t                 archive_read_header_position(struct archive *);
+
+/* Read data from the body of an entry.  Similar to read(2). */
+ssize_t                 archive_read_data(struct archive *, void *, size_t);
+/*
+ * A zero-copy version of archive_read_data that also exposes the file offset
+ * of each returned block.  Note that the client has no way to specify
+ * the desired size of the block.  The API does guarantee that offsets will
+ * be strictly increasing and that returned blocks will not overlap.
+ */
+int             archive_read_data_block(struct archive *a,
+                   const void **buff, size_t *size, off_t *offset);
+
+/*-
+ * Some convenience functions that are built on archive_read_data:
+ *  'skip': skips entire entry
+ *  'into_buffer': writes data into memory buffer that you provide
+ *  'into_fd': writes data to specified filedes
+ */
+int             archive_read_data_skip(struct archive *);
+int             archive_read_data_into_buffer(struct archive *, void *buffer,
+                    ssize_t len);
+int             archive_read_data_into_fd(struct archive *, int fd);
+
+/*-
+ * Convenience function to recreate the current entry (whose header
+ * has just been read) on disk.
+ *
+ * This does quite a bit more than just copy data to disk. It also:
+ *  - Creates intermediate directories as required.
+ *  - Manages directory permissions:  non-writable directories will
+ *    be initially created with write permission enabled; when the
+ *    archive is closed, dir permissions are edited to the values specified
+ *    in the archive.
+ *  - Checks hardlinks:  hardlinks will not be extracted unless the
+ *    linked-to file was also extracted within the same session. (TODO)
+ */
+
+/* The "flags" argument selects optional behavior, 'OR' the flags you want. */
+
+/* Default: Do not try to set owner/group. */
+#define        ARCHIVE_EXTRACT_OWNER   (1)
+/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */
+#define        ARCHIVE_EXTRACT_PERM    (2)
+/* Default: Do not restore mtime/atime. */
+#define        ARCHIVE_EXTRACT_TIME    (4)
+/* Default: Replace existing files. */
+#define        ARCHIVE_EXTRACT_NO_OVERWRITE (8)
+/* Default: Try create first, unlink only if create fails with EEXIST. */
+#define        ARCHIVE_EXTRACT_UNLINK  (16)
+/* Default: Do not restore ACLs. */
+#define        ARCHIVE_EXTRACT_ACL     (32)
+/* Default: Do not restore fflags. */
+#define        ARCHIVE_EXTRACT_FFLAGS  (64)
+/* Default: Do not restore xattrs. */
+#define        ARCHIVE_EXTRACT_XATTR   (128)
+/* Default: Do not try to guard against extracts redirected by symlinks. */
+/* Note: With ARCHIVE_EXTRACT_UNLINK, will remove any intermediate symlink. */
+#define        ARCHIVE_EXTRACT_SECURE_SYMLINKS (256)
+/* Default: Do not reject entries with '..' as path elements. */
+#define        ARCHIVE_EXTRACT_SECURE_NODOTDOT (512)
+
+int             archive_read_extract(struct archive *, struct archive_entry *,
+                    int flags);
+void            archive_read_extract_set_progress_callback(struct archive *,
+                    void (*_progress_func)(void *), void *_user_data);
+
+/* Record the dev/ino of a file that will not be written.  This is
+ * generally set to the dev/ino of the archive being read. */
+void           archive_read_extract_set_skip_file(struct archive *,
+                    dev_t, ino_t);
+
+/* Close the file and release most resources. */
+int             archive_read_close(struct archive *);
+/* Release all resources and destroy the object. */
+/* Note that archive_read_finish will call archive_read_close for you. */
+#if ARCHIVE_API_VERSION > 1
+int             archive_read_finish(struct archive *);
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+/* Erroneously declared to return void in libarchive 1.x */
+void            archive_read_finish(struct archive *);
+#endif
+
+/*-
+ * To create an archive:
+ *   1) Ask archive_write_new for a archive writer object.
+ *   2) Set any global properties.  In particular, you should set
+ *      the compression and format to use.
+ *   3) Call archive_write_open to open the file (most people
+ *       will use archive_write_open_file or archive_write_open_fd,
+ *       which provide convenient canned I/O callbacks for you).
+ *   4) For each entry:
+ *      - construct an appropriate struct archive_entry structure
+ *      - archive_write_header to write the header
+ *      - archive_write_data to write the entry data
+ *   5) archive_write_close to close the output
+ *   6) archive_write_finish to cleanup the writer and release resources
+ */
+struct archive *archive_write_new(void);
+int             archive_write_set_bytes_per_block(struct archive *,
+                    int bytes_per_block);
+int             archive_write_get_bytes_per_block(struct archive *);
+/* XXX This is badly misnamed; suggestions appreciated. XXX */
+int             archive_write_set_bytes_in_last_block(struct archive *,
+                    int bytes_in_last_block);
+int             archive_write_get_bytes_in_last_block(struct archive *);
+
+/* The dev/ino of a file that won't be archived.  This is used
+ * to avoid recursively adding an archive to itself. */
+int             archive_write_set_skip_file(struct archive *, dev_t, ino_t);
+
+int             archive_write_set_compression_bzip2(struct archive *);
+int             archive_write_set_compression_gzip(struct archive *);
+int             archive_write_set_compression_none(struct archive *);
+/* A convenience function to set the format based on the code or name. */
+int             archive_write_set_format(struct archive *, int format_code);
+int             archive_write_set_format_by_name(struct archive *,
+                    const char *name);
+/* To minimize link pollution, use one or more of the following. */
+int             archive_write_set_format_cpio(struct archive *);
+/* TODO: int archive_write_set_format_old_tar(struct archive *); */
+int             archive_write_set_format_pax(struct archive *);
+int             archive_write_set_format_pax_restricted(struct archive *);
+int             archive_write_set_format_shar(struct archive *);
+int             archive_write_set_format_shar_dump(struct archive *);
+int             archive_write_set_format_ustar(struct archive *);
+int             archive_write_open(struct archive *, void *,
+                    archive_open_callback *, archive_write_callback *,
+                    archive_close_callback *);
+int             archive_write_open_fd(struct archive *, int _fd);
+int             archive_write_open_filename(struct archive *, const char *_file);
+/* A deprecated synonym for archive_write_open_filename() */
+int             archive_write_open_file(struct archive *, const char *_file);
+int             archive_write_open_FILE(struct archive *, FILE *);
+/* _buffSize is the size of the buffer, _used refers to a variable that
+ * will be updated after each write into the buffer. */
+int             archive_write_open_memory(struct archive *,
+                       void *_buffer, size_t _buffSize, size_t *_used);
+
+/*
+ * Note that the library will truncate writes beyond the size provided
+ * to archive_write_header or pad if the provided data is short.
+ */
+int             archive_write_header(struct archive *,
+                    struct archive_entry *);
+#if ARCHIVE_API_VERSION > 1
+ssize_t                 archive_write_data(struct archive *, const void *, size_t);
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+/* This was erroneously declared to return "int" in libarchive 1.x. */
+int             archive_write_data(struct archive *, const void *, size_t);
+#endif
+ssize_t                 archive_write_data_block(struct archive *, const void *, size_t, off_t);
+int             archive_write_finish_entry(struct archive *);
+int             archive_write_close(struct archive *);
+#if ARCHIVE_API_VERSION > 1
+int             archive_write_finish(struct archive *);
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+/* Return value was incorrect in libarchive 1.x. */
+void            archive_write_finish(struct archive *);
+#endif
+
+/*-
+ * To create objects on disk:
+ *   1) Ask archive_write_disk_new for a new archive_write_disk object.
+ *   2) Set any global properties.  In particular, you should set
+ *      the compression and format to use.
+ *   3) For each entry:
+ *      - construct an appropriate struct archive_entry structure
+ *      - archive_write_header to create the file/dir/etc on disk
+ *      - archive_write_data to write the entry data
+ *   4) archive_write_finish to cleanup the writer and release resources
+ *
+ * In particular, you can use this in conjunction with archive_read()
+ * to pull entries out of an archive and create them on disk.
+ */
+struct archive *archive_write_disk_new(void);
+/* This file will not be overwritten. */
+int             archive_write_disk_set_skip_file(struct archive *,
+                    dev_t, ino_t);
+/* Set flags to control how the next item gets created. */
+int             archive_write_disk_set_options(struct archive *,
+                    int flags);
+/*
+ * The lookup functions are given uname/uid (or gname/gid) pairs and
+ * return a uid (gid) suitable for this system.  These are used for
+ * restoring ownership and for setting ACLs.  The default functions
+ * are naive, they just return the uid/gid.  These are small, so reasonable
+ * for applications that don't need to preserve ownership; they
+ * are probably also appropriate for applications that are doing
+ * same-system backup and restore.
+ */
+/*
+ * The "standard" lookup functions use common system calls to lookup
+ * the uname/gname, falling back to the uid/gid if the names can't be
+ * found.  They cache lookups and are reasonably fast, but can be very
+ * large, so they are not used unless you ask for them.  In
+ * particular, these match the specifications of POSIX "pax" and old
+ * POSIX "tar".
+ */
+int             archive_write_disk_set_standard_lookup(struct archive *);
+/*
+ * If neither the default (naive) nor the standard (big) functions suit
+ * your needs, you can write your own and register them.  Be sure to
+ * include a cleanup function if you have allocated private data.
+ */
+int             archive_write_disk_set_group_lookup(struct archive *,
+                    void *private_data,
+                    gid_t (*loookup)(void *, const char *gname, gid_t gid),
+                    void (*cleanup)(void *));
+int             archive_write_disk_set_user_lookup(struct archive *,
+                    void *private_data,
+                    uid_t (*)(void *, const char *uname, uid_t uid),
+                    void (*cleanup)(void *));
+
+/*
+ * Accessor functions to read/set various information in
+ * the struct archive object:
+ */
+/* Bytes written after compression or read before decompression. */
+int64_t                 archive_position_compressed(struct archive *);
+/* Bytes written to compressor or read from decompressor. */
+int64_t                 archive_position_uncompressed(struct archive *);
+
+const char     *archive_compression_name(struct archive *);
+int             archive_compression(struct archive *);
+int             archive_errno(struct archive *);
+const char     *archive_error_string(struct archive *);
+const char     *archive_format_name(struct archive *);
+int             archive_format(struct archive *);
+void            archive_clear_error(struct archive *);
+void            archive_set_error(struct archive *, int _err, const char *fmt, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !ARCHIVE_H_INCLUDED */
diff --git a/contrib/libarchive-2.0/libarchive/archive_check_magic.c b/contrib/libarchive-2.0/libarchive/archive_check_magic.c
new file mode 100644 (file)
index 0000000..2bbd3c6
--- /dev/null
@@ -0,0 +1,118 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_check_magic.c,v 1.7 2007/01/09 08:05:54 kientzle Exp $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive_private.h"
+
+static void
+errmsg(const char *m)
+{
+       write(STDERR_FILENO, m, strlen(m));
+}
+
+static void
+diediedie(void)
+{
+       *(char *)0 = 1; /* Deliberately segfault and force a coredump. */
+       _exit(1);       /* If that didn't work, just exit with an error. */
+}
+
+static const char *
+state_name(unsigned s)
+{
+       switch (s) {
+       case ARCHIVE_STATE_NEW:         return ("new");
+       case ARCHIVE_STATE_HEADER:      return ("header");
+       case ARCHIVE_STATE_DATA:        return ("data");
+       case ARCHIVE_STATE_EOF:         return ("eof");
+       case ARCHIVE_STATE_CLOSED:      return ("closed");
+       case ARCHIVE_STATE_FATAL:       return ("fatal");
+       default:                        return ("??");
+       }
+}
+
+
+static void
+write_all_states(int states)
+{
+       unsigned lowbit;
+
+       /* A trick for computing the lowest set bit. */
+       while ((lowbit = states & (-states)) != 0) {
+               states &= ~lowbit;              /* Clear the low bit. */
+               errmsg(state_name(lowbit));
+               if (states != 0)
+                       errmsg("/");
+       }
+}
+
+/*
+ * Check magic value and current state; bail if it isn't valid.
+ *
+ * This is designed to catch serious programming errors that violate
+ * the libarchive API.
+ */
+void
+__archive_check_magic(struct archive *a, unsigned magic, unsigned state,
+    const char *function)
+{
+       if (a->magic != magic) {
+               errmsg("INTERNAL ERROR: Function ");
+               errmsg(function);
+               errmsg(" invoked with invalid struct archive structure.\n");
+               diediedie();
+       }
+
+       if (state == ARCHIVE_STATE_ANY)
+               return;
+
+       if ((a->state & state) == 0) {
+               errmsg("INTERNAL ERROR: Function '");
+               errmsg(function);
+               errmsg("' invoked with archive structure in state '");
+               write_all_states(a->state);
+               errmsg("', should be in state '");
+               write_all_states(state);
+               errmsg("'\n");
+               diediedie();
+       }
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_entry.3 b/contrib/libarchive-2.0/libarchive/archive_entry.3
new file mode 100644 (file)
index 0000000..807fd32
--- /dev/null
@@ -0,0 +1,344 @@
+.\" 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 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 AUTHOR 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.
+.\"
+.\" $FreeBSD: src/lib/libarchive/archive_entry.3,v 1.13 2007/03/03 07:37:36 kientzle Exp $
+.\"
+.Dd December 15, 2003
+.Dt archive_entry 3
+.Os
+.Sh NAME
+.Nm archive_entry_acl_add_entry ,
+.Nm archive_entry_acl_add_entry_w ,
+.Nm archive_entry_acl_clear ,
+.Nm archive_entry_acl_count ,
+.Nm archive_entry_acl_next ,
+.Nm archive_entry_acl_next_w ,
+.Nm archive_entry_acl_reset ,
+.Nm archive_entry_acl_text_w ,
+.Nm archive_entry_atime ,
+.Nm archive_entry_atime_nsec ,
+.Nm archive_entry_clear ,
+.Nm archive_entry_clone ,
+.Nm archive_entry_copy_fflags_text_w ,
+.Nm archive_entry_copy_gname_w ,
+.Nm archive_entry_copy_hardlink ,
+.Nm archive_entry_copy_hardlink_w ,
+.Nm archive_entry_copy_pathname_w ,
+.Nm archive_entry_copy_stat ,
+.Nm archive_entry_copy_symlink ,
+.Nm archive_entry_copy_symlink_w ,
+.Nm archive_entry_copy_uname_w ,
+.Nm archive_entry_dev ,
+.Nm archive_entry_fflags ,
+.Nm archive_entry_fflags_text ,
+.Nm archive_entry_free ,
+.Nm archive_entry_gid ,
+.Nm archive_entry_gname ,
+.Nm archive_entry_hardlink ,
+.Nm archive_entry_ino ,
+.Nm archive_entry_mode ,
+.Nm archive_entry_mtime ,
+.Nm archive_entry_mtime_nsec ,
+.Nm archive_entry_new ,
+.Nm archive_entry_pathname ,
+.Nm archive_entry_pathname_w ,
+.Nm archive_entry_rdev ,
+.Nm archive_entry_rdevmajor ,
+.Nm archive_entry_rdevminor ,
+.Nm archive_entry_set_fflags ,
+.Nm archive_entry_set_gid ,
+.Nm archive_entry_set_gname ,
+.Nm archive_entry_set_hardlink ,
+.Nm archive_entry_set_link ,
+.Nm archive_entry_set_mode ,
+.Nm archive_entry_set_mtime ,
+.Nm archive_entry_set_pathname ,
+.Nm archive_entry_set_rdevmajor ,
+.Nm archive_entry_set_rdevminor ,
+.Nm archive_entry_set_size ,
+.Nm archive_entry_set_symlink ,
+.Nm archive_entry_set_uid ,
+.Nm archive_entry_set_uname ,
+.Nm archive_entry_size ,
+.Nm archive_entry_stat ,
+.Nm archive_entry_symlink ,
+.Nm archive_entry_uid ,
+.Nm archive_entry_uname
+.Nd functions for manipulating archive entry descriptions
+.Sh SYNOPSIS
+.In archive_entry.h
+.Ft void
+.Fn archive_entry_acl_add_entry "struct archive_entry *" "int type" "int permset" "int tag" "int qual" "const char *name"
+.Ft void
+.Fn archive_entry_acl_add_entry_w "struct archive_entry *" "int type" "int permset" "int tag" "int qual" "const wchar_t *name"
+.Ft void
+.Fn archive_entry_acl_clear "struct archive_entry *"
+.Ft int
+.Fn archive_entry_acl_count "struct archive_entry *" "int type"
+.Ft int
+.Fn archive_entry_acl_next "struct archive_entry *" "int want_type" "int *type" "int *permset" "int *tag" "int *qual" "const char **name"
+.Ft int
+.Fn archive_entry_acl_next_w "struct archive_entry *" "int want_type" "int *type" "int *permset" "int *tag" "int *qual" "const wchar_t **name"
+.Ft int
+.Fn archive_entry_acl_reset "struct archive_entry *" "int want_type"
+.Ft const wchar_t *
+.Fn archive_entry_acl_text_w "struct archive_entry *" "int flags"
+.Ft time_t
+.Fn archive_entry_atime "struct archive_entry *"
+.Ft long
+.Fn archive_entry_atime_nsec "struct archive_entry *"
+.Ft "struct archive_entry *"
+.Fn archive_entry_clear "struct archive_entry *"
+.Ft struct archive_entry *
+.Fn archive_entry_clone "struct archive_entry *"
+.Ft const wchar_t *
+.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *"
+.Ft void
+.Fn archive_entry_copy_gname_w "struct archive_entry *" "const wchar_t *"
+.Ft void
+.Fn archive_entry_copy_hardlink "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_copy_hardlink_w "struct archive_entry *" "const wchar_t *"
+.Ft void
+.Fn archive_entry_copy_pathname_w "struct archive_entry *" "const wchar_t *"
+.Ft void
+.Fn archive_entry_copy_stat "struct archive_entry *" "const struct stat *"
+.Ft void
+.Fn archive_entry_copy_symlink "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_copy_symlink_w "struct archive_entry *" "const wchar_t *"
+.Ft void
+.Fn archive_entry_copy_uname_w "struct archive_entry *" "const wchar_t *"
+.Ft dev_t
+.Fn archive_entry_dev "struct archive_entry *"
+.Ft void
+.Fn archive_entry_fflags "struct archive_entry *" "unsigned long *set" "unsigned long *clear"
+.Ft const char *
+.Fn archive_entry_fflags_text "struct archive_entry *"
+.Ft void
+.Fn archive_entry_free "struct archive_entry *"
+.Ft const char *
+.Fn archive_entry_gname "struct archive_entry *"
+.Ft const char *
+.Fn archive_entry_hardlink "struct archive_entry *"
+.Ft ino_t
+.Fn archive_entry_ino "struct archive_entry *"
+.Ft mode_t
+.Fn archive_entry_mode "struct archive_entry *"
+.Ft time_t
+.Fn archive_entry_mtime "struct archive_entry *"
+.Ft long
+.Fn archive_entry_mtime_nsec "struct archive_entry *"
+.Ft struct archive_entry *
+.Fn archive_entry_new "void"
+.Ft const char *
+.Fn archive_entry_pathname "struct archive_entry *"
+.Ft const wchar_t *
+.Fn archive_entry_pathname_w "struct archive_entry *"
+.Ft dev_t
+.Fn archive_entry_rdev "struct archive_entry *"
+.Ft dev_t
+.Fn archive_entry_rdevmajor "struct archive_entry *"
+.Ft dev_t
+.Fn archive_entry_rdevminor "struct archive_entry *"
+.Ft void
+.Fn archive_entry_set_fflags "struct archive_entry *" "unsigned long set" "unsigned long clear"
+.Ft void
+.Fn archive_entry_set_gid "struct archive_entry *" "gid_t"
+.Ft void
+.Fn archive_entry_set_gname "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_set_hardlink "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_set_link "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_set_mode "struct archive_entry *" "mode_t"
+.Ft void
+.Fn archive_entry_set_mtime "struct archive_entry *" "time_t" "long nanos"
+.Ft void
+.Fn archive_entry_set_pathname "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_set_rdevmajor "struct archive_entry *" "dev_t"
+.Ft void
+.Fn archive_entry_set_rdevminor "struct archive_entry *" "dev_t"
+.Ft void
+.Fn archive_entry_set_size "struct archive_entry *" "int64_t"
+.Ft void
+.Fn archive_entry_set_symlink "struct archive_entry *" "const char *"
+.Ft void
+.Fn archive_entry_set_uid "struct archive_entry *" "uid_t"
+.Ft void
+.Fn archive_entry_set_uname "struct archive_entry *" "const char *"
+.Ft int64_t
+.Fn archive_entry_size "struct archive_entry *"
+.Ft const struct stat *
+.Fn archive_entry_stat "struct archive_entry *"
+.Ft const char *
+.Fn archive_entry_symlink "struct archive_entry *"
+.Ft const char *
+.Fn archive_entry_uname "struct archive_entry *"
+.Sh DESCRIPTION
+These functions create and manipulate data objects that
+represent entries within an archive.
+You can think of a
+.Tn struct archive_entry
+as a heavy-duty version of
+.Tn struct stat :
+it includes everything from
+.Tn struct stat
+plus associated pathname, textual group and user names, etc.
+These objects are used by
+.Xr libarchive 3
+to represent the metadata associated with a particular
+entry in an archive.
+.Ss Create and Destroy
+There are functions to allocate, destroy, clear, and copy
+.Va archive_entry
+objects:
+.Bl -tag -compact -width indent
+.It Fn archive_entry_clear
+Erases the object, resetting all internal fields to the
+same state as a newly-created object.
+This is provided to allow you to quickly recycle objects
+without thrashing the heap.
+.It Fn archive_entry_clone
+A deep copy operation; all text fields are duplicated.
+.It Fn archive_entry_free
+Releases the
+.Tn struct archive_entry
+object.
+.It Fn archive_entry_new
+Allocate and return a blank
+.Tn struct archive_entry
+object.
+.El
+.Ss Set and Get Functions
+Most of the functions here set or read entries in an object.
+Such functions have one of the following forms:
+.Bl -tag -compact -width indent
+.It Fn archive_entry_set_XXXX
+Stores the provided data in the object.
+In particular, for strings, the pointer is stored,
+not the referenced string.
+.It Fn archive_entry_copy_XXXX
+As above, except that the referenced data is copied
+into the object.
+.It Fn archive_entry_XXXX
+Returns the specified data.
+In the case of strings, a const-qualified pointer to
+the string is returned.
+.El
+String data can be set or accessed as wide character strings
+or normal
+.Va char
+strings.
+The functions that use wide character strings are suffixed with
+.Cm _w .
+Note that these are different representations of the same data:
+For example, if you store a narrow string and read the corresponding
+wide string, the object will transparently convert formats
+using the current locale.
+Similarly, if you store a wide string and then store a
+narrow string for the same data, the previously-set wide string will
+be discarded in favor of the new data.
+.Pp
+There are a few set/get functions that merit additional description:
+.Bl -tag -compact -width indent
+.It Fn archive_entry_set_link
+This function sets the symlink field if it is already set.
+Otherwise, it sets the hardlink field.
+.El
+.Ss File Flags
+File flags are transparently converted between a bitmap
+representation and a textual format.
+For example, if you set the bitmap and ask for text, the library
+will build a canonical text format.
+However, if you set a text format and request a text format,
+you will get back the same text, even if it is ill-formed.
+If you need to canonicalize a textual flags string, you should first set the
+text form, then request the bitmap form, then use that to set the bitmap form.
+Setting the bitmap format will clear the internal text representation
+and force it to be reconstructed when you next request the text form.
+.Pp
+The bitmap format consists of two integers, one containing bits
+that should be set, the other specifying bits that should be
+cleared.
+Bits not mentioned in either bitmap will be ignored.
+Usually, the bitmap of bits to be cleared will be set to zero.
+In unusual circumstances, you can force a fully-specified set
+of file flags by setting the bitmap of flags to clear to the complement
+of the bitmap of flags to set.
+(This differs from
+.Xr fflagstostr 3 ,
+which only includes names for set bits.)
+Converting a bitmap to a textual string is a platform-specific
+operation; bits that are not meaningful on the current platform
+will be ignored.
+.Pp
+The canonical text format is a comma-separated list of flag names.
+The
+.Fn archive_entry_copy_fflags_text_w
+function parses the provided text and sets the internal bitmap values.
+This is a platform-specific operation; names that are not meaningful
+on the current platform will be ignored.
+The function returns a pointer to the start of the first name that was not
+recognized, or NULL if every name was recognized.
+Note that every name--including names that follow an unrecognized name--will
+be evaluated, and the bitmaps will be set to reflect every name that is
+recognized.
+(In particular, this differs from
+.Xr strtofflags 3 ,
+which stops parsing at the first unrecognized name.)
+.Ss ACL Handling
+XXX This needs serious help.
+XXX
+.Pp
+An
+.Dq Access Control List
+(ACL) is a list of permissions that grant access to particular users or
+groups beyond what would normally be provided by standard POSIX mode bits.
+The ACL handling here addresses some deficiencies in the POSIX.1e draft 17 ACL
+specification.
+In particular, POSIX.1e draft 17 specifies several different formats, but
+none of those formats include both textual user/group names and numeric
+UIDs/GIDs.
+.Pp
+XXX explain ACL stuff XXX
+.\" .Sh EXAMPLE
+.\" .Sh RETURN VALUES
+.\" .Sh ERRORS
+.Sh SEE ALSO
+.Xr archive 3
+.Sh HISTORY
+The
+.Nm libarchive
+library first appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm libarchive
+library was written by
+.An Tim Kientzle Aq kientzle@acm.org .
+.\" .Sh BUGS
diff --git a/contrib/libarchive-2.0/libarchive/archive_entry.c b/contrib/libarchive-2.0/libarchive/archive_entry.c
new file mode 100644 (file)
index 0000000..4564767
--- /dev/null
@@ -0,0 +1,1770 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_entry.c,v 1.40 2007/03/11 10:29:52 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#else
+#ifdef MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#endif
+#endif
+#ifdef HAVE_EXT2FS_EXT2_FS_H
+#include <ext2fs/ext2_fs.h>    /* for Linux file flags */
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>  /* for Linux file flags */
+#endif
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>     /* for Linux file flags */
+#endif
+#include <stddef.h>
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+/* Obtain suitable wide-character manipulation functions. */
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#else
+static size_t wcslen(const wchar_t *s)
+{
+       const wchar_t *p = s;
+       while (*p != L'\0')
+               ++p;
+       return p - s;
+}
+static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
+{
+       wchar_t *dest = s1;
+       while ((*s1 = *s2) != L'\0')
+               ++s1, ++s2;
+       return dest;
+}
+#define wmemcpy(a,b,i)  (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
+/* Good enough for simple equality testing, but not for sorting. */
+#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+#undef max
+#define        max(a, b)       ((a)>(b)?(a):(b))
+
+/*
+ * Handle wide character (i.e., Unicode) and non-wide character
+ * strings transparently.
+ *
+ */
+
+struct aes {
+       const char *aes_mbs;
+       char *aes_mbs_alloc;
+       const wchar_t *aes_wcs;
+       wchar_t *aes_wcs_alloc;
+};
+
+struct ae_acl {
+       struct ae_acl *next;
+       int     type;                   /* E.g., access or default */
+       int     tag;                    /* E.g., user/group/other/mask */
+       int     permset;                /* r/w/x bits */
+       int     id;                     /* uid/gid for user/group */
+       struct aes name;                /* uname/gname */
+};
+
+struct ae_xattr {
+       struct ae_xattr *next;
+
+       char    *name;
+       void    *value;
+       size_t  size;
+};
+
+static void    aes_clean(struct aes *);
+static void    aes_copy(struct aes *dest, struct aes *src);
+static const char *    aes_get_mbs(struct aes *);
+static const wchar_t * aes_get_wcs(struct aes *);
+static void    aes_set_mbs(struct aes *, const char *mbs);
+static void    aes_copy_mbs(struct aes *, const char *mbs);
+/* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */
+static void    aes_copy_wcs(struct aes *, const wchar_t *wcs);
+static void    aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t);
+
+static char *   ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
+static const wchar_t   *ae_wcstofflags(const wchar_t *stringp,
+                   unsigned long *setp, unsigned long *clrp);
+static void    append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
+                   const wchar_t *wname, int perm, int id);
+static void    append_id_w(wchar_t **wp, int id);
+
+static int     acl_special(struct archive_entry *entry,
+                   int type, int permset, int tag);
+static struct ae_acl *acl_new_entry(struct archive_entry *entry,
+                   int type, int permset, int tag, int id);
+static int     isint_w(const wchar_t *start, const wchar_t *end, int *result);
+static void    next_field_w(const wchar_t **wp, const wchar_t **start,
+                   const wchar_t **end, wchar_t *sep);
+static int     prefix_w(const wchar_t *start, const wchar_t *end,
+                   const wchar_t *test);
+static void
+archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type,
+                   int permset, int tag, int id, const wchar_t *name, size_t);
+
+
+/*
+ * Description of an archive entry.
+ *
+ * Basically, this is a "struct stat" with a few text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries
+ * that are supported by "pax interchange" format.  However, GNU, ustar,
+ * cpio, and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry.  Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry).  There are tricky
+ * API issues involved, so this is not going to happen until
+ * there's a real demand for it.
+ *
+ * TODO: Design a good API for handling sparse files.
+ */
+struct archive_entry {
+       /*
+        * Note that ae_stat.st_mode & S_IFMT  can be  0!
+        *
+        * This occurs when the actual file type of the object is not
+        * in the archive.  For example, 'tar' archives store
+        * hardlinks without marking the type of the underlying
+        * object.
+        */
+       struct stat ae_stat;
+
+       /*
+        * Use aes here so that we get transparent mbs<->wcs conversions.
+        */
+       struct aes ae_fflags_text;      /* Text fflags per fflagstostr(3) */
+       unsigned long ae_fflags_set;            /* Bitmap fflags */
+       unsigned long ae_fflags_clear;
+       struct aes ae_gname;            /* Name of owning group */
+       struct aes ae_hardlink; /* Name of target for hardlink */
+       struct aes ae_pathname; /* Name of entry */
+       struct aes ae_symlink;          /* symlink contents */
+       struct aes ae_uname;            /* Name of owner */
+
+       struct ae_acl   *acl_head;
+       struct ae_acl   *acl_p;
+       int              acl_state;     /* See acl_next for details. */
+       wchar_t         *acl_text_w;
+
+       struct ae_xattr *xattr_head;
+       struct ae_xattr *xattr_p;
+};
+
+static void
+aes_clean(struct aes *aes)
+{
+       if (aes->aes_mbs_alloc) {
+               free(aes->aes_mbs_alloc);
+               aes->aes_mbs_alloc = NULL;
+       }
+       if (aes->aes_wcs_alloc) {
+               free(aes->aes_wcs_alloc);
+               aes->aes_wcs_alloc = NULL;
+       }
+       memset(aes, 0, sizeof(*aes));
+}
+
+static void
+aes_copy(struct aes *dest, struct aes *src)
+{
+       *dest = *src;
+       if (src->aes_mbs != NULL) {
+               dest->aes_mbs_alloc = strdup(src->aes_mbs);
+               dest->aes_mbs = dest->aes_mbs_alloc;
+               if (dest->aes_mbs == NULL)
+                       __archive_errx(1, "No memory for aes_copy()");
+       }
+
+       if (src->aes_wcs != NULL) {
+               dest->aes_wcs_alloc = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1)
+                   * sizeof(wchar_t));
+               dest->aes_wcs = dest->aes_wcs_alloc;
+               if (dest->aes_wcs == NULL)
+                       __archive_errx(1, "No memory for aes_copy()");
+               wcscpy(dest->aes_wcs_alloc, src->aes_wcs);
+       }
+}
+
+static const char *
+aes_get_mbs(struct aes *aes)
+{
+       if (aes->aes_mbs == NULL && aes->aes_wcs == NULL)
+               return NULL;
+       if (aes->aes_mbs == NULL && aes->aes_wcs != NULL) {
+               /*
+                * XXX Need to estimate the number of byte in the
+                * multi-byte form.  Assume that, on average, wcs
+                * chars encode to no more than 3 bytes.  There must
+                * be a better way... XXX
+                */
+               int mbs_length = wcslen(aes->aes_wcs) * 3 + 64;
+               aes->aes_mbs_alloc = (char *)malloc(mbs_length);
+               aes->aes_mbs = aes->aes_mbs_alloc;
+               if (aes->aes_mbs == NULL)
+                       __archive_errx(1, "No memory for aes_get_mbs()");
+               wcstombs(aes->aes_mbs_alloc, aes->aes_wcs, mbs_length - 1);
+               aes->aes_mbs_alloc[mbs_length - 1] = 0;
+       }
+       return (aes->aes_mbs);
+}
+
+static const wchar_t *
+aes_get_wcs(struct aes *aes)
+{
+       if (aes->aes_wcs == NULL && aes->aes_mbs == NULL)
+               return NULL;
+       if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) {
+               /*
+                * No single byte will be more than one wide character,
+                * so this length estimate will always be big enough.
+                */
+               int wcs_length = strlen(aes->aes_mbs);
+               aes->aes_wcs_alloc
+                   = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t));
+               aes->aes_wcs = aes->aes_wcs_alloc;
+               if (aes->aes_wcs == NULL)
+                       __archive_errx(1, "No memory for aes_get_wcs()");
+               mbstowcs(aes->aes_wcs_alloc, aes->aes_mbs, wcs_length);
+               aes->aes_wcs_alloc[wcs_length] = 0;
+       }
+       return (aes->aes_wcs);
+}
+
+static void
+aes_set_mbs(struct aes *aes, const char *mbs)
+{
+       if (aes->aes_mbs_alloc) {
+               free(aes->aes_mbs_alloc);
+               aes->aes_mbs_alloc = NULL;
+       }
+       if (aes->aes_wcs_alloc) {
+               free(aes->aes_wcs_alloc);
+               aes->aes_wcs_alloc = NULL;
+       }
+       aes->aes_mbs = mbs;
+       aes->aes_wcs = NULL;
+}
+
+static void
+aes_copy_mbs(struct aes *aes, const char *mbs)
+{
+       if (aes->aes_mbs_alloc) {
+               free(aes->aes_mbs_alloc);
+               aes->aes_mbs_alloc = NULL;
+       }
+       if (aes->aes_wcs_alloc) {
+               free(aes->aes_wcs_alloc);
+               aes->aes_wcs_alloc = NULL;
+       }
+       aes->aes_mbs_alloc = (char *)malloc((strlen(mbs) + 1) * sizeof(char));
+       if (aes->aes_mbs_alloc == NULL)
+               __archive_errx(1, "No memory for aes_copy_mbs()");
+       strcpy(aes->aes_mbs_alloc, mbs);
+       aes->aes_mbs = aes->aes_mbs_alloc;
+       aes->aes_wcs = NULL;
+}
+
+#if 0
+static void
+aes_set_wcs(struct aes *aes, const wchar_t *wcs)
+{
+       if (aes->aes_mbs_alloc) {
+               free(aes->aes_mbs_alloc);
+               aes->aes_mbs_alloc = NULL;
+       }
+       if (aes->aes_wcs_alloc) {
+               free(aes->aes_wcs_alloc);
+               aes->aes_wcs_alloc = NULL;
+       }
+       aes->aes_mbs = NULL;
+       aes->aes_wcs = wcs;
+}
+#endif
+
+static void
+aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
+{
+       aes_copy_wcs_len(aes, wcs, wcslen(wcs));
+}
+
+static void
+aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len)
+{
+       if (aes->aes_mbs_alloc) {
+               free(aes->aes_mbs_alloc);
+               aes->aes_mbs_alloc = NULL;
+       }
+       if (aes->aes_wcs_alloc) {
+               free(aes->aes_wcs_alloc);
+               aes->aes_wcs_alloc = NULL;
+       }
+       aes->aes_mbs = NULL;
+       aes->aes_wcs_alloc = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
+       if (aes->aes_wcs_alloc == NULL)
+               __archive_errx(1, "No memory for aes_copy_wcs()");
+       wmemcpy(aes->aes_wcs_alloc, wcs, len);
+       aes->aes_wcs_alloc[len] = L'\0';
+       aes->aes_wcs = aes->aes_wcs_alloc;
+}
+
+struct archive_entry *
+archive_entry_clear(struct archive_entry *entry)
+{
+       aes_clean(&entry->ae_fflags_text);
+       aes_clean(&entry->ae_gname);
+       aes_clean(&entry->ae_hardlink);
+       aes_clean(&entry->ae_pathname);
+       aes_clean(&entry->ae_symlink);
+       aes_clean(&entry->ae_uname);
+       archive_entry_acl_clear(entry);
+       archive_entry_xattr_clear(entry);
+       memset(entry, 0, sizeof(*entry));
+       return entry;
+}
+
+struct archive_entry *
+archive_entry_clone(struct archive_entry *entry)
+{
+       struct archive_entry *entry2;
+
+       /* Allocate new structure and copy over all of the fields. */
+       entry2 = (struct archive_entry *)malloc(sizeof(*entry2));
+       if (entry2 == NULL)
+               return (NULL);
+       memset(entry2, 0, sizeof(*entry2));
+       entry2->ae_stat = entry->ae_stat;
+       entry2->ae_fflags_set = entry->ae_fflags_set;
+       entry2->ae_fflags_clear = entry->ae_fflags_clear;
+
+       aes_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
+       aes_copy(&entry2->ae_gname, &entry->ae_gname);
+       aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
+       aes_copy(&entry2->ae_pathname, &entry->ae_pathname);
+       aes_copy(&entry2->ae_symlink, &entry->ae_symlink);
+       aes_copy(&entry2->ae_uname, &entry->ae_uname);
+
+       /* XXX TODO: Copy ACL data over as well. XXX */
+       /* XXX TODO: Copy xattr data over as well. XXX */
+       return (entry2);
+}
+
+void
+archive_entry_free(struct archive_entry *entry)
+{
+       archive_entry_clear(entry);
+       free(entry);
+}
+
+struct archive_entry *
+archive_entry_new(void)
+{
+       struct archive_entry *entry;
+
+       entry = (struct archive_entry *)malloc(sizeof(*entry));
+       if (entry == NULL)
+               return (NULL);
+       memset(entry, 0, sizeof(*entry));
+       return (entry);
+}
+
+/*
+ * Functions for reading fields from an archive_entry.
+ */
+
+time_t
+archive_entry_atime(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_atime);
+}
+
+long
+archive_entry_atime_nsec(struct archive_entry *entry)
+{
+       (void)entry; /* entry can be unused here. */
+       return (ARCHIVE_STAT_ATIME_NANOS(&entry->ae_stat));
+}
+
+time_t
+archive_entry_ctime(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_ctime);
+}
+
+long
+archive_entry_ctime_nsec(struct archive_entry *entry)
+{
+       (void)entry; /* entry can be unused here. */
+       return (ARCHIVE_STAT_CTIME_NANOS(&entry->ae_stat));
+}
+
+dev_t
+archive_entry_dev(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_dev);
+}
+
+void
+archive_entry_fflags(struct archive_entry *entry,
+    unsigned long *set, unsigned long *clear)
+{
+       *set = entry->ae_fflags_set;
+       *clear = entry->ae_fflags_clear;
+}
+
+/*
+ * Note: if text was provided, this just returns that text.  If you
+ * really need the text to be rebuilt in a canonical form, set the
+ * text, ask for the bitmaps, then set the bitmaps.  (Setting the
+ * bitmaps clears any stored text.)  This design is deliberate: if
+ * we're editing archives, we don't want to discard flags just because
+ * they aren't supported on the current system.  The bitmap<->text
+ * conversions are platform-specific (see below).
+ */
+const char *
+archive_entry_fflags_text(struct archive_entry *entry)
+{
+       const char *f;
+       char *p;
+
+       f = aes_get_mbs(&entry->ae_fflags_text);
+       if (f != NULL)
+               return (f);
+
+       if (entry->ae_fflags_set == 0  &&  entry->ae_fflags_clear == 0)
+               return (NULL);
+
+       p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear);
+       if (p == NULL)
+               return (NULL);
+
+       aes_copy_mbs(&entry->ae_fflags_text, p);
+       free(p);
+       f = aes_get_mbs(&entry->ae_fflags_text);
+       return (f);
+}
+
+gid_t
+archive_entry_gid(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_gid);
+}
+
+const char *
+archive_entry_gname(struct archive_entry *entry)
+{
+       return (aes_get_mbs(&entry->ae_gname));
+}
+
+const wchar_t *
+archive_entry_gname_w(struct archive_entry *entry)
+{
+       return (aes_get_wcs(&entry->ae_gname));
+}
+
+const char *
+archive_entry_hardlink(struct archive_entry *entry)
+{
+       return (aes_get_mbs(&entry->ae_hardlink));
+}
+
+const wchar_t *
+archive_entry_hardlink_w(struct archive_entry *entry)
+{
+       return (aes_get_wcs(&entry->ae_hardlink));
+}
+
+ino_t
+archive_entry_ino(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_ino);
+}
+
+mode_t
+archive_entry_mode(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_mode);
+}
+
+time_t
+archive_entry_mtime(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_mtime);
+}
+
+long
+archive_entry_mtime_nsec(struct archive_entry *entry)
+{
+       (void)entry; /* entry can be unused here. */
+       return (ARCHIVE_STAT_MTIME_NANOS(&entry->ae_stat));
+}
+
+const char *
+archive_entry_pathname(struct archive_entry *entry)
+{
+       return (aes_get_mbs(&entry->ae_pathname));
+}
+
+const wchar_t *
+archive_entry_pathname_w(struct archive_entry *entry)
+{
+       return (aes_get_wcs(&entry->ae_pathname));
+}
+
+dev_t
+archive_entry_rdev(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_rdev);
+}
+
+dev_t
+archive_entry_rdevmajor(struct archive_entry *entry)
+{
+       return (major(entry->ae_stat.st_rdev));
+}
+
+dev_t
+archive_entry_rdevminor(struct archive_entry *entry)
+{
+       return (minor(entry->ae_stat.st_rdev));
+}
+
+int64_t
+archive_entry_size(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_size);
+}
+
+const struct stat *
+archive_entry_stat(struct archive_entry *entry)
+{
+       return (&entry->ae_stat);
+}
+
+const char *
+archive_entry_symlink(struct archive_entry *entry)
+{
+       return (aes_get_mbs(&entry->ae_symlink));
+}
+
+const wchar_t *
+archive_entry_symlink_w(struct archive_entry *entry)
+{
+       return (aes_get_wcs(&entry->ae_symlink));
+}
+
+uid_t
+archive_entry_uid(struct archive_entry *entry)
+{
+       return (entry->ae_stat.st_uid);
+}
+
+const char *
+archive_entry_uname(struct archive_entry *entry)
+{
+       return (aes_get_mbs(&entry->ae_uname));
+}
+
+const wchar_t *
+archive_entry_uname_w(struct archive_entry *entry)
+{
+       return (aes_get_wcs(&entry->ae_uname));
+}
+
+/*
+ * Functions to set archive_entry properties.
+ */
+
+/*
+ * Note "copy" not "set" here.  The "set" functions that accept a pointer
+ * only store the pointer; they don't copy the underlying object.
+ */
+void
+archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
+{
+       entry->ae_stat = *st;
+}
+
+void
+archive_entry_set_fflags(struct archive_entry *entry,
+    unsigned long set, unsigned long clear)
+{
+       aes_clean(&entry->ae_fflags_text);
+       entry->ae_fflags_set = set;
+       entry->ae_fflags_clear = clear;
+}
+
+const wchar_t *
+archive_entry_copy_fflags_text_w(struct archive_entry *entry,
+    const wchar_t *flags)
+{
+       aes_copy_wcs(&entry->ae_fflags_text, flags);
+       return (ae_wcstofflags(flags,
+                   &entry->ae_fflags_set, &entry->ae_fflags_clear));
+}
+
+void
+archive_entry_set_gid(struct archive_entry *entry, gid_t g)
+{
+       entry->ae_stat.st_gid = g;
+}
+
+void
+archive_entry_set_gname(struct archive_entry *entry, const char *name)
+{
+       aes_set_mbs(&entry->ae_gname, name);
+}
+
+void
+archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
+{
+       aes_copy_wcs(&entry->ae_gname, name);
+}
+
+void
+archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
+{
+       aes_set_mbs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
+{
+       aes_copy_mbs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
+{
+       aes_copy_wcs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
+{
+       entry->ae_stat.st_atime = t;
+       ARCHIVE_STAT_SET_ATIME_NANOS(&entry->ae_stat, ns);
+}
+
+void
+archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
+{
+       entry->ae_stat.st_ctime = t;
+       ARCHIVE_STAT_SET_CTIME_NANOS(&entry->ae_stat, ns);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_set_link(struct archive_entry *entry, const char *target)
+{
+       if (entry->ae_symlink.aes_mbs != NULL ||
+           entry->ae_symlink.aes_wcs != NULL)
+               aes_set_mbs(&entry->ae_symlink, target);
+       else
+               aes_set_mbs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_set_mode(struct archive_entry *entry, mode_t m)
+{
+       entry->ae_stat.st_mode = m;
+}
+
+void
+archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns)
+{
+       entry->ae_stat.st_mtime = m;
+       ARCHIVE_STAT_SET_MTIME_NANOS(&entry->ae_stat, ns);
+}
+
+void
+archive_entry_set_pathname(struct archive_entry *entry, const char *name)
+{
+       aes_set_mbs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_copy_pathname(struct archive_entry *entry, const char *name)
+{
+       aes_copy_mbs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
+{
+       aes_copy_wcs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)
+{
+       dev_t d;
+
+       d = entry->ae_stat.st_rdev;
+       entry->ae_stat.st_rdev = makedev(major(m), minor(d));
+}
+
+void
+archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)
+{
+       dev_t d;
+
+       d = entry->ae_stat.st_rdev;
+       entry->ae_stat.st_rdev = makedev(major(d), minor(m));
+}
+
+void
+archive_entry_set_size(struct archive_entry *entry, int64_t s)
+{
+       entry->ae_stat.st_size = s;
+}
+
+void
+archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
+{
+       aes_set_mbs(&entry->ae_symlink, linkname);
+}
+
+void
+archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)
+{
+       aes_copy_mbs(&entry->ae_symlink, linkname);
+}
+
+void
+archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
+{
+       aes_copy_wcs(&entry->ae_symlink, linkname);
+}
+
+void
+archive_entry_set_uid(struct archive_entry *entry, uid_t u)
+{
+       entry->ae_stat.st_uid = u;
+}
+
+void
+archive_entry_set_uname(struct archive_entry *entry, const char *name)
+{
+       aes_set_mbs(&entry->ae_uname, name);
+}
+
+void
+archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
+{
+       aes_copy_wcs(&entry->ae_uname, name);
+}
+
+/*
+ * ACL management.  The following would, of course, be a lot simpler
+ * if: 1) the last draft of POSIX.1e were a really thorough and
+ * complete standard that addressed the needs of ACL archiving and 2)
+ * everyone followed it faithfully.  Alas, neither is true, so the
+ * following is a lot more complex than might seem necessary to the
+ * uninitiated.
+ */
+
+void
+archive_entry_acl_clear(struct archive_entry *entry)
+{
+       struct ae_acl   *ap;
+
+       while (entry->acl_head != NULL) {
+               ap = entry->acl_head->next;
+               aes_clean(&entry->acl_head->name);
+               free(entry->acl_head);
+               entry->acl_head = ap;
+       }
+       if (entry->acl_text_w != NULL) {
+               free(entry->acl_text_w);
+               entry->acl_text_w = NULL;
+       }
+       entry->acl_p = NULL;
+       entry->acl_state = 0; /* Not counting. */
+}
+
+/*
+ * Add a single ACL entry to the internal list of ACL data.
+ */
+void
+archive_entry_acl_add_entry(struct archive_entry *entry,
+    int type, int permset, int tag, int id, const char *name)
+{
+       struct ae_acl *ap;
+
+       if (acl_special(entry, type, permset, tag) == 0)
+               return;
+       ap = acl_new_entry(entry, type, permset, tag, id);
+       if (ap == NULL) {
+               /* XXX Error XXX */
+               return;
+       }
+       if (name != NULL  &&  *name != '\0')
+               aes_copy_mbs(&ap->name, name);
+       else
+               aes_clean(&ap->name);
+}
+
+/*
+ * As above, but with a wide-character name.
+ */
+void
+archive_entry_acl_add_entry_w(struct archive_entry *entry,
+    int type, int permset, int tag, int id, const wchar_t *name)
+{
+       archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name));
+}
+
+void
+archive_entry_acl_add_entry_w_len(struct archive_entry *entry,
+    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
+{
+       struct ae_acl *ap;
+
+       if (acl_special(entry, type, permset, tag) == 0)
+               return;
+       ap = acl_new_entry(entry, type, permset, tag, id);
+       if (ap == NULL) {
+               /* XXX Error XXX */
+               return;
+       }
+       if (name != NULL  &&  *name != L'\0' && len > 0)
+               aes_copy_wcs_len(&ap->name, name, len);
+       else
+               aes_clean(&ap->name);
+}
+
+/*
+ * If this ACL entry is part of the standard POSIX permissions set,
+ * store the permissions in the stat structure and return zero.
+ */
+static int
+acl_special(struct archive_entry *entry, int type, int permset, int tag)
+{
+       if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+               switch (tag) {
+               case ARCHIVE_ENTRY_ACL_USER_OBJ:
+                       entry->ae_stat.st_mode &= ~0700;
+                       entry->ae_stat.st_mode |= (permset & 7) << 6;
+                       return (0);
+               case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+                       entry->ae_stat.st_mode &= ~0070;
+                       entry->ae_stat.st_mode |= (permset & 7) << 3;
+                       return (0);
+               case ARCHIVE_ENTRY_ACL_OTHER:
+                       entry->ae_stat.st_mode &= ~0007;
+                       entry->ae_stat.st_mode |= permset & 7;
+                       return (0);
+               }
+       }
+       return (1);
+}
+
+/*
+ * Allocate and populate a new ACL entry with everything but the
+ * name.
+ */
+static struct ae_acl *
+acl_new_entry(struct archive_entry *entry,
+    int type, int permset, int tag, int id)
+{
+       struct ae_acl *ap;
+
+       if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS &&
+           type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
+               return (NULL);
+       if (entry->acl_text_w != NULL) {
+               free(entry->acl_text_w);
+               entry->acl_text_w = NULL;
+       }
+
+       /* XXX TODO: More sanity-checks on the arguments XXX */
+
+       /* If there's a matching entry already in the list, overwrite it. */
+       for (ap = entry->acl_head; ap != NULL; ap = ap->next) {
+               if (ap->type == type && ap->tag == tag && ap->id == id) {
+                       ap->permset = permset;
+                       return (ap);
+               }
+       }
+
+       /* Add a new entry to the list. */
+       ap = (struct ae_acl *)malloc(sizeof(*ap));
+       if (ap == NULL)
+               return (NULL);
+       memset(ap, 0, sizeof(*ap));
+       ap->next = entry->acl_head;
+       entry->acl_head = ap;
+       ap->type = type;
+       ap->tag = tag;
+       ap->id = id;
+       ap->permset = permset;
+       return (ap);
+}
+
+/*
+ * Return a count of entries matching "want_type".
+ */
+int
+archive_entry_acl_count(struct archive_entry *entry, int want_type)
+{
+       int count;
+       struct ae_acl *ap;
+
+       count = 0;
+       ap = entry->acl_head;
+       while (ap != NULL) {
+               if ((ap->type & want_type) != 0)
+                       count++;
+               ap = ap->next;
+       }
+
+       if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
+               count += 3;
+       return (count);
+}
+
+/*
+ * Prepare for reading entries from the ACL data.  Returns a count
+ * of entries matching "want_type", or zero if there are no
+ * non-extended ACL entries of that type.
+ */
+int
+archive_entry_acl_reset(struct archive_entry *entry, int want_type)
+{
+       int count, cutoff;
+
+       count = archive_entry_acl_count(entry, want_type);
+
+       /*
+        * If the only entries are the three standard ones,
+        * then don't return any ACL data.  (In this case,
+        * client can just use chmod(2) to set permissions.)
+        */
+       if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+               cutoff = 3;
+       else
+               cutoff = 0;
+
+       if (count > cutoff)
+               entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
+       else
+               entry->acl_state = 0;
+       entry->acl_p = entry->acl_head;
+       return (count);
+}
+
+/*
+ * Return the next ACL entry in the list.  Fake entries for the
+ * standard permissions and include them in the returned list.
+ */
+
+int
+archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
+    int *permset, int *tag, int *id, const char **name)
+{
+       *name = NULL;
+       *id = -1;
+
+       /*
+        * The acl_state is either zero (no entries available), -1
+        * (reading from list), or an entry type (retrieve that type
+        * from ae_stat.st_mode).
+        */
+       if (entry->acl_state == 0)
+               return (ARCHIVE_WARN);
+
+       /* The first three access entries are special. */
+       if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+               switch (entry->acl_state) {
+               case ARCHIVE_ENTRY_ACL_USER_OBJ:
+                       *permset = (entry->ae_stat.st_mode >> 6) & 7;
+                       *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+                       *tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+                       entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+                       return (ARCHIVE_OK);
+               case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+                       *permset = (entry->ae_stat.st_mode >> 3) & 7;
+                       *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+                       *tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+                       entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
+                       return (ARCHIVE_OK);
+               case ARCHIVE_ENTRY_ACL_OTHER:
+                       *permset = entry->ae_stat.st_mode & 7;
+                       *type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+                       *tag = ARCHIVE_ENTRY_ACL_OTHER;
+                       entry->acl_state = -1;
+                       entry->acl_p = entry->acl_head;
+                       return (ARCHIVE_OK);
+               default:
+                       break;
+               }
+       }
+
+       while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0)
+               entry->acl_p = entry->acl_p->next;
+       if (entry->acl_p == NULL) {
+               entry->acl_state = 0;
+               return (ARCHIVE_EOF); /* End of ACL entries. */
+       }
+       *type = entry->acl_p->type;
+       *permset = entry->acl_p->permset;
+       *tag = entry->acl_p->tag;
+       *id = entry->acl_p->id;
+       *name = aes_get_mbs(&entry->acl_p->name);
+       entry->acl_p = entry->acl_p->next;
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Generate a text version of the ACL.  The flags parameter controls
+ * the style of the generated ACL.
+ */
+const wchar_t *
+archive_entry_acl_text_w(struct archive_entry *entry, int flags)
+{
+       int count;
+       int length;
+       const wchar_t *wname;
+       const wchar_t *prefix;
+       wchar_t separator;
+       struct ae_acl *ap;
+       int id;
+       wchar_t *wp;
+
+       if (entry->acl_text_w != NULL) {
+               free (entry->acl_text_w);
+               entry->acl_text_w = NULL;
+       }
+
+       separator = L',';
+       count = 0;
+       length = 0;
+       ap = entry->acl_head;
+       while (ap != NULL) {
+               if ((ap->type & flags) != 0) {
+                       count++;
+                       if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
+                           (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
+                               length += 8; /* "default:" */
+                       length += 5; /* tag name */
+                       length += 1; /* colon */
+                       wname = aes_get_wcs(&ap->name);
+                       if (wname != NULL)
+                               length += wcslen(wname);
+                       else
+                               length += sizeof(uid_t) * 3 + 1;
+                       length ++; /* colon */
+                       length += 3; /* rwx */
+                       length += 1; /* colon */
+                       length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
+                       length ++; /* newline */
+               }
+               ap = ap->next;
+       }
+
+       if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
+               length += 10; /* "user::rwx\n" */
+               length += 11; /* "group::rwx\n" */
+               length += 11; /* "other::rwx\n" */
+       }
+
+       if (count == 0)
+               return (NULL);
+
+       /* Now, allocate the string and actually populate it. */
+       wp = entry->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
+       if (wp == NULL)
+               __archive_errx(1, "No memory to generate the text version of the ACL");
+       count = 0;
+       if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
+                   entry->ae_stat.st_mode & 0700, -1);
+               *wp++ = ',';
+               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
+                   entry->ae_stat.st_mode & 0070, -1);
+               *wp++ = ',';
+               append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
+                   entry->ae_stat.st_mode & 0007, -1);
+               count += 3;
+
+               ap = entry->acl_head;
+               while (ap != NULL) {
+                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+                               wname = aes_get_wcs(&ap->name);
+                               *wp++ = separator;
+                               if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
+                                       id = ap->id;
+                               else
+                                       id = -1;
+                               append_entry_w(&wp, NULL, ap->tag, wname,
+                                   ap->permset, id);
+                               count++;
+                       }
+                       ap = ap->next;
+               }
+       }
+
+
+       if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
+               if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
+                       prefix = L"default:";
+               else
+                       prefix = NULL;
+               ap = entry->acl_head;
+               count = 0;
+               while (ap != NULL) {
+                       if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
+                               wname = aes_get_wcs(&ap->name);
+                               if (count > 0)
+                                       *wp++ = separator;
+                               if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
+                                       id = ap->id;
+                               else
+                                       id = -1;
+                               append_entry_w(&wp, prefix, ap->tag,
+                                   wname, ap->permset, id);
+                               count ++;
+                       }
+                       ap = ap->next;
+               }
+       }
+
+       return (entry->acl_text_w);
+}
+
+static void
+append_id_w(wchar_t **wp, int id)
+{
+       if (id < 0)
+               id = 0;
+       if (id > 9)
+               append_id_w(wp, id / 10);
+       *(*wp)++ = L"0123456789"[id % 10];
+}
+
+static void
+append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
+    const wchar_t *wname, int perm, int id)
+{
+       if (prefix != NULL) {
+               wcscpy(*wp, prefix);
+               *wp += wcslen(*wp);
+       }
+       switch (tag) {
+       case ARCHIVE_ENTRY_ACL_USER_OBJ:
+               wname = NULL;
+               id = -1;
+               /* FALL THROUGH */
+       case ARCHIVE_ENTRY_ACL_USER:
+               wcscpy(*wp, L"user");
+               break;
+       case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+               wname = NULL;
+               id = -1;
+               /* FALL THROUGH */
+       case ARCHIVE_ENTRY_ACL_GROUP:
+               wcscpy(*wp, L"group");
+               break;
+       case ARCHIVE_ENTRY_ACL_MASK:
+               wcscpy(*wp, L"mask");
+               wname = NULL;
+               id = -1;
+               break;
+       case ARCHIVE_ENTRY_ACL_OTHER:
+               wcscpy(*wp, L"other");
+               wname = NULL;
+               id = -1;
+               break;
+       }
+       *wp += wcslen(*wp);
+       *(*wp)++ = L':';
+       if (wname != NULL) {
+               wcscpy(*wp, wname);
+               *wp += wcslen(*wp);
+       } else if (tag == ARCHIVE_ENTRY_ACL_USER
+           || tag == ARCHIVE_ENTRY_ACL_GROUP) {
+               append_id_w(wp, id);
+               id = -1;
+       }
+       *(*wp)++ = L':';
+       *(*wp)++ = (perm & 0444) ? L'r' : L'-';
+       *(*wp)++ = (perm & 0222) ? L'w' : L'-';
+       *(*wp)++ = (perm & 0111) ? L'x' : L'-';
+       if (id != -1) {
+               *(*wp)++ = L':';
+               append_id_w(wp, id);
+       }
+       **wp = L'\0';
+}
+
+/*
+ * Parse a textual ACL.  This automatically recognizes and supports
+ * extensions described above.  The 'type' argument is used to
+ * indicate the type that should be used for any entries not
+ * explicitly marked as "default:".
+ */
+int
+__archive_entry_acl_parse_w(struct archive_entry *entry,
+    const wchar_t *text, int default_type)
+{
+       struct {
+               const wchar_t *start;
+               const wchar_t *end;
+       } field[4];
+
+       int fields;
+       int type, tag, permset, id;
+       const wchar_t *p;
+       wchar_t sep;
+
+       while (text != NULL  &&  *text != L'\0') {
+               /*
+                * Parse the fields out of the next entry,
+                * advance 'text' to start of next entry.
+                */
+               fields = 0;
+               do {
+                       const wchar_t *start, *end;
+                       next_field_w(&text, &start, &end, &sep);
+                       if (fields < 4) {
+                               field[fields].start = start;
+                               field[fields].end = end;
+                       }
+                       ++fields;
+               } while (sep == L':');
+
+               if (fields < 3)
+                       return (ARCHIVE_WARN);
+
+               /* Check for a numeric ID in field 1 or 3. */
+               id = -1;
+               isint_w(field[1].start, field[1].end, &id);
+               /* Field 3 is optional. */
+               if (id == -1 && fields > 3)
+                       isint_w(field[3].start, field[3].end, &id);
+
+               /* Parse the permissions from field 2. */
+               permset = 0;
+               p = field[2].start;
+               while (p < field[2].end) {
+                       switch (*p++) {
+                       case 'r': case 'R':
+                               permset |= ARCHIVE_ENTRY_ACL_READ;
+                               break;
+                       case 'w': case 'W':
+                               permset |= ARCHIVE_ENTRY_ACL_WRITE;
+                               break;
+                       case 'x': case 'X':
+                               permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+                               break;
+                       case '-':
+                               break;
+                       default:
+                               return (ARCHIVE_WARN);
+                       }
+               }
+
+               /*
+                * Solaris extension:  "defaultuser::rwx" is the
+                * default ACL corresponding to "user::rwx", etc.
+                */
+               if (field[0].end-field[0].start > 7
+                   && wmemcmp(field[0].start, L"default", 7) == 0) {
+                       type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+                       field[0].start += 7;
+               } else
+                       type = default_type;
+
+               if (prefix_w(field[0].start, field[0].end, L"user")) {
+                       if (id != -1 || field[1].start < field[1].end)
+                               tag = ARCHIVE_ENTRY_ACL_USER;
+                       else
+                               tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+               } else if (prefix_w(field[0].start, field[0].end, L"group")) {
+                       if (id != -1 || field[1].start < field[1].end)
+                               tag = ARCHIVE_ENTRY_ACL_GROUP;
+                       else
+                               tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+               } else if (prefix_w(field[0].start, field[0].end, L"other")) {
+                       if (id != -1 || field[1].start < field[1].end)
+                               return (ARCHIVE_WARN);
+                       tag = ARCHIVE_ENTRY_ACL_OTHER;
+               } else if (prefix_w(field[0].start, field[0].end, L"mask")) {
+                       if (id != -1 || field[1].start < field[1].end)
+                               return (ARCHIVE_WARN);
+                       tag = ARCHIVE_ENTRY_ACL_MASK;
+               } else
+                       return (ARCHIVE_WARN);
+
+               /* Add entry to the internal list. */
+               archive_entry_acl_add_entry_w_len(entry, type, permset,
+                   tag, id, field[1].start, field[1].end - field[1].start);
+       }
+       return (ARCHIVE_OK);
+}
+
+/*
+ * extended attribute handling
+ */
+
+void
+archive_entry_xattr_clear(struct archive_entry *entry)
+{
+       struct ae_xattr *xp;
+
+       while (entry->xattr_head != NULL) {
+               xp = entry->xattr_head->next;
+               free(entry->xattr_head->name);
+               free(entry->xattr_head->value);
+               free(entry->xattr_head);
+               entry->xattr_head = xp;
+       }
+
+       entry->xattr_head = NULL;
+}
+
+void
+archive_entry_xattr_add_entry(struct archive_entry *entry,
+       const char *name, const void *value, size_t size)
+{
+       struct ae_xattr *xp;
+
+       for (xp = entry->xattr_head; xp != NULL; xp = xp->next)
+               ;
+
+       if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL)
+               /* XXX Error XXX */
+               return;
+
+       xp->name = strdup(name);
+       if ((xp->value = malloc(size)) != NULL) {
+               memcpy(xp->value, value, size);
+               xp->size = size;
+       } else
+               xp->size = 0;
+
+       xp->next = entry->xattr_head;
+       entry->xattr_head = xp;
+}
+
+
+/*
+ * returns number of the extended attribute entries
+ */
+int
+archive_entry_xattr_count(struct archive_entry *entry)
+{
+       struct ae_xattr *xp;
+       int count = 0;
+
+       for (xp = entry->xattr_head; xp != NULL; xp = xp->next)
+               count++;
+
+       return count;
+}
+
+int
+archive_entry_xattr_reset(struct archive_entry * entry)
+{
+       entry->xattr_p = entry->xattr_head;
+
+       return archive_entry_xattr_count(entry);
+}
+
+int
+archive_entry_xattr_next(struct archive_entry * entry,
+       const char **name, const void **value, size_t *size)
+{
+       if (entry->xattr_p) {
+               *name = entry->xattr_p->name;
+               *value = entry->xattr_p->value;
+               *size = entry->xattr_p->size;
+
+               entry->xattr_p = entry->xattr_p->next;
+
+               return (ARCHIVE_OK);
+       } else {
+               *name = NULL;
+               *name = NULL;
+               *size = (size_t)0;
+               return (ARCHIVE_WARN);
+       }
+}
+
+/*
+ * end of xattr handling
+ */
+
+/*
+ * Parse a string to a positive decimal integer.  Returns true if
+ * the string is non-empty and consists only of decimal digits,
+ * false otherwise.
+ */
+static int
+isint_w(const wchar_t *start, const wchar_t *end, int *result)
+{
+       int n = 0;
+       if (start >= end)
+               return (0);
+       while (start < end) {
+               if (*start < '0' || *start > '9')
+                       return (0);
+               if (n > (INT_MAX / 10))
+                       n = INT_MAX;
+               else {
+                       n *= 10;
+                       n += *start - '0';
+               }
+               start++;
+       }
+       *result = n;
+       return (1);
+}
+
+/*
+ * Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]".  *wp is updated
+ * to point to just after the separator.  *start points to the first
+ * character of the matched text and *end just after the last
+ * character of the matched identifier.  In particular *end - *start
+ * is the length of the field body, not including leading or trailing
+ * whitespace.
+ */
+static void
+next_field_w(const wchar_t **wp, const wchar_t **start,
+    const wchar_t **end, wchar_t *sep)
+{
+       /* Skip leading whitespace to find start of field. */
+       while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
+               (*wp)++;
+       }
+       *start = *wp;
+
+       /* Scan for the separator. */
+       while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
+           **wp != L'\n') {
+               (*wp)++;
+       }
+       *sep = **wp;
+
+       /* Trim trailing whitespace to locate end of field. */
+       *end = *wp - 1;
+       while (**end == L' ' || **end == L'\t' || **end == L'\n') {
+               (*end)--;
+       }
+       (*end)++;
+
+       /* Adjust scanner location. */
+       if (**wp != L'\0')
+               (*wp)++;
+}
+
+/*
+ * Return true if the characters [start...end) are a prefix of 'test'.
+ * This makes it easy to handle the obvious abbreviations: 'u' for 'user', etc.
+ */
+static int
+prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
+{
+       if (start == end)
+               return (0);
+
+       if (*start++ != *test++)
+               return (0);
+
+       while (start < end  &&  *start++ == *test++)
+               ;
+
+       if (start < end)
+               return (0);
+
+       return (1);
+}
+
+
+/*
+ * Following code is modified from UC Berkeley sources, and
+ * is subject to the following copyright notice.
+ */
+
+/*-
+ * Copyright (c) 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.
+ * 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.
+ */
+
+static struct flag {
+       const char      *name;
+       const wchar_t   *wname;
+       unsigned long    set;
+       unsigned long    clear;
+} flags[] = {
+       /* Preferred (shorter) names per flag first, all prefixed by "no" */
+#ifdef SF_APPEND
+       { "nosappnd",   L"nosappnd",            SF_APPEND,      0 },
+       { "nosappend",  L"nosappend",           SF_APPEND,      0 },
+#endif
+#ifdef  EXT2_APPEND_FL                         /* 'a' */
+       { "nosappnd",   L"nosappnd",            EXT2_APPEND_FL, 0 },
+       { "nosappend",  L"nosappend",           EXT2_APPEND_FL, 0 },
+#endif
+#ifdef SF_ARCHIVED
+       { "noarch",     L"noarch",              SF_ARCHIVED,    0 },
+       { "noarchived", L"noarchived",          SF_ARCHIVED,    0 },
+#endif
+#ifdef SF_IMMUTABLE
+       { "noschg",     L"noschg",              SF_IMMUTABLE,   0 },
+       { "noschange",  L"noschange",           SF_IMMUTABLE,   0 },
+       { "nosimmutable",       L"nosimmutable",        SF_IMMUTABLE,   0 },
+#endif
+#ifdef EXT2_IMMUTABLE_FL                       /* 'i' */
+       { "noschg",     L"noschg",              EXT2_IMMUTABLE_FL,      0 },
+       { "noschange",  L"noschange",           EXT2_IMMUTABLE_FL,      0 },
+       { "nosimmutable",       L"nosimmutable",        EXT2_IMMUTABLE_FL,      0 },
+#endif
+#ifdef SF_NOUNLINK
+       { "nosunlnk",   L"nosunlnk",            SF_NOUNLINK,    0 },
+       { "nosunlink",  L"nosunlink",           SF_NOUNLINK,    0 },
+#endif
+#ifdef SF_SNAPSHOT
+       { "nosnapshot", L"nosnapshot",  SF_SNAPSHOT,    0 },
+#endif
+#ifdef UF_APPEND
+       { "nouappnd",   L"nouappnd",            UF_APPEND,      0 },
+       { "nouappend",  L"nouappend",           UF_APPEND,      0 },
+#endif
+#ifdef UF_IMMUTABLE
+       { "nouchg",     L"nouchg",              UF_IMMUTABLE,   0 },
+       { "nouchange",  L"nouchange",           UF_IMMUTABLE,   0 },
+       { "nouimmutable",       L"nouimmutable",        UF_IMMUTABLE,   0 },
+#endif
+#ifdef UF_NODUMP
+       { "nodump",     L"nodump",              0,              UF_NODUMP},
+#endif
+#ifdef EXT2_NODUMP_FL                          /* 'd' */
+       { "nodump",     L"nodump",              0,              EXT2_NODUMP_FL},
+#endif
+#ifdef UF_OPAQUE
+       { "noopaque",   L"noopaque",            UF_OPAQUE,      0 },
+#endif
+#ifdef UF_NOUNLINK
+       { "nouunlnk",   L"nouunlnk",            UF_NOUNLINK,    0 },
+       { "nouunlink",  L"nouunlink",           UF_NOUNLINK,    0 },
+#endif
+#ifdef EXT2_COMPR_FL                           /* 'c' */
+        { "nocompress",        L"nocompress",          EXT2_COMPR_FL,  0 },
+#endif
+
+#ifdef EXT2_NOATIME_FL                         /* 'A' */
+        { "noatime",   L"noatime",             0,              EXT2_NOATIME_FL},
+#endif
+       { NULL,         NULL,                   0,              0 }
+};
+
+/*
+ * fflagstostr --
+ *     Convert file flags to a comma-separated string.  If no flags
+ *     are set, return the empty string.
+ */
+char *
+ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
+{
+       char *string, *dp;
+       const char *sp;
+       unsigned long bits;
+       struct flag *flag;
+       int     length;
+
+       bits = bitset | bitclear;
+       length = 0;
+       for (flag = flags; flag->name != NULL; flag++)
+               if (bits & (flag->set | flag->clear)) {
+                       length += strlen(flag->name) + 1;
+                       bits &= ~(flag->set | flag->clear);
+               }
+
+       if (length == 0)
+               return (NULL);
+       string = (char *)malloc(length);
+       if (string == NULL)
+               return (NULL);
+
+       dp = string;
+       for (flag = flags; flag->name != NULL; flag++) {
+               if (bitset & flag->set || bitclear & flag->clear) {
+                       sp = flag->name + 2;
+               } else if (bitset & flag->clear  ||  bitclear & flag->set) {
+                       sp = flag->name;
+               } else
+                       continue;
+               bitset &= ~(flag->set | flag->clear);
+               bitclear &= ~(flag->set | flag->clear);
+               if (dp > string)
+                       *dp++ = ',';
+               while ((*dp++ = *sp++) != '\0')
+                       ;
+               dp--;
+       }
+
+       *dp = '\0';
+       return (string);
+}
+
+/*
+ * wcstofflags --
+ *     Take string of arguments and return file flags.  This
+ *     version works a little differently than strtofflags(3).
+ *     In particular, it always tests every token, skipping any
+ *     unrecognized tokens.  It returns a pointer to the first
+ *     unrecognized token, or NULL if every token was recognized.
+ *     This version is also const-correct and does not modify the
+ *     provided string.
+ */
+const wchar_t *
+ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
+{
+       const wchar_t *start, *end;
+       struct flag *flag;
+       unsigned long set, clear;
+       const wchar_t *failed;
+
+       set = clear = 0;
+       start = s;
+       failed = NULL;
+       /* Find start of first token. */
+       while (*start == L'\t'  ||  *start == L' '  ||  *start == L',')
+               start++;
+       while (*start != L'\0') {
+               /* Locate end of token. */
+               end = start;
+               while (*end != L'\0'  &&  *end != L'\t'  &&
+                   *end != L' '  &&  *end != L',')
+                       end++;
+               for (flag = flags; flag->wname != NULL; flag++) {
+                       if (wmemcmp(start, flag->wname, end - start) == 0) {
+                               /* Matched "noXXXX", so reverse the sense. */
+                               clear |= flag->set;
+                               set |= flag->clear;
+                               break;
+                       } else if (wmemcmp(start, flag->wname + 2, end - start)
+                           == 0) {
+                               /* Matched "XXXX", so don't reverse. */
+                               set |= flag->set;
+                               clear |= flag->clear;
+                               break;
+                       }
+               }
+               /* Ignore unknown flag names. */
+               if (flag->wname == NULL  &&  failed == NULL)
+                       failed = start;
+
+               /* Find start of next token. */
+               start = end;
+               while (*start == L'\t'  ||  *start == L' '  ||  *start == L',')
+                       start++;
+
+       }
+
+       if (setp)
+               *setp = set;
+       if (clrp)
+               *clrp = clear;
+
+       /* Return location of first failure. */
+       return (failed);
+}
+
+
+#ifdef TEST
+#include <stdio.h>
+int
+main(int argc, char **argv)
+{
+       struct archive_entry *entry = archive_entry_new();
+       unsigned long set, clear;
+       const wchar_t *remainder;
+
+       remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,");
+       archive_entry_fflags(entry, &set, &clear);
+
+       wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder);
+
+       wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry));
+       return (0);
+}
+#endif
diff --git a/contrib/libarchive-2.0/libarchive/archive_entry.h b/contrib/libarchive-2.0/libarchive/archive_entry.h
new file mode 100644 (file)
index 0000000..3718cc2
--- /dev/null
@@ -0,0 +1,251 @@
+/*-
+ * 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: src/lib/libarchive/archive_entry.h,v 1.21 2007/03/01 06:22:34 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_ENTRY_H_INCLUDED
+#define        ARCHIVE_ENTRY_H_INCLUDED
+
+#include <stddef.h>  /* for wchar_t */
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Description of an archive entry.
+ *
+ * Basically, a "struct stat" with a few text fields added in.
+ *
+ * TODO: Add "comment", "charset", and possibly other entries that are
+ * supported by "pax interchange" format.  However, GNU, ustar, cpio,
+ * and other variants don't support these features, so they're not an
+ * excruciatingly high priority right now.
+ *
+ * TODO: "pax interchange" format allows essentially arbitrary
+ * key/value attributes to be attached to any entry.  Supporting
+ * such extensions may make this library useful for special
+ * applications (e.g., a package manager could attach special
+ * package-management attributes to each entry).
+ */
+struct archive_entry;
+
+/*
+ * Basic object manipulation
+ */
+
+struct archive_entry   *archive_entry_clear(struct archive_entry *);
+/* The 'clone' function does a deep copy; all of the strings are copied too. */
+struct archive_entry   *archive_entry_clone(struct archive_entry *);
+void                    archive_entry_free(struct archive_entry *);
+struct archive_entry   *archive_entry_new(void);
+
+/*
+ * Retrieve fields from an archive_entry.
+ */
+
+time_t                  archive_entry_atime(struct archive_entry *);
+long                    archive_entry_atime_nsec(struct archive_entry *);
+time_t                  archive_entry_ctime(struct archive_entry *);
+long                    archive_entry_ctime_nsec(struct archive_entry *);
+dev_t                   archive_entry_dev(struct archive_entry *);
+void                    archive_entry_fflags(struct archive_entry *,
+                            unsigned long *set, unsigned long *clear);
+const char             *archive_entry_fflags_text(struct archive_entry *);
+gid_t                   archive_entry_gid(struct archive_entry *);
+const char             *archive_entry_gname(struct archive_entry *);
+const wchar_t          *archive_entry_gname_w(struct archive_entry *);
+const char             *archive_entry_hardlink(struct archive_entry *);
+const wchar_t          *archive_entry_hardlink_w(struct archive_entry *);
+ino_t                   archive_entry_ino(struct archive_entry *);
+mode_t                  archive_entry_mode(struct archive_entry *);
+time_t                  archive_entry_mtime(struct archive_entry *);
+long                    archive_entry_mtime_nsec(struct archive_entry *);
+const char             *archive_entry_pathname(struct archive_entry *);
+const wchar_t          *archive_entry_pathname_w(struct archive_entry *);
+dev_t                   archive_entry_rdev(struct archive_entry *);
+dev_t                   archive_entry_rdevmajor(struct archive_entry *);
+dev_t                   archive_entry_rdevminor(struct archive_entry *);
+int64_t                         archive_entry_size(struct archive_entry *);
+const struct stat      *archive_entry_stat(struct archive_entry *);
+const char             *archive_entry_symlink(struct archive_entry *);
+const wchar_t          *archive_entry_symlink_w(struct archive_entry *);
+uid_t                   archive_entry_uid(struct archive_entry *);
+const char             *archive_entry_uname(struct archive_entry *);
+const wchar_t          *archive_entry_uname_w(struct archive_entry *);
+
+/*
+ * Set fields in an archive_entry.
+ *
+ * Note that string 'set' functions do not copy the string, only the pointer.
+ * In contrast, 'copy' functions do copy the object pointed to.
+ */
+
+void   archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+void   archive_entry_set_atime(struct archive_entry *, time_t, long);
+void   archive_entry_set_ctime(struct archive_entry *, time_t, long);
+void   archive_entry_set_fflags(struct archive_entry *,
+           unsigned long set, unsigned long clear);
+/* Returns pointer to start of first invalid token, or NULL if none. */
+/* Note that all recognized tokens are processed, regardless. */
+const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
+           const wchar_t *);
+void   archive_entry_set_gid(struct archive_entry *, gid_t);
+void   archive_entry_set_gname(struct archive_entry *, const char *);
+void   archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
+void   archive_entry_set_hardlink(struct archive_entry *, const char *);
+void   archive_entry_copy_hardlink(struct archive_entry *, const char *);
+void   archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
+void   archive_entry_set_link(struct archive_entry *, const char *);
+void   archive_entry_set_mode(struct archive_entry *, mode_t);
+void   archive_entry_set_mtime(struct archive_entry *, time_t, long);
+void   archive_entry_set_pathname(struct archive_entry *, const char *);
+void   archive_entry_copy_pathname(struct archive_entry *, const char *);
+void   archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
+void   archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
+void   archive_entry_set_rdevminor(struct archive_entry *, dev_t);
+void   archive_entry_set_size(struct archive_entry *, int64_t);
+void   archive_entry_set_symlink(struct archive_entry *, const char *);
+void   archive_entry_copy_symlink(struct archive_entry *, const char *);
+void   archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
+void   archive_entry_set_uid(struct archive_entry *, uid_t);
+void   archive_entry_set_uname(struct archive_entry *, const char *);
+void   archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
+
+/*
+ * ACL routines.  This used to simply store and return text-format ACL
+ * strings, but that proved insufficient for a number of reasons:
+ *   = clients need control over uname/uid and gname/gid mappings
+ *   = there are many different ACL text formats
+ *   = would like to be able to read/convert archives containing ACLs
+ *     on platforms that lack ACL libraries
+ */
+
+/*
+ * Permission bits mimic POSIX.1e.  Note that I've not followed POSIX.1e's
+ * "permset"/"perm" abstract type nonsense.  A permset is just a simple
+ * bitmap, following long-standing Unix tradition.
+ */
+#define        ARCHIVE_ENTRY_ACL_EXECUTE       1
+#define        ARCHIVE_ENTRY_ACL_WRITE         2
+#define        ARCHIVE_ENTRY_ACL_READ          4
+
+/* We need to be able to specify either or both of these. */
+#define        ARCHIVE_ENTRY_ACL_TYPE_ACCESS   256
+#define        ARCHIVE_ENTRY_ACL_TYPE_DEFAULT  512
+
+/* Tag values mimic POSIX.1e */
+#define        ARCHIVE_ENTRY_ACL_USER          10001   /* Specified user. */
+#define        ARCHIVE_ENTRY_ACL_USER_OBJ      10002   /* User who owns the file. */
+#define        ARCHIVE_ENTRY_ACL_GROUP         10003   /* Specified group. */
+#define        ARCHIVE_ENTRY_ACL_GROUP_OBJ     10004   /* Group who owns the file. */
+#define        ARCHIVE_ENTRY_ACL_MASK          10005   /* Modify group access. */
+#define        ARCHIVE_ENTRY_ACL_OTHER         10006   /* Public. */
+
+/*
+ * Set the ACL by clearing it and adding entries one at a time.
+ * Unlike the POSIX.1e ACL routines, you must specify the type
+ * (access/default) for each entry.  Internally, the ACL data is just
+ * a soup of entries.  API calls here allow you to retrieve just the
+ * entries of interest.  This design (which goes against the spirit of
+ * POSIX.1e) is useful for handling archive formats that combine
+ * default and access information in a single ACL list.
+ */
+void    archive_entry_acl_clear(struct archive_entry *);
+void    archive_entry_acl_add_entry(struct archive_entry *,
+            int type, int permset, int tag, int qual, const char *name);
+void    archive_entry_acl_add_entry_w(struct archive_entry *,
+            int type, int permset, int tag, int qual, const wchar_t *name);
+
+/*
+ * To retrieve the ACL, first "reset", then repeatedly ask for the
+ * "next" entry.  The want_type parameter allows you to request only
+ * access entries or only default entries.
+ */
+int     archive_entry_acl_reset(struct archive_entry *, int want_type);
+int     archive_entry_acl_next(struct archive_entry *, int want_type,
+            int *type, int *permset, int *tag, int *qual, const char **name);
+int     archive_entry_acl_next_w(struct archive_entry *, int want_type,
+            int *type, int *permset, int *tag, int *qual,
+            const wchar_t **name);
+
+/*
+ * Construct a text-format ACL.  The flags argument is a bitmask that
+ * can include any of the following:
+ *
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include access entries.
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include default entries.
+ * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
+ *    each ACL entry.  (As used by 'star'.)
+ * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
+ *    default ACL entry.
+ */
+#define        ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID        1024
+#define        ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT    2048
+const wchar_t  *archive_entry_acl_text_w(struct archive_entry *, int flags);
+
+/* Return a count of entries matching 'want_type' */
+int     archive_entry_acl_count(struct archive_entry *, int want_type);
+
+/*
+ * Private ACL parser.  This is private because it handles some
+ * very weird formats that clients should not be messing with.
+ * Clients should only deal with their platform-native formats.
+ * Because of the need to support many formats cleanly, new arguments
+ * are likely to get added on a regular basis.  Clients who try to use
+ * this interface are likely to be surprised when it changes.
+ *
+ * You were warned!
+ */
+int             __archive_entry_acl_parse_w(struct archive_entry *,
+                    const wchar_t *, int type);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * extended attributes
+ */
+
+void    archive_entry_xattr_clear(struct archive_entry *);
+void    archive_entry_xattr_add_entry(struct archive_entry *,
+            const char *name, const void *value, size_t size);
+
+/*
+ * To retrieve the xattr list, first "reset", then repeatedly ask for the
+ * "next" entry.
+ */
+
+int    archive_entry_xattr_count(struct archive_entry *);
+int    archive_entry_xattr_reset(struct archive_entry *);
+int    archive_entry_xattr_next(struct archive_entry *,
+            const char **name, const void **value, size_t *);
+
+
+#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
diff --git a/contrib/libarchive-2.0/libarchive/archive_platform.h b/contrib/libarchive-2.0/libarchive/archive_platform.h
new file mode 100644 (file)
index 0000000..a7e771a
--- /dev/null
@@ -0,0 +1,140 @@
+/*-
+ * 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: src/lib/libarchive/archive_platform.h,v 1.24 2007/01/09 08:05:54 kientzle Exp $
+ */
+
+/*
+ * This header is the first thing included in any of the libarchive
+ * source files.  As far as possible, platform-specific issues should
+ * be dealt with here and not within individual source files.  I'm
+ * actively trying to minimize #if blocks within the main source,
+ * since they obfuscate the code.
+ */
+
+#ifndef ARCHIVE_PLATFORM_H_INCLUDED
+#define        ARCHIVE_PLATFORM_H_INCLUDED
+
+#if defined(HAVE_CONFIG_H)
+/* Most POSIX platforms use the 'configure' script to build config.h */
+#include "../config.h"
+#elif defined(__FreeBSD__)
+/* Building as part of FreeBSD system requires a pre-built config.h. */
+#include "config_freebsd.h"
+#elif defined(_WIN32)
+/* Win32 can't run the 'configure' script. */
+#include "config_windows.h"
+#else
+/* Warn if the library hasn't been (automatically or manually) configured. */
+#error Oops: No config.h and no pre-built configuration in archive_platform.h.
+#endif
+
+/*
+ * The config files define a lot of feature macros.  The following
+ * uses those macros to select/define replacements and include key
+ * headers as required.
+ */
+
+/* No non-FreeBSD platform will have __FBSDID, so just define it here. */
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>  /* For __FBSDID */
+#else
+#define        __FBSDID(a)     /* null */
+#endif
+
+/* Try to get standard C99-style integer type definitions. */
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#elif HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+/*
+ * If this platform has <sys/acl.h>, acl_create(), acl_init(),
+ * acl_set_file(), and ACL_USER, we assume it has the rest of the
+ * POSIX.1e draft functions used in archive_read_extract.c.
+ */
+#if HAVE_SYS_ACL_H && HAVE_ACL_CREATE_ENTRY && HAVE_ACL_INIT && HAVE_ACL_SET_FILE && HAVE_ACL_USER
+#define        HAVE_POSIX_ACL  1
+#endif
+
+/*
+ * If we can't restore metadata using a file descriptor, then
+ * for compatibility's sake, close files before trying to restore metadata.
+ */
+#if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN)
+#define        CAN_RESTORE_METADATA_FD
+#endif
+
+/* Set up defaults for internal error codes. */
+#ifndef ARCHIVE_ERRNO_FILE_FORMAT
+#if HAVE_EFTYPE
+#define        ARCHIVE_ERRNO_FILE_FORMAT EFTYPE
+#else
+#if HAVE_EILSEQ
+#define        ARCHIVE_ERRNO_FILE_FORMAT EILSEQ
+#else
+#define        ARCHIVE_ERRNO_FILE_FORMAT EINVAL
+#endif
+#endif
+#endif
+
+#ifndef ARCHIVE_ERRNO_PROGRAMMER
+#define        ARCHIVE_ERRNO_PROGRAMMER EINVAL
+#endif
+
+#ifndef ARCHIVE_ERRNO_MISC
+#define        ARCHIVE_ERRNO_MISC (-1)
+#endif
+
+/* Select the best way to set/get hi-res timestamps. */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+/* FreeBSD uses "timespec" members. */
+#define        ARCHIVE_STAT_ATIME_NANOS(st)    (st)->st_atimespec.tv_nsec
+#define        ARCHIVE_STAT_CTIME_NANOS(st)    (st)->st_ctimespec.tv_nsec
+#define        ARCHIVE_STAT_MTIME_NANOS(st)    (st)->st_mtimespec.tv_nsec
+#define        ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atimespec.tv_nsec = (n)
+#define        ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctimespec.tv_nsec = (n)
+#define        ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtimespec.tv_nsec = (n)
+#else
+#if HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+/* Linux uses "tim" members. */
+#define        ARCHIVE_STAT_ATIME_NANOS(pstat) (pstat)->st_atim.tv_nsec
+#define        ARCHIVE_STAT_CTIME_NANOS(pstat) (pstat)->st_ctim.tv_nsec
+#define        ARCHIVE_STAT_MTIME_NANOS(pstat) (pstat)->st_mtim.tv_nsec
+#define        ARCHIVE_STAT_SET_ATIME_NANOS(st, n) (st)->st_atim.tv_nsec = (n)
+#define        ARCHIVE_STAT_SET_CTIME_NANOS(st, n) (st)->st_ctim.tv_nsec = (n)
+#define        ARCHIVE_STAT_SET_MTIME_NANOS(st, n) (st)->st_mtim.tv_nsec = (n)
+#else
+/* If we can't find a better way, just use stubs. */
+#define        ARCHIVE_STAT_ATIME_NANOS(pstat) 0
+#define        ARCHIVE_STAT_CTIME_NANOS(pstat) 0
+#define        ARCHIVE_STAT_MTIME_NANOS(pstat) 0
+#define        ARCHIVE_STAT_SET_ATIME_NANOS(st, n) ((void)(n))
+#define        ARCHIVE_STAT_SET_CTIME_NANOS(st, n) ((void)(n))
+#define        ARCHIVE_STAT_SET_MTIME_NANOS(st, n) ((void)(n))
+#endif
+#endif
+
+#endif /* !ARCHIVE_H_INCLUDED */
diff --git a/contrib/libarchive-2.0/libarchive/archive_private.h b/contrib/libarchive-2.0/libarchive/archive_private.h
new file mode 100644 (file)
index 0000000..f9d0961
--- /dev/null
@@ -0,0 +1,99 @@
+/*-
+ * 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: src/lib/libarchive/archive_private.h,v 1.28 2007/03/03 07:37:36 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_PRIVATE_H_INCLUDED
+#define        ARCHIVE_PRIVATE_H_INCLUDED
+
+#include "archive.h"
+#include "archive_string.h"
+
+#define        ARCHIVE_WRITE_MAGIC     (0xb0c5c0deU)
+#define        ARCHIVE_READ_MAGIC      (0xdeb0c5U)
+#define ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)
+
+#define        ARCHIVE_STATE_ANY       0xFFFFU
+#define        ARCHIVE_STATE_NEW       1U
+#define        ARCHIVE_STATE_HEADER    2U
+#define        ARCHIVE_STATE_DATA      4U
+#define ARCHIVE_STATE_DATA_END 8U
+#define        ARCHIVE_STATE_EOF       0x10U
+#define        ARCHIVE_STATE_CLOSED    0x20U
+#define        ARCHIVE_STATE_FATAL     0x8000U
+
+struct archive_vtable {
+       int     (*archive_write_close)(struct archive *);
+       int     (*archive_write_finish)(struct archive *);
+       int     (*archive_write_header)(struct archive *,
+           struct archive_entry *);
+       int     (*archive_write_finish_entry)(struct archive *);
+       ssize_t (*archive_write_data)(struct archive *,
+           const void *, size_t);
+       ssize_t (*archive_write_data_block)(struct archive *,
+           const void *, size_t, off_t);
+};
+
+struct archive {
+       /*
+        * The magic/state values are used to sanity-check the
+        * client's usage.  If an API function is called at a
+        * ridiculous time, or the client passes us an invalid
+        * pointer, these values allow me to catch that.
+        */
+       unsigned          magic;
+       unsigned          state;
+
+       /*
+        * Some public API functions depend on the "real" type of the
+        * archive object.
+        */
+       struct archive_vtable *vtable;
+
+       int               archive_format;
+       const char       *archive_format_name;
+
+       int       compression_code;     /* Currently active compression. */
+       const char *compression_name;
+
+       /* Position in UNCOMPRESSED data stream. */
+       off_t             file_position;
+       /* Position in COMPRESSED data stream. */
+       off_t             raw_position;
+
+       int               archive_error_number;
+       const char       *error;
+       struct archive_string   error_string;
+};
+
+/* Check magic value and state; exit if it isn't valid. */
+void   __archive_check_magic(struct archive *, unsigned magic,
+           unsigned state, const char *func);
+
+void   __archive_errx(int retvalue, const char *msg);
+
+#define        err_combine(a,b)        ((a) < (b) ? (a) : (b))
+
+#endif
diff --git a/contrib/libarchive-2.0/libarchive/archive_read.3 b/contrib/libarchive-2.0/libarchive/archive_read.3
new file mode 100644 (file)
index 0000000..1a83ff4
--- /dev/null
@@ -0,0 +1,510 @@
+.\" 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 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 AUTHOR 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.
+.\"
+.\" $FreeBSD: src/lib/libarchive/archive_read.3,v 1.31 2007/03/03 07:37:36 kientzle Exp $
+.\"
+.Dd August 19, 2006
+.Dt archive_read 3
+.Os
+.Sh NAME
+.Nm archive_read_new ,
+.Nm archive_read_support_compression_all ,
+.Nm archive_read_support_compression_bzip2 ,
+.Nm archive_read_support_compression_compress ,
+.Nm archive_read_support_compression_gzip ,
+.Nm archive_read_support_compression_none ,
+.Nm archive_read_support_format_all ,
+.Nm archive_read_support_format_cpio ,
+.Nm archive_read_support_format_empty ,
+.Nm archive_read_support_format_iso9660 ,
+.Nm archive_read_support_format_tar ,
+.Nm archive_read_support_format_zip ,
+.Nm archive_read_open ,
+.Nm archive_read_open2 ,
+.Nm archive_read_open_fd ,
+.Nm archive_read_open_FILE ,
+.Nm archive_read_open_filename ,
+.Nm archive_read_open_memory ,
+.Nm archive_read_next_header ,
+.Nm archive_read_data ,
+.Nm archive_read_data_block ,
+.Nm archive_read_data_skip ,
+.Nm archive_read_data_into_buffer ,
+.Nm archive_read_data_into_fd ,
+.Nm archive_read_extract ,
+.Nm archive_read_extract_set_progress_callback ,
+.Nm archive_read_close ,
+.Nm archive_read_finish
+.Nd functions for reading streaming archives
+.Sh SYNOPSIS
+.In archive.h
+.Ft struct archive *
+.Fn archive_read_new "void"
+.Ft int
+.Fn archive_read_support_compression_all "struct archive *"
+.Ft int
+.Fn archive_read_support_compression_bzip2 "struct archive *"
+.Ft int
+.Fn archive_read_support_compression_compress "struct archive *"
+.Ft int
+.Fn archive_read_support_compression_gzip "struct archive *"
+.Ft int
+.Fn archive_read_support_compression_none "struct archive *"
+.Ft int
+.Fn archive_read_support_format_all "struct archive *"
+.Ft int
+.Fn archive_read_support_format_cpio "struct archive *"
+.Ft int
+.Fn archive_read_support_format_empty "struct archive *"
+.Ft int
+.Fn archive_read_support_format_iso9660 "struct archive *"
+.Ft int
+.Fn archive_read_support_format_tar "struct archive *"
+.Ft int
+.Fn archive_read_support_format_zip "struct archive *"
+.Ft int
+.Fn archive_read_open "struct archive *" "void *client_data" "archive_open_callback *" "archive_read_callback *" "archive_close_callback *"
+.Ft int
+.Fn archive_read_open2 "struct archive *" "void *client_data" "archive_open_callback *" "archive_read_callback *" "archive_skip_callback *" "archive_close_callback *"
+.Ft int
+.Fn archive_read_open_FILE "struct archive *" "FILE *file"
+.Ft int
+.Fn archive_read_open_fd "struct archive *" "int fd" "size_t block_size"
+.Ft int
+.Fn archive_read_open_filename "struct archive *" "const char *filename" "size_t block_size"
+.Ft int
+.Fn archive_read_open_memory "struct archive *" "void *buff" "size_t size"
+.Ft int
+.Fn archive_read_next_header "struct archive *" "struct archive_entry **"
+.Ft ssize_t
+.Fn archive_read_data "struct archive *" "void *buff" "size_t len"
+.Ft int
+.Fn archive_read_data_block "struct archive *" "const void **buff" "size_t *len" "off_t *offset"
+.Ft int
+.Fn archive_read_data_skip "struct archive *"
+.Ft int
+.Fn archive_read_data_into_buffer "struct archive *" "void *" "ssize_t len"
+.Ft int
+.Fn archive_read_data_into_fd "struct archive *" "int fd"
+.Ft int
+.Fn archive_read_extract "struct archive *" "struct archive_entry *" "int flags"
+.Ft void
+.Fn archive_read_extract_set_progress_callback "struct archive *" "void (*func)(void *)" "void *user_data"
+.Ft int
+.Fn archive_read_close "struct archive *"
+.Ft int
+.Fn archive_read_finish "struct archive *"
+.Sh DESCRIPTION
+These functions provide a complete API for reading streaming archives.
+The general process is to first create the
+.Tn struct archive
+object, set options, initialize the reader, iterate over the archive
+headers and associated data, then close the archive and release all
+resources.
+The following summary describes the functions in approximately the
+order they would be used:
+.Bl -tag -compact -width indent
+.It Fn archive_read_new
+Allocates and initializes a
+.Tn struct archive
+object suitable for reading from an archive.
+.It Fn archive_read_support_compression_all , Fn archive_read_support_compression_bzip2 , Fn archive_read_support_compression_compress , Fn archive_read_support_compression_gzip , Fn archive_read_support_compression_none
+Enables auto-detection code and decompression support for the
+specified compression.
+Note that
+.Dq none
+is always enabled by default.
+For convenience,
+.Fn archive_read_support_compression_all
+enables all available decompression code.
+.It Fn archive_read_support_format_all , Fn archive_read_support_format_cpio , Fn archive_read_support_format_empty , Fn archive_read_support_format_iso9660 , Fn archive_read_support_format_tar, Fn archive_read_support_format_zip
+Enables support---including auto-detection code---for the
+specified archive format.
+For example,
+.Fn archive_read_support_format_tar
+enables support for a variety of standard tar formats, old-style tar,
+ustar, pax interchange format, and many common variants.
+For convenience,
+.Fn archive_read_support_format_all
+enables support for all available formats.
+Only empty archives are supported by default.
+.It Fn archive_read_open
+The same as
+.Fn archive_read_open2 ,
+except that the skip callback is assumed to be
+.Dv NULL .
+.It Fn archive_read_open2
+Freeze the settings, open the archive, and prepare for reading entries.
+This is the most generic version of this call, which accepts
+four callback functions.
+Most clients will want to use
+.Fn archive_read_open_filename ,
+.Fn archive_read_open_FILE ,
+.Fn archive_read_open_fd ,
+or
+.Fn archive_read_open_memory
+instead.
+The library invokes the client-provided functions to obtain
+raw bytes from the archive.
+.It Fn archive_read_open_FILE
+Like
+.Fn archive_read_open ,
+except that it accepts a
+.Ft "FILE *"
+pointer.
+This function should not be used with tape drives or other devices
+that require strict I/O blocking.
+.It Fn archive_read_open_fd
+Like
+.Fn archive_read_open ,
+except that it accepts a file descriptor and block size rather than
+a set of function pointers.
+Note that the file descriptor will not be automatically closed at
+end-of-archive.
+This function is safe for use with tape drives or other blocked devices.
+.It Fn archive_read_open_file
+This is a deprecated synonym for
+.Fn archive_read_open_filename .
+.It Fn archive_read_open_filename
+Like
+.Fn archive_read_open ,
+except that it accepts a simple filename and a block size.
+A NULL filename represents standard input.
+This function is safe for use with tape drives or other blocked devices.
+.It Fn archive_read_open_memory
+Like
+.Fn archive_read_open ,
+except that it accepts a pointer and size of a block of
+memory containing the archive data.
+.It Fn archive_read_next_header
+Read the header for the next entry and return a pointer to
+a
+.Tn struct archive_entry .
+.It Fn archive_read_data
+Read data associated with the header just read.
+Internally, this is a convenience function that calls
+.Fn archive_read_data_block
+and fills any gaps with nulls so that callers see a single
+continuous stream of data.
+.It Fn archive_read_data_block
+Return the next available block of data for this entry.
+Unlike
+.Fn archive_read_data ,
+the
+.Fn archive_read_data_block
+function avoids copying data and allows you to correctly handle
+sparse files, as supported by some archive formats.
+The library guarantees that offsets will increase and that blocks
+will not overlap.
+Note that the blocks returned from this function can be much larger
+than the block size read from disk, due to compression
+and internal buffer optimizations.
+.It Fn archive_read_data_skip
+A convenience function that repeatedly calls
+.Fn archive_read_data_block
+to skip all of the data for this archive entry.
+.It Fn archive_read_data_into_buffer
+A convenience function that repeatedly calls
+.Fn archive_read_data_block
+to copy the entire entry into the client-supplied buffer.
+Note that the client is responsible for sizing the buffer appropriately.
+.It Fn archive_read_data_into_fd
+A convenience function that repeatedly calls
+.Fn archive_read_data_block
+to copy the entire entry to the provided file descriptor.
+.It Fn archive_read_extract , Fn archive_read_extract_set_skip_file
+A convenience function that wraps the corresponding
+.Xr archive_write_disk 3
+interfaces.
+The first call to
+.Fn archive_read_extract
+creates a restore object using
+.Xr archive_write_disk_new 3
+and
+.Xr archive_write_disk_set_standard_lookup 3 ,
+then transparently invokes
+.Xr archive_write_disk_set_options 3 ,
+.Xr archive_write_header 3 ,
+.Xr archive_write_data 3 ,
+and
+.Xr archive_write_finish_entry 3
+to create the entry on disk and copy data into it.
+The
+.Va flags
+argument is passed unmodified to
+.Xr archiv_write_disk_set_options 3 .
+.It Fn archive_read_extract_set_progress_callback
+Sets a pointer to a user-defined callback that can be used
+for updating progress displays during extraction.
+The progress function will be invoked during the extraction of large
+regular files.
+The progress function will be invoked with the pointer provided to this call.
+Generally, the data pointed to should include a reference to the archive
+object and the archive_entry object so that various statistics
+can be retrieved for the progress display.
+.It Fn archive_read_close
+Complete the archive and invoke the close callback.
+.It Fn archive_read_finish
+Invokes
+.Fn archive_read_close
+if it was not invoked manually, then release all resources.
+Note: In libarchive 1.x, this function was declared to return
+.Ft void ,
+which made it impossible to detect certain errors when
+.Fn archive_read_close
+was invoked implicitly from this function.
+The declaration is corrected beginning with libarchive 2.0.
+.El
+.Pp
+Note that the library determines most of the relevant information about
+the archive by inspection.
+In particular, it automatically detects
+.Xr gzip 1
+or
+.Xr bzip2 1
+compression and transparently performs the appropriate decompression.
+It also automatically detects the archive format.
+.Pp
+A complete description of the
+.Tn struct archive
+and
+.Tn struct archive_entry
+objects can be found in the overview manual page for
+.Xr libarchive 3 .
+.Sh CLIENT CALLBACKS
+The callback functions must match the following prototypes:
+.Bl -item -offset indent
+.It
+.Ft typedef ssize_t
+.Fn archive_read_callback "struct archive *" "void *client_data" "const void **buffer"
+.It
+.\" #if ARCHIVE_API_VERSION < 2
+.Ft typedef int
+.Fn archive_skip_callback "struct archive *" "void *client_data" "size_t request"
+.\" #else
+.\" .Ft typedef off_t
+.\" .Fn archive_skip_callback "struct archive *" "void *client_data" "off_t request"
+.\" #endif
+.It
+.Ft typedef int
+.Fn archive_open_callback "struct archive *" "void *client_data"
+.It
+.Ft typedef int
+.Fn archive_close_callback "struct archive *" "void *client_data"
+.El
+.Pp
+The open callback is invoked by
+.Fn archive_open .
+It should return
+.Cm ARCHIVE_OK
+if the underlying file or data source is successfully
+opened.
+If the open fails, it should call
+.Fn archive_set_error
+to register an error code and message and return
+.Cm ARCHIVE_FATAL .
+.Pp
+The read callback is invoked whenever the library
+requires raw bytes from the archive.
+The read callback should read data into a buffer,
+set the
+.Li const void **buffer
+argument to point to the available data, and
+return a count of the number of bytes available.
+The library will invoke the read callback again
+only after it has consumed this data.
+The library imposes no constraints on the size
+of the data blocks returned.
+On end-of-file, the read callback should
+return zero.
+On error, the read callback should invoke
+.Fn archive_set_error
+to register an error code and message and
+return -1.
+.Pp
+The skip callback is invoked when the
+library wants to ignore a block of data.
+The return value is the number of bytes actually
+skipped, which may differ from the request.
+If the callback cannot skip data, it should return
+zero.
+If the skip callback is not provided (the
+function pointer is
+.Dv NULL ),
+the library will invoke the read function
+instead and simply discard the result.
+A skip callback can provide significant
+performance gains when reading uncompressed
+archives from slow disk drives or other media
+that can skip quickly.
+.Pp
+The close callback is invoked by archive_close when
+the archive processing is complete.
+The callback should return
+.Cm ARCHIVE_OK
+on success.
+On failure, the callback should invoke
+.Fn archive_set_error
+to register an error code and message and
+return
+.Cm ARCHIVE_FATAL.
+.Sh EXAMPLE
+The following illustrates basic usage of the library.
+In this example,
+the callback functions are simply wrappers around the standard
+.Xr open 2 ,
+.Xr read 2 ,
+and
+.Xr close 2
+system calls.
+.Bd -literal -offset indent
+void
+list_archive(const char *name)
+{
+  struct mydata *mydata;
+  struct archive *a;
+  struct archive_entry *entry;
+
+  mydata = malloc(sizeof(struct mydata));
+  a = archive_read_new();
+  mydata->name = name;
+  archive_read_support_compression_all(a);
+  archive_read_support_format_all(a);
+  archive_read_open(a, mydata, myopen, myread, myclose);
+  while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
+    printf("%s\\n",archive_entry_pathname(entry));
+    archive_read_data_skip(a);
+  }
+  archive_read_finish(a);
+  free(mydata);
+}
+
+ssize_t
+myread(struct archive *a, void *client_data, const void **buff)
+{
+  struct mydata *mydata = client_data;
+
+  *buff = mydata->buff;
+  return (read(mydata->fd, mydata->buff, 10240));
+}
+
+int
+myopen(struct archive *a, void *client_data)
+{
+  struct mydata *mydata = client_data;
+
+  mydata->fd = open(mydata->name, O_RDONLY);
+  return (mydata->fd >= 0 ? ARCHIVE_OK : ARCHIVE_FATAL);
+}
+
+int
+myclose(struct archive *a, void *client_data)
+{
+  struct mydata *mydata = client_data;
+
+  if (mydata->fd > 0)
+    close(mydata->fd);
+  return (ARCHIVE_OK);
+}
+.Ed
+.Sh RETURN VALUES
+Most functions return zero on success, non-zero on error.
+The possible return codes include:
+.Cm ARCHIVE_OK
+(the operation succeeded),
+.Cm ARCHIVE_WARN
+(the operation succeeded but a non-critical error was encountered),
+.Cm ARCHIVE_EOF
+(end-of-archive was encountered),
+.Cm ARCHIVE_RETRY
+(the operation failed but can be retried),
+and
+.Cm ARCHIVE_FATAL
+(there was a fatal error; the archive should be closed immediately).
+Detailed error codes and textual descriptions are available from the
+.Fn archive_errno
+and
+.Fn archive_error_string
+functions.
+.Pp
+.Fn archive_read_new
+returns a pointer to a freshly allocated
+.Tn struct archive
+object.
+It returns
+.Dv NULL
+on error.
+.Pp
+.Fn archive_read_data
+returns a count of bytes actually read or zero at the end of the entry.
+On error, a value of
+.Cm ARCHIVE_FATAL ,
+.Cm ARCHIVE_WARN ,
+or
+.Cm ARCHIVE_RETRY
+is returned and an error code and textual description can be retrieved from the
+.Fn archive_errno
+and
+.Fn archive_error_string
+functions.
+.Pp
+The library expects the client callbacks to behave similarly.
+If there is an error, you can use
+.Fn archive_set_error
+to set an appropriate error code and description,
+then return one of the non-zero values above.
+(Note that the value eventually returned to the client may
+not be the same; many errors that are not critical at the level
+of basic I/O can prevent the archive from being properly read,
+thus most I/O errors eventually cause
+.Cm ARCHIVE_FATAL
+to be returned.)
+.\" .Sh ERRORS
+.Sh SEE ALSO
+.Xr tar 1 ,
+.Xr archive 3 ,
+.Xr archive_util 3 ,
+.Xr tar 5
+.Sh HISTORY
+The
+.Nm libarchive
+library first appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm libarchive
+library was written by
+.An Tim Kientzle Aq kientzle@acm.org .
+.Sh BUGS
+Many traditional archiver programs treat
+empty files as valid empty archives.
+For example, many implementations of
+.Xr tar 1
+allow you to append entries to an empty file.
+Of course, it is impossible to determine the format of an empty file
+by inspecting the contents, so this library treats empty files as
+having a special
+.Dq empty
+format.
diff --git a/contrib/libarchive-2.0/libarchive/archive_read.c b/contrib/libarchive-2.0/libarchive/archive_read.c
new file mode 100644 (file)
index 0000000..37d9890
--- /dev/null
@@ -0,0 +1,667 @@
+/*-
+ * 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 file contains the "essential" portions of the read API, that
+ * is, stuff that will probably always be used by any client that
+ * actually needs to read an archive.  Optional pieces have been, as
+ * far as possible, separated out into separate files to avoid
+ * needlessly bloating statically-linked clients.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read.c,v 1.30 2007/03/03 07:37:36 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+static int     choose_decompressor(struct archive_read *, const void*, size_t);
+static int     choose_format(struct archive_read *);
+
+/*
+ * Allocate, initialize and return a struct archive object.
+ */
+struct archive *
+archive_read_new(void)
+{
+       struct archive_read *a;
+       unsigned char   *nulls;
+
+       a = (struct archive_read *)malloc(sizeof(*a));
+       if (a == NULL)
+               return (NULL);
+       memset(a, 0, sizeof(*a));
+       a->archive.magic = ARCHIVE_READ_MAGIC;
+       a->bytes_per_block = ARCHIVE_DEFAULT_BYTES_PER_BLOCK;
+
+       a->null_length = 1024;
+       nulls = (unsigned char *)malloc(a->null_length);
+       if (nulls == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate archive object 'nulls' element");
+               free(a);
+               return (NULL);
+       }
+       memset(nulls, 0, a->null_length);
+       a->nulls = nulls;
+
+       a->archive.state = ARCHIVE_STATE_NEW;
+       a->entry = archive_entry_new();
+
+       /* We always support uncompressed archives. */
+       archive_read_support_compression_none(&a->archive);
+
+       return (&a->archive);
+}
+
+/*
+ * Record the do-not-extract-to file. This belongs in archive_read_extract.c.
+ */
+void
+archive_read_extract_set_skip_file(struct archive *_a, dev_t d, ino_t i)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY,
+           "archive_read_extract_set_skip_file");
+       a->skip_file_dev = d;
+       a->skip_file_ino = i;
+}
+
+
+/*
+ * Open the archive
+ */
+int
+archive_read_open(struct archive *a, void *client_data,
+    archive_open_callback *client_opener, archive_read_callback *client_reader,
+    archive_close_callback *client_closer)
+{
+       /* Old archive_read_open() is just a thin shell around
+        * archive_read_open2. */
+       return archive_read_open2(a, client_data, client_opener,
+           client_reader, NULL, client_closer);
+}
+
+int
+archive_read_open2(struct archive *_a, void *client_data,
+    archive_open_callback *client_opener,
+    archive_read_callback *client_reader,
+    archive_skip_callback *client_skipper,
+    archive_close_callback *client_closer)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       const void *buffer;
+       ssize_t bytes_read;
+       int high_bidder;
+       int e;
+
+       __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW, "archive_read_open");
+
+       if (client_reader == NULL)
+               __archive_errx(1,
+                   "No reader function provided to archive_read_open");
+
+       /*
+        * Set these NULL initially.  If the open or initial read fails,
+        * we'll leave them NULL to indicate that the file is invalid.
+        * (In particular, this helps ensure that the closer doesn't
+        * get called more than once.)
+        */
+       a->client_opener = NULL;
+       a->client_reader = NULL;
+       a->client_skipper = NULL;
+       a->client_closer = NULL;
+       a->client_data = NULL;
+
+       /* Open data source. */
+       if (client_opener != NULL) {
+               e =(client_opener)(&a->archive, client_data);
+               if (e != 0) {
+                       /* If the open failed, call the closer to clean up. */
+                       if (client_closer)
+                               (client_closer)(&a->archive, client_data);
+                       return (e);
+               }
+       }
+
+       /* Read first block now for format detection. */
+       bytes_read = (client_reader)(&a->archive, client_data, &buffer);
+
+       if (bytes_read < 0) {
+               /* If the first read fails, close before returning error. */
+               if (client_closer)
+                       (client_closer)(&a->archive, client_data);
+               /* client_reader should have already set error information. */
+               return (ARCHIVE_FATAL);
+       }
+
+       /* Now that the client callbacks have worked, remember them. */
+       a->client_opener = client_opener; /* Do we need to remember this? */
+       a->client_reader = client_reader;
+       a->client_skipper = client_skipper;
+       a->client_closer = client_closer;
+       a->client_data = client_data;
+
+       /* Select a decompression routine. */
+       high_bidder = choose_decompressor(a, buffer, bytes_read);
+       if (high_bidder < 0)
+               return (ARCHIVE_FATAL);
+
+       /* Initialize decompression routine with the first block of data. */
+       e = (a->decompressors[high_bidder].init)(a, buffer, bytes_read);
+
+       if (e == ARCHIVE_OK)
+               a->archive.state = ARCHIVE_STATE_HEADER;
+
+       return (e);
+}
+
+/*
+ * Allow each registered decompression routine to bid on whether it
+ * wants to handle this stream.  Return index of winning bidder.
+ */
+static int
+choose_decompressor(struct archive_read *a,
+    const void *buffer, size_t bytes_read)
+{
+       int decompression_slots, i, bid, best_bid, best_bid_slot;
+
+       decompression_slots = sizeof(a->decompressors) /
+           sizeof(a->decompressors[0]);
+
+       best_bid = -1;
+       best_bid_slot = -1;
+
+       for (i = 0; i < decompression_slots; i++) {
+               if (a->decompressors[i].bid) {
+                       bid = (a->decompressors[i].bid)(buffer, bytes_read);
+                       if ((bid > best_bid) || (best_bid_slot < 0)) {
+                               best_bid = bid;
+                               best_bid_slot = i;
+                       }
+               }
+       }
+
+       /*
+        * There were no bidders; this is a serious programmer error
+        * and demands a quick and definitive abort.
+        */
+       if (best_bid_slot < 0)
+               __archive_errx(1, "No decompressors were registered; you "
+                   "must call at least one "
+                   "archive_read_support_compression_XXX function in order "
+                   "to successfully read an archive.");
+
+       /*
+        * There were bidders, but no non-zero bids; this means we  can't
+        * support this stream.
+        */
+       if (best_bid < 1) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Unrecognized archive format");
+               return (ARCHIVE_FATAL);
+       }
+
+       return (best_bid_slot);
+}
+
+/*
+ * Read header of next entry.
+ */
+int
+archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       struct archive_entry *entry;
+       int slot, ret;
+
+       __archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+           ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+           "archive_read_next_header");
+
+       *entryp = NULL;
+       entry = a->entry;
+       archive_entry_clear(entry);
+       archive_clear_error(&a->archive);
+
+       /*
+        * If client didn't consume entire data, skip any remainder
+        * (This is especially important for GNU incremental directories.)
+        */
+       if (a->archive.state == ARCHIVE_STATE_DATA) {
+               ret = archive_read_data_skip(&a->archive);
+               if (ret == ARCHIVE_EOF) {
+                       archive_set_error(&a->archive, EIO, "Premature end-of-file.");
+                       a->archive.state = ARCHIVE_STATE_FATAL;
+                       return (ARCHIVE_FATAL);
+               }
+               if (ret != ARCHIVE_OK)
+                       return (ret);
+       }
+
+       /* Record start-of-header. */
+       a->header_position = a->archive.file_position;
+
+       slot = choose_format(a);
+       if (slot < 0) {
+               a->archive.state = ARCHIVE_STATE_FATAL;
+               return (ARCHIVE_FATAL);
+       }
+       a->format = &(a->formats[slot]);
+       a->pformat_data = &(a->format->format_data);
+       ret = (a->format->read_header)(a, entry);
+
+       /*
+        * EOF and FATAL are persistent at this layer.  By
+        * modifying the state, we guarantee that future calls to
+        * read a header or read data will fail.
+        */
+       switch (ret) {
+       case ARCHIVE_EOF:
+               a->archive.state = ARCHIVE_STATE_EOF;
+               break;
+       case ARCHIVE_OK:
+               a->archive.state = ARCHIVE_STATE_DATA;
+               break;
+       case ARCHIVE_WARN:
+               a->archive.state = ARCHIVE_STATE_DATA;
+               break;
+       case ARCHIVE_RETRY:
+               break;
+       case ARCHIVE_FATAL:
+               a->archive.state = ARCHIVE_STATE_FATAL;
+               break;
+       }
+
+       *entryp = entry;
+       a->read_data_output_offset = 0;
+       a->read_data_remaining = 0;
+       return (ret);
+}
+
+/*
+ * Allow each registered format to bid on whether it wants to handle
+ * the next entry.  Return index of winning bidder.
+ */
+static int
+choose_format(struct archive_read *a)
+{
+       int slots;
+       int i;
+       int bid, best_bid;
+       int best_bid_slot;
+
+       slots = sizeof(a->formats) / sizeof(a->formats[0]);
+       best_bid = -1;
+       best_bid_slot = -1;
+
+       /* Set up a->format and a->pformat_data for convenience of bidders. */
+       a->format = &(a->formats[0]);
+       for (i = 0; i < slots; i++, a->format++) {
+               if (a->format->bid) {
+                       a->pformat_data = &(a->format->format_data);
+                       bid = (a->format->bid)(a);
+                       if (bid == ARCHIVE_FATAL)
+                               return (ARCHIVE_FATAL);
+                       if ((bid > best_bid) || (best_bid_slot < 0)) {
+                               best_bid = bid;
+                               best_bid_slot = i;
+                       }
+               }
+       }
+
+       /*
+        * There were no bidders; this is a serious programmer error
+        * and demands a quick and definitive abort.
+        */
+       if (best_bid_slot < 0)
+               __archive_errx(1, "No formats were registered; you must "
+                   "invoke at least one archive_read_support_format_XXX "
+                   "function in order to successfully read an archive.");
+
+       /*
+        * There were bidders, but no non-zero bids; this means we
+        * can't support this stream.
+        */
+       if (best_bid < 1) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Unrecognized archive format");
+               return (ARCHIVE_FATAL);
+       }
+
+       return (best_bid_slot);
+}
+
+/*
+ * Return the file offset (within the uncompressed data stream) where
+ * the last header started.
+ */
+int64_t
+archive_read_header_position(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       __archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_read_header_position");
+       return (a->header_position);
+}
+
+/*
+ * Read data from an archive entry, using a read(2)-style interface.
+ * This is a convenience routine that just calls
+ * archive_read_data_block and copies the results into the client
+ * buffer, filling any gaps with zero bytes.  Clients using this
+ * API can be completely ignorant of sparse-file issues; sparse files
+ * will simply be padded with nulls.
+ *
+ * DO NOT intermingle calls to this function and archive_read_data_block
+ * to read a single entry body.
+ */
+ssize_t
+archive_read_data(struct archive *_a, void *buff, size_t s)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       char    *dest;
+       size_t   bytes_read;
+       size_t   len;
+       int      r;
+
+       bytes_read = 0;
+       dest = (char *)buff;
+
+       while (s > 0) {
+               if (a->read_data_remaining <= 0) {
+                       r = archive_read_data_block(&a->archive,
+                           (const void **)&a->read_data_block,
+                           &a->read_data_remaining,
+                           &a->read_data_offset);
+                       if (r == ARCHIVE_EOF)
+                               return (bytes_read);
+                       /*
+                        * Error codes are all negative, so the status
+                        * return here cannot be confused with a valid
+                        * byte count.  (ARCHIVE_OK is zero.)
+                        */
+                       if (r < ARCHIVE_OK)
+                               return (r);
+               }
+
+               if (a->read_data_offset < a->read_data_output_offset) {
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                           "Encountered out-of-order sparse blocks");
+                       return (ARCHIVE_RETRY);
+               }
+
+               /* Compute the amount of zero padding needed. */
+               if (a->read_data_output_offset + (off_t)s <
+                   a->read_data_offset) {
+                       len = s;
+               } else if (a->read_data_output_offset <
+                   a->read_data_offset) {
+                       len = a->read_data_offset -
+                           a->read_data_output_offset;
+               } else
+                       len = 0;
+
+               /* Add zeroes. */
+               memset(dest, 0, len);
+               s -= len;
+               a->read_data_output_offset += len;
+               dest += len;
+               bytes_read += len;
+
+               /* Copy data if there is any space left. */
+               if (s > 0) {
+                       len = a->read_data_remaining;
+                       if (len > s)
+                               len = s;
+                       memcpy(dest, a->read_data_block, len);
+                       s -= len;
+                       a->read_data_block += len;
+                       a->read_data_remaining -= len;
+                       a->read_data_output_offset += len;
+                       a->read_data_offset += len;
+                       dest += len;
+                       bytes_read += len;
+               }
+       }
+       return (bytes_read);
+}
+
+/*
+ * Skip over all remaining data in this entry.
+ */
+int
+archive_read_data_skip(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       int r;
+       const void *buff;
+       size_t size;
+       off_t offset;
+
+       __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+           "archive_read_data_skip");
+
+       if (a->format->read_data_skip != NULL)
+               r = (a->format->read_data_skip)(a);
+       else {
+               while ((r = archive_read_data_block(&a->archive,
+                           &buff, &size, &offset))
+                   == ARCHIVE_OK)
+                       ;
+       }
+
+       if (r == ARCHIVE_EOF)
+               r = ARCHIVE_OK;
+
+       a->archive.state = ARCHIVE_STATE_HEADER;
+       return (r);
+}
+
+/*
+ * Read the next block of entry data from the archive.
+ * This is a zero-copy interface; the client receives a pointer,
+ * size, and file offset of the next available block of data.
+ *
+ * Returns ARCHIVE_OK if the operation is successful, ARCHIVE_EOF if
+ * the end of entry is encountered.
+ */
+int
+archive_read_data_block(struct archive *_a,
+    const void **buff, size_t *size, off_t *offset)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+           "archive_read_data_block");
+
+       if (a->format->read_data == NULL) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+                   "Internal error: "
+                   "No format_read_data_block function registered");
+               return (ARCHIVE_FATAL);
+       }
+
+       return (a->format->read_data)(a, buff, size, offset);
+}
+
+/*
+ * Close the file and release most resources.
+ *
+ * Be careful: client might just call read_new and then read_finish.
+ * Don't assume we actually read anything or performed any non-trivial
+ * initialization.
+ */
+int
+archive_read_close(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
+
+       __archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_read_close");
+       a->archive.state = ARCHIVE_STATE_CLOSED;
+
+       /* Call cleanup functions registered by optional components. */
+       if (a->cleanup_archive_extract != NULL)
+               r = (a->cleanup_archive_extract)(a);
+
+       /* TODO: Finish the format processing. */
+
+       /* Close the input machinery. */
+       if (a->compression_finish != NULL) {
+               r1 = (a->compression_finish)(a);
+               if (r1 < r)
+                       r = r1;
+       }
+
+       return (r);
+}
+
+/*
+ * Release memory and other resources.
+ */
+#if ARCHIVE_API_VERSION > 1
+int
+#else
+/* Temporarily allow library to compile with either 1.x or 2.0 API. */
+void
+#endif
+archive_read_finish(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       int i;
+       int slots;
+       int r = ARCHIVE_OK;
+
+       __archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY,
+           "archive_read_finish");
+       if (a->archive.state != ARCHIVE_STATE_CLOSED)
+               r = archive_read_close(&a->archive);
+
+       /* Cleanup format-specific data. */
+       slots = sizeof(a->formats) / sizeof(a->formats[0]);
+       for (i = 0; i < slots; i++) {
+               a->pformat_data = &(a->formats[i].format_data);
+               if (a->formats[i].cleanup)
+                       (a->formats[i].cleanup)(a);
+       }
+
+       /* Casting a pointer to int allows us to remove 'const.' */
+       free((void *)(uintptr_t)(const void *)a->nulls);
+       archive_string_free(&a->archive.error_string);
+       if (a->entry)
+               archive_entry_free(a->entry);
+       a->archive.magic = 0;
+       free(a);
+#if ARCHIVE_API_VERSION > 1
+       return (r);
+#endif
+}
+
+/*
+ * Used internally by read format handlers to register their bid and
+ * initialization functions.
+ */
+int
+__archive_read_register_format(struct archive_read *a,
+    void *format_data,
+    int (*bid)(struct archive_read *),
+    int (*read_header)(struct archive_read *, struct archive_entry *),
+    int (*read_data)(struct archive_read *, const void **, size_t *, off_t *),
+    int (*read_data_skip)(struct archive_read *),
+    int (*cleanup)(struct archive_read *))
+{
+       int i, number_slots;
+
+       __archive_check_magic(&a->archive,
+           ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+           "__archive_read_register_format");
+
+       number_slots = sizeof(a->formats) / sizeof(a->formats[0]);
+
+       for (i = 0; i < number_slots; i++) {
+               if (a->formats[i].bid == bid)
+                       return (ARCHIVE_WARN); /* We've already installed */
+               if (a->formats[i].bid == NULL) {
+                       a->formats[i].bid = bid;
+                       a->formats[i].read_header = read_header;
+                       a->formats[i].read_data = read_data;
+                       a->formats[i].read_data_skip = read_data_skip;
+                       a->formats[i].cleanup = cleanup;
+                       a->formats[i].format_data = format_data;
+                       return (ARCHIVE_OK);
+               }
+       }
+
+       __archive_errx(1, "Not enough slots for format registration");
+       return (ARCHIVE_FATAL); /* Never actually called. */
+}
+
+/*
+ * Used internally by decompression routines to register their bid and
+ * initialization functions.
+ */
+int
+__archive_read_register_compression(struct archive_read *a,
+    int (*bid)(const void *, size_t),
+    int (*init)(struct archive_read *, const void *, size_t))
+{
+       int i, number_slots;
+
+       __archive_check_magic(&a->archive,
+           ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+           "__archive_read_register_compression");
+
+       number_slots = sizeof(a->decompressors) / sizeof(a->decompressors[0]);
+
+       for (i = 0; i < number_slots; i++) {
+               if (a->decompressors[i].bid == bid)
+                       return (ARCHIVE_OK); /* We've already installed */
+               if (a->decompressors[i].bid == NULL) {
+                       a->decompressors[i].bid = bid;
+                       a->decompressors[i].init = init;
+                       return (ARCHIVE_OK);
+               }
+       }
+
+       __archive_errx(1, "Not enough slots for compression registration");
+       return (ARCHIVE_FATAL); /* Never actually executed. */
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_data_into_buffer.c b/contrib/libarchive-2.0/libarchive/archive_read_data_into_buffer.c
new file mode 100644 (file)
index 0000000..8b57fd2
--- /dev/null
@@ -0,0 +1,50 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_buffer.c,v 1.6 2007/01/09 08:05:55 kientzle Exp $");
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+
+int
+archive_read_data_into_buffer(struct archive *a, void *d, ssize_t len)
+{
+       char *dest;
+       ssize_t bytes_read, total_bytes;
+
+       dest = (char *)d;
+       total_bytes = 0;
+       bytes_read = archive_read_data(a, dest, len);
+       while (bytes_read > 0) {
+               total_bytes += bytes_read;
+               bytes_read = archive_read_data(a, dest + total_bytes,
+                   len - total_bytes);
+       }
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_data_into_fd.c b/contrib/libarchive-2.0/libarchive/archive_read_data_into_fd.c
new file mode 100644 (file)
index 0000000..38238c4
--- /dev/null
@@ -0,0 +1,89 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_data_into_fd.c,v 1.14 2007/03/03 07:37:36 kientzle Exp $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* Maximum amount of data to write at one time. */
+#define        MAX_WRITE       (1024 * 1024)
+
+/*
+ * This implementation minimizes copying of data and is sparse-file aware.
+ */
+int
+archive_read_data_into_fd(struct archive *a, int fd)
+{
+       int r;
+       const void *buff;
+       size_t size;
+       ssize_t bytes_to_write, bytes_written, total_written;
+       off_t offset;
+       off_t output_offset;
+
+       __archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA, "archive_read_data_into_fd");
+
+       total_written = 0;
+       output_offset = 0;
+
+       while ((r = archive_read_data_block(a, &buff, &size, &offset)) ==
+           ARCHIVE_OK) {
+               const char *p = buff;
+               if (offset > output_offset) {
+                       lseek(fd, offset - output_offset, SEEK_CUR);
+                       output_offset = offset;
+               }
+               while (size > 0) {
+                       bytes_to_write = size;
+                       if (bytes_to_write > MAX_WRITE)
+                               bytes_to_write = MAX_WRITE;
+                       bytes_written = write(fd, p, bytes_to_write);
+                       if (bytes_written < 0) {
+                               archive_set_error(a, errno, "Write error");
+                               return (-1);
+                       }
+                       output_offset += bytes_written;
+                       total_written += bytes_written;
+                       p += bytes_written;
+                       size -= bytes_written;
+               }
+       }
+
+       if (r != ARCHIVE_EOF)
+               return (r);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_extract.c b/contrib/libarchive-2.0/libarchive/archive_read_extract.c
new file mode 100644 (file)
index 0000000..a06671e
--- /dev/null
@@ -0,0 +1,164 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_extract.c,v 1.54 2007/03/11 10:29:52 kientzle Exp $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_write_disk_private.h"
+
+struct extract {
+       struct archive *ad; /* archive_write_disk object */
+
+       /* Progress function invoked during extract. */
+       void                    (*extract_progress)(void *);
+       void                     *extract_progress_user_data;
+};
+
+static int     archive_read_extract_cleanup(struct archive_read *);
+static int     copy_data(struct archive *ar, struct archive *aw);
+static struct extract *get_extract(struct archive_read *);
+
+static struct extract *
+get_extract(struct archive_read *a)
+{
+       /* If we haven't initialized, do it now. */
+       /* This also sets up a lot of global state. */
+       if (a->extract == NULL) {
+               a->extract = (struct extract *)malloc(sizeof(*a->extract));
+               if (a->extract == NULL) {
+                       archive_set_error(&a->archive, ENOMEM, "Can't extract");
+                       return (NULL);
+               }
+               a->extract->ad = archive_write_disk_new();
+               if (a->extract->ad == NULL) {
+                       archive_set_error(&a->archive, ENOMEM, "Can't extract");
+                       return (NULL);
+               }
+               archive_write_disk_set_standard_lookup(a->extract->ad);
+               a->cleanup_archive_extract = archive_read_extract_cleanup;
+       }
+       return (a->extract);
+}
+
+int
+archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       struct extract *extract;
+       int r, r2;
+
+       extract = get_extract(a);
+       if (extract == NULL)
+               return (ARCHIVE_FATAL);
+
+       /* Set up for this particular entry. */
+       extract = a->extract;
+       archive_write_disk_set_options(a->extract->ad, flags);
+       archive_write_disk_set_skip_file(a->extract->ad,
+           a->skip_file_dev, a->skip_file_ino);
+       r = archive_write_header(a->extract->ad, entry);
+       if (r == ARCHIVE_OK)
+               /* If there's an FD, pour data into it. */
+               r = copy_data(_a, a->extract->ad);
+       if (r != ARCHIVE_OK)
+               archive_set_error(&a->archive,
+                   archive_errno(extract->ad),
+                   "%s", archive_error_string(extract->ad));
+       r2 = archive_write_finish_entry(a->extract->ad);
+       /* Use the first message. */
+       if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
+               archive_set_error(&a->archive,
+                   archive_errno(extract->ad),
+                   "%s", archive_error_string(extract->ad));
+       /* Use the worst error return. */
+       if (r2 < r)
+               r = r2;
+       return (r);
+}
+
+void
+archive_read_extract_set_progress_callback(struct archive *_a,
+    void (*progress_func)(void *), void *user_data)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       struct extract *extract = get_extract(a);
+       if (extract != NULL) {
+               extract->extract_progress = progress_func;
+               extract->extract_progress_user_data = user_data;
+       }
+}
+
+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)
+                       return (ARCHIVE_OK);
+               if (r != ARCHIVE_OK)
+                       return (r);
+               r = archive_write_data_block(aw, buff, size, offset);
+               if (r != ARCHIVE_OK)
+                       return (r);
+       }
+}
+
+/*
+ * Cleanup function for archive_extract.
+ */
+static int
+archive_read_extract_cleanup(struct archive_read *a)
+{
+       int ret = ARCHIVE_OK;
+
+#if ARCHIVE_API_VERSION > 1
+       ret =
+#endif
+           archive_write_finish(a->extract->ad);
+       free(a->extract);
+       a->extract = NULL;
+       return (ret);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_open_fd.c b/contrib/libarchive-2.0/libarchive/archive_read_open_fd.c
new file mode 100644 (file)
index 0000000..4db681c
--- /dev/null
@@ -0,0 +1,165 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_fd.c,v 1.11 2007/01/09 08:05:55 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct read_fd_data {
+       int      fd;
+       size_t   block_size;
+       void    *buffer;
+};
+
+static int     file_close(struct archive *, void *);
+static int     file_open(struct archive *, void *);
+static ssize_t file_read(struct archive *, void *, const void **buff);
+#if ARCHIVE_API_VERSION < 2
+static ssize_t file_skip(struct archive *, void *, size_t request);
+#else
+static off_t   file_skip(struct archive *, void *, off_t request);
+#endif
+
+int
+archive_read_open_fd(struct archive *a, int fd, size_t block_size)
+{
+       struct read_fd_data *mine;
+
+       mine = (struct read_fd_data *)malloc(sizeof(*mine));
+       if (mine == NULL) {
+               archive_set_error(a, ENOMEM, "No memory");
+               return (ARCHIVE_FATAL);
+       }
+       mine->block_size = block_size;
+       mine->buffer = malloc(mine->block_size);
+       if (mine->buffer == NULL) {
+               archive_set_error(a, ENOMEM, "No memory");
+               free(mine);
+               return (ARCHIVE_FATAL);
+       }
+       mine->fd = fd;
+       return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+       struct read_fd_data *mine = (struct read_fd_data *)client_data;
+       struct stat st;
+
+       if (fstat(mine->fd, &st) != 0) {
+               archive_set_error(a, errno, "Can't stat fd %d", mine->fd);
+               return (ARCHIVE_FATAL);
+       }
+
+       if (S_ISREG(st.st_mode))
+               archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+       return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+       struct read_fd_data *mine = (struct read_fd_data *)client_data;
+       ssize_t bytes_read;
+
+       *buff = mine->buffer;
+       bytes_read = read(mine->fd, mine->buffer, mine->block_size);
+       if (bytes_read < 0) {
+               archive_set_error(a, errno, "Error reading fd %d", mine->fd);
+       }
+       return (bytes_read);
+}
+
+#if ARCHIVE_API_VERSION < 2
+static ssize_t
+file_skip(struct archive *a, void *client_data, size_t request)
+#else
+static off_t
+file_skip(struct archive *a, void *client_data, off_t request)
+#endif
+{
+       struct read_fd_data *mine = (struct read_fd_data *)client_data;
+       off_t old_offset, new_offset;
+
+       /* Reduce request to the next smallest multiple of block_size */
+       request = (request / mine->block_size) * mine->block_size;
+       /*
+        * Hurray for lazy evaluation: if the first lseek fails, the second
+        * one will not be executed.
+        */
+       if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
+           ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
+       {
+               if (errno == ESPIPE)
+               {
+                       /*
+                        * Failure to lseek() can be caused by the file
+                        * descriptor pointing to a pipe, socket or FIFO.
+                        * Return 0 here, so the compression layer will use
+                        * read()s instead to advance the file descriptor.
+                        * It's slower of course, but works as well.
+                        */
+                       return (0);
+               }
+               /*
+                * There's been an error other than ESPIPE. This is most
+                * likely caused by a programmer error (too large request)
+                * or a corrupted archive file.
+                */
+               archive_set_error(a, errno, "Error seeking");
+               return (-1);
+       }
+       return (new_offset - old_offset);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+       struct read_fd_data *mine = (struct read_fd_data *)client_data;
+
+       (void)a; /* UNUSED */
+       if (mine->buffer != NULL)
+               free(mine->buffer);
+       free(mine);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_open_file.c b/contrib/libarchive-2.0/libarchive/archive_read_open_file.c
new file mode 100644 (file)
index 0000000..6d83955
--- /dev/null
@@ -0,0 +1,157 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_file.c,v 1.19 2007/01/09 08:05:55 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct read_FILE_data {
+       FILE    *f;
+       size_t   block_size;
+       void    *buffer;
+};
+
+static int     file_close(struct archive *, void *);
+static int     file_open(struct archive *, void *);
+static ssize_t file_read(struct archive *, void *, const void **buff);
+#if ARCHIVE_API_VERSION < 2
+static ssize_t file_skip(struct archive *, void *, size_t request);
+#else
+static off_t   file_skip(struct archive *, void *, off_t request);
+#endif
+
+int
+archive_read_open_FILE(struct archive *a, FILE *f)
+{
+       struct read_FILE_data *mine;
+
+       mine = (struct read_FILE_data *)malloc(sizeof(*mine));
+       if (mine == NULL) {
+               archive_set_error(a, ENOMEM, "No memory");
+               return (ARCHIVE_FATAL);
+       }
+       mine->block_size = 128 * 1024;
+       mine->buffer = malloc(mine->block_size);
+       if (mine->buffer == NULL) {
+               archive_set_error(a, ENOMEM, "No memory");
+               free(mine);
+               return (ARCHIVE_FATAL);
+       }
+       mine->f = f;
+       return (archive_read_open2(a, mine, file_open, file_read,
+                   file_skip, file_close));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+       struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+       struct stat st;
+
+       /*
+        * If we can't fstat() the file, it may just be that
+        * it's not a file.  (FILE * objects can wrap many kinds
+        * of I/O streams.)
+        */
+       if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode))
+               archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+
+       return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+       struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+       ssize_t bytes_read;
+
+       *buff = mine->buffer;
+       bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f);
+       if (bytes_read < 0) {
+               archive_set_error(a, errno, "Error reading file");
+       }
+       return (bytes_read);
+}
+
+#if ARCHIVE_API_VERSION < 2
+static ssize_t
+file_skip(struct archive *a, void *client_data, size_t request)
+#else
+static off_t
+file_skip(struct archive *a, void *client_data, off_t request)
+#endif
+{
+       struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+
+       /*
+        * Note: the 'fd' and 'filename' versions round the request
+        * down to a multiple of the block size to ensure proper
+        * operation on block-oriented media such as tapes.  But stdio
+        * doesn't work with such media (it doesn't ensure blocking),
+        * so we don't need to bother.
+        */
+#if HAVE_FSEEKO
+       if (fseeko(mine->f, request, SEEK_CUR) != 0)
+#else
+       if (fseek(mine->f, request, SEEK_CUR) != 0)
+#endif
+       {
+               archive_set_error(a, errno, "Error skipping forward");
+               return (ARCHIVE_FATAL);
+       }
+       return (request);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+       struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+
+       (void)a; /* UNUSED */
+       if (mine->buffer != NULL)
+               free(mine->buffer);
+       free(mine);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_open_filename.c b/contrib/libarchive-2.0/libarchive/archive_read_open_filename.c
new file mode 100644 (file)
index 0000000..47bc4ef
--- /dev/null
@@ -0,0 +1,240 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_filename.c,v 1.18 2007/01/09 08:05:55 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+
+struct read_file_data {
+       int      fd;
+       size_t   block_size;
+       void    *buffer;
+       mode_t   st_mode;  /* Mode bits for opened file. */
+       char     filename[1]; /* Must be last! */
+};
+
+static int     file_close(struct archive *, void *);
+static int     file_open(struct archive *, void *);
+static ssize_t file_read(struct archive *, void *, const void **buff);
+#if ARCHIVE_API_VERSION < 2
+static ssize_t file_skip(struct archive *, void *, size_t request);
+#else
+static off_t   file_skip(struct archive *, void *, off_t request);
+#endif
+
+int
+archive_read_open_file(struct archive *a, const char *filename,
+    size_t block_size)
+{
+       return (archive_read_open_filename(a, filename, block_size));
+}
+
+int
+archive_read_open_filename(struct archive *a, const char *filename,
+    size_t block_size)
+{
+       struct read_file_data *mine;
+
+       if (filename == NULL || filename[0] == '\0') {
+               mine = (struct read_file_data *)malloc(sizeof(*mine));
+               if (mine == NULL) {
+                       archive_set_error(a, ENOMEM, "No memory");
+                       return (ARCHIVE_FATAL);
+               }
+               mine->filename[0] = '\0';
+       } else {
+               mine = (struct read_file_data *)malloc(sizeof(*mine) + strlen(filename));
+               if (mine == NULL) {
+                       archive_set_error(a, ENOMEM, "No memory");
+                       return (ARCHIVE_FATAL);
+               }
+               strcpy(mine->filename, filename);
+       }
+       mine->block_size = block_size;
+       mine->buffer = NULL;
+       mine->fd = -1;
+       return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+       struct read_file_data *mine = (struct read_file_data *)client_data;
+       struct stat st;
+
+       mine->buffer = malloc(mine->block_size);
+       if (mine->buffer == NULL) {
+               archive_set_error(a, ENOMEM, "No memory");
+               return (ARCHIVE_FATAL);
+       }
+       if (mine->filename[0] != '\0')
+               mine->fd = open(mine->filename, O_RDONLY);
+       else
+               mine->fd = 0; /* Fake "open" for stdin. */
+       if (mine->fd < 0) {
+               archive_set_error(a, errno, "Failed to open '%s'",
+                   mine->filename);
+               return (ARCHIVE_FATAL);
+       }
+       if (fstat(mine->fd, &st) == 0) {
+               /* If we're reading a file from disk, ensure that we don't
+                  overwrite it with an extracted file. */
+               if (S_ISREG(st.st_mode))
+                       archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+               /* Remember mode so close can decide whether to flush. */
+               mine->st_mode = st.st_mode;
+       } else {
+               if (mine->filename[0] == '\0')
+                       archive_set_error(a, errno, "Can't stat stdin");
+               else
+                       archive_set_error(a, errno, "Can't stat '%s'",
+                           mine->filename);
+               return (ARCHIVE_FATAL);
+       }
+       return (0);
+}
+
+static ssize_t
+file_read(struct archive *a, void *client_data, const void **buff)
+{
+       struct read_file_data *mine = (struct read_file_data *)client_data;
+       ssize_t bytes_read;
+
+       *buff = mine->buffer;
+       bytes_read = read(mine->fd, mine->buffer, mine->block_size);
+       if (bytes_read < 0) {
+               if (mine->filename[0] == '\0')
+                       archive_set_error(a, errno, "Error reading stdin");
+               else
+                       archive_set_error(a, errno, "Error reading '%s'",
+                           mine->filename);
+       }
+       return (bytes_read);
+}
+
+#if ARCHIVE_API_VERSION < 2
+static ssize_t
+file_skip(struct archive *a, void *client_data, size_t request)
+#else
+static off_t
+file_skip(struct archive *a, void *client_data, off_t request)
+#endif
+{
+       struct read_file_data *mine = (struct read_file_data *)client_data;
+       off_t old_offset, new_offset;
+
+       /* Reduce request to the next smallest multiple of block_size */
+       request = (request / mine->block_size) * mine->block_size;
+       /*
+        * Hurray for lazy evaluation: if the first lseek fails, the second
+        * one will not be executed.
+        */
+       if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
+           ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
+       {
+               if (errno == ESPIPE)
+               {
+                       /*
+                        * Failure to lseek() can be caused by the file
+                        * descriptor pointing to a pipe, socket or FIFO.
+                        * Return 0 here, so the compression layer will use
+                        * read()s instead to advance the file descriptor.
+                        * It's slower of course, but works as well.
+                        */
+                       return (0);
+               }
+               /*
+                * There's been an error other than ESPIPE. This is most
+                * likely caused by a programmer error (too large request)
+                * or a corrupted archive file.
+                */
+               if (mine->filename[0] == '\0')
+                       /*
+                        * Should never get here, since lseek() on stdin ought
+                        * to return an ESPIPE error.
+                        */
+                       archive_set_error(a, errno, "Error seeking in stdin");
+               else
+                       archive_set_error(a, errno, "Error seeking in '%s'",
+                           mine->filename);
+               return (-1);
+       }
+       return (new_offset - old_offset);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+       struct read_file_data *mine = (struct read_file_data *)client_data;
+
+       (void)a; /* UNUSED */
+
+       /*
+        * Sometimes, we should flush the input before closing.
+        *   Regular files: faster to just close without flush.
+        *   Devices: must not flush (user might need to
+        *      read the "next" item on a non-rewind device).
+        *   Pipes and sockets:  must flush (otherwise, the
+        *      program feeding the pipe or socket may complain).
+        * Here, I flush everything except for regular files and
+        * device nodes.
+        */
+       if (!S_ISREG(mine->st_mode)
+           && !S_ISCHR(mine->st_mode)
+           && !S_ISBLK(mine->st_mode)) {
+               ssize_t bytesRead;
+               do {
+                       bytesRead = read(mine->fd, mine->buffer,
+                           mine->block_size);
+               } while (bytesRead > 0);
+       }
+       /* If a named file was opened, then it needs to be closed. */
+       if (mine->filename[0] != '\0')
+               close(mine->fd);
+       if (mine->buffer != NULL)
+               free(mine->buffer);
+       free(mine);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_open_memory.c b/contrib/libarchive-2.0/libarchive/archive_read_open_memory.c
new file mode 100644 (file)
index 0000000..3bd3df5
--- /dev/null
@@ -0,0 +1,156 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_memory.c,v 1.3 2007/01/09 08:05:55 kientzle Exp $");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+
+/*
+ * Glue to read an archive from a block of memory.
+ *
+ * This is mostly a huge help in building test harnesses;
+ * test programs can build archives in memory and read them
+ * back again without having to mess with files on disk.
+ */
+
+struct read_memory_data {
+       unsigned char   *buffer;
+       unsigned char   *end;
+       ssize_t  read_size;
+};
+
+static int     memory_read_close(struct archive *, void *);
+static int     memory_read_open(struct archive *, void *);
+#if ARCHIVE_API_VERSION < 2
+static ssize_t memory_read_skip(struct archive *, void *, size_t request);
+#else
+static off_t   memory_read_skip(struct archive *, void *, off_t request);
+#endif
+static ssize_t memory_read(struct archive *, void *, const void **buff);
+
+int
+archive_read_open_memory(struct archive *a, void *buff, size_t size)
+{
+       return archive_read_open_memory2(a, buff, size, size);
+}
+
+/*
+ * Don't use _open_memory2() in production code; the archive_read_open_memory()
+ * version is the one you really want.  This is just here so that
+ * test harnesses can exercise block operations inside the library.
+ */
+int
+archive_read_open_memory2(struct archive *a, void *buff,
+    size_t size, size_t read_size)
+{
+       struct read_memory_data *mine;
+
+       mine = (struct read_memory_data *)malloc(sizeof(*mine));
+       if (mine == NULL) {
+               archive_set_error(a, ENOMEM, "No memory");
+               return (ARCHIVE_FATAL);
+       }
+       memset(mine, 0, sizeof(*mine));
+       mine->buffer = (unsigned char *)buff;
+       mine->end = mine->buffer + size;
+       mine->read_size = read_size;
+       return (archive_read_open2(a, mine, memory_read_open,
+                   memory_read, memory_read_skip, memory_read_close));
+}
+
+/*
+ * There's nothing to open.
+ */
+static int
+memory_read_open(struct archive *a, void *client_data)
+{
+       (void)a; /* UNUSED */
+       (void)client_data; /* UNUSED */
+       return (ARCHIVE_OK);
+}
+
+/*
+ * This is scary simple:  Just advance a pointer.  Limiting
+ * to read_size is not technically necessary, but it exercises
+ * more of the internal logic when used with a small block size
+ * in a test harness.  Production use should not specify a block
+ * size; then this is much faster.
+ */
+static ssize_t
+memory_read(struct archive *a, void *client_data, const void **buff)
+{
+       struct read_memory_data *mine = (struct read_memory_data *)client_data;
+       ssize_t size;
+
+       (void)a; /* UNUSED */
+       *buff = mine->buffer;
+       size = mine->end - mine->buffer;
+       if (size > mine->read_size)
+               size = mine->read_size;
+        mine->buffer += size;
+       return (size);
+}
+
+/*
+ * Advancing is just as simple.  Again, this is doing more than
+ * necessary in order to better exercise internal code when used
+ * as a test harness.
+ */
+#if ARCHIVE_API_VERSION < 2
+static ssize_t
+memory_read_skip(struct archive *a, void *client_data, size_t skip)
+#else
+static off_t
+memory_read_skip(struct archive *a, void *client_data, off_t skip)
+#endif
+{
+       struct read_memory_data *mine = (struct read_memory_data *)client_data;
+
+       (void)a; /* UNUSED */
+       if (mine->buffer + skip > mine->end)
+               skip = mine->end - mine->buffer;
+       /* Round down to block size. */
+       skip /= mine->read_size;
+       skip *= mine->read_size;
+       mine->buffer += skip;
+       return (skip);
+}
+
+/*
+ * Close is just cleaning up our one small bit of data.
+ */
+static int
+memory_read_close(struct archive *a, void *client_data)
+{
+       struct read_memory_data *mine = (struct read_memory_data *)client_data;
+       (void)a; /* UNUSED */
+       free(mine);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_private.h b/contrib/libarchive-2.0/libarchive/archive_read_private.h
new file mode 100644 (file)
index 0000000..cca2fd8
--- /dev/null
@@ -0,0 +1,188 @@
+/*-
+ * 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: src/lib/libarchive/archive_read_private.h,v 1.1 2007/03/03 07:37:36 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED
+#define        ARCHIVE_READ_PRIVATE_H_INCLUDED
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_private.h"
+
+struct archive_read {
+       struct archive  archive;
+
+       struct archive_entry    *entry;
+
+       /* Dev/ino of the archive being read/written. */
+       dev_t             skip_file_dev;
+       ino_t             skip_file_ino;
+
+       /* Utility:  Pointer to a block of nulls. */
+       const unsigned char     *nulls;
+       size_t                   null_length;
+
+       /*
+        * Used by archive_read_data() to track blocks and copy
+        * data to client buffers, filling gaps with zero bytes.
+        */
+       const char       *read_data_block;
+       off_t             read_data_offset;
+       off_t             read_data_output_offset;
+       size_t            read_data_remaining;
+
+       /* Callbacks to open/read/write/close archive stream. */
+       archive_open_callback   *client_opener;
+       archive_read_callback   *client_reader;
+       archive_skip_callback   *client_skipper;
+       archive_write_callback  *client_writer;
+       archive_close_callback  *client_closer;
+       void                    *client_data;
+
+       /*
+        * Blocking information.  Note that bytes_in_last_block is
+        * misleadingly named; I should find a better name.  These
+        * control the final output from all compressors, including
+        * compression_none.
+        */
+       int               bytes_per_block;
+       int               bytes_in_last_block;
+
+       /*
+        * These control whether data within a gzip/bzip2 compressed
+        * stream gets padded or not.  If pad_uncompressed is set,
+        * the data will be padded to a full block before being
+        * compressed.  The pad_uncompressed_byte determines the value
+        * that will be used for padding.  Note that these have no
+        * effect on compression "none."
+        */
+       int               pad_uncompressed;
+       int               pad_uncompressed_byte; /* TODO: Support this. */
+
+       /* File offset of beginning of most recently-read header. */
+       off_t             header_position;
+
+       /*
+        * Detection functions for decompression: bid functions are
+        * given a block of data from the beginning of the stream and
+        * can bid on whether or not they support the data stream.
+        * General guideline: bid the number of bits that you actually
+        * test, e.g., 16 if you test a 2-byte magic value.  The
+        * highest bidder will have their init function invoked, which
+        * can set up pointers to specific handlers.
+        *
+        * On write, the client just invokes an archive_write_set function
+        * which sets up the data here directly.
+        */
+       struct {
+               int     (*bid)(const void *buff, size_t);
+               int     (*init)(struct archive_read *, const void *buff, size_t);
+       }       decompressors[4];
+       /* Read/write data stream (with compression). */
+       void     *compression_data;             /* Data for (de)compressor. */
+       int     (*compression_init)(struct archive_read *);     /* Initialize. */
+       int     (*compression_finish)(struct archive_read *);
+       int     (*compression_write)(struct archive_read *, const void *, size_t);
+       /*
+        * Read uses a peek/consume I/O model: the decompression code
+        * returns a pointer to the requested block and advances the
+        * file position only when requested by a consume call.  This
+        * reduces copying and also simplifies look-ahead for format
+        * detection.
+        */
+       ssize_t (*compression_read_ahead)(struct archive_read *,
+                   const void **, size_t request);
+       ssize_t (*compression_read_consume)(struct archive_read *, size_t);
+       off_t (*compression_skip)(struct archive_read *, off_t);
+
+       /*
+        * Format detection is mostly the same as compression
+        * detection, with two significant differences: The bidders
+        * use the read_ahead calls above to examine the stream rather
+        * than having the supervisor hand them a block of data to
+        * examine, and the auction is repeated for every header.
+        * Winning bidders should set the archive_format and
+        * archive_format_name appropriately.  Bid routines should
+        * check archive_format and decline to bid if the format of
+        * the last header was incompatible.
+        *
+        * Again, write support is considerably simpler because there's
+        * no need for an auction.
+        */
+
+       struct archive_format_descriptor {
+               int     (*bid)(struct archive_read *);
+               int     (*read_header)(struct archive_read *, struct archive_entry *);
+               int     (*read_data)(struct archive_read *, const void **, size_t *, off_t *);
+               int     (*read_data_skip)(struct archive_read *);
+               int     (*cleanup)(struct archive_read *);
+               void     *format_data;  /* Format-specific data for readers. */
+       }       formats[8];
+       struct archive_format_descriptor        *format; /* Active format. */
+
+       /*
+        * Storage for format-specific data.  Note that there can be
+        * multiple format readers active at one time, so we need to
+        * allow for multiple format readers to have their data
+        * available.  The pformat_data slot here is the solution: on
+        * read, it is guaranteed to always point to a void* variable
+        * that the format can use.
+        */
+       void    **pformat_data;         /* Pointer to current format_data. */
+       void     *format_data;          /* Used by writers. */
+
+       /*
+        * Pointers to format-specific functions for writing.  They're
+        * initialized by archive_write_set_format_XXX() calls.
+        */
+       int     (*format_init)(struct archive *); /* Only used on write. */
+       int     (*format_finish)(struct archive *);
+       int     (*format_finish_entry)(struct archive *);
+       int     (*format_write_header)(struct archive *,
+                   struct archive_entry *);
+       ssize_t (*format_write_data)(struct archive *,
+                   const void *buff, size_t);
+
+       /*
+        * Various information needed by archive_extract.
+        */
+       struct extract           *extract;
+       int                     (*cleanup_archive_extract)(struct archive_read *);
+};
+
+int    __archive_read_register_format(struct archive_read *a,
+           void *format_data,
+           int (*bid)(struct archive_read *),
+           int (*read_header)(struct archive_read *, struct archive_entry *),
+           int (*read_data)(struct archive_read *, const void **, size_t *, off_t *),
+           int (*read_data_skip)(struct archive_read *),
+           int (*cleanup)(struct archive_read *));
+
+int    __archive_read_register_compression(struct archive_read *a,
+           int (*bid)(const void *, size_t),
+           int (*init)(struct archive_read *, const void *, size_t));
+
+#endif
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_all.c b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_all.c
new file mode 100644 (file)
index 0000000..da2b246
--- /dev/null
@@ -0,0 +1,43 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_all.c,v 1.6 2007/01/09 08:05:55 kientzle Exp $");
+
+#include "archive.h"
+
+int
+archive_read_support_compression_all(struct archive *a)
+{
+#if HAVE_BZLIB_H
+       archive_read_support_compression_bzip2(a);
+#endif
+       /* The decompress code doesn't use an outside library. */
+       archive_read_support_compression_compress(a);
+#if HAVE_ZLIB_H
+       archive_read_support_compression_gzip(a);
+#endif
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_bzip2.c b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_bzip2.c
new file mode 100644 (file)
index 0000000..7cfc97a
--- /dev/null
@@ -0,0 +1,405 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_bzip2.c,v 1.13 2007/03/03 07:37:36 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if HAVE_BZLIB_H
+struct private_data {
+       bz_stream        stream;
+       char            *uncompressed_buffer;
+       size_t           uncompressed_buffer_size;
+       char            *read_next;
+       int64_t          total_out;
+};
+
+static int     finish(struct archive_read *);
+static ssize_t read_ahead(struct archive_read *, const void **, size_t);
+static ssize_t read_consume(struct archive_read *, size_t);
+static int     drive_decompressor(struct archive_read *a, struct private_data *);
+#endif
+
+/* These two functions are defined even if we lack bzlib.  See below. */
+static int     bid(const void *, size_t);
+static int     init(struct archive_read *, const void *, size_t);
+
+int
+archive_read_support_compression_bzip2(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       return (__archive_read_register_compression(a, bid, init));
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * This logic returns zero if any part of the signature fails.  It
+ * also tries to Do The Right Thing if a very short buffer prevents us
+ * from verifying as much as we would like.
+ */
+static int
+bid(const void *buff, size_t len)
+{
+       const unsigned char *buffer;
+       int bits_checked;
+
+       if (len < 1)
+               return (0);
+
+       buffer = (const unsigned char *)buff;
+       bits_checked = 0;
+       if (buffer[0] != 'B')   /* Verify first ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 2)
+               return (bits_checked);
+
+       if (buffer[1] != 'Z')   /* Verify second ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 3)
+               return (bits_checked);
+
+       if (buffer[2] != 'h')   /* Verify third ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 4)
+               return (bits_checked);
+
+       if (buffer[3] < '1' || buffer[3] > '9')
+               return (0);
+       bits_checked += 5;
+
+       /*
+        * Research Question: Can we do any more to verify that this
+        * really is BZip2 format??  For 99.9% of the time, the above
+        * test is sufficient, but it would be nice to do a more
+        * thorough check.  It's especially troubling that the BZip2
+        * signature begins with all ASCII characters; a tar archive
+        * whose first filename begins with 'BZh3' would potentially
+        * fool this logic.  (It may also be possible to guard against
+        * such anomalies in archive_read_support_compression_none.)
+        */
+
+       return (bits_checked);
+}
+
+#ifndef HAVE_BZLIB_H
+
+/*
+ * If we don't have bzlib on this system, we can't actually do the
+ * decompression.  We can, however, still detect bzip2-compressed
+ * archives and emit a useful message.
+ */
+static int
+init(struct archive *a, const void *buff, size_t n)
+{
+       (void)a;        /* UNUSED */
+       (void)buff;     /* UNUSED */
+       (void)n;        /* UNUSED */
+
+       archive_set_error(a, -1,
+           "This version of libarchive was compiled without bzip2 support");
+       return (ARCHIVE_FATAL);
+}
+
+
+#else
+
+/*
+ * Setup the callbacks.
+ */
+static int
+init(struct archive_read *a, const void *buff, size_t n)
+{
+       struct private_data *state;
+       int ret;
+
+       a->archive.compression_code = ARCHIVE_COMPRESSION_BZIP2;
+       a->archive.compression_name = "bzip2";
+
+       state = (struct private_data *)malloc(sizeof(*state));
+       if (state == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate data for %s decompression",
+                   a->archive.compression_name);
+               return (ARCHIVE_FATAL);
+       }
+       memset(state, 0, sizeof(*state));
+
+       state->uncompressed_buffer_size = 64 * 1024;
+       state->uncompressed_buffer = (char *)malloc(state->uncompressed_buffer_size);
+       state->stream.next_out = state->uncompressed_buffer;
+       state->read_next = state->uncompressed_buffer;
+       state->stream.avail_out = state->uncompressed_buffer_size;
+
+       if (state->uncompressed_buffer == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate %s decompression buffers",
+                   a->archive.compression_name);
+               free(state);
+               return (ARCHIVE_FATAL);
+       }
+
+       /*
+        * A bug in bzlib.h: stream.next_in should be marked 'const'
+        * but isn't (the library never alters data through the
+        * next_in pointer, only reads it).  The result: this ugly
+        * cast to remove 'const'.
+        */
+       state->stream.next_in = (char *)(uintptr_t)(const void *)buff;
+       state->stream.avail_in = n;
+
+       a->compression_read_ahead = read_ahead;
+       a->compression_read_consume = read_consume;
+       a->compression_skip = NULL; /* not supported */
+       a->compression_finish = finish;
+
+       /* Initialize compression library. */
+       ret = BZ2_bzDecompressInit(&(state->stream),
+           0 /* library verbosity */,
+           0 /* don't use slow low-mem algorithm */);
+
+       /* If init fails, try using low-memory algorithm instead. */
+       if (ret == BZ_MEM_ERROR) {
+               ret = BZ2_bzDecompressInit(&(state->stream),
+                   0 /* library verbosity */,
+                   1 /* do use slow low-mem algorithm */);
+       }
+
+       if (ret == BZ_OK) {
+               a->compression_data = state;
+               return (ARCHIVE_OK);
+       }
+
+       /* Library setup failed: Clean up. */
+       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+           "Internal error initializing %s library",
+           a->archive.compression_name);
+       free(state->uncompressed_buffer);
+       free(state);
+
+       /* Override the error message if we know what really went wrong. */
+       switch (ret) {
+       case BZ_PARAM_ERROR:
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Internal error initializing compression library: "
+                   "invalid setup parameter");
+               break;
+       case BZ_MEM_ERROR:
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Internal error initializing compression library: "
+                   "out of memory");
+               break;
+       case BZ_CONFIG_ERROR:
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Internal error initializing compression library: "
+                   "mis-compiled library");
+               break;
+       }
+
+       return (ARCHIVE_FATAL);
+}
+
+/*
+ * Return a block of data from the decompression buffer.  Decompress more
+ * as necessary.
+ */
+static ssize_t
+read_ahead(struct archive_read *a, const void **p, size_t min)
+{
+       struct private_data *state;
+       int read_avail, was_avail, ret;
+
+       state = (struct private_data *)a->compression_data;
+       was_avail = -1;
+       if (!a->client_reader) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+                   "No read callback is registered?  "
+                   "This is probably an internal programming error.");
+               return (ARCHIVE_FATAL);
+       }
+
+       read_avail = state->stream.next_out - state->read_next;
+
+       if (read_avail + state->stream.avail_out < min) {
+               memmove(state->uncompressed_buffer, state->read_next,
+                   read_avail);
+               state->read_next = state->uncompressed_buffer;
+               state->stream.next_out = state->read_next + read_avail;
+               state->stream.avail_out
+                   = state->uncompressed_buffer_size - read_avail;
+       }
+
+       while (was_avail < read_avail &&        /* Made some progress. */
+           read_avail < (int)min &&            /* Haven't satisfied min. */
+           read_avail < (int)state->uncompressed_buffer_size) { /* !full */
+               if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK)
+                       return (ret);
+               was_avail = read_avail;
+               read_avail = state->stream.next_out - state->read_next;
+       }
+
+       *p = state->read_next;
+       return (read_avail);
+}
+
+/*
+ * Mark a previously-returned block of data as read.
+ */
+static ssize_t
+read_consume(struct archive_read *a, size_t n)
+{
+       struct private_data *state;
+
+       state = (struct private_data *)a->compression_data;
+       a->archive.file_position += n;
+       state->read_next += n;
+       if (state->read_next > state->stream.next_out)
+               __archive_errx(1, "Request to consume too many "
+                   "bytes from bzip2 decompressor");
+       return (n);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+finish(struct archive_read *a)
+{
+       struct private_data *state;
+       int ret;
+
+       state = (struct private_data *)a->compression_data;
+       ret = ARCHIVE_OK;
+       switch (BZ2_bzDecompressEnd(&(state->stream))) {
+       case BZ_OK:
+               break;
+       default:
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Failed to clean up %s compressor",
+                   a->archive.compression_name);
+               ret = ARCHIVE_FATAL;
+       }
+
+       free(state->uncompressed_buffer);
+       free(state);
+
+       a->compression_data = NULL;
+       if (a->client_closer != NULL)
+               (a->client_closer)(&a->archive, a->client_data);
+
+       return (ret);
+}
+
+/*
+ * Utility function to pull data through decompressor, reading input
+ * blocks as necessary.
+ */
+static int
+drive_decompressor(struct archive_read *a, struct private_data *state)
+{
+       ssize_t ret;
+       int decompressed, total_decompressed;
+       char *output;
+
+       total_decompressed = 0;
+       for (;;) {
+               if (state->stream.avail_in == 0) {
+                       ret = (a->client_reader)(&a->archive, a->client_data,
+                           (const void **)&state->stream.next_in);
+                       if (ret < 0) {
+                               /*
+                                * TODO: Find a better way to handle
+                                * this read failure.
+                                */
+                               goto fatal;
+                       }
+                       if (ret == 0  &&  total_decompressed == 0) {
+                               archive_set_error(&a->archive, EIO,
+                                   "Premature end of %s compressed data",
+                                   a->archive.compression_name);
+                               return (ARCHIVE_FATAL);
+                       }
+                       a->archive.raw_position += ret;
+                       state->stream.avail_in = ret;
+               }
+
+               {
+                       output = state->stream.next_out;
+
+                       /* Decompress some data. */
+                       ret = BZ2_bzDecompress(&(state->stream));
+                       decompressed = state->stream.next_out - output;
+
+                       /* Accumulate the total bytes of output. */
+                       state->total_out += decompressed;
+                       total_decompressed += decompressed;
+
+                       switch (ret) {
+                       case BZ_OK: /* Decompressor made some progress. */
+                               if (decompressed > 0)
+                                       return (ARCHIVE_OK);
+                               break;
+                       case BZ_STREAM_END:     /* Found end of stream. */
+                               return (ARCHIVE_OK);
+                       default:
+                               /* Any other return value is an error. */
+                               goto fatal;
+                       }
+               }
+       }
+       return (ARCHIVE_OK);
+
+       /* Return a fatal error. */
+fatal:
+       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+           "%s decompression failed", a->archive.compression_name);
+       return (ARCHIVE_FATAL);
+}
+
+#endif /* HAVE_BZLIB_H */
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_compress.c b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_compress.c
new file mode 100644 (file)
index 0000000..6f801d3
--- /dev/null
@@ -0,0 +1,492 @@
+/*-
+ * 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 code borrows heavily from "compress" source code, which is
+ * protected by the following copyright.  (Clause 3 dropped by request
+ * of the Regents.)
+ */
+
+/*-
+ * Copyright (c) 1985, 1986, 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Diomidis Spinellis and James A. Woods, derived from original
+ * work by Spencer Thomas and Joseph Orost.
+ *
+ * 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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_compress.c,v 1.8 2007/03/03 07:37:36 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+/*
+ * Because LZW decompression is pretty simple, I've just implemented
+ * the whole decompressor here (cribbing from "compress" source code,
+ * of course), rather than relying on an external library.  I have
+ * made an effort to clarify and simplify the algorithm, so the
+ * names and structure here don't exactly match those used by compress.
+ */
+
+struct private_data {
+       /* Input variables. */
+       const unsigned char     *next_in;
+       size_t                   avail_in;
+       int                      bit_buffer;
+       int                      bits_avail;
+       size_t                   bytes_in_section;
+
+       /* Output variables. */
+       size_t                   uncompressed_buffer_size;
+       void                    *uncompressed_buffer;
+       unsigned char           *read_next;   /* Data for client. */
+       unsigned char           *next_out;    /* Where to write new data. */
+       size_t                   avail_out;   /* Space at end of buffer. */
+
+       /* Decompression status variables. */
+       int                      use_reset_code;
+       int                      end_of_stream; /* EOF status. */
+       int                      maxcode;       /* Largest code. */
+       int                      maxcode_bits;  /* Length of largest code. */
+       int                      section_end_code; /* When to increase bits. */
+       int                      bits;          /* Current code length. */
+       int                      oldcode;       /* Previous code. */
+       int                      finbyte;       /* Last byte of prev code. */
+
+       /* Dictionary. */
+       int                      free_ent;       /* Next dictionary entry. */
+       unsigned char            suffix[65536];
+       uint16_t                 prefix[65536];
+
+       /*
+        * Scratch area for expanding dictionary entries.  Note:
+        * "worst" case here comes from compressing /dev/zero: the
+        * last code in the dictionary will code a sequence of
+        * 65536-256 zero bytes.  Thus, we need stack space to expand
+        * a 65280-byte dictionary entry.  (Of course, 32640:1
+        * compression could also be considered the "best" case. ;-)
+        */
+       unsigned char           *stackp;
+       unsigned char            stack[65300];
+};
+
+static int     bid(const void *, size_t);
+static int     finish(struct archive_read *);
+static int     init(struct archive_read *, const void *, size_t);
+static ssize_t read_ahead(struct archive_read *, const void **, size_t);
+static ssize_t read_consume(struct archive_read *, size_t);
+static int     getbits(struct archive_read *, struct private_data *, int n);
+static int     next_code(struct archive_read *a, struct private_data *state);
+
+int
+archive_read_support_compression_compress(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       return (__archive_read_register_compression(a, bid, init));
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * This logic returns zero if any part of the signature fails.  It
+ * also tries to Do The Right Thing if a very short buffer prevents us
+ * from verifying as much as we would like.
+ */
+static int
+bid(const void *buff, size_t len)
+{
+       const unsigned char *buffer;
+       int bits_checked;
+
+       if (len < 1)
+               return (0);
+
+       buffer = (const unsigned char *)buff;
+       bits_checked = 0;
+       if (buffer[0] != 037)   /* Verify first ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 2)
+               return (bits_checked);
+
+       if (buffer[1] != 0235)  /* Verify second ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 3)
+               return (bits_checked);
+
+       /*
+        * TODO: Verify more.
+        */
+
+       return (bits_checked);
+}
+
+/*
+ * Setup the callbacks.
+ */
+static int
+init(struct archive_read *a, const void *buff, size_t n)
+{
+       struct private_data *state;
+       int code;
+
+       a->archive.compression_code = ARCHIVE_COMPRESSION_COMPRESS;
+       a->archive.compression_name = "compress (.Z)";
+
+       a->compression_read_ahead = read_ahead;
+       a->compression_read_consume = read_consume;
+       a->compression_skip = NULL; /* not supported */
+       a->compression_finish = finish;
+
+       state = (struct private_data *)malloc(sizeof(*state));
+       if (state == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate data for %s decompression",
+                   a->archive.compression_name);
+               return (ARCHIVE_FATAL);
+       }
+       memset(state, 0, sizeof(*state));
+       a->compression_data = state;
+
+       state->uncompressed_buffer_size = 64 * 1024;
+       state->uncompressed_buffer = malloc(state->uncompressed_buffer_size);
+
+       if (state->uncompressed_buffer == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate %s decompression buffers",
+                   a->archive.compression_name);
+               goto fatal;
+       }
+
+       state->next_in = (const unsigned char *)buff;
+       state->avail_in = n;
+       state->read_next = state->next_out = (unsigned char *)state->uncompressed_buffer;
+       state->avail_out = state->uncompressed_buffer_size;
+
+       code = getbits(a, state, 8);
+       if (code != 037) /* This should be impossible. */
+               goto fatal;
+
+       code = getbits(a, state, 8);
+       if (code != 0235) {
+               /* This can happen if the library is receiving 1-byte
+                * blocks and gzip and compress are both enabled.
+                * You can't distinguish gzip and compress only from
+                * the first byte. */
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Compress signature did not match.");
+               goto fatal;
+       }
+
+       code = getbits(a, state, 8);
+       state->maxcode_bits = code & 0x1f;
+       state->maxcode = (1 << state->maxcode_bits);
+       state->use_reset_code = code & 0x80;
+
+       /* Initialize decompressor. */
+       state->free_ent = 256;
+       state->stackp = state->stack;
+       if (state->use_reset_code)
+               state->free_ent++;
+       state->bits = 9;
+       state->section_end_code = (1<<state->bits) - 1;
+       state->oldcode = -1;
+       for (code = 255; code >= 0; code--) {
+               state->prefix[code] = 0;
+               state->suffix[code] = code;
+       }
+       next_code(a, state);
+       return (ARCHIVE_OK);
+
+fatal:
+       finish(a);
+       return (ARCHIVE_FATAL);
+}
+
+/*
+ * Return a block of data from the decompression buffer.  Decompress more
+ * as necessary.
+ */
+static ssize_t
+read_ahead(struct archive_read *a, const void **p, size_t min)
+{
+       struct private_data *state;
+       int read_avail, was_avail, ret;
+
+       state = (struct private_data *)a->compression_data;
+       was_avail = -1;
+       if (!a->client_reader) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+                   "No read callback is registered?  "
+                   "This is probably an internal programming error.");
+               return (ARCHIVE_FATAL);
+       }
+
+       read_avail = state->next_out - state->read_next;
+
+       if (read_avail < (int)min  &&  state->end_of_stream) {
+               if (state->end_of_stream == ARCHIVE_EOF)
+                       return (0);
+               else
+                       return (-1);
+       }
+
+       if (read_avail < (int)min) {
+               memmove(state->uncompressed_buffer, state->read_next,
+                   read_avail);
+               state->read_next = (unsigned char *)state->uncompressed_buffer;
+               state->next_out = state->read_next + read_avail;
+               state->avail_out
+                   = state->uncompressed_buffer_size - read_avail;
+
+               while (read_avail < (int)state->uncompressed_buffer_size
+                       && !state->end_of_stream) {
+                       if (state->stackp > state->stack) {
+                               *state->next_out++ = *--state->stackp;
+                               state->avail_out--;
+                               read_avail++;
+                       } else {
+                               ret = next_code(a, state);
+                               if (ret == ARCHIVE_EOF)
+                                       state->end_of_stream = ret;
+                               else if (ret != ARCHIVE_OK)
+                                       return (ret);
+                       }
+               }
+       }
+
+       *p = state->read_next;
+       return (read_avail);
+}
+
+/*
+ * Mark a previously-returned block of data as read.
+ */
+static ssize_t
+read_consume(struct archive_read *a, size_t n)
+{
+       struct private_data *state;
+
+       state = (struct private_data *)a->compression_data;
+       a->archive.file_position += n;
+       state->read_next += n;
+       if (state->read_next > state->next_out)
+               __archive_errx(1, "Request to consume too many "
+                   "bytes from compress decompressor");
+       return (n);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+finish(struct archive_read *a)
+{
+       struct private_data *state;
+       int ret = ARCHIVE_OK;
+
+       state = (struct private_data *)a->compression_data;
+
+       if (state != NULL) {
+               if (state->uncompressed_buffer != NULL)
+                       free(state->uncompressed_buffer);
+               free(state);
+       }
+
+       a->compression_data = NULL;
+       if (a->client_closer != NULL)
+               ret = (a->client_closer)(&a->archive, a->client_data);
+
+       return (ret);
+}
+
+/*
+ * Process the next code and fill the stack with the expansion
+ * of the code.  Returns ARCHIVE_FATAL if there is a fatal I/O or
+ * format error, ARCHIVE_EOF if we hit end of data, ARCHIVE_OK otherwise.
+ */
+static int
+next_code(struct archive_read *a, struct private_data *state)
+{
+       int code, newcode;
+
+       static int debug_buff[1024];
+       static unsigned debug_index;
+
+       code = newcode = getbits(a, state, state->bits);
+       if (code < 0)
+               return (code);
+
+       debug_buff[debug_index++] = code;
+       if (debug_index >= sizeof(debug_buff)/sizeof(debug_buff[0]))
+               debug_index = 0;
+
+       /* If it's a reset code, reset the dictionary. */
+       if ((code == 256) && state->use_reset_code) {
+               /*
+                * The original 'compress' implementation blocked its
+                * I/O in a manner that resulted in junk bytes being
+                * inserted after every reset.  The next section skips
+                * this junk.  (Yes, the number of *bytes* to skip is
+                * a function of the current *bit* length.)
+                */
+               int skip_bytes =  state->bits -
+                   (state->bytes_in_section % state->bits);
+               skip_bytes %= state->bits;
+               state->bits_avail = 0; /* Discard rest of this byte. */
+               while (skip_bytes-- > 0) {
+                       code = getbits(a, state, 8);
+                       if (code < 0)
+                               return (code);
+               }
+               /* Now, actually do the reset. */
+               state->bytes_in_section = 0;
+               state->bits = 9;
+               state->section_end_code = (1 << state->bits) - 1;
+               state->free_ent = 257;
+               state->oldcode = -1;
+               return (next_code(a, state));
+       }
+
+       if (code > state->free_ent) {
+               /* An invalid code is a fatal error. */
+               archive_set_error(&a->archive, -1, "Invalid compressed data");
+               return (ARCHIVE_FATAL);
+       }
+
+       /* Special case for KwKwK string. */
+       if (code >= state->free_ent) {
+               *state->stackp++ = state->finbyte;
+               code = state->oldcode;
+       }
+
+       /* Generate output characters in reverse order. */
+       while (code >= 256) {
+               *state->stackp++ = state->suffix[code];
+               code = state->prefix[code];
+       }
+       *state->stackp++ = state->finbyte = code;
+
+       /* Generate the new entry. */
+       code = state->free_ent;
+       if (code < state->maxcode && state->oldcode >= 0) {
+               state->prefix[code] = state->oldcode;
+               state->suffix[code] = state->finbyte;
+               ++state->free_ent;
+       }
+       if (state->free_ent > state->section_end_code) {
+               state->bits++;
+               state->bytes_in_section = 0;
+               if (state->bits == state->maxcode_bits)
+                       state->section_end_code = state->maxcode;
+               else
+                       state->section_end_code = (1 << state->bits) - 1;
+       }
+
+       /* Remember previous code. */
+       state->oldcode = newcode;
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Return next 'n' bits from stream.
+ *
+ * -1 indicates end of available data.
+ */
+static int
+getbits(struct archive_read *a, struct private_data *state, int n)
+{
+       int code, ret;
+       static const int mask[] = {
+               0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff,
+               0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+       };
+
+
+       while (state->bits_avail < n) {
+               if (state->avail_in <= 0) {
+                       ret = (a->client_reader)(&a->archive, a->client_data,
+                           (const void **)&state->next_in);
+                       if (ret < 0)
+                               return (ARCHIVE_FATAL);
+                       if (ret == 0)
+                               return (ARCHIVE_EOF);
+                       a->archive.raw_position += ret;
+                       state->avail_in = ret;
+               }
+               state->bit_buffer |= *state->next_in++ << state->bits_avail;
+               state->avail_in--;
+               state->bits_avail += 8;
+               state->bytes_in_section++;
+       }
+
+       code = state->bit_buffer;
+       state->bit_buffer >>= n;
+       state->bits_avail -= n;
+
+       return (code & mask[n]);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_gzip.c b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_gzip.c
new file mode 100644 (file)
index 0000000..ccda36e
--- /dev/null
@@ -0,0 +1,543 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_gzip.c,v 1.13 2007/03/03 07:37:36 kientzle Exp $");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#ifdef HAVE_ZLIB_H
+struct private_data {
+       z_stream         stream;
+       unsigned char   *uncompressed_buffer;
+       size_t           uncompressed_buffer_size;
+       unsigned char   *read_next;
+       int64_t          total_out;
+       unsigned long    crc;
+       char             header_done;
+};
+
+static int     finish(struct archive_read *);
+static ssize_t read_ahead(struct archive_read *, const void **, size_t);
+static ssize_t read_consume(struct archive_read *, size_t);
+static int     drive_decompressor(struct archive_read *a, struct private_data *);
+#endif
+
+/* These two functions are defined even if we lack zlib.  See below. */
+static int     bid(const void *, size_t);
+static int     init(struct archive_read *, const void *, size_t);
+
+int
+archive_read_support_compression_gzip(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       return (__archive_read_register_compression(a, bid, init));
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * This logic returns zero if any part of the signature fails.  It
+ * also tries to Do The Right Thing if a very short buffer prevents us
+ * from verifying as much as we would like.
+ */
+static int
+bid(const void *buff, size_t len)
+{
+       const unsigned char *buffer;
+       int bits_checked;
+
+       if (len < 1)
+               return (0);
+
+       buffer = (const unsigned char *)buff;
+       bits_checked = 0;
+       if (buffer[0] != 037)   /* Verify first ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 2)
+               return (bits_checked);
+
+       if (buffer[1] != 0213)  /* Verify second ID byte. */
+               return (0);
+       bits_checked += 8;
+       if (len < 3)
+               return (bits_checked);
+
+       if (buffer[2] != 8)     /* Compression must be 'deflate'. */
+               return (0);
+       bits_checked += 8;
+       if (len < 4)
+               return (bits_checked);
+
+       if ((buffer[3] & 0xE0)!= 0)     /* No reserved flags set. */
+               return (0);
+       bits_checked += 3;
+       if (len < 5)
+               return (bits_checked);
+
+       /*
+        * TODO: Verify more; in particular, gzip has an optional
+        * header CRC, which would give us 16 more verified bits.  We
+        * may also be able to verify certain constraints on other
+        * fields.
+        */
+
+       return (bits_checked);
+}
+
+
+#ifndef HAVE_ZLIB_H
+
+/*
+ * If we don't have zlib on this system, we can't actually do the
+ * decompression.  We can, however, still detect gzip-compressed
+ * archives and emit a useful message.
+ */
+static int
+init(struct archive_read *a, const void *buff, size_t n)
+{
+       (void)a;        /* UNUSED */
+       (void)buff;     /* UNUSED */
+       (void)n;        /* UNUSED */
+
+       archive_set_error(a, -1,
+           "This version of libarchive was compiled without gzip support");
+       return (ARCHIVE_FATAL);
+}
+
+
+#else
+
+/*
+ * Setup the callbacks.
+ */
+static int
+init(struct archive_read *a, const void *buff, size_t n)
+{
+       struct private_data *state;
+       int ret;
+
+       a->archive.compression_code = ARCHIVE_COMPRESSION_GZIP;
+       a->archive.compression_name = "gzip";
+
+       state = (struct private_data *)malloc(sizeof(*state));
+       if (state == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate data for %s decompression",
+                   a->archive.compression_name);
+               return (ARCHIVE_FATAL);
+       }
+       memset(state, 0, sizeof(*state));
+
+       state->crc = crc32(0L, NULL, 0);
+       state->header_done = 0; /* We've not yet begun to parse header... */
+
+       state->uncompressed_buffer_size = 64 * 1024;
+       state->uncompressed_buffer = (unsigned char *)malloc(state->uncompressed_buffer_size);
+       state->stream.next_out = state->uncompressed_buffer;
+       state->read_next = state->uncompressed_buffer;
+       state->stream.avail_out = state->uncompressed_buffer_size;
+
+       if (state->uncompressed_buffer == NULL) {
+               archive_set_error(&a->archive, ENOMEM,
+                   "Can't allocate %s decompression buffers",
+                   a->archive.compression_name);
+               free(state);
+               return (ARCHIVE_FATAL);
+       }
+
+       /*
+        * A bug in zlib.h: stream.next_in should be marked 'const'
+        * but isn't (the library never alters data through the
+        * next_in pointer, only reads it).  The result: this ugly
+        * cast to remove 'const'.
+        */
+       state->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff;
+       state->stream.avail_in = n;
+
+       a->compression_read_ahead = read_ahead;
+       a->compression_read_consume = read_consume;
+       a->compression_skip = NULL; /* not supported */
+       a->compression_finish = finish;
+
+       /*
+        * TODO: Do I need to parse the gzip header before calling
+        * inflateInit2()?  In particular, one of the header bytes
+        * marks "best compression" or "fastest", which may be
+        * appropriate for setting the second parameter here.
+        * However, I think the only penalty for not setting it
+        * correctly is wasted memory.  If this is necessary, it
+        * should probably go into drive_decompressor() below.
+        */
+
+       /* Initialize compression library. */
+       ret = inflateInit2(&(state->stream),
+           -15 /* Don't check for zlib header */);
+       if (ret == Z_OK) {
+               a->compression_data = state;
+               return (ARCHIVE_OK);
+       }
+
+       /* Library setup failed: Clean up. */
+       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+           "Internal error initializing %s library",
+           a->archive.compression_name);
+       free(state->uncompressed_buffer);
+       free(state);
+
+       /* Override the error message if we know what really went wrong. */
+       switch (ret) {
+       case Z_STREAM_ERROR:
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Internal error initializing compression library: "
+                   "invalid setup parameter");
+               break;
+       case Z_MEM_ERROR:
+               archive_set_error(&a->archive, ENOMEM,
+                   "Internal error initializing compression library: "
+                   "out of memory");
+               break;
+       case Z_VERSION_ERROR:
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Internal error initializing compression library: "
+                   "invalid library version");
+               break;
+       }
+
+       return (ARCHIVE_FATAL);
+}
+
+/*
+ * Return a block of data from the decompression buffer.  Decompress more
+ * as necessary.
+ */
+static ssize_t
+read_ahead(struct archive_read *a, const void **p, size_t min)
+{
+       struct private_data *state;
+       int read_avail, was_avail, ret;
+
+       state = (struct private_data *)a->compression_data;
+       was_avail = -1;
+       if (!a->client_reader) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+                   "No read callback is registered?  "
+                   "This is probably an internal programming error.");
+               return (ARCHIVE_FATAL);
+       }
+
+       read_avail = state->stream.next_out - state->read_next;
+
+       if (read_avail + state->stream.avail_out < min) {
+               memmove(state->uncompressed_buffer, state->read_next,
+                   read_avail);
+               state->read_next = state->uncompressed_buffer;
+               state->stream.next_out = state->read_next + read_avail;
+               state->stream.avail_out
+                   = state->uncompressed_buffer_size - read_avail;
+       }
+
+       while (was_avail < read_avail &&        /* Made some progress. */
+           read_avail < (int)min &&            /* Haven't satisfied min. */
+           read_avail < (int)state->uncompressed_buffer_size) { /* !full */
+               if ((ret = drive_decompressor(a, state)) != ARCHIVE_OK)
+                       return (ret);
+               was_avail = read_avail;
+               read_avail = state->stream.next_out - state->read_next;
+       }
+
+       *p = state->read_next;
+       return (read_avail);
+}
+
+/*
+ * Mark a previously-returned block of data as read.
+ */
+static ssize_t
+read_consume(struct archive_read *a, size_t n)
+{
+       struct private_data *state;
+
+       state = (struct private_data *)a->compression_data;
+       a->archive.file_position += n;
+       state->read_next += n;
+       if (state->read_next > state->stream.next_out)
+               __archive_errx(1, "Request to consume too many "
+                   "bytes from gzip decompressor");
+       return (n);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+finish(struct archive_read *a)
+{
+       struct private_data *state;
+       int ret;
+
+       state = (struct private_data *)a->compression_data;
+       ret = ARCHIVE_OK;
+       switch (inflateEnd(&(state->stream))) {
+       case Z_OK:
+               break;
+       default:
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "Failed to clean up %s compressor",
+                   a->archive.compression_name);
+               ret = ARCHIVE_FATAL;
+       }
+
+       free(state->uncompressed_buffer);
+       free(state);
+
+       a->compression_data = NULL;
+       if (a->client_closer != NULL)
+               (a->client_closer)(&a->archive, a->client_data);
+
+       return (ret);
+}
+
+/*
+ * Utility function to pull data through decompressor, reading input
+ * blocks as necessary.
+ */
+static int
+drive_decompressor(struct archive_read *a, struct private_data *state)
+{
+       ssize_t ret;
+       int decompressed, total_decompressed;
+       int count, flags, header_state;
+       unsigned char *output;
+       unsigned char b;
+
+       flags = 0;
+       count = 0;
+       header_state = 0;
+       total_decompressed = 0;
+       for (;;) {
+               if (state->stream.avail_in == 0) {
+                       ret = (a->client_reader)(&a->archive, a->client_data,
+                           (const void **)&state->stream.next_in);
+                       if (ret < 0) {
+                               /*
+                                * TODO: Find a better way to handle
+                                * this read failure.
+                                */
+                               goto fatal;
+                       }
+                       if (ret == 0  &&  total_decompressed == 0) {
+                               archive_set_error(&a->archive, EIO,
+                                   "Premature end of %s compressed data",
+                                   a->archive.compression_name);
+                               return (ARCHIVE_FATAL);
+                       }
+                       a->archive.raw_position += ret;
+                       state->stream.avail_in = ret;
+               }
+
+               if (!state->header_done) {
+                       /*
+                        * If still parsing the header, interpret the
+                        * next byte.
+                        */
+                       b = *(state->stream.next_in++);
+                       state->stream.avail_in--;
+
+                       /*
+                        * Yes, this is somewhat crude, but it works,
+                        * GZip format isn't likely to change anytime
+                        * in the near future, and header parsing is
+                        * certainly not a performance issue, so
+                        * there's little point in making this more
+                        * elegant.  Of course, if you see an easy way
+                        * to make this more elegant, please let me
+                        * know.. ;-)
+                        */
+                       switch (header_state) {
+                       case 0: /* First byte of signature. */
+                               if (b != 037)
+                                       goto fatal;
+                               header_state = 1;
+                               break;
+                       case 1: /* Second byte of signature. */
+                               if (b != 0213)
+                                       goto fatal;
+                               header_state = 2;
+                               break;
+                       case 2: /* Compression type must be 8. */
+                               if (b != 8)
+                                       goto fatal;
+                               header_state = 3;
+                               break;
+                       case 3: /* GZip flags. */
+                               flags = b;
+                               header_state = 4;
+                               break;
+                       case 4: case 5: case 6: case 7: /* Mod time. */
+                               header_state++;
+                               break;
+                       case 8: /* Deflate flags. */
+                               header_state = 9;
+                               break;
+                       case 9: /* OS. */
+                               header_state = 10;
+                               break;
+                       case 10: /* Optional Extra: First byte of Length. */
+                               if ((flags & 4)) {
+                                       count = 255 & (int)b;
+                                       header_state = 11;
+                                       break;
+                               }
+                               /*
+                                * Fall through if there is no
+                                * Optional Extra field.
+                                */
+                       case 11: /* Optional Extra: Second byte of Length. */
+                               if ((flags & 4)) {
+                                       count = (0xff00 & ((int)b << 8)) | count;
+                                       header_state = 12;
+                                       break;
+                               }
+                               /*
+                                * Fall through if there is no
+                                * Optional Extra field.
+                                */
+                       case 12: /* Optional Extra Field: counted length. */
+                               if ((flags & 4)) {
+                                       --count;
+                                       if (count == 0) header_state = 13;
+                                       else header_state = 12;
+                                       break;
+                               }
+                               /*
+                                * Fall through if there is no
+                                * Optional Extra field.
+                                */
+                       case 13: /* Optional Original Filename. */
+                               if ((flags & 8)) {
+                                       if (b == 0) header_state = 14;
+                                       else header_state = 13;
+                                       break;
+                               }
+                               /*
+                                * Fall through if no Optional
+                                * Original Filename.
+                                */
+                       case 14: /* Optional Comment. */
+                               if ((flags & 16)) {
+                                       if (b == 0) header_state = 15;
+                                       else header_state = 14;
+                                       break;
+                               }
+                               /* Fall through if no Optional Comment. */
+                       case 15: /* Optional Header CRC: First byte. */
+                               if ((flags & 2)) {
+                                       header_state = 16;
+                                       break;
+                               }
+                               /* Fall through if no Optional Header CRC. */
+                       case 16: /* Optional Header CRC: Second byte. */
+                               if ((flags & 2)) {
+                                       header_state = 17;
+                                       break;
+                               }
+                               /* Fall through if no Optional Header CRC. */
+                       case 17: /* First byte of compressed data. */
+                               state->header_done = 1; /* done with header */
+                               state->stream.avail_in++;
+                               state->stream.next_in--;
+                       }
+
+                       /*
+                        * TODO: Consider moving the inflateInit2 call
+                        * here so it can include the compression type
+                        * from the header?
+                        */
+               } else {
+                       output = state->stream.next_out;
+
+                       /* Decompress some data. */
+                       ret = inflate(&(state->stream), 0);
+                       decompressed = state->stream.next_out - output;
+
+                       /* Accumulate the CRC of the uncompressed data. */
+                       state->crc = crc32(state->crc, output, decompressed);
+
+                       /* Accumulate the total bytes of output. */
+                       state->total_out += decompressed;
+                       total_decompressed += decompressed;
+
+                       switch (ret) {
+                       case Z_OK: /* Decompressor made some progress. */
+                               if (decompressed > 0)
+                                       return (ARCHIVE_OK);
+                               break;
+                       case Z_STREAM_END: /* Found end of stream. */
+                               /*
+                                * TODO: Verify gzip trailer
+                                * (uncompressed length and CRC).
+                                */
+                               return (ARCHIVE_OK);
+                       default:
+                               /* Any other return value is an error. */
+                               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                                   "gzip decompression failed (%s)",
+                                   state->stream.msg);
+                               goto fatal;
+                       }
+               }
+       }
+       return (ARCHIVE_OK);
+
+       /* Return a fatal error. */
+fatal:
+       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+           "%s decompression failed", a->archive.compression_name);
+       return (ARCHIVE_FATAL);
+}
+
+#endif /* HAVE_ZLIB_H */
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_compression_none.c b/contrib/libarchive-2.0/libarchive/archive_read_support_compression_none.c
new file mode 100644 (file)
index 0000000..dde0aee
--- /dev/null
@@ -0,0 +1,366 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_compression_none.c,v 1.15 2007/03/03 07:37:36 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct archive_decompress_none {
+       char            *buffer;
+       size_t           buffer_size;
+       char            *next;          /* Current read location. */
+       size_t           avail;         /* Bytes in my buffer. */
+       const char      *client_buff;   /* Client buffer information. */
+       size_t           client_total;
+       const char      *client_next;
+       size_t           client_avail;
+       char             end_of_file;
+       char             fatal;
+};
+
+/*
+ * Size of internal buffer used for combining short reads.  This is
+ * also an upper limit on the size of a read request.  Recall,
+ * however, that we can (and will!) return blocks of data larger than
+ * this.  The read semantics are: you ask for a minimum, I give you a
+ * pointer to my best-effort match and tell you how much data is
+ * there.  It could be less than you asked for, it could be much more.
+ * For example, a client might use mmap() to "read" the entire file as
+ * a single block.  In that case, I will return that entire block to
+ * my clients.
+ */
+#define        BUFFER_SIZE     65536
+
+#define minimum(a, b) (a < b ? a : b)
+
+static int     archive_decompressor_none_bid(const void *, size_t);
+static int     archive_decompressor_none_finish(struct archive_read *);
+static int     archive_decompressor_none_init(struct archive_read *,
+                   const void *, size_t);
+static ssize_t archive_decompressor_none_read_ahead(struct archive_read *,
+                   const void **, size_t);
+static ssize_t archive_decompressor_none_read_consume(struct archive_read *,
+                   size_t);
+static off_t   archive_decompressor_none_skip(struct archive_read *, off_t);
+
+int
+archive_read_support_compression_none(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       return (__archive_read_register_compression(a,
+                   archive_decompressor_none_bid,
+                   archive_decompressor_none_init));
+}
+
+/*
+ * Try to detect an "uncompressed" archive.
+ */
+static int
+archive_decompressor_none_bid(const void *buff, size_t len)
+{
+       (void)buff;
+       (void)len;
+
+       return (1); /* Default: We'll take it if noone else does. */
+}
+
+static int
+archive_decompressor_none_init(struct archive_read *a, const void *buff, size_t n)
+{
+       struct archive_decompress_none  *state;
+
+       a->archive.compression_code = ARCHIVE_COMPRESSION_NONE;
+       a->archive.compression_name = "none";
+
+       state = (struct archive_decompress_none *)malloc(sizeof(*state));
+       if (!state) {
+               archive_set_error(&a->archive, ENOMEM, "Can't allocate input data");
+               return (ARCHIVE_FATAL);
+       }
+       memset(state, 0, sizeof(*state));
+
+       state->buffer_size = BUFFER_SIZE;
+       state->buffer = (char *)malloc(state->buffer_size);
+       state->next = state->buffer;
+       if (state->buffer == NULL) {
+               free(state);
+               archive_set_error(&a->archive, ENOMEM, "Can't allocate input buffer");
+               return (ARCHIVE_FATAL);
+       }
+
+       /* Save reference to first block of data. */
+       state->client_buff = (const char *)buff;
+       state->client_total = n;
+       state->client_next = state->client_buff;
+       state->client_avail = state->client_total;
+
+       a->compression_data = state;
+       a->compression_read_ahead = archive_decompressor_none_read_ahead;
+       a->compression_read_consume = archive_decompressor_none_read_consume;
+       a->compression_skip = archive_decompressor_none_skip;
+       a->compression_finish = archive_decompressor_none_finish;
+
+       return (ARCHIVE_OK);
+}
+
+/*
+ * We just pass through pointers to the client buffer if we can.
+ * If the client buffer is short, then we copy stuff to our internal
+ * buffer to combine reads.
+ */
+static ssize_t
+archive_decompressor_none_read_ahead(struct archive_read *a, const void **buff,
+    size_t min)
+{
+       struct archive_decompress_none *state;
+       ssize_t bytes_read;
+
+       state = (struct archive_decompress_none *)a->compression_data;
+       if (state->fatal)
+               return (-1);
+
+       /*
+        * Don't make special efforts to handle requests larger than
+        * the copy buffer.
+        */
+       if (min > state->buffer_size)
+               min = state->buffer_size;
+
+       /*
+        * Try to satisfy the request directly from the client
+        * buffer.  We can do this if all of the data in the copy
+        * buffer was copied from the current client buffer.  This
+        * also covers the case where the copy buffer is empty and
+        * the client buffer has all the data we need.
+        */
+       if (state->client_total >= state->client_avail + state->avail
+           && state->client_avail + state->avail >= min) {
+               state->client_avail += state->avail;
+               state->client_next -= state->avail;
+               state->avail = 0;
+               state->next = state->buffer;
+               *buff = state->client_next;
+               return (state->client_avail);
+       }
+
+       /*
+        * If we can't use client buffer, we'll have to use copy buffer.
+        */
+
+       /* Move data forward in copy buffer if necessary. */
+       if (state->next > state->buffer &&
+           state->next + min > state->buffer + state->buffer_size) {
+               if (state->avail > 0)
+                       memmove(state->buffer, state->next, state->avail);
+               state->next = state->buffer;
+       }
+
+       /* Collect data in copy buffer to fulfill request. */
+       while (state->avail < min) {
+               /* Copy data from client buffer to our copy buffer. */
+               if (state->client_avail > 0) {
+                       /* First estimate: copy to fill rest of buffer. */
+                       size_t tocopy = (state->buffer + state->buffer_size)
+                           - (state->next + state->avail);
+                       /* Don't copy more than is available. */
+                       if (tocopy > state->client_avail)
+                               tocopy = state->client_avail;
+                       memcpy(state->next + state->avail, state->client_next,
+                           tocopy);
+                       state->client_next += tocopy;
+                       state->client_avail -= tocopy;
+                       state->avail += tocopy;
+               } else {
+                       /* There is no more client data: fetch more. */
+                       /*
+                        * It seems to me that const void ** and const
+                        * char ** should be compatible, but they
+                        * aren't, hence the cast.
+                        */
+                       bytes_read = (a->client_reader)(&a->archive,
+                           a->client_data,
+                           (const void **)&state->client_buff);
+                       if (bytes_read < 0) {           /* Read error. */
+                               state->client_total = state->client_avail = 0;
+                               state->client_next = state->client_buff = NULL;
+                               state->fatal = 1;
+                               return (-1);
+                       }
+                       if (bytes_read == 0) {          /* End-of-file. */
+                               state->client_total = state->client_avail = 0;
+                               state->client_next = state->client_buff = NULL;
+                               state->end_of_file = 1;
+                               break;
+                       }
+                       a->archive.raw_position += bytes_read;
+                       state->client_total = bytes_read;
+                       state->client_avail = state->client_total;
+                       state->client_next = state->client_buff;
+               }
+       }
+
+       *buff = state->next;
+       return (state->avail);
+}
+
+/*
+ * Mark the appropriate data as used.  Note that the request here will
+ * often be much smaller than the size of the previous read_ahead
+ * request.
+ */
+static ssize_t
+archive_decompressor_none_read_consume(struct archive_read *a, size_t request)
+{
+       struct archive_decompress_none *state;
+
+       state = (struct archive_decompress_none *)a->compression_data;
+       if (state->avail > 0) {
+               /* Read came from copy buffer. */
+               state->next += request;
+               state->avail -= request;
+       } else {
+               /* Read came from client buffer. */
+               state->client_next += request;
+               state->client_avail -= request;
+       }
+       a->archive.file_position += request;
+       return (request);
+}
+
+/*
+ * Skip forward by exactly the requested bytes or else return
+ * ARCHIVE_FATAL.  Note that this differs from the contract for
+ * read_ahead, which does not guarantee a minimum count.
+ */
+static off_t
+archive_decompressor_none_skip(struct archive_read *a, off_t request)
+{
+       struct archive_decompress_none *state;
+       off_t bytes_skipped, total_bytes_skipped = 0;
+       size_t min;
+
+       state = (struct archive_decompress_none *)a->compression_data;
+       if (state->fatal)
+               return (-1);
+       /*
+        * If there is data in the buffers already, use that first.
+        */
+       if (state->avail > 0) {
+               min = minimum(request, (off_t)state->avail);
+               bytes_skipped = archive_decompressor_none_read_consume(a, min);
+               request -= bytes_skipped;
+               total_bytes_skipped += bytes_skipped;
+       }
+       if (state->client_avail > 0) {
+               min = minimum(request, (off_t)state->client_avail);
+               bytes_skipped = archive_decompressor_none_read_consume(a, min);
+               request -= bytes_skipped;
+               total_bytes_skipped += bytes_skipped;
+       }
+       if (request == 0)
+               return (total_bytes_skipped);
+       /*
+        * If a client_skipper was provided, try that first.
+        */
+#if ARCHIVE_API_VERSION < 2
+       if ((a->client_skipper != NULL) && (request < SSIZE_MAX)) {
+#else
+       if (a->client_skipper != NULL) {
+#endif
+               bytes_skipped = (a->client_skipper)(&a->archive,
+                   a->client_data, request);
+               if (bytes_skipped < 0) {        /* error */
+                       state->client_total = state->client_avail = 0;
+                       state->client_next = state->client_buff = NULL;
+                       state->fatal = 1;
+                       return (bytes_skipped);
+               }
+               total_bytes_skipped += bytes_skipped;
+               a->archive.file_position += bytes_skipped;
+               request -= bytes_skipped;
+               state->client_next = state->client_buff;
+               a->archive.raw_position += bytes_skipped;
+               state->client_avail = state->client_total = 0;
+       }
+       /*
+        * Note that client_skipper will usually not satisfy the
+        * full request (due to low-level blocking concerns),
+        * so even if client_skipper is provided, we may still
+        * have to use ordinary reads to finish out the request.
+        */
+       while (request > 0) {
+               const void* dummy_buffer;
+               ssize_t bytes_read;
+               bytes_read = archive_decompressor_none_read_ahead(a,
+                   &dummy_buffer, request);
+               if (bytes_read < 0)
+                       return (bytes_read);
+               if (bytes_read == 0) {
+                       /* We hit EOF before we satisfied the skip request. */
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                           "Truncated input file (need to skip %jd bytes)",
+                           (intmax_t)request);
+                       return (ARCHIVE_FATAL);
+               }
+               min = (size_t)(minimum(bytes_read, request));
+               bytes_read = archive_decompressor_none_read_consume(a, min);
+               total_bytes_skipped += bytes_read;
+               request -= bytes_read;
+       }
+       return (total_bytes_skipped);
+}
+
+static int
+archive_decompressor_none_finish(struct archive_read *a)
+{
+       struct archive_decompress_none  *state;
+
+       state = (struct archive_decompress_none *)a->compression_data;
+       free(state->buffer);
+       free(state);
+       a->compression_data = NULL;
+       if (a->client_closer != NULL)
+               return ((a->client_closer)(&a->archive, a->client_data));
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_format_all.c b/contrib/libarchive-2.0/libarchive/archive_read_support_format_all.c
new file mode 100644 (file)
index 0000000..281e98c
--- /dev/null
@@ -0,0 +1,40 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_all.c,v 1.8 2007/02/01 06:18:16 kientzle Exp $");
+
+#include "archive.h"
+
+int
+archive_read_support_format_all(struct archive *a)
+{
+       archive_read_support_format_cpio(a);
+       archive_read_support_format_empty(a);
+       archive_read_support_format_iso9660(a);
+       archive_read_support_format_tar(a);
+       archive_read_support_format_zip(a);
+       return (ARCHIVE_OK);
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_format_cpio.c b/contrib/libarchive-2.0/libarchive/archive_read_support_format_cpio.c
new file mode 100644 (file)
index 0000000..2eb1656
--- /dev/null
@@ -0,0 +1,624 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_cpio.c,v 1.22 2007/03/03 07:37:36 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+/* #include <stdint.h> */ /* See archive_platform.h */
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct cpio_bin_header {
+       unsigned char   c_magic[2];
+       unsigned char   c_dev[2];
+       unsigned char   c_ino[2];
+       unsigned char   c_mode[2];
+       unsigned char   c_uid[2];
+       unsigned char   c_gid[2];
+       unsigned char   c_nlink[2];
+       unsigned char   c_rdev[2];
+       unsigned char   c_mtime[4];
+       unsigned char   c_namesize[2];
+       unsigned char   c_filesize[4];
+};
+
+struct cpio_odc_header {
+       char    c_magic[6];
+       char    c_dev[6];
+       char    c_ino[6];
+       char    c_mode[6];
+       char    c_uid[6];
+       char    c_gid[6];
+       char    c_nlink[6];
+       char    c_rdev[6];
+       char    c_mtime[11];
+       char    c_namesize[6];
+       char    c_filesize[11];
+};
+
+struct cpio_newc_header {
+       char    c_magic[6];
+       char    c_ino[8];
+       char    c_mode[8];
+       char    c_uid[8];
+       char    c_gid[8];
+       char    c_nlink[8];
+       char    c_mtime[8];
+       char    c_filesize[8];
+       char    c_devmajor[8];
+       char    c_devminor[8];
+       char    c_rdevmajor[8];
+       char    c_rdevminor[8];
+       char    c_namesize[8];
+       char    c_crc[8];
+};
+
+struct links_entry {
+        struct links_entry      *next;
+        struct links_entry      *previous;
+        int                      links;
+        dev_t                    dev;
+        ino_t                    ino;
+        char                    *name;
+};
+
+#define        CPIO_MAGIC   0x13141516
+struct cpio {
+       int                       magic;
+       int                     (*read_header)(struct archive_read *, struct cpio *,
+                                    struct stat *, size_t *, size_t *);
+       struct links_entry       *links_head;
+       struct archive_string     entry_name;
+       struct archive_string     entry_linkname;
+       off_t                     entry_bytes_remaining;
+       off_t                     entry_offset;
+       off_t                     entry_padding;
+};
+
+static int64_t atol16(const char *, unsigned);
+static int64_t atol8(const char *, unsigned);
+static int     archive_read_format_cpio_bid(struct archive_read *);
+static int     archive_read_format_cpio_cleanup(struct archive_read *);
+static int     archive_read_format_cpio_read_data(struct archive_read *,
+                   const void **, size_t *, off_t *);
+static int     archive_read_format_cpio_read_header(struct archive_read *,
+                   struct archive_entry *);
+static int     be4(const unsigned char *);
+static int     header_bin_be(struct archive_read *, struct cpio *, struct stat *,
+                   size_t *, size_t *);
+static int     header_bin_le(struct archive_read *, struct cpio *, struct stat *,
+                   size_t *, size_t *);
+static int     header_newc(struct archive_read *, struct cpio *, struct stat *,
+                   size_t *, size_t *);
+static int     header_odc(struct archive_read *, struct cpio *, struct stat *,
+                   size_t *, size_t *);
+static int     le4(const unsigned char *);
+static void    record_hardlink(struct cpio *cpio, struct archive_entry *entry,
+                   const struct stat *st);
+
+int
+archive_read_support_format_cpio(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       struct cpio *cpio;
+       int r;
+
+       cpio = (struct cpio *)malloc(sizeof(*cpio));
+       if (cpio == NULL) {
+               archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+               return (ARCHIVE_FATAL);
+       }
+       memset(cpio, 0, sizeof(*cpio));
+       cpio->magic = CPIO_MAGIC;
+
+       r = __archive_read_register_format(a,
+           cpio,
+           archive_read_format_cpio_bid,
+           archive_read_format_cpio_read_header,
+           archive_read_format_cpio_read_data,
+           NULL,
+           archive_read_format_cpio_cleanup);
+
+       if (r != ARCHIVE_OK)
+               free(cpio);
+       return (ARCHIVE_OK);
+}
+
+
+static int
+archive_read_format_cpio_bid(struct archive_read *a)
+{
+       int bid, bytes_read;
+       const void *h;
+       const unsigned char *p;
+       struct cpio *cpio;
+
+       cpio = (struct cpio *)*(a->pformat_data);
+       bid = 0;
+       bytes_read = (a->compression_read_ahead)(a, &h, 6);
+       /* Convert error code into error return. */
+       if (bytes_read < 0)
+               return ((int)bytes_read);
+       if (bytes_read < 6)
+               return (-1);
+
+       p = (const unsigned char *)h;
+       if (memcmp(p, "070707", 6) == 0) {
+               /* ASCII cpio archive (odc, POSIX.1) */
+               cpio->read_header = header_odc;
+               bid += 48;
+               /*
+                * XXX TODO:  More verification; Could check that only octal
+                * digits appear in appropriate header locations. XXX
+                */
+       } else if (memcmp(p, "070701", 6) == 0) {
+               /* ASCII cpio archive (SVR4 without CRC) */
+               cpio->read_header = header_newc;
+               bid += 48;
+               /*
+                * XXX TODO:  More verification; Could check that only hex
+                * digits appear in appropriate header locations. XXX
+                */
+       } else if (memcmp(p, "070702", 6) == 0) {
+               /* ASCII cpio archive (SVR4 with CRC) */
+               /* XXX TODO: Flag that we should check the CRC. XXX */
+               cpio->read_header = header_newc;
+               bid += 48;
+               /*
+                * XXX TODO:  More verification; Could check that only hex
+                * digits appear in appropriate header locations. XXX
+                */
+       } else if (p[0] * 256 + p[1] == 070707) {
+               /* big-endian binary cpio archives */
+               cpio->read_header = header_bin_be;
+               bid += 16;
+               /* Is more verification possible here? */
+       } else if (p[0] + p[1] * 256 == 070707) {
+               /* little-endian binary cpio archives */
+               cpio->read_header = header_bin_le;
+               bid += 16;
+               /* Is more verification possible here? */
+       } else
+               return (ARCHIVE_WARN);
+
+       return (bid);
+}
+
+static int
+archive_read_format_cpio_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+       struct stat st;
+       struct cpio *cpio;
+       size_t bytes;
+       const void *h;
+       size_t namelength;
+       size_t name_pad;
+       int r;
+
+       memset(&st, 0, sizeof(st));
+
+       cpio = (struct cpio *)*(a->pformat_data);
+       r = (cpio->read_header(a, cpio, &st, &namelength, &name_pad));
+
+       if (r != ARCHIVE_OK)
+               return (r);
+
+       /* Assign all of the 'stat' fields at once. */
+       archive_entry_copy_stat(entry, &st);
+
+       /* Read name from buffer. */
+       bytes = (a->compression_read_ahead)(a, &h, namelength + name_pad);
+       if (bytes < namelength + name_pad)
+           return (ARCHIVE_FATAL);
+       (a->compression_read_consume)(a, namelength + name_pad);
+       archive_strncpy(&cpio->entry_name, (const char *)h, namelength);
+       archive_entry_set_pathname(entry, cpio->entry_name.s);
+       cpio->entry_offset = 0;
+
+       /* If this is a symlink, read the link contents. */
+       if (S_ISLNK(st.st_mode)) {
+               bytes = (a->compression_read_ahead)(a, &h,
+                   cpio->entry_bytes_remaining);
+               if ((off_t)bytes < cpio->entry_bytes_remaining)
+                       return (ARCHIVE_FATAL);
+               (a->compression_read_consume)(a, cpio->entry_bytes_remaining);
+               archive_strncpy(&cpio->entry_linkname, (const char *)h,
+                   cpio->entry_bytes_remaining);
+               archive_entry_set_symlink(entry, cpio->entry_linkname.s);
+               cpio->entry_bytes_remaining = 0;
+       }
+
+       /* Compare name to "TRAILER!!!" to test for end-of-archive. */
+       if (namelength == 11 && strcmp((const char *)h, "TRAILER!!!") == 0) {
+           /* TODO: Store file location of start of block. */
+           archive_set_error(&a->archive, 0, NULL);
+           return (ARCHIVE_EOF);
+       }
+
+       /* Detect and record hardlinks to previously-extracted entries. */
+       record_hardlink(cpio, entry, &st);
+
+       return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cpio_read_data(struct archive_read *a,
+    const void **buff, size_t *size, off_t *offset)
+{
+       ssize_t bytes_read;
+       struct cpio *cpio;
+
+       cpio = (struct cpio *)*(a->pformat_data);
+       if (cpio->entry_bytes_remaining > 0) {
+               bytes_read = (a->compression_read_ahead)(a, buff, 1);
+               if (bytes_read <= 0)
+                       return (ARCHIVE_FATAL);
+               if (bytes_read > cpio->entry_bytes_remaining)
+                       bytes_read = cpio->entry_bytes_remaining;
+               *size = bytes_read;
+               *offset = cpio->entry_offset;
+               cpio->entry_offset += bytes_read;
+               cpio->entry_bytes_remaining -= bytes_read;
+               (a->compression_read_consume)(a, bytes_read);
+               return (ARCHIVE_OK);
+       } else {
+               while (cpio->entry_padding > 0) {
+                       bytes_read = (a->compression_read_ahead)(a, buff, 1);
+                       if (bytes_read <= 0)
+                               return (ARCHIVE_FATAL);
+                       if (bytes_read > cpio->entry_padding)
+                               bytes_read = cpio->entry_padding;
+                       (a->compression_read_consume)(a, bytes_read);
+                       cpio->entry_padding -= bytes_read;
+               }
+               *buff = NULL;
+               *size = 0;
+               *offset = cpio->entry_offset;
+               return (ARCHIVE_EOF);
+       }
+}
+
+static int
+header_newc(struct archive_read *a, struct cpio *cpio, struct stat *st,
+    size_t *namelength, size_t *name_pad)
+{
+       const void *h;
+       const struct cpio_newc_header *header;
+       size_t bytes;
+
+       /* Read fixed-size portion of header. */
+       bytes = (a->compression_read_ahead)(a, &h, sizeof(struct cpio_newc_header));
+       if (bytes < sizeof(struct cpio_newc_header))
+           return (ARCHIVE_FATAL);
+       (a->compression_read_consume)(a, sizeof(struct cpio_newc_header));
+
+       /* Parse out hex fields into struct stat. */
+       header = (const struct cpio_newc_header *)h;
+
+       if (memcmp(header->c_magic, "070701", 6) == 0) {
+               a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
+               a->archive.archive_format_name = "ASCII cpio (SVR4 with no CRC)";
+       } else if (memcmp(header->c_magic, "070702", 6) == 0) {
+               a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_CRC;
+               a->archive.archive_format_name = "ASCII cpio (SVR4 with CRC)";
+       } else {
+               /* TODO: Abort here? */
+       }
+
+       st->st_dev = makedev(
+               atol16(header->c_devmajor, sizeof(header->c_devmajor)),
+               atol16(header->c_devminor, sizeof(header->c_devminor)));
+       st->st_ino = atol16(header->c_ino, sizeof(header->c_ino));
+       st->st_mode = atol16(header->c_mode, sizeof(header->c_mode));
+       st->st_uid = atol16(header->c_uid, sizeof(header->c_uid));
+       st->st_gid = atol16(header->c_gid, sizeof(header->c_gid));
+       st->st_nlink = atol16(header->c_nlink, sizeof(header->c_nlink));
+       st->st_rdev = makedev(
+               atol16(header->c_rdevmajor, sizeof(header->c_rdevmajor)),
+               atol16(header->c_rdevminor, sizeof(header->c_rdevminor)));
+       st->st_mtime = atol16(header->c_mtime, sizeof(header->c_mtime));
+       *namelength = atol16(header->c_namesize, sizeof(header->c_namesize));
+       /* Pad name to 2 more than a multiple of 4. */
+       *name_pad = (2 - *namelength) & 3;
+
+       /*
+        * Note: entry_bytes_remaining is at least 64 bits and
+        * therefore guaranteed to be big enough for a 33-bit file
+        * size.  struct stat.st_size may only be 32 bits, so
+        * assigning there first could lose information.
+        */
+       cpio->entry_bytes_remaining =
+           atol16(header->c_filesize, sizeof(header->c_filesize));
+       st->st_size = cpio->entry_bytes_remaining;
+       /* Pad file contents to a multiple of 4. */
+       cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
+       return (ARCHIVE_OK);
+}
+
+static int
+header_odc(struct archive_read *a, struct cpio *cpio, struct stat *st,
+    size_t *namelength, size_t *name_pad)
+{
+       const void *h;
+       const struct cpio_odc_header *header;
+       size_t bytes;
+
+       a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
+       a->archive.archive_format_name = "POSIX octet-oriented cpio";
+
+       /* Read fixed-size portion of header. */
+       bytes = (a->compression_read_ahead)(a, &h, sizeof(struct cpio_odc_header));
+       if (bytes < sizeof(struct cpio_odc_header))
+           return (ARCHIVE_FATAL);
+       (a->compression_read_consume)(a, sizeof(struct cpio_odc_header));
+
+       /* Parse out octal fields into struct stat. */
+       header = (const struct cpio_odc_header *)h;
+
+       st->st_dev = atol8(header->c_dev, sizeof(header->c_dev));
+       st->st_ino = atol8(header->c_ino, sizeof(header->c_ino));
+       st->st_mode = atol8(header->c_mode, sizeof(header->c_mode));
+       st->st_uid = atol8(header->c_uid, sizeof(header->c_uid));
+       st->st_gid = atol8(header->c_gid, sizeof(header->c_gid));
+       st->st_nlink = atol8(header->c_nlink, sizeof(header->c_nlink));
+       st->st_rdev = atol8(header->c_rdev, sizeof(header->c_rdev));
+       st->st_mtime = atol8(header->c_mtime, sizeof(header->c_mtime));
+       *namelength = atol8(header->c_namesize, sizeof(header->c_namesize));
+       *name_pad = 0; /* No padding of filename. */
+
+       /*
+        * Note: entry_bytes_remaining is at least 64 bits and
+        * therefore guaranteed to be big enough for a 33-bit file
+        * size.  struct stat.st_size may only be 32 bits, so
+        * assigning there first could lose information.
+        */
+       cpio->entry_bytes_remaining =
+           atol8(header->c_filesize, sizeof(header->c_filesize));
+       st->st_size = cpio->entry_bytes_remaining;
+       cpio->entry_padding = 0;
+       return (ARCHIVE_OK);
+}
+
+static int
+header_bin_le(struct archive_read *a, struct cpio *cpio, struct stat *st,
+    size_t *namelength, size_t *name_pad)
+{
+       const void *h;
+       const struct cpio_bin_header *header;
+       size_t bytes;
+
+       a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE;
+       a->archive.archive_format_name = "cpio (little-endian binary)";
+
+       /* Read fixed-size portion of header. */
+       bytes = (a->compression_read_ahead)(a, &h, sizeof(struct cpio_bin_header));
+       if (bytes < sizeof(struct cpio_bin_header))
+           return (ARCHIVE_FATAL);
+       (a->compression_read_consume)(a, sizeof(struct cpio_bin_header));
+
+       /* Parse out binary fields into struct stat. */
+       header = (const struct cpio_bin_header *)h;
+
+       st->st_dev = header->c_dev[0] + header->c_dev[1] * 256;
+       st->st_ino = header->c_ino[0] + header->c_ino[1] * 256;
+       st->st_mode = header->c_mode[0] + header->c_mode[1] * 256;
+       st->st_uid = header->c_uid[0] + header->c_uid[1] * 256;
+       st->st_gid = header->c_gid[0] + header->c_gid[1] * 256;
+       st->st_nlink = header->c_nlink[0] + header->c_nlink[1] * 256;
+       st->st_rdev = header->c_rdev[0] + header->c_rdev[1] * 256;
+       st->st_mtime = le4(header->c_mtime);
+       *namelength = header->c_namesize[0] + header->c_namesize[1] * 256;
+       *name_pad = *namelength & 1; /* Pad to even. */
+
+       cpio->entry_bytes_remaining = le4(header->c_filesize);
+       st->st_size = cpio->entry_bytes_remaining;
+       cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
+       return (ARCHIVE_OK);
+}
+
+static int
+header_bin_be(struct archive_read *a, struct cpio *cpio, struct stat *st,
+    size_t *namelength, size_t *name_pad)
+{
+       const void *h;
+       const struct cpio_bin_header *header;
+       size_t bytes;
+
+       a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE;
+       a->archive.archive_format_name = "cpio (big-endian binary)";
+
+       /* Read fixed-size portion of header. */
+       bytes = (a->compression_read_ahead)(a, &h,
+           sizeof(struct cpio_bin_header));
+       if (bytes < sizeof(struct cpio_bin_header))
+           return (ARCHIVE_FATAL);
+       (a->compression_read_consume)(a, sizeof(struct cpio_bin_header));
+
+       /* Parse out binary fields into struct stat. */
+       header = (const struct cpio_bin_header *)h;
+       st->st_dev = header->c_dev[0] * 256 + header->c_dev[1];
+       st->st_ino = header->c_ino[0] * 256 + header->c_ino[1];
+       st->st_mode = header->c_mode[0] * 256 + header->c_mode[1];
+       st->st_uid = header->c_uid[0] * 256 + header->c_uid[1];
+       st->st_gid = header->c_gid[0] * 256 + header->c_gid[1];
+       st->st_nlink = header->c_nlink[0] * 256 + header->c_nlink[1];
+       st->st_rdev = header->c_rdev[0] * 256 + header->c_rdev[1];
+       st->st_mtime = be4(header->c_mtime);
+       *namelength = header->c_namesize[0] * 256 + header->c_namesize[1];
+       *name_pad = *namelength & 1; /* Pad to even. */
+
+       cpio->entry_bytes_remaining = be4(header->c_filesize);
+       st->st_size = cpio->entry_bytes_remaining;
+       cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
+       return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cpio_cleanup(struct archive_read *a)
+{
+       struct cpio *cpio;
+
+       cpio = (struct cpio *)*(a->pformat_data);
+        /* Free inode->name map */
+        while (cpio->links_head != NULL) {
+                struct links_entry *lp = cpio->links_head->next;
+
+                if (cpio->links_head->name)
+                        free(cpio->links_head->name);
+                free(cpio->links_head);
+                cpio->links_head = lp;
+        }
+
+       free(cpio);
+       *(a->pformat_data) = NULL;
+       return (ARCHIVE_OK);
+}
+
+static int
+le4(const unsigned char *p)
+{
+       return ((p[0]<<16) + (p[1]<<24) + (p[2]<<0) + (p[3]<<8));
+}
+
+
+static int
+be4(const unsigned char *p)
+{
+       return (p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24));
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+static int64_t
+atol8(const char *p, unsigned char_cnt)
+{
+       int64_t l;
+       int digit;
+
+       l = 0;
+       while (char_cnt-- > 0) {
+               if (*p >= '0' && *p <= '7')
+                       digit = *p - '0';
+               else
+                       return (l);
+               p++;
+               l <<= 3;
+               l |= digit;
+       }
+       return (l);
+}
+
+static int64_t
+atol16(const char *p, unsigned char_cnt)
+{
+       int64_t l;
+       int digit;
+
+       l = 0;
+       while (char_cnt-- > 0) {
+               if (*p >= 'a' && *p <= 'f')
+                       digit = *p - 'a' + 10;
+               else if (*p >= 'A' && *p <= 'F')
+                       digit = *p - 'A' + 10;
+               else if (*p >= '0' && *p <= '9')
+                       digit = *p - '0';
+               else
+                       return (l);
+               p++;
+               l <<= 4;
+               l |= digit;
+       }
+       return (l);
+}
+
+static void
+record_hardlink(struct cpio *cpio, struct archive_entry *entry,
+    const struct stat *st)
+{
+        struct links_entry      *le;
+
+        /*
+         * First look in the list of multiply-linked files.  If we've
+         * already dumped it, convert this entry to a hard link entry.
+         */
+        for (le = cpio->links_head; le; le = le->next) {
+                if (le->dev == st->st_dev && le->ino == st->st_ino) {
+                        archive_entry_set_hardlink(entry, le->name);
+
+                        if (--le->links <= 0) {
+                                if (le->previous != NULL)
+                                        le->previous->next = le->next;
+                                if (le->next != NULL)
+                                        le->next->previous = le->previous;
+                                if (cpio->links_head == le)
+                                        cpio->links_head = le->next;
+                                free(le);
+                        }
+
+                        return;
+                }
+        }
+
+        le = (struct links_entry *)malloc(sizeof(struct links_entry));
+       if (le == NULL)
+               __archive_errx(1, "Out of memory adding file to list");
+        if (cpio->links_head != NULL)
+                cpio->links_head->previous = le;
+        le->next = cpio->links_head;
+        le->previous = NULL;
+        cpio->links_head = le;
+        le->dev = st->st_dev;
+        le->ino = st->st_ino;
+        le->links = st->st_nlink - 1;
+        le->name = strdup(archive_entry_pathname(entry));
+       if (le->name == NULL)
+               __archive_errx(1, "Out of memory adding file to list");
+}
diff --git a/contrib/libarchive-2.0/libarchive/archive_read_support_format_empty.c b/contrib/libarchive-2.0/libarchive/archive_read_support_format_empty.c
new file mode 100644 (file)
index 0000000..acf1069
--- /dev/null
@@ -0,0 +1,92 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: src/lib/libarchive/archive_read_support_format_empty.c,v 1.2 2007/03/03 07:37:36 kientzle Exp $");
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+static int     archive_read_format_empty_bid(struct archive_read *);
+static int     archive_read_format_empty_read_data(struct archive_read *,
+                   const void **, size_t *, off_t *);
+static int     archive_read_format_empty_read_header(struct archive_read *,
+                   struct archive_entry *);
+int
+archive_read_support_format_empty(struct archive *_a)
+{
+       struct archive_read *a = (struct archive_read *)_a;
+       int r;
+
+       r = __archive_read_register_format(a,
+           NULL,
+           archive_read_format_empty_bid,
+           archive_read_format_empty_read_header,
+           archive_read_format_empty_read_data,
+           NULL,
+           NULL);
+
+       return (r);
+}
+
+
+static int
+archive_read_format_empty_bid(struct archive_read *a)
+{
+       int bytes_read;
+       const void *h;
+
+       bytes_read = (a->compression_read_ahead)(a, &h, 1);
+       if (bytes_read > 0)
+               return (-1);
+       return (1);
+}
+
+static int
+archive_read_format_empty_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+       (void)a; /* UNUSED */
+       (void)entry; /* UNUSED */
+
+       a->archive.archive_format = ARCHIVE_FORMAT_EMPTY;
+       a->archive.archive_format_name = "Empty file";
+
+       return (ARCHIVE_EOF);
+}
+
+static int
+archive_read_format_empty_read_data(struct archive_read *a,
+    const void **buff, size_t *size, off_t *offset)
+{
+       (void)a; /* UNUSED */
+       (void)buff; /*