diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # libarchive
 
+## 2.2.5.1
+
+  * Add `static` flag to cabal which builds from bundled sources rather than
+    `pkg-config`
+
 ## 2.2.5.0
 
   * Speed improvements in places
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@
 make
 ```
 
-so that you have appropriate test data downloaded.
+to download test data.
 
 ## Performance
 
diff --git a/c/archive.h b/c/archive.h
new file mode 100644
--- /dev/null
+++ b/c/archive.h
@@ -0,0 +1,1197 @@
+/*-
+ * Copyright (c) 2003-2010 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.50 2008/05/26 17:00:22 kientzle Exp $
+ */
+
+#ifndef ARCHIVE_H_INCLUDED
+#define	ARCHIVE_H_INCLUDED
+
+/*
+ * The version number is expressed as a single integer that makes it
+ * easy to compare versions at build time: for version a.b.c, the
+ * version number is printf("%d%03d%03d",a,b,c).  For example, if you
+ * know your application requires version 2.12.108 or later, you can
+ * assert that ARCHIVE_VERSION_NUMBER >= 2012108.
+ */
+/* Note: Compiler will complain if this does not match archive_entry.h! */
+#define	ARCHIVE_VERSION_NUMBER 3004003
+
+#include <sys/stat.h>
+#include <stddef.h>  /* for wchar_t */
+#include <stdio.h> /* For FILE * */
+#include <time.h> /* For time_t */
+
+/*
+ * Note: archive.h is for use outside of libarchive; the configuration
+ * headers (config.h, archive_platform.h, etc.) are purely internal.
+ * Do NOT use HAVE_XXX configuration macros to control the behavior of
+ * this header!  If you must conditionalize, use predefined compiler and/or
+ * platform macros.
+ */
+#if defined(__BORLANDC__) && __BORLANDC__ >= 0x560
+# include <stdint.h>
+#elif !defined(__WATCOMC__) && !defined(_MSC_VER) && !defined(__INTERIX) && !defined(__BORLANDC__) && !defined(_SCO_DS) && !defined(__osf__) && !defined(__CLANG_INTTYPES_H)
+# include <inttypes.h>
+#endif
+
+/* Get appropriate definitions of 64-bit integer */
+#if !defined(__LA_INT64_T_DEFINED)
+/* Older code relied on the __LA_INT64_T macro; after 4.0 we'll switch to the typedef exclusively. */
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_INT64_T la_int64_t
+# endif
+#define __LA_INT64_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+typedef __int64 la_int64_t;
+# else
+# include <unistd.h>  /* ssize_t */
+#  if defined(_SCO_DS) || defined(__osf__)
+typedef long long la_int64_t;
+#  else
+typedef int64_t la_int64_t;
+#  endif
+# endif
+#endif
+
+/* The la_ssize_t should match the type used in 'struct stat' */
+#if !defined(__LA_SSIZE_T_DEFINED)
+/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_SSIZE_T la_ssize_t
+# endif
+#define __LA_SSIZE_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+#  if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
+typedef ssize_t la_ssize_t;
+#  elif defined(_WIN64)
+typedef __int64 la_ssize_t;
+#  else
+typedef long la_ssize_t;
+#  endif
+# else
+# include <unistd.h>  /* ssize_t */
+typedef ssize_t la_ssize_t;
+# endif
+#endif
+
+/* Large file support for Android */
+#ifdef __ANDROID__
+#include "android_lf.h"
+#endif
+
+/*
+ * On Windows, define LIBARCHIVE_STATIC if you're building or using a
+ * .lib.  The default here assumes you're building a DLL.  Only
+ * libarchive source should ever define __LIBARCHIVE_BUILD.
+ */
+#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
+# ifdef __LIBARCHIVE_BUILD
+#  ifdef __GNUC__
+#   define __LA_DECL	__attribute__((dllexport)) extern
+#  else
+#   define __LA_DECL	__declspec(dllexport)
+#  endif
+# else
+#  ifdef __GNUC__
+#   define __LA_DECL
+#  else
+#   define __LA_DECL	__declspec(dllimport)
+#  endif
+# endif
+#else
+/* Static libraries or non-Windows needs no special declaration. */
+# define __LA_DECL
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 3 && !defined(__MINGW32__)
+#define	__LA_PRINTF(fmtarg, firstvararg) \
+	__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
+#else
+#define	__LA_PRINTF(fmtarg, firstvararg)	/* nothing */
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1
+# define __LA_DEPRECATED __attribute__((deprecated))
+#else
+# define __LA_DEPRECATED
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The version number is provided as both a macro and a function.
+ * The macro identifies the installed header; the function identifies
+ * the library version (which may not be the same if you're using a
+ * dynamically-linked version of the library).  Of course, if the
+ * header and library are very different, you should expect some
+ * strangeness.  Don't do that.
+ */
+__LA_DECL int		archive_version_number(void);
+
+/*
+ * Textual name/version of the library, useful for version displays.
+ */
+#define	ARCHIVE_VERSION_ONLY_STRING "3.4.3"
+#define	ARCHIVE_VERSION_STRING "libarchive " ARCHIVE_VERSION_ONLY_STRING
+__LA_DECL const char *	archive_version_string(void);
+
+/*
+ * Detailed textual name/version of the library and its dependencies.
+ * This has the form:
+ *    "libarchive x.y.z zlib/a.b.c liblzma/d.e.f ... etc ..."
+ * the list of libraries described here will vary depending on how
+ * libarchive was compiled.
+ */
+__LA_DECL const char *	archive_version_details(void);
+
+/*
+ * Returns NULL if libarchive was compiled without the associated library.
+ * Otherwise, returns the version number that libarchive was compiled
+ * against.
+ */
+__LA_DECL const char *  archive_zlib_version(void);
+__LA_DECL const char *  archive_liblzma_version(void);
+__LA_DECL const char *  archive_bzlib_version(void);
+__LA_DECL const char *  archive_liblz4_version(void);
+__LA_DECL const char *  archive_libzstd_version(void);
+
+/* 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. */
+/* But if write_header is "fatal," then this archive is dead and useless. */
+#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 la_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.
+ * This may skip fewer bytes than requested; it may even skip zero bytes.
+ * If you do skip fewer bytes than requested, libarchive will invoke your
+ * read callback and discard data as necessary to make up the full skip.
+ */
+typedef la_int64_t	archive_skip_callback(struct archive *,
+			    void *_client_data, la_int64_t request);
+
+/* Seeks to specified location in the file and returns the position.
+ * Whence values are SEEK_SET, SEEK_CUR, SEEK_END from stdio.h.
+ * Return ARCHIVE_FATAL if the seek fails for any reason.
+ */
+typedef la_int64_t	archive_seek_callback(struct archive *,
+    void *_client_data, la_int64_t offset, int whence);
+
+/* Returns size actually written, zero on EOF, -1 on error. */
+typedef la_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);
+
+/* Switches from one client data object to the next/prev client data object.
+ * This is useful for reading from different data blocks such as a set of files
+ * that make up one large file.
+ */
+typedef int archive_switch_callback(struct archive *, void *_client_data1,
+			    void *_client_data2);
+
+/*
+ * Returns a passphrase used for encryption or decryption, NULL on nothing
+ * to do and give it up.
+ */
+typedef const char *archive_passphrase_callback(struct archive *,
+			    void *_client_data);
+
+/*
+ * Codes to identify various stream filters.
+ */
+#define	ARCHIVE_FILTER_NONE	0
+#define	ARCHIVE_FILTER_GZIP	1
+#define	ARCHIVE_FILTER_BZIP2	2
+#define	ARCHIVE_FILTER_COMPRESS	3
+#define	ARCHIVE_FILTER_PROGRAM	4
+#define	ARCHIVE_FILTER_LZMA	5
+#define	ARCHIVE_FILTER_XZ	6
+#define	ARCHIVE_FILTER_UU	7
+#define	ARCHIVE_FILTER_RPM	8
+#define	ARCHIVE_FILTER_LZIP	9
+#define	ARCHIVE_FILTER_LRZIP	10
+#define	ARCHIVE_FILTER_LZOP	11
+#define	ARCHIVE_FILTER_GRZIP	12
+#define	ARCHIVE_FILTER_LZ4	13
+#define	ARCHIVE_FILTER_ZSTD	14
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+#define	ARCHIVE_COMPRESSION_NONE	ARCHIVE_FILTER_NONE
+#define	ARCHIVE_COMPRESSION_GZIP	ARCHIVE_FILTER_GZIP
+#define	ARCHIVE_COMPRESSION_BZIP2	ARCHIVE_FILTER_BZIP2
+#define	ARCHIVE_COMPRESSION_COMPRESS	ARCHIVE_FILTER_COMPRESS
+#define	ARCHIVE_COMPRESSION_PROGRAM	ARCHIVE_FILTER_PROGRAM
+#define	ARCHIVE_COMPRESSION_LZMA	ARCHIVE_FILTER_LZMA
+#define	ARCHIVE_COMPRESSION_XZ		ARCHIVE_FILTER_XZ
+#define	ARCHIVE_COMPRESSION_UU		ARCHIVE_FILTER_UU
+#define	ARCHIVE_COMPRESSION_RPM		ARCHIVE_FILTER_RPM
+#define	ARCHIVE_COMPRESSION_LZIP	ARCHIVE_FILTER_LZIP
+#define	ARCHIVE_COMPRESSION_LRZIP	ARCHIVE_FILTER_LRZIP
+#endif
+
+/*
+ * 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 formats.  (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_CPIO_AFIO_LARGE		(ARCHIVE_FORMAT_CPIO | 6)
+#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
+#define	ARCHIVE_FORMAT_AR			0x70000
+#define	ARCHIVE_FORMAT_AR_GNU			(ARCHIVE_FORMAT_AR | 1)
+#define	ARCHIVE_FORMAT_AR_BSD			(ARCHIVE_FORMAT_AR | 2)
+#define	ARCHIVE_FORMAT_MTREE			0x80000
+#define	ARCHIVE_FORMAT_RAW			0x90000
+#define	ARCHIVE_FORMAT_XAR			0xA0000
+#define	ARCHIVE_FORMAT_LHA			0xB0000
+#define	ARCHIVE_FORMAT_CAB			0xC0000
+#define	ARCHIVE_FORMAT_RAR			0xD0000
+#define	ARCHIVE_FORMAT_7ZIP			0xE0000
+#define	ARCHIVE_FORMAT_WARC			0xF0000
+#define	ARCHIVE_FORMAT_RAR_V5			0x100000
+
+/*
+ * Codes returned by archive_read_format_capabilities().
+ *
+ * This list can be extended with values between 0 and 0xffff.
+ * The original purpose of this list was to let different archive
+ * format readers expose their general capabilities in terms of
+ * encryption.
+ */
+#define ARCHIVE_READ_FORMAT_CAPS_NONE (0) /* no special capabilities */
+#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA (1<<0)  /* reader can detect encrypted data */
+#define ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA (1<<1)  /* reader can detect encryptable metadata (pathname, mtime, etc.) */
+
+/*
+ * Codes returned by archive_read_has_encrypted_entries().
+ *
+ * In case the archive does not support encryption detection at all
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned. If the reader
+ * for some other reason (e.g. not enough bytes read) cannot say if
+ * there are encrypted entries, ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW
+ * is returned.
+ */
+#define ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED -2
+#define ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW -1
+
+/*-
+ * 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_free to end processing.
+ */
+__LA_DECL 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.
+ */
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+__LA_DECL int archive_read_support_compression_all(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_bzip2(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_compress(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_gzip(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_lzip(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_lzma(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_none(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_program(struct archive *,
+		     const char *command) __LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_program_signature
+		(struct archive *, const char *,
+		 const void * /* match */, size_t) __LA_DEPRECATED;
+
+__LA_DECL int archive_read_support_compression_rpm(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_uu(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_read_support_compression_xz(struct archive *)
+		__LA_DEPRECATED;
+#endif
+
+__LA_DECL int archive_read_support_filter_all(struct archive *);
+__LA_DECL int archive_read_support_filter_bzip2(struct archive *);
+__LA_DECL int archive_read_support_filter_compress(struct archive *);
+__LA_DECL int archive_read_support_filter_gzip(struct archive *);
+__LA_DECL int archive_read_support_filter_grzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lrzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lz4(struct archive *);
+__LA_DECL int archive_read_support_filter_lzip(struct archive *);
+__LA_DECL int archive_read_support_filter_lzma(struct archive *);
+__LA_DECL int archive_read_support_filter_lzop(struct archive *);
+__LA_DECL int archive_read_support_filter_none(struct archive *);
+__LA_DECL int archive_read_support_filter_program(struct archive *,
+		     const char *command);
+__LA_DECL int archive_read_support_filter_program_signature
+		(struct archive *, const char * /* cmd */,
+				    const void * /* match */, size_t);
+__LA_DECL int archive_read_support_filter_rpm(struct archive *);
+__LA_DECL int archive_read_support_filter_uu(struct archive *);
+__LA_DECL int archive_read_support_filter_xz(struct archive *);
+__LA_DECL int archive_read_support_filter_zstd(struct archive *);
+
+__LA_DECL int archive_read_support_format_7zip(struct archive *);
+__LA_DECL int archive_read_support_format_all(struct archive *);
+__LA_DECL int archive_read_support_format_ar(struct archive *);
+__LA_DECL int archive_read_support_format_by_code(struct archive *, int);
+__LA_DECL int archive_read_support_format_cab(struct archive *);
+__LA_DECL int archive_read_support_format_cpio(struct archive *);
+__LA_DECL int archive_read_support_format_empty(struct archive *);
+__LA_DECL int archive_read_support_format_gnutar(struct archive *);
+__LA_DECL int archive_read_support_format_iso9660(struct archive *);
+__LA_DECL int archive_read_support_format_lha(struct archive *);
+__LA_DECL int archive_read_support_format_mtree(struct archive *);
+__LA_DECL int archive_read_support_format_rar(struct archive *);
+__LA_DECL int archive_read_support_format_rar5(struct archive *);
+__LA_DECL int archive_read_support_format_raw(struct archive *);
+__LA_DECL int archive_read_support_format_tar(struct archive *);
+__LA_DECL int archive_read_support_format_warc(struct archive *);
+__LA_DECL int archive_read_support_format_xar(struct archive *);
+/* archive_read_support_format_zip() enables both streamable and seekable
+ * zip readers. */
+__LA_DECL int archive_read_support_format_zip(struct archive *);
+/* Reads Zip archives as stream from beginning to end.  Doesn't
+ * correctly handle SFX ZIP files or ZIP archives that have been modified
+ * in-place. */
+__LA_DECL int archive_read_support_format_zip_streamable(struct archive *);
+/* Reads starting from central directory; requires seekable input. */
+__LA_DECL int archive_read_support_format_zip_seekable(struct archive *);
+
+/* Functions to manually set the format and filters to be used. This is
+ * useful to bypass the bidding process when the format and filters to use
+ * is known in advance.
+ */
+__LA_DECL int archive_read_set_format(struct archive *, int);
+__LA_DECL int archive_read_append_filter(struct archive *, int);
+__LA_DECL int archive_read_append_filter_program(struct archive *,
+    const char *);
+__LA_DECL int archive_read_append_filter_program_signature
+    (struct archive *, const char *, const void * /* match */, size_t);
+
+/* Set various callbacks. */
+__LA_DECL int archive_read_set_open_callback(struct archive *,
+    archive_open_callback *);
+__LA_DECL int archive_read_set_read_callback(struct archive *,
+    archive_read_callback *);
+__LA_DECL int archive_read_set_seek_callback(struct archive *,
+    archive_seek_callback *);
+__LA_DECL int archive_read_set_skip_callback(struct archive *,
+    archive_skip_callback *);
+__LA_DECL int archive_read_set_close_callback(struct archive *,
+    archive_close_callback *);
+/* Callback used to switch between one data object to the next */
+__LA_DECL int archive_read_set_switch_callback(struct archive *,
+    archive_switch_callback *);
+
+/* This sets the first data object. */
+__LA_DECL int archive_read_set_callback_data(struct archive *, void *);
+/* This sets data object at specified index */
+__LA_DECL int archive_read_set_callback_data2(struct archive *, void *,
+    unsigned int);
+/* This adds a data object at the specified index. */
+__LA_DECL int archive_read_add_callback_data(struct archive *, void *,
+    unsigned int);
+/* This appends a data object to the end of list */
+__LA_DECL int archive_read_append_callback_data(struct archive *, void *);
+/* This prepends a data object to the beginning of list */
+__LA_DECL int archive_read_prepend_callback_data(struct archive *, void *);
+
+/* Opening freezes the callbacks. */
+__LA_DECL int archive_read_open1(struct archive *);
+
+/* Convenience wrappers around the above. */
+__LA_DECL int archive_read_open(struct archive *, void *_client_data,
+		     archive_open_callback *, archive_read_callback *,
+		     archive_close_callback *);
+__LA_DECL 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. */
+__LA_DECL int archive_read_open_filename(struct archive *,
+		     const char *_filename, size_t _block_size);
+/* Use this for reading multivolume files by filenames.
+ * NOTE: Must be NULL terminated. Sorting is NOT done. */
+__LA_DECL int archive_read_open_filenames(struct archive *,
+		     const char **_filenames, size_t _block_size);
+__LA_DECL int archive_read_open_filename_w(struct archive *,
+		     const wchar_t *_filename, size_t _block_size);
+/* archive_read_open_file() is a deprecated synonym for ..._open_filename(). */
+__LA_DECL int archive_read_open_file(struct archive *,
+		     const char *_filename, size_t _block_size) __LA_DEPRECATED;
+/* Read an archive that's stored in memory. */
+__LA_DECL int archive_read_open_memory(struct archive *,
+		     const void * buff, size_t size);
+/* A more involved version that is only used for internal testing. */
+__LA_DECL int archive_read_open_memory2(struct archive *a, const void *buff,
+		     size_t size, size_t read_size);
+/* Read an archive that's already open, using the file descriptor. */
+__LA_DECL 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. */
+__LA_DECL int archive_read_open_FILE(struct archive *, FILE *_file);
+
+/* Parses and returns next entry header. */
+__LA_DECL int archive_read_next_header(struct archive *,
+		     struct archive_entry **);
+
+/* Parses and returns next entry header using the archive_entry passed in */
+__LA_DECL int archive_read_next_header2(struct archive *,
+		     struct archive_entry *);
+
+/*
+ * Retrieve the byte offset in UNCOMPRESSED data where last-read
+ * header started.
+ */
+__LA_DECL la_int64_t		 archive_read_header_position(struct archive *);
+
+/*
+ * Returns 1 if the archive contains at least one encrypted entry.
+ * If the archive format not support encryption at all
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.
+ * If for any other reason (e.g. not enough data read so far)
+ * we cannot say whether there are encrypted entries, then
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.
+ * In general, this function will return values below zero when the
+ * reader is uncertain or totally incapable of encryption support.
+ * When this function returns 0 you can be sure that the reader
+ * supports encryption detection but no encrypted entries have
+ * been found yet.
+ *
+ * NOTE: If the metadata/header of an archive is also encrypted, you
+ * cannot rely on the number of encrypted entries. That is why this
+ * function does not return the number of encrypted entries but#
+ * just shows that there are some.
+ */
+__LA_DECL int	archive_read_has_encrypted_entries(struct archive *);
+
+/*
+ * Returns a bitmask of capabilities that are supported by the archive format reader.
+ * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned.
+ */
+__LA_DECL int		 archive_read_format_capabilities(struct archive *);
+
+/* Read data from the body of an entry.  Similar to read(2). */
+__LA_DECL la_ssize_t		 archive_read_data(struct archive *,
+				    void *, size_t);
+
+/* Seek within the body of an entry.  Similar to lseek(2). */
+__LA_DECL la_int64_t archive_seek_data(struct archive *, la_int64_t, int);
+
+/*
+ * 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.
+ */
+__LA_DECL int archive_read_data_block(struct archive *a,
+		    const void **buff, size_t *size, la_int64_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
+ */
+__LA_DECL int archive_read_data_skip(struct archive *);
+__LA_DECL int archive_read_data_into_fd(struct archive *, int fd);
+
+/*
+ * Set read options.
+ */
+/* Apply option to the format only. */
+__LA_DECL int archive_read_set_format_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option to the filter only. */
+__LA_DECL int archive_read_set_filter_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option to both the format and the filter. */
+__LA_DECL int archive_read_set_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option string to both the format and the filter. */
+__LA_DECL int archive_read_set_options(struct archive *_a,
+			    const char *opts);
+
+/*
+ * Add a decryption passphrase.
+ */
+__LA_DECL int archive_read_add_passphrase(struct archive *, const char *);
+__LA_DECL int archive_read_set_passphrase_callback(struct archive *,
+			    void *client_data, archive_passphrase_callback *);
+
+
+/*-
+ * 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			(0x0001)
+/* Default: Do obey umask, do not restore SUID/SGID/SVTX bits. */
+#define	ARCHIVE_EXTRACT_PERM			(0x0002)
+/* Default: Do not restore mtime/atime. */
+#define	ARCHIVE_EXTRACT_TIME			(0x0004)
+/* Default: Replace existing files. */
+#define	ARCHIVE_EXTRACT_NO_OVERWRITE 		(0x0008)
+/* Default: Try create first, unlink only if create fails with EEXIST. */
+#define	ARCHIVE_EXTRACT_UNLINK			(0x0010)
+/* Default: Do not restore ACLs. */
+#define	ARCHIVE_EXTRACT_ACL			(0x0020)
+/* Default: Do not restore fflags. */
+#define	ARCHIVE_EXTRACT_FFLAGS			(0x0040)
+/* Default: Do not restore xattrs. */
+#define	ARCHIVE_EXTRACT_XATTR 			(0x0080)
+/* 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		(0x0100)
+/* Default: Do not reject entries with '..' as path elements. */
+#define	ARCHIVE_EXTRACT_SECURE_NODOTDOT		(0x0200)
+/* Default: Create parent directories as needed. */
+#define	ARCHIVE_EXTRACT_NO_AUTODIR		(0x0400)
+/* Default: Overwrite files, even if one on disk is newer. */
+#define	ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER	(0x0800)
+/* Detect blocks of 0 and write holes instead. */
+#define	ARCHIVE_EXTRACT_SPARSE			(0x1000)
+/* Default: Do not restore Mac extended metadata. */
+/* This has no effect except on Mac OS. */
+#define	ARCHIVE_EXTRACT_MAC_METADATA		(0x2000)
+/* Default: Use HFS+ compression if it was compressed. */
+/* This has no effect except on Mac OS v10.6 or later. */
+#define	ARCHIVE_EXTRACT_NO_HFS_COMPRESSION	(0x4000)
+/* Default: Do not use HFS+ compression if it was not compressed. */
+/* This has no effect except on Mac OS v10.6 or later. */
+#define	ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED	(0x8000)
+/* Default: Do not reject entries with absolute paths */
+#define ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS (0x10000)
+/* Default: Do not clear no-change flags when unlinking object */
+#define	ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS	(0x20000)
+/* Default: Do not extract atomically (using rename) */
+#define	ARCHIVE_EXTRACT_SAFE_WRITES		(0x40000)
+
+__LA_DECL int archive_read_extract(struct archive *, struct archive_entry *,
+		     int flags);
+__LA_DECL int archive_read_extract2(struct archive *, struct archive_entry *,
+		     struct archive * /* dest */);
+__LA_DECL 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. */
+__LA_DECL void		archive_read_extract_set_skip_file(struct archive *,
+		     la_int64_t, la_int64_t);
+
+/* Close the file and release most resources. */
+__LA_DECL int		 archive_read_close(struct archive *);
+/* Release all resources and destroy the object. */
+/* Note that archive_read_free will call archive_read_close for you. */
+__LA_DECL int		 archive_read_free(struct archive *);
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Synonym for archive_read_free() for backwards compatibility. */
+__LA_DECL int		 archive_read_finish(struct archive *) __LA_DEPRECATED;
+#endif
+
+/*-
+ * To create an archive:
+ *   1) Ask archive_write_new for an 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_free to cleanup the writer and release resources
+ */
+__LA_DECL struct archive	*archive_write_new(void);
+__LA_DECL int archive_write_set_bytes_per_block(struct archive *,
+		     int bytes_per_block);
+__LA_DECL int archive_write_get_bytes_per_block(struct archive *);
+/* XXX This is badly misnamed; suggestions appreciated. XXX */
+__LA_DECL int archive_write_set_bytes_in_last_block(struct archive *,
+		     int bytes_in_last_block);
+__LA_DECL 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. */
+__LA_DECL int archive_write_set_skip_file(struct archive *,
+    la_int64_t, la_int64_t);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+__LA_DECL int archive_write_set_compression_bzip2(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_compress(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_gzip(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_lzip(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_lzma(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_none(struct archive *)
+		__LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_program(struct archive *,
+		     const char *cmd) __LA_DEPRECATED;
+__LA_DECL int archive_write_set_compression_xz(struct archive *)
+		__LA_DEPRECATED;
+#endif
+
+/* A convenience function to set the filter based on the code. */
+__LA_DECL int archive_write_add_filter(struct archive *, int filter_code);
+__LA_DECL int archive_write_add_filter_by_name(struct archive *,
+		     const char *name);
+__LA_DECL int archive_write_add_filter_b64encode(struct archive *);
+__LA_DECL int archive_write_add_filter_bzip2(struct archive *);
+__LA_DECL int archive_write_add_filter_compress(struct archive *);
+__LA_DECL int archive_write_add_filter_grzip(struct archive *);
+__LA_DECL int archive_write_add_filter_gzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lrzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lz4(struct archive *);
+__LA_DECL int archive_write_add_filter_lzip(struct archive *);
+__LA_DECL int archive_write_add_filter_lzma(struct archive *);
+__LA_DECL int archive_write_add_filter_lzop(struct archive *);
+__LA_DECL int archive_write_add_filter_none(struct archive *);
+__LA_DECL int archive_write_add_filter_program(struct archive *,
+		     const char *cmd);
+__LA_DECL int archive_write_add_filter_uuencode(struct archive *);
+__LA_DECL int archive_write_add_filter_xz(struct archive *);
+__LA_DECL int archive_write_add_filter_zstd(struct archive *);
+
+
+/* A convenience function to set the format based on the code or name. */
+__LA_DECL int archive_write_set_format(struct archive *, int format_code);
+__LA_DECL int archive_write_set_format_by_name(struct archive *,
+		     const char *name);
+/* To minimize link pollution, use one or more of the following. */
+__LA_DECL int archive_write_set_format_7zip(struct archive *);
+__LA_DECL int archive_write_set_format_ar_bsd(struct archive *);
+__LA_DECL int archive_write_set_format_ar_svr4(struct archive *);
+__LA_DECL int archive_write_set_format_cpio(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);
+__LA_DECL int archive_write_set_format_gnutar(struct archive *);
+__LA_DECL int archive_write_set_format_iso9660(struct archive *);
+__LA_DECL int archive_write_set_format_mtree(struct archive *);
+__LA_DECL int archive_write_set_format_mtree_classic(struct archive *);
+/* TODO: int archive_write_set_format_old_tar(struct archive *); */
+__LA_DECL int archive_write_set_format_pax(struct archive *);
+__LA_DECL int archive_write_set_format_pax_restricted(struct archive *);
+__LA_DECL int archive_write_set_format_raw(struct archive *);
+__LA_DECL int archive_write_set_format_shar(struct archive *);
+__LA_DECL int archive_write_set_format_shar_dump(struct archive *);
+__LA_DECL int archive_write_set_format_ustar(struct archive *);
+__LA_DECL int archive_write_set_format_v7tar(struct archive *);
+__LA_DECL int archive_write_set_format_warc(struct archive *);
+__LA_DECL int archive_write_set_format_xar(struct archive *);
+__LA_DECL int archive_write_set_format_zip(struct archive *);
+__LA_DECL int archive_write_set_format_filter_by_ext(struct archive *a, const char *filename);
+__LA_DECL int archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext);
+__LA_DECL int archive_write_zip_set_compression_deflate(struct archive *);
+__LA_DECL int archive_write_zip_set_compression_store(struct archive *);
+__LA_DECL int archive_write_open(struct archive *, void *,
+		     archive_open_callback *, archive_write_callback *,
+		     archive_close_callback *);
+__LA_DECL int archive_write_open_fd(struct archive *, int _fd);
+__LA_DECL int archive_write_open_filename(struct archive *, const char *_file);
+__LA_DECL int archive_write_open_filename_w(struct archive *,
+		     const wchar_t *_file);
+/* A deprecated synonym for archive_write_open_filename() */
+__LA_DECL int archive_write_open_file(struct archive *, const char *_file)
+		__LA_DEPRECATED;
+__LA_DECL 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. */
+__LA_DECL 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.
+ */
+__LA_DECL int archive_write_header(struct archive *,
+		     struct archive_entry *);
+__LA_DECL la_ssize_t	archive_write_data(struct archive *,
+			    const void *, size_t);
+
+/* This interface is currently only available for archive_write_disk handles.  */
+__LA_DECL la_ssize_t	 archive_write_data_block(struct archive *,
+				    const void *, size_t, la_int64_t);
+
+__LA_DECL int		 archive_write_finish_entry(struct archive *);
+__LA_DECL int		 archive_write_close(struct archive *);
+/* Marks the archive as FATAL so that a subsequent free() operation
+ * won't try to close() cleanly.  Provides a fast abort capability
+ * when the client discovers that things have gone wrong. */
+__LA_DECL int            archive_write_fail(struct archive *);
+/* This can fail if the archive wasn't already closed, in which case
+ * archive_write_free() will implicitly call archive_write_close(). */
+__LA_DECL int		 archive_write_free(struct archive *);
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Synonym for archive_write_free() for backwards compatibility. */
+__LA_DECL int		 archive_write_finish(struct archive *) __LA_DEPRECATED;
+#endif
+
+/*
+ * Set write options.
+ */
+/* Apply option to the format only. */
+__LA_DECL int archive_write_set_format_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option to the filter only. */
+__LA_DECL int archive_write_set_filter_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option to both the format and the filter. */
+__LA_DECL int archive_write_set_option(struct archive *_a,
+			    const char *m, const char *o,
+			    const char *v);
+/* Apply option string to both the format and the filter. */
+__LA_DECL int archive_write_set_options(struct archive *_a,
+			    const char *opts);
+
+/*
+ * Set a encryption passphrase.
+ */
+__LA_DECL int archive_write_set_passphrase(struct archive *_a, const char *p);
+__LA_DECL int archive_write_set_passphrase_callback(struct archive *,
+			    void *client_data, archive_passphrase_callback *);
+
+/*-
+ * ARCHIVE_WRITE_DISK API
+ *
+ * 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 probably
+ *      want to set the options.
+ *   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_free 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.
+ */
+__LA_DECL struct archive	*archive_write_disk_new(void);
+/* This file will not be overwritten. */
+__LA_DECL int archive_write_disk_set_skip_file(struct archive *,
+    la_int64_t, la_int64_t);
+/* Set flags to control how the next item gets created.
+ * This accepts a bitmask of ARCHIVE_EXTRACT_XXX flags defined above. */
+__LA_DECL 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".
+ */
+__LA_DECL 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.
+ */
+__LA_DECL int archive_write_disk_set_group_lookup(struct archive *,
+    void * /* private_data */,
+    la_int64_t (*)(void *, const char *, la_int64_t),
+    void (* /* cleanup */)(void *));
+__LA_DECL int archive_write_disk_set_user_lookup(struct archive *,
+    void * /* private_data */,
+    la_int64_t (*)(void *, const char *, la_int64_t),
+    void (* /* cleanup */)(void *));
+__LA_DECL la_int64_t archive_write_disk_gid(struct archive *, const char *, la_int64_t);
+__LA_DECL la_int64_t archive_write_disk_uid(struct archive *, const char *, la_int64_t);
+
+/*
+ * ARCHIVE_READ_DISK API
+ *
+ * This is still evolving and somewhat experimental.
+ */
+__LA_DECL struct archive *archive_read_disk_new(void);
+/* The names for symlink modes here correspond to an old BSD
+ * command-line argument convention: -L, -P, -H */
+/* Follow all symlinks. */
+__LA_DECL int archive_read_disk_set_symlink_logical(struct archive *);
+/* Follow no symlinks. */
+__LA_DECL int archive_read_disk_set_symlink_physical(struct archive *);
+/* Follow symlink initially, then not. */
+__LA_DECL int archive_read_disk_set_symlink_hybrid(struct archive *);
+/* TODO: Handle Linux stat32/stat64 ugliness. <sigh> */
+__LA_DECL int archive_read_disk_entry_from_file(struct archive *,
+    struct archive_entry *, int /* fd */, const struct stat *);
+/* Look up gname for gid or uname for uid. */
+/* Default implementations are very, very stupid. */
+__LA_DECL const char *archive_read_disk_gname(struct archive *, la_int64_t);
+__LA_DECL const char *archive_read_disk_uname(struct archive *, la_int64_t);
+/* "Standard" implementation uses getpwuid_r, getgrgid_r and caches the
+ * results for performance. */
+__LA_DECL int	archive_read_disk_set_standard_lookup(struct archive *);
+/* You can install your own lookups if you like. */
+__LA_DECL int	archive_read_disk_set_gname_lookup(struct archive *,
+    void * /* private_data */,
+    const char *(* /* lookup_fn */)(void *, la_int64_t),
+    void (* /* cleanup_fn */)(void *));
+__LA_DECL int	archive_read_disk_set_uname_lookup(struct archive *,
+    void * /* private_data */,
+    const char *(* /* lookup_fn */)(void *, la_int64_t),
+    void (* /* cleanup_fn */)(void *));
+/* Start traversal. */
+__LA_DECL int	archive_read_disk_open(struct archive *, const char *);
+__LA_DECL int	archive_read_disk_open_w(struct archive *, const wchar_t *);
+/*
+ * Request that current entry be visited.  If you invoke it on every
+ * directory, you'll get a physical traversal.  This is ignored if the
+ * current entry isn't a directory or a link to a directory.  So, if
+ * you invoke this on every returned path, you'll get a full logical
+ * traversal.
+ */
+__LA_DECL int	archive_read_disk_descend(struct archive *);
+__LA_DECL int	archive_read_disk_can_descend(struct archive *);
+__LA_DECL int	archive_read_disk_current_filesystem(struct archive *);
+__LA_DECL int	archive_read_disk_current_filesystem_is_synthetic(struct archive *);
+__LA_DECL int	archive_read_disk_current_filesystem_is_remote(struct archive *);
+/* Request that the access time of the entry visited by traversal be restored. */
+__LA_DECL int  archive_read_disk_set_atime_restored(struct archive *);
+/*
+ * Set behavior. The "flags" argument selects optional behavior.
+ */
+/* Request that the access time of the entry visited by traversal be restored.
+ * This is the same as archive_read_disk_set_atime_restored. */
+#define	ARCHIVE_READDISK_RESTORE_ATIME		(0x0001)
+/* Default: Do not skip an entry which has nodump flags. */
+#define	ARCHIVE_READDISK_HONOR_NODUMP		(0x0002)
+/* Default: Skip a mac resource fork file whose prefix is "._" because of
+ * using copyfile. */
+#define	ARCHIVE_READDISK_MAC_COPYFILE		(0x0004)
+/* Default: Traverse mount points. */
+#define	ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS	(0x0008)
+/* Default: Xattrs are read from disk. */
+#define	ARCHIVE_READDISK_NO_XATTR		(0x0010)
+/* Default: ACLs are read from disk. */
+#define	ARCHIVE_READDISK_NO_ACL			(0x0020)
+/* Default: File flags are read from disk. */
+#define	ARCHIVE_READDISK_NO_FFLAGS		(0x0040)
+
+__LA_DECL int  archive_read_disk_set_behavior(struct archive *,
+		    int flags);
+
+/*
+ * Set archive_match object that will be used in archive_read_disk to
+ * know whether an entry should be skipped. The callback function
+ * _excluded_func will be invoked when an entry is skipped by the result
+ * of archive_match.
+ */
+__LA_DECL int	archive_read_disk_set_matching(struct archive *,
+		    struct archive *_matching, void (*_excluded_func)
+		    (struct archive *, void *, struct archive_entry *),
+		    void *_client_data);
+__LA_DECL int	archive_read_disk_set_metadata_filter_callback(struct archive *,
+		    int (*_metadata_filter_func)(struct archive *, void *,
+		    	struct archive_entry *), void *_client_data);
+
+/* Simplified cleanup interface;
+ * This calls archive_read_free() or archive_write_free() as needed. */
+__LA_DECL int	archive_free(struct archive *);
+
+/*
+ * Accessor functions to read/set various information in
+ * the struct archive object:
+ */
+
+/* Number of filters in the current filter pipeline. */
+/* Filter #0 is the one closest to the format, -1 is a synonym for the
+ * last filter, which is always the pseudo-filter that wraps the
+ * client callbacks. */
+__LA_DECL int		 archive_filter_count(struct archive *);
+__LA_DECL la_int64_t	 archive_filter_bytes(struct archive *, int);
+__LA_DECL int		 archive_filter_code(struct archive *, int);
+__LA_DECL const char *	 archive_filter_name(struct archive *, int);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* These don't properly handle multiple filters, so are deprecated and
+ * will eventually be removed. */
+/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, -1); */
+__LA_DECL la_int64_t	 archive_position_compressed(struct archive *)
+				__LA_DEPRECATED;
+/* As of libarchive 3.0, this is an alias for archive_filter_bytes(a, 0); */
+__LA_DECL la_int64_t	 archive_position_uncompressed(struct archive *)
+				__LA_DEPRECATED;
+/* As of libarchive 3.0, this is an alias for archive_filter_name(a, 0); */
+__LA_DECL const char	*archive_compression_name(struct archive *)
+				__LA_DEPRECATED;
+/* As of libarchive 3.0, this is an alias for archive_filter_code(a, 0); */
+__LA_DECL int		 archive_compression(struct archive *)
+				__LA_DEPRECATED;
+#endif
+
+__LA_DECL int		 archive_errno(struct archive *);
+__LA_DECL const char	*archive_error_string(struct archive *);
+__LA_DECL const char	*archive_format_name(struct archive *);
+__LA_DECL int		 archive_format(struct archive *);
+__LA_DECL void		 archive_clear_error(struct archive *);
+__LA_DECL void		 archive_set_error(struct archive *, int _err,
+			    const char *fmt, ...) __LA_PRINTF(3, 4);
+__LA_DECL void		 archive_copy_error(struct archive *dest,
+			    struct archive *src);
+__LA_DECL int		 archive_file_count(struct archive *);
+
+/*
+ * ARCHIVE_MATCH API
+ */
+__LA_DECL struct archive *archive_match_new(void);
+__LA_DECL int	archive_match_free(struct archive *);
+
+/*
+ * Test if archive_entry is excluded.
+ * This is a convenience function. This is the same as calling all
+ * archive_match_path_excluded, archive_match_time_excluded
+ * and archive_match_owner_excluded.
+ */
+__LA_DECL int	archive_match_excluded(struct archive *,
+		    struct archive_entry *);
+
+/*
+ * Test if pathname is excluded. The conditions are set by following functions.
+ */
+__LA_DECL int	archive_match_path_excluded(struct archive *,
+		    struct archive_entry *);
+/* Control recursive inclusion of directory content when directory is included. Default on. */
+__LA_DECL int	archive_match_set_inclusion_recursion(struct archive *, int);
+/* Add exclusion pathname pattern. */
+__LA_DECL int	archive_match_exclude_pattern(struct archive *, const char *);
+__LA_DECL int	archive_match_exclude_pattern_w(struct archive *,
+		    const wchar_t *);
+/* Add exclusion pathname pattern from file. */
+__LA_DECL int	archive_match_exclude_pattern_from_file(struct archive *,
+		    const char *, int _nullSeparator);
+__LA_DECL int	archive_match_exclude_pattern_from_file_w(struct archive *,
+		    const wchar_t *, int _nullSeparator);
+/* Add inclusion pathname pattern. */
+__LA_DECL int	archive_match_include_pattern(struct archive *, const char *);
+__LA_DECL int	archive_match_include_pattern_w(struct archive *,
+		    const wchar_t *);
+/* Add inclusion pathname pattern from file. */
+__LA_DECL int	archive_match_include_pattern_from_file(struct archive *,
+		    const char *, int _nullSeparator);
+__LA_DECL int	archive_match_include_pattern_from_file_w(struct archive *,
+		    const wchar_t *, int _nullSeparator);
+/*
+ * How to get statistic information for inclusion patterns.
+ */
+/* Return the amount number of unmatched inclusion patterns. */
+__LA_DECL int	archive_match_path_unmatched_inclusions(struct archive *);
+/* Return the pattern of unmatched inclusion with ARCHIVE_OK.
+ * Return ARCHIVE_EOF if there is no inclusion pattern. */
+__LA_DECL int	archive_match_path_unmatched_inclusions_next(
+		    struct archive *, const char **);
+__LA_DECL int	archive_match_path_unmatched_inclusions_next_w(
+		    struct archive *, const wchar_t **);
+
+/*
+ * Test if a file is excluded by its time stamp.
+ * The conditions are set by following functions.
+ */
+__LA_DECL int	archive_match_time_excluded(struct archive *,
+		    struct archive_entry *);
+
+/*
+ * Flags to tell a matching type of time stamps. These are used for
+ * following functions.
+ */
+/* Time flag: mtime to be tested. */
+#define ARCHIVE_MATCH_MTIME	(0x0100)
+/* Time flag: ctime to be tested. */
+#define ARCHIVE_MATCH_CTIME	(0x0200)
+/* Comparison flag: Match the time if it is newer than. */
+#define ARCHIVE_MATCH_NEWER	(0x0001)
+/* Comparison flag: Match the time if it is older than. */
+#define ARCHIVE_MATCH_OLDER	(0x0002)
+/* Comparison flag: Match the time if it is equal to. */
+#define ARCHIVE_MATCH_EQUAL	(0x0010)
+/* Set inclusion time. */
+__LA_DECL int	archive_match_include_time(struct archive *, int _flag,
+		    time_t _sec, long _nsec);
+/* Set inclusion time by a date string. */
+__LA_DECL int	archive_match_include_date(struct archive *, int _flag,
+		    const char *_datestr);
+__LA_DECL int	archive_match_include_date_w(struct archive *, int _flag,
+		    const wchar_t *_datestr);
+/* Set inclusion time by a particular file. */
+__LA_DECL int	archive_match_include_file_time(struct archive *,
+		    int _flag, const char *_pathname);
+__LA_DECL int	archive_match_include_file_time_w(struct archive *,
+		    int _flag, const wchar_t *_pathname);
+/* Add exclusion entry. */
+__LA_DECL int	archive_match_exclude_entry(struct archive *,
+		    int _flag, struct archive_entry *);
+
+/*
+ * Test if a file is excluded by its uid ,gid, uname or gname.
+ * The conditions are set by following functions.
+ */
+__LA_DECL int	archive_match_owner_excluded(struct archive *,
+		    struct archive_entry *);
+/* Add inclusion uid, gid, uname and gname. */
+__LA_DECL int	archive_match_include_uid(struct archive *, la_int64_t);
+__LA_DECL int	archive_match_include_gid(struct archive *, la_int64_t);
+__LA_DECL int	archive_match_include_uname(struct archive *, const char *);
+__LA_DECL int	archive_match_include_uname_w(struct archive *,
+		    const wchar_t *);
+__LA_DECL int	archive_match_include_gname(struct archive *, const char *);
+__LA_DECL int	archive_match_include_gname_w(struct archive *,
+		    const wchar_t *);
+
+/* Utility functions */
+/* Convenience function to sort a NULL terminated list of strings */
+__LA_DECL int archive_utility_string_sort(char **);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* These are meaningless outside of this header. */
+#undef __LA_DECL
+
+#endif /* !ARCHIVE_H_INCLUDED */
diff --git a/c/archive_acl.c b/c/archive_acl.c
new file mode 100644
--- /dev/null
+++ b/c/archive_acl.c
@@ -0,0 +1,2097 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive_acl_private.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+#undef max
+#define	max(a, b)	((a)>(b)?(a):(b))
+
+#ifndef HAVE_WMEMCMP
+/* Good enough for simple equality testing, but not for sorting. */
+#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+static int	acl_special(struct archive_acl *acl,
+		    int type, int permset, int tag);
+static struct archive_acl_entry *acl_new_entry(struct archive_acl *acl,
+		    int type, int permset, int tag, int id);
+static int	archive_acl_add_entry_len_l(struct archive_acl *acl,
+		    int type, int permset, int tag, int id, const char *name,
+		    size_t len, struct archive_string_conv *sc);
+static int	archive_acl_text_want_type(struct archive_acl *acl, int flags);
+static ssize_t	archive_acl_text_len(struct archive_acl *acl, int want_type,
+		    int flags, int wide, struct archive *a,
+		    struct archive_string_conv *sc);
+static int	isint_w(const wchar_t *start, const wchar_t *end, int *result);
+static int	ismode_w(const wchar_t *start, const wchar_t *end, int *result);
+static int	is_nfs4_flags_w(const wchar_t *start, const wchar_t *end,
+		    int *result);
+static int	is_nfs4_perms_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 void	append_entry_w(wchar_t **wp, const wchar_t *prefix, int type,
+		    int tag, int flags, const wchar_t *wname, int perm, int id);
+static void	append_id_w(wchar_t **wp, int id);
+static int	isint(const char *start, const char *end, int *result);
+static int	ismode(const char *start, const char *end, int *result);
+static int	is_nfs4_flags(const char *start, const char *end,
+		    int *result);
+static int	is_nfs4_perms(const char *start, const char *end,
+		    int *result);
+static void	next_field(const char **p, const char **start,
+		    const char **end, char *sep);
+static void	append_entry(char **p, const char *prefix, int type,
+		    int tag, int flags, const char *name, int perm, int id);
+static void	append_id(char **p, int id);
+
+static const struct {
+	const int perm;
+	const char c;
+	const wchar_t wc;
+} nfsv4_acl_perm_map[] = {
+	{ ARCHIVE_ENTRY_ACL_READ_DATA | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, 'r',
+	    L'r' },
+	{ ARCHIVE_ENTRY_ACL_WRITE_DATA | ARCHIVE_ENTRY_ACL_ADD_FILE, 'w',
+	    L'w' },
+	{ ARCHIVE_ENTRY_ACL_EXECUTE, 'x', L'x' },
+	{ ARCHIVE_ENTRY_ACL_APPEND_DATA | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY,
+	    'p', L'p' },
+	{ ARCHIVE_ENTRY_ACL_DELETE, 'd', L'd' },
+	{ ARCHIVE_ENTRY_ACL_DELETE_CHILD, 'D', L'D' },
+	{ ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, 'a', L'a' },
+	{ ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, 'A', L'A' },
+	{ ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, 'R', L'R' },
+	{ ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, 'W', L'W' },
+	{ ARCHIVE_ENTRY_ACL_READ_ACL, 'c', L'c' },
+	{ ARCHIVE_ENTRY_ACL_WRITE_ACL, 'C', L'C' },
+	{ ARCHIVE_ENTRY_ACL_WRITE_OWNER, 'o', L'o' },
+	{ ARCHIVE_ENTRY_ACL_SYNCHRONIZE, 's', L's' }
+};
+
+static const int nfsv4_acl_perm_map_size = (int)(sizeof(nfsv4_acl_perm_map) /
+    sizeof(nfsv4_acl_perm_map[0]));
+
+static const struct {
+	const int perm;
+	const char c;
+	const wchar_t wc;
+} nfsv4_acl_flag_map[] = {
+	{ ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, 'f', L'f' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, 'd', L'd' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, 'i', L'i' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, 'n', L'n' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, 'S', L'S' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, 'F', L'F' },
+	{ ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, 'I', L'I' }
+};
+
+static const int nfsv4_acl_flag_map_size = (int)(sizeof(nfsv4_acl_flag_map) /
+    sizeof(nfsv4_acl_flag_map[0]));
+
+void
+archive_acl_clear(struct archive_acl *acl)
+{
+	struct archive_acl_entry *ap;
+
+	while (acl->acl_head != NULL) {
+		ap = acl->acl_head->next;
+		archive_mstring_clean(&acl->acl_head->name);
+		free(acl->acl_head);
+		acl->acl_head = ap;
+	}
+	free(acl->acl_text_w);
+	acl->acl_text_w = NULL;
+	free(acl->acl_text);
+	acl->acl_text = NULL;
+	acl->acl_p = NULL;
+	acl->acl_types = 0;
+	acl->acl_state = 0; /* Not counting. */
+}
+
+void
+archive_acl_copy(struct archive_acl *dest, struct archive_acl *src)
+{
+	struct archive_acl_entry *ap, *ap2;
+
+	archive_acl_clear(dest);
+
+	dest->mode = src->mode;
+	ap = src->acl_head;
+	while (ap != NULL) {
+		ap2 = acl_new_entry(dest,
+		    ap->type, ap->permset, ap->tag, ap->id);
+		if (ap2 != NULL)
+			archive_mstring_copy(&ap2->name, &ap->name);
+		ap = ap->next;
+	}
+}
+
+int
+archive_acl_add_entry(struct archive_acl *acl,
+    int type, int permset, int tag, int id, const char *name)
+{
+	struct archive_acl_entry *ap;
+
+	if (acl_special(acl, type, permset, tag) == 0)
+		return ARCHIVE_OK;
+	ap = acl_new_entry(acl, type, permset, tag, id);
+	if (ap == NULL) {
+		/* XXX Error XXX */
+		return ARCHIVE_FAILED;
+	}
+	if (name != NULL  &&  *name != '\0')
+		archive_mstring_copy_mbs(&ap->name, name);
+	else
+		archive_mstring_clean(&ap->name);
+	return ARCHIVE_OK;
+}
+
+int
+archive_acl_add_entry_w_len(struct archive_acl *acl,
+    int type, int permset, int tag, int id, const wchar_t *name, size_t len)
+{
+	struct archive_acl_entry *ap;
+
+	if (acl_special(acl, type, permset, tag) == 0)
+		return ARCHIVE_OK;
+	ap = acl_new_entry(acl, type, permset, tag, id);
+	if (ap == NULL) {
+		/* XXX Error XXX */
+		return ARCHIVE_FAILED;
+	}
+	if (name != NULL  &&  *name != L'\0' && len > 0)
+		archive_mstring_copy_wcs_len(&ap->name, name, len);
+	else
+		archive_mstring_clean(&ap->name);
+	return ARCHIVE_OK;
+}
+
+static int
+archive_acl_add_entry_len_l(struct archive_acl *acl,
+    int type, int permset, int tag, int id, const char *name, size_t len,
+    struct archive_string_conv *sc)
+{
+	struct archive_acl_entry *ap;
+	int r;
+
+	if (acl_special(acl, type, permset, tag) == 0)
+		return ARCHIVE_OK;
+	ap = acl_new_entry(acl, type, permset, tag, id);
+	if (ap == NULL) {
+		/* XXX Error XXX */
+		return ARCHIVE_FAILED;
+	}
+	if (name != NULL  &&  *name != '\0' && len > 0) {
+		r = archive_mstring_copy_mbs_len_l(&ap->name, name, len, sc);
+	} else {
+		r = 0;
+		archive_mstring_clean(&ap->name);
+	}
+	if (r == 0)
+		return (ARCHIVE_OK);
+	else if (errno == ENOMEM)
+		return (ARCHIVE_FATAL);
+	else
+		return (ARCHIVE_WARN);
+}
+
+/*
+ * 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_acl *acl, int type, int permset, int tag)
+{
+	if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS
+	    && ((permset & ~007) == 0)) {
+		switch (tag) {
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			acl->mode &= ~0700;
+			acl->mode |= (permset & 7) << 6;
+			return (0);
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			acl->mode &= ~0070;
+			acl->mode |= (permset & 7) << 3;
+			return (0);
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			acl->mode &= ~0007;
+			acl->mode |= permset & 7;
+			return (0);
+		}
+	}
+	return (1);
+}
+
+/*
+ * Allocate and populate a new ACL entry with everything but the
+ * name.
+ */
+static struct archive_acl_entry *
+acl_new_entry(struct archive_acl *acl,
+    int type, int permset, int tag, int id)
+{
+	struct archive_acl_entry *ap, *aq;
+
+	/* Type argument must be a valid NFS4 or POSIX.1e type.
+	 * The type must agree with anything already set and
+	 * the permset must be compatible. */
+	if (type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			return (NULL);
+		}
+		if (permset &
+		    ~(ARCHIVE_ENTRY_ACL_PERMS_NFS4
+			| ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4)) {
+			return (NULL);
+		}
+	} else	if (type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
+		if (acl->acl_types & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
+			return (NULL);
+		}
+		if (permset & ~ARCHIVE_ENTRY_ACL_PERMS_POSIX1E) {
+			return (NULL);
+		}
+	} else {
+		return (NULL);
+	}
+
+	/* Verify the tag is valid and compatible with NFS4 or POSIX.1e. */
+	switch (tag) {
+	case ARCHIVE_ENTRY_ACL_USER:
+	case ARCHIVE_ENTRY_ACL_USER_OBJ:
+	case ARCHIVE_ENTRY_ACL_GROUP:
+	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+		/* Tags valid in both NFS4 and POSIX.1e */
+		break;
+	case ARCHIVE_ENTRY_ACL_MASK:
+	case ARCHIVE_ENTRY_ACL_OTHER:
+		/* Tags valid only in POSIX.1e. */
+		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) {
+			return (NULL);
+		}
+		break;
+	case ARCHIVE_ENTRY_ACL_EVERYONE:
+		/* Tags valid only in NFS4. */
+		if (type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			return (NULL);
+		}
+		break;
+	default:
+		/* No other values are valid. */
+		return (NULL);
+	}
+
+	free(acl->acl_text_w);
+	acl->acl_text_w = NULL;
+	free(acl->acl_text);
+	acl->acl_text = NULL;
+
+	/*
+	 * If there's a matching entry already in the list, overwrite it.
+	 * NFSv4 entries may be repeated and are not overwritten.
+	 *
+	 * TODO: compare names of no id is provided (needs more rework)
+	 */
+	ap = acl->acl_head;
+	aq = NULL;
+	while (ap != NULL) {
+		if (((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0) &&
+		    ap->type == type && ap->tag == tag && ap->id == id) {
+			if (id != -1 || (tag != ARCHIVE_ENTRY_ACL_USER &&
+			    tag != ARCHIVE_ENTRY_ACL_GROUP)) {
+				ap->permset = permset;
+				return (ap);
+			}
+		}
+		aq = ap;
+		ap = ap->next;
+	}
+
+	/* Add a new entry to the end of the list. */
+	ap = (struct archive_acl_entry *)calloc(1, sizeof(*ap));
+	if (ap == NULL)
+		return (NULL);
+	if (aq == NULL)
+		acl->acl_head = ap;
+	else
+		aq->next = ap;
+	ap->type = type;
+	ap->tag = tag;
+	ap->id = id;
+	ap->permset = permset;
+	acl->acl_types |= type;
+	return (ap);
+}
+
+/*
+ * Return a count of entries matching "want_type".
+ */
+int
+archive_acl_count(struct archive_acl *acl, int want_type)
+{
+	int count;
+	struct archive_acl_entry *ap;
+
+	count = 0;
+	ap = acl->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);
+}
+
+/*
+ * Return a bitmask of stored ACL types in an ACL list
+ */
+int
+archive_acl_types(struct archive_acl *acl)
+{
+	return (acl->acl_types);
+}
+
+/*
+ * 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_acl_reset(struct archive_acl *acl, int want_type)
+{
+	int count, cutoff;
+
+	count = archive_acl_count(acl, 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)
+		acl->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
+	else
+		acl->acl_state = 0;
+	acl->acl_p = acl->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_acl_next(struct archive *a, struct archive_acl *acl, 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.aest_mode).
+	 */
+	if (acl->acl_state == 0)
+		return (ARCHIVE_WARN);
+
+	/* The first three access entries are special. */
+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+		switch (acl->acl_state) {
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			*permset = (acl->mode >> 6) & 7;
+			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+			*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+			acl->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+			return (ARCHIVE_OK);
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			*permset = (acl->mode >> 3) & 7;
+			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+			*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+			acl->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
+			return (ARCHIVE_OK);
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			*permset = acl->mode & 7;
+			*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+			*tag = ARCHIVE_ENTRY_ACL_OTHER;
+			acl->acl_state = -1;
+			acl->acl_p = acl->acl_head;
+			return (ARCHIVE_OK);
+		default:
+			break;
+		}
+	}
+
+	while (acl->acl_p != NULL && (acl->acl_p->type & want_type) == 0)
+		acl->acl_p = acl->acl_p->next;
+	if (acl->acl_p == NULL) {
+		acl->acl_state = 0;
+		*type = 0;
+		*permset = 0;
+		*tag = 0;
+		*id = -1;
+		*name = NULL;
+		return (ARCHIVE_EOF); /* End of ACL entries. */
+	}
+	*type = acl->acl_p->type;
+	*permset = acl->acl_p->permset;
+	*tag = acl->acl_p->tag;
+	*id = acl->acl_p->id;
+	if (archive_mstring_get_mbs(a, &acl->acl_p->name, name) != 0) {
+		if (errno == ENOMEM)
+			return (ARCHIVE_FATAL);
+		*name = NULL;
+	}
+	acl->acl_p = acl->acl_p->next;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Determine what type of ACL do we want
+ */
+static int
+archive_acl_text_want_type(struct archive_acl *acl, int flags)
+{
+	int want_type;
+
+	/* Check if ACL is NFSv4 */
+	if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		/* NFSv4 should never mix with POSIX.1e */
+		if ((acl->acl_types & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
+			return (0);
+		else
+			return (ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+	}
+
+	/* Now deal with POSIX.1e ACLs */
+
+	want_type = 0;
+	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+		want_type |= ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+	if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+		want_type |= ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+
+	/* By default we want both access and default ACLs */
+	if (want_type == 0)
+		return (ARCHIVE_ENTRY_ACL_TYPE_POSIX1E);
+
+	return (want_type);
+}
+
+/*
+ * Calculate ACL text string length
+ */
+static ssize_t
+archive_acl_text_len(struct archive_acl *acl, int want_type, int flags,
+    int wide, struct archive *a, struct archive_string_conv *sc) {
+	struct archive_acl_entry *ap;
+	const char *name;
+	const wchar_t *wname;
+	int count, idlen, tmp, r;
+	ssize_t length;
+	size_t len;
+
+	count = 0;
+	length = 0;
+	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+		if ((ap->type & want_type) == 0)
+			continue;
+		/*
+		 * Filemode-mapping ACL entries are stored exclusively in
+		 * ap->mode so they should not be in the list
+		 */
+		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+			continue;
+		count++;
+		if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0
+		    && (ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+			length += 8; /* "default:" */
+		switch (ap->tag) {
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+				length += 6; /* "owner@" */
+				break;
+			}
+			/* FALLTHROUGH */
+		case ARCHIVE_ENTRY_ACL_USER:
+		case ARCHIVE_ENTRY_ACL_MASK:
+			length += 4; /* "user", "mask" */
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+				length += 6; /* "group@" */
+				break;
+			}
+			/* FALLTHROUGH */
+		case ARCHIVE_ENTRY_ACL_GROUP:
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			length += 5; /* "group", "other" */
+			break;
+		case ARCHIVE_ENTRY_ACL_EVERYONE:
+			length += 9; /* "everyone@" */
+			break;
+		}
+		length += 1; /* colon after tag */
+		if (ap->tag == ARCHIVE_ENTRY_ACL_USER ||
+		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) {
+			if (wide) {
+				r = archive_mstring_get_wcs(a, &ap->name,
+				    &wname);
+				if (r == 0 && wname != NULL)
+					length += wcslen(wname);
+				else if (r < 0 && errno == ENOMEM)
+					return (0);
+				else
+					length += sizeof(uid_t) * 3 + 1;
+			} else {
+				r = archive_mstring_get_mbs_l(&ap->name, &name,
+				    &len, sc);
+				if (r != 0)
+					return (0);
+				if (len > 0 && name != NULL)
+					length += len;
+				else
+					length += sizeof(uid_t) * 3 + 1;
+			}
+			length += 1; /* colon after user or group name */
+		} else if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4)
+			length += 1; /* 2nd colon empty user,group or other */
+
+		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0)
+		    && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0)
+		    && (ap->tag == ARCHIVE_ENTRY_ACL_OTHER
+		    || ap->tag == ARCHIVE_ENTRY_ACL_MASK)) {
+			/* Solaris has no colon after other: and mask: */
+			length = length - 1;
+		}
+
+		if (want_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			/* rwxpdDaARWcCos:fdinSFI:deny */
+			length += 27;
+			if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DENY) == 0)
+				length += 1; /* allow, alarm, audit */
+		} else
+			length += 3; /* rwx */
+
+		if ((ap->tag == ARCHIVE_ENTRY_ACL_USER ||
+		    ap->tag == ARCHIVE_ENTRY_ACL_GROUP) &&
+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0) {
+			length += 1; /* colon */
+			/* ID digit count */
+			idlen = 1;
+			tmp = ap->id;
+			while (tmp > 9) {
+				tmp = tmp / 10;
+				idlen++;
+			}
+			length += idlen;
+		}
+		length ++; /* entry separator */
+	}
+
+	/* Add filemode-mapping access entries to the length */
+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+		if ((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) != 0) {
+			/* "user::rwx\ngroup::rwx\nother:rwx\n" */
+			length += 31;
+		} else {
+			/* "user::rwx\ngroup::rwx\nother::rwx\n" */
+			length += 32;
+		}
+	} else if (count == 0)
+		return (0);
+
+	/* The terminating character is included in count */
+	return (length);
+}
+
+/*
+ * Generate a wide text version of the ACL. The flags parameter controls
+ * the type and style of the generated ACL.
+ */
+wchar_t *
+archive_acl_to_text_w(struct archive_acl *acl, ssize_t *text_len, int flags,
+    struct archive *a)
+{
+	int count;
+	ssize_t length;
+	size_t len;
+	const wchar_t *wname;
+	const wchar_t *prefix;
+	wchar_t separator;
+	struct archive_acl_entry *ap;
+	int id, r, want_type;
+	wchar_t *wp, *ws;
+
+	want_type = archive_acl_text_want_type(acl, flags);
+
+	/* Both NFSv4 and POSIX.1 types found */
+	if (want_type == 0)
+		return (NULL);
+
+	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
+		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+	length = archive_acl_text_len(acl, want_type, flags, 1, a, NULL);
+
+	if (length == 0)
+		return (NULL);
+
+	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
+		separator = L',';
+	else
+		separator = L'\n';
+
+	/* Now, allocate the string and actually populate it. */
+	wp = ws = (wchar_t *)malloc(length * sizeof(wchar_t));
+	if (wp == NULL) {
+		if (errno == ENOMEM)
+			__archive_errx(1, "No memory");
+		return (NULL);
+	}
+	count = 0;
+
+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
+		    acl->mode & 0700, -1);
+		*wp++ = separator;
+		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
+		    acl->mode & 0070, -1);
+		*wp++ = separator;
+		append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
+		    acl->mode & 0007, -1);
+		count += 3;
+	}
+
+	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+		if ((ap->type & want_type) == 0)
+			continue;
+		/*
+		 * Filemode-mapping ACL entries are stored exclusively in
+		 * ap->mode so they should not be in the list
+		 */
+		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+			continue;
+		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+			prefix = L"default:";
+		else
+			prefix = NULL;
+		r = archive_mstring_get_wcs(a, &ap->name, &wname);
+		if (r == 0) {
+			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->type, ap->tag, flags,
+			    wname, ap->permset, id);
+			count++;
+		} else if (r < 0 && errno == ENOMEM) {
+			free(ws);
+			return (NULL);
+		}
+	}
+
+	/* Add terminating character */
+	*wp++ = L'\0';
+
+	len = wcslen(ws);
+
+	if ((ssize_t)len > (length - 1))
+		__archive_errx(1, "Buffer overrun");
+
+	if (text_len != NULL)
+		*text_len = len;
+
+	return (ws);
+}
+
+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 type,
+    int tag, int flags, const wchar_t *wname, int perm, int id)
+{
+	int i;
+
+	if (prefix != NULL) {
+		wcscpy(*wp, prefix);
+		*wp += wcslen(*wp);
+	}
+	switch (tag) {
+	case ARCHIVE_ENTRY_ACL_USER_OBJ:
+		wname = NULL;
+		id = -1;
+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+			wcscpy(*wp, L"owner@");
+			break;
+		}
+		/* FALLTHROUGH */
+	case ARCHIVE_ENTRY_ACL_USER:
+		wcscpy(*wp, L"user");
+		break;
+	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+		wname = NULL;
+		id = -1;
+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+			wcscpy(*wp, L"group@");
+			break;
+		}
+		/* FALLTHROUGH */
+	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;
+	case ARCHIVE_ENTRY_ACL_EVERYONE:
+		wcscpy(*wp, L"everyone@");
+		wname = NULL;
+		id = -1;
+		break;
+	}
+	*wp += wcslen(*wp);
+	*(*wp)++ = L':';
+	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
+	    tag == ARCHIVE_ENTRY_ACL_USER ||
+	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
+		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);
+			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
+				id = -1;
+		}
+		/* Solaris style has no second colon after other and mask */
+		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
+		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
+		    && tag != ARCHIVE_ENTRY_ACL_MASK))
+			*(*wp)++ = L':';
+	}
+	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+		/* POSIX.1e ACL perms */
+		*(*wp)++ = (perm & 0444) ? L'r' : L'-';
+		*(*wp)++ = (perm & 0222) ? L'w' : L'-';
+		*(*wp)++ = (perm & 0111) ? L'x' : L'-';
+	} else {
+		/* NFSv4 ACL perms */
+		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
+			if (perm & nfsv4_acl_perm_map[i].perm)
+				*(*wp)++ = nfsv4_acl_perm_map[i].wc;
+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+				*(*wp)++ = L'-';
+		}
+		*(*wp)++ = L':';
+		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
+			if (perm & nfsv4_acl_flag_map[i].perm)
+				*(*wp)++ = nfsv4_acl_flag_map[i].wc;
+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+				*(*wp)++ = L'-';
+		}
+		*(*wp)++ = L':';
+		switch (type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			wcscpy(*wp, L"allow");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			wcscpy(*wp, L"deny");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			wcscpy(*wp, L"audit");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+			wcscpy(*wp, L"alarm");
+			break;
+		default:
+			break;
+		}
+		*wp += wcslen(*wp);
+	}
+	if (id != -1) {
+		*(*wp)++ = L':';
+		append_id_w(wp, id);
+	}
+}
+
+/*
+ * Generate a text version of the ACL. The flags parameter controls
+ * the type and style of the generated ACL.
+ */
+char *
+archive_acl_to_text_l(struct archive_acl *acl, ssize_t *text_len, int flags,
+    struct archive_string_conv *sc)
+{
+	int count;
+	ssize_t length;
+	size_t len;
+	const char *name;
+	const char *prefix;
+	char separator;
+	struct archive_acl_entry *ap;
+	int id, r, want_type;
+	char *p, *s;
+
+	want_type = archive_acl_text_want_type(acl, flags);
+
+	/* Both NFSv4 and POSIX.1 types found */
+	if (want_type == 0)
+		return (NULL);
+
+	if (want_type == ARCHIVE_ENTRY_ACL_TYPE_POSIX1E)
+		flags |= ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+	length = archive_acl_text_len(acl, want_type, flags, 0, NULL, sc);
+
+	if (length == 0)
+		return (NULL);
+
+	if (flags & ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA)
+		separator = ',';
+	else
+		separator = '\n';
+
+	/* Now, allocate the string and actually populate it. */
+	p = s = (char *)malloc(length * sizeof(char));
+	if (p == NULL) {
+		if (errno == ENOMEM)
+			__archive_errx(1, "No memory");
+		return (NULL);
+	}
+	count = 0;
+
+	if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_USER_OBJ, flags, NULL,
+		    acl->mode & 0700, -1);
+		*p++ = separator;
+		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_GROUP_OBJ, flags, NULL,
+		    acl->mode & 0070, -1);
+		*p++ = separator;
+		append_entry(&p, NULL, ARCHIVE_ENTRY_ACL_TYPE_ACCESS,
+		    ARCHIVE_ENTRY_ACL_OTHER, flags, NULL,
+		    acl->mode & 0007, -1);
+		count += 3;
+	}
+
+	for (ap = acl->acl_head; ap != NULL; ap = ap->next) {
+		if ((ap->type & want_type) == 0)
+			continue;
+		/*
+		 * Filemode-mapping ACL entries are stored exclusively in
+		 * ap->mode so they should not be in the list
+		 */
+		if ((ap->type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS)
+		    && (ap->tag == ARCHIVE_ENTRY_ACL_USER_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ
+		    || ap->tag == ARCHIVE_ENTRY_ACL_OTHER))
+			continue;
+		if (ap->type == ARCHIVE_ENTRY_ACL_TYPE_DEFAULT &&
+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+			prefix = "default:";
+		else
+			prefix = NULL;
+		r = archive_mstring_get_mbs_l(
+		    &ap->name, &name, &len, sc);
+		if (r != 0) {
+			free(s);
+			return (NULL);
+		}
+		if (count > 0)
+			*p++ = separator;
+		if (name == NULL ||
+		    (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)) {
+			id = ap->id;
+		} else {
+			id = -1;
+		}
+		append_entry(&p, prefix, ap->type, ap->tag, flags, name,
+		    ap->permset, id);
+		count++;
+	}
+
+	/* Add terminating character */
+	*p++ = '\0';
+
+	len = strlen(s);
+
+	if ((ssize_t)len > (length - 1))
+		__archive_errx(1, "Buffer overrun");
+
+	if (text_len != NULL)
+		*text_len = len;
+
+	return (s);
+}
+
+static void
+append_id(char **p, int id)
+{
+	if (id < 0)
+		id = 0;
+	if (id > 9)
+		append_id(p, id / 10);
+	*(*p)++ = "0123456789"[id % 10];
+}
+
+static void
+append_entry(char **p, const char *prefix, int type,
+    int tag, int flags, const char *name, int perm, int id)
+{
+	int i;
+
+	if (prefix != NULL) {
+		strcpy(*p, prefix);
+		*p += strlen(*p);
+	}
+	switch (tag) {
+	case ARCHIVE_ENTRY_ACL_USER_OBJ:
+		name = NULL;
+		id = -1;
+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+			strcpy(*p, "owner@");
+			break;
+		}
+		/* FALLTHROUGH */
+	case ARCHIVE_ENTRY_ACL_USER:
+		strcpy(*p, "user");
+		break;
+	case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+		name = NULL;
+		id = -1;
+		if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+			strcpy(*p, "group@");
+			break;
+		}
+		/* FALLTHROUGH */
+	case ARCHIVE_ENTRY_ACL_GROUP:
+		strcpy(*p, "group");
+		break;
+	case ARCHIVE_ENTRY_ACL_MASK:
+		strcpy(*p, "mask");
+		name = NULL;
+		id = -1;
+		break;
+	case ARCHIVE_ENTRY_ACL_OTHER:
+		strcpy(*p, "other");
+		name = NULL;
+		id = -1;
+		break;
+	case ARCHIVE_ENTRY_ACL_EVERYONE:
+		strcpy(*p, "everyone@");
+		name = NULL;
+		id = -1;
+		break;
+	}
+	*p += strlen(*p);
+	*(*p)++ = ':';
+	if (((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) ||
+	    tag == ARCHIVE_ENTRY_ACL_USER ||
+	    tag == ARCHIVE_ENTRY_ACL_GROUP) {
+		if (name != NULL) {
+			strcpy(*p, name);
+			*p += strlen(*p);
+		} else if (tag == ARCHIVE_ENTRY_ACL_USER
+		    || tag == ARCHIVE_ENTRY_ACL_GROUP) {
+			append_id(p, id);
+			if ((type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) == 0)
+				id = -1;
+		}
+		/* Solaris style has no second colon after other and mask */
+		if (((flags & ARCHIVE_ENTRY_ACL_STYLE_SOLARIS) == 0)
+		    || (tag != ARCHIVE_ENTRY_ACL_OTHER
+		    && tag != ARCHIVE_ENTRY_ACL_MASK))
+			*(*p)++ = ':';
+	}
+	if ((type & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+		/* POSIX.1e ACL perms */
+		*(*p)++ = (perm & 0444) ? 'r' : '-';
+		*(*p)++ = (perm & 0222) ? 'w' : '-';
+		*(*p)++ = (perm & 0111) ? 'x' : '-';
+	} else {
+		/* NFSv4 ACL perms */
+		for (i = 0; i < nfsv4_acl_perm_map_size; i++) {
+			if (perm & nfsv4_acl_perm_map[i].perm)
+				*(*p)++ = nfsv4_acl_perm_map[i].c;
+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+				*(*p)++ = '-';
+		}
+		*(*p)++ = ':';
+		for (i = 0; i < nfsv4_acl_flag_map_size; i++) {
+			if (perm & nfsv4_acl_flag_map[i].perm)
+				*(*p)++ = nfsv4_acl_flag_map[i].c;
+			else if ((flags & ARCHIVE_ENTRY_ACL_STYLE_COMPACT) == 0)
+				*(*p)++ = '-';
+		}
+		*(*p)++ = ':';
+		switch (type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			strcpy(*p, "allow");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			strcpy(*p, "deny");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			strcpy(*p, "audit");
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+			strcpy(*p, "alarm");
+			break;
+		}
+		*p += strlen(*p);
+	}
+	if (id != -1) {
+		*(*p)++ = ':';
+		append_id(p, id);
+	}
+}
+
+/*
+ * Parse a wide ACL text string.
+ *
+ * The want_type argument may be one of the following:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
+ *
+ * POSIX.1e ACL entries prefixed with "default:" are treated as
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
+ */
+int
+archive_acl_from_text_w(struct archive_acl *acl, const wchar_t *text,
+    int want_type)
+{
+	struct {
+		const wchar_t *start;
+		const wchar_t *end;
+	} field[6], name;
+
+	const wchar_t *s, *st;
+
+	int numfields, fields, n, r, sol, ret;
+	int type, types, tag, permset, id;
+	size_t len;
+	wchar_t sep;
+
+	ret = ARCHIVE_OK;
+	types = 0;
+
+	switch (want_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+		__LA_FALLTHROUGH;
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+		numfields = 5;
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		numfields = 6;
+		break;
+	default:
+		return (ARCHIVE_FATAL);
+	}
+
+	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 < numfields) {
+				field[fields].start = start;
+				field[fields].end = end;
+			}
+			++fields;
+		} while (sep == L':');
+
+		/* Set remaining fields to blank. */
+		for (n = fields; n < numfields; ++n)
+			field[n].start = field[n].end = NULL;
+
+		if (field[0].start != NULL && *(field[0].start) == L'#') {
+			/* Comment, skip entry */
+			continue;
+		}
+
+		n = 0;
+		sol = 0;
+		id = -1;
+		permset = 0;
+		name.start = name.end = NULL;
+
+		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			/* POSIX.1e ACLs */
+			/*
+			 * Default keyword "default:user::rwx"
+			 * if found, we have one more field
+			 *
+			 * We also support old Solaris extension:
+			 * "defaultuser::rwx" is the default ACL corresponding
+			 * to "user::rwx", etc. valid only for first field
+			 */
+			s = field[0].start;
+			len = field[0].end - field[0].start;
+			if (*s == L'd' && (len == 1 || (len >= 7
+			    && wmemcmp((s + 1), L"efault", 6) == 0))) {
+				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+				if (len > 7)
+					field[0].start += 7;
+				else
+					n = 1;
+			} else
+				type = want_type;
+
+			/* Check for a numeric ID in field n+1 or n+3. */
+			isint_w(field[n + 1].start, field[n + 1].end, &id);
+			/* Field n+3 is optional. */
+			if (id == -1 && fields > n+3)
+				isint_w(field[n + 3].start, field[n + 3].end,
+				    &id);
+
+			tag = 0;
+			s = field[n].start;
+			st = field[n].start + 1;
+			len = field[n].end - field[n].start;
+
+			switch (*s) {
+			case L'u':
+				if (len == 1 || (len == 4
+				    && wmemcmp(st, L"ser", 3) == 0))
+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				break;
+			case L'g':
+				if (len == 1 || (len == 5
+				    && wmemcmp(st, L"roup", 4) == 0))
+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case L'o':
+				if (len == 1 || (len == 5
+				    && wmemcmp(st, L"ther", 4) == 0))
+					tag = ARCHIVE_ENTRY_ACL_OTHER;
+				break;
+			case L'm':
+				if (len == 1 || (len == 4
+				    && wmemcmp(st, L"ask", 3) == 0))
+					tag = ARCHIVE_ENTRY_ACL_MASK;
+				break;
+			default:
+					break;
+			}
+
+			switch (tag) {
+			case ARCHIVE_ENTRY_ACL_OTHER:
+			case ARCHIVE_ENTRY_ACL_MASK:
+				if (fields == (n + 2)
+				    && field[n + 1].start < field[n + 1].end
+				    && ismode_w(field[n + 1].start,
+				    field[n + 1].end, &permset)) {
+					/* This is Solaris-style "other:rwx" */
+					sol = 1;
+				} else if (fields == (n + 3) &&
+				    field[n + 1].start < field[n + 1].end) {
+					/* Invalid mask or other field */
+					ret = ARCHIVE_WARN;
+					continue;
+				}
+				break;
+			case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+				if (id != -1 ||
+				    field[n + 1].start < field[n + 1].end) {
+					name = field[n + 1];
+					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+						tag = ARCHIVE_ENTRY_ACL_USER;
+					else
+						tag = ARCHIVE_ENTRY_ACL_GROUP;
+				}
+				break;
+			default:
+				/* Invalid tag, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+
+			/*
+			 * Without "default:" we expect mode in field 2
+			 * Exception: Solaris other and mask fields
+			 */
+			if (permset == 0 && !ismode_w(field[n + 2 - sol].start,
+			    field[n + 2 - sol].end, &permset)) {
+				/* Invalid mode, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+		} else {
+			/* NFS4 ACLs */
+			s = field[0].start;
+			len = field[0].end - field[0].start;
+			tag = 0;
+
+			switch (len) {
+			case 4:
+				if (wmemcmp(s, L"user", 4) == 0)
+					tag = ARCHIVE_ENTRY_ACL_USER;
+				break;
+			case 5:
+				if (wmemcmp(s, L"group", 5) == 0)
+					tag = ARCHIVE_ENTRY_ACL_GROUP;
+				break;
+			case 6:
+				if (wmemcmp(s, L"owner@", 6) == 0)
+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				else if (wmemcmp(s, L"group@", len) == 0)
+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case 9:
+				if (wmemcmp(s, L"everyone@", 9) == 0)
+					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+			default:
+				break;
+			}
+
+			if (tag == 0) {
+				/* Invalid tag, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
+			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
+				n = 1;
+				name = field[1];
+				isint_w(name.start, name.end, &id);
+			} else
+				n = 0;
+
+			if (!is_nfs4_perms_w(field[1 + n].start,
+			    field[1 + n].end, &permset)) {
+				/* Invalid NFSv4 perms, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			if (!is_nfs4_flags_w(field[2 + n].start,
+			    field[2 + n].end, &permset)) {
+				/* Invalid NFSv4 flags, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			s = field[3 + n].start;
+			len = field[3 + n].end - field[3 + n].start;
+			type = 0;
+			if (len == 4) {
+				if (wmemcmp(s, L"deny", 4) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+			} else if (len == 5) {
+				if (wmemcmp(s, L"allow", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+				else if (wmemcmp(s, L"audit", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+				else if (wmemcmp(s, L"alarm", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+			}
+			if (type == 0) {
+				/* Invalid entry type, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			isint_w(field[4 + n].start, field[4 + n].end, &id);
+		}
+
+		/* Add entry to the internal list. */
+		r = archive_acl_add_entry_w_len(acl, type, permset,
+		    tag, id, name.start, name.end - name.start);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if (r != ARCHIVE_OK)
+			ret = ARCHIVE_WARN;
+		types |= type;
+	}
+
+	/* Reset ACL */
+	archive_acl_reset(acl, types);
+
+	return (ret);
+}
+
+/*
+ * 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 / 10 && (*start - '0') > INT_MAX % 10)) {
+			n = INT_MAX;
+		} else {
+			n *= 10;
+			n += *start - '0';
+		}
+		start++;
+	}
+	*result = n;
+	return (1);
+}
+
+/*
+ * Parse a string as a mode field.  Returns true if
+ * the string is non-empty and consists only of mode characters,
+ * false otherwise.
+ */
+static int
+ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+	const wchar_t *p;
+
+	if (start >= end)
+		return (0);
+	p = start;
+	*permset = 0;
+	while (p < end) {
+		switch (*p++) {
+		case L'r': case L'R':
+			*permset |= ARCHIVE_ENTRY_ACL_READ;
+			break;
+		case L'w': case L'W':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE;
+			break;
+		case L'x': case L'X':
+			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+			break;
+		case L'-':
+			break;
+		default:
+			return (0);
+		}
+	}
+	return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL permission field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * permission characters, false otherwise
+ */
+static int
+is_nfs4_perms_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+	const wchar_t *p = start;
+
+	while (p < end) {
+		switch (*p++) {
+		case L'r':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
+			break;
+		case L'w':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
+			break;
+		case L'x':
+			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+			break;
+		case L'p':
+			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
+			break;
+		case L'D':
+			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
+			break;
+		case L'd':
+			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
+			break;
+		case L'a':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
+			break;
+		case L'A':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
+			break;
+		case L'R':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
+			break;
+		case L'W':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
+			break;
+		case L'c':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
+			break;
+		case L'C':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
+			break;
+		case L'o':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+			break;
+		case L's':
+			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+			break;
+		case L'-':
+			break;
+		default:
+			return(0);
+		}
+	}
+	return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL flags field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * flag characters, false otherwise
+ */
+static int
+is_nfs4_flags_w(const wchar_t *start, const wchar_t *end, int *permset)
+{
+	const wchar_t *p = start;
+
+	while (p < end) {
+		switch(*p++) {
+		case L'f':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
+			break;
+		case L'd':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
+			break;
+		case L'i':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
+			break;
+		case L'n':
+			*permset |=
+			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
+			break;
+		case L'S':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
+			break;
+		case L'F':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
+			break;
+		case L'I':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
+			break;
+		case L'-':
+			break;
+		default:
+			return (0);
+		}
+	}
+	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 != L'#') {
+		(*wp)++;
+	}
+	*sep = **wp;
+
+	/* Locate end of field, trim trailing whitespace if necessary */
+	if (*wp == *start) {
+		*end = *wp;
+	} else {
+		*end = *wp - 1;
+		while (**end == L' ' || **end == L'\t' || **end == L'\n') {
+			(*end)--;
+		}
+		(*end)++;
+	}
+
+	/* Handle in-field comments */
+	if (*sep == L'#') {
+		while (**wp != L'\0' && **wp != L',' && **wp != L'\n') {
+			(*wp)++;
+		}
+		*sep = **wp;
+	}
+
+	/* Adjust scanner location. */
+	if (**wp != L'\0')
+		(*wp)++;
+}
+
+/*
+ * Parse an ACL text string.
+ *
+ * The want_type argument may be one of the following:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - text is a POSIX.1e ACL of type ACCESS
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - text is a POSIX.1e ACL of type DEFAULT
+ * ARCHIVE_ENTRY_ACL_TYPE_NFS4 - text is as a NFSv4 ACL
+ *
+ * POSIX.1e ACL entries prefixed with "default:" are treated as
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT unless type is ARCHIVE_ENTRY_ACL_TYPE_NFS4
+ */
+int
+archive_acl_from_text_l(struct archive_acl *acl, const char *text,
+    int want_type, struct archive_string_conv *sc)
+{
+	struct {
+		const char *start;
+		const char *end;
+	} field[6], name;
+
+	const char *s, *st;
+	int numfields, fields, n, r, sol, ret;
+	int type, types, tag, permset, id;
+	size_t len;
+	char sep;
+
+	switch (want_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+		want_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+		__LA_FALLTHROUGH;
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+		numfields = 5;
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		numfields = 6;
+		break;
+	default:
+		return (ARCHIVE_FATAL);
+	}
+
+	ret = ARCHIVE_OK;
+	types = 0;
+
+	while (text != NULL &&  *text != '\0') {
+		/*
+		 * Parse the fields out of the next entry,
+		 * advance 'text' to start of next entry.
+		 */
+		fields = 0;
+		do {
+			const char *start, *end;
+			next_field(&text, &start, &end, &sep);
+			if (fields < numfields) {
+				field[fields].start = start;
+				field[fields].end = end;
+			}
+			++fields;
+		} while (sep == ':');
+
+		/* Set remaining fields to blank. */
+		for (n = fields; n < numfields; ++n)
+			field[n].start = field[n].end = NULL;
+
+		if (field[0].start != NULL && *(field[0].start) == '#') {
+			/* Comment, skip entry */
+			continue;
+		}
+
+		n = 0;
+		sol = 0;
+		id = -1;
+		permset = 0;
+		name.start = name.end = NULL;
+
+		if (want_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			/* POSIX.1e ACLs */
+			/*
+			 * Default keyword "default:user::rwx"
+			 * if found, we have one more field
+			 *
+			 * We also support old Solaris extension:
+			 * "defaultuser::rwx" is the default ACL corresponding
+			 * to "user::rwx", etc. valid only for first field
+			 */
+			s = field[0].start;
+			len = field[0].end - field[0].start;
+			if (*s == 'd' && (len == 1 || (len >= 7
+			    && memcmp((s + 1), "efault", 6) == 0))) {
+				type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+				if (len > 7)
+					field[0].start += 7;
+				else
+					n = 1;
+			} else
+				type = want_type;
+
+			/* Check for a numeric ID in field n+1 or n+3. */
+			isint(field[n + 1].start, field[n + 1].end, &id);
+			/* Field n+3 is optional. */
+			if (id == -1 && fields > (n + 3))
+				isint(field[n + 3].start, field[n + 3].end,
+				    &id);
+
+			tag = 0;
+			s = field[n].start;
+			st = field[n].start + 1;
+			len = field[n].end - field[n].start;
+
+			if (len == 0) {
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+
+			switch (*s) {
+			case 'u':
+				if (len == 1 || (len == 4
+				    && memcmp(st, "ser", 3) == 0))
+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				break;
+			case 'g':
+				if (len == 1 || (len == 5
+				    && memcmp(st, "roup", 4) == 0))
+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case 'o':
+				if (len == 1 || (len == 5
+				    && memcmp(st, "ther", 4) == 0))
+					tag = ARCHIVE_ENTRY_ACL_OTHER;
+				break;
+			case 'm':
+				if (len == 1 || (len == 4
+				    && memcmp(st, "ask", 3) == 0))
+					tag = ARCHIVE_ENTRY_ACL_MASK;
+				break;
+			default:
+					break;
+			}
+
+			switch (tag) {
+			case ARCHIVE_ENTRY_ACL_OTHER:
+			case ARCHIVE_ENTRY_ACL_MASK:
+				if (fields == (n + 2)
+				    && field[n + 1].start < field[n + 1].end
+				    && ismode(field[n + 1].start,
+				    field[n + 1].end, &permset)) {
+					/* This is Solaris-style "other:rwx" */
+					sol = 1;
+				} else if (fields == (n + 3) &&
+				    field[n + 1].start < field[n + 1].end) {
+					/* Invalid mask or other field */
+					ret = ARCHIVE_WARN;
+					continue;
+				}
+				break;
+			case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+				if (id != -1 ||
+				    field[n + 1].start < field[n + 1].end) {
+					name = field[n + 1];
+					if (tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
+						tag = ARCHIVE_ENTRY_ACL_USER;
+					else
+						tag = ARCHIVE_ENTRY_ACL_GROUP;
+				}
+				break;
+			default:
+				/* Invalid tag, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+
+			/*
+			 * Without "default:" we expect mode in field 3
+			 * Exception: Solaris other and mask fields
+			 */
+			if (permset == 0 && !ismode(field[n + 2 - sol].start,
+			    field[n + 2 - sol].end, &permset)) {
+				/* Invalid mode, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+		} else {
+			/* NFS4 ACLs */
+			s = field[0].start;
+			len = field[0].end - field[0].start;
+			tag = 0;
+
+			switch (len) {
+			case 4:
+				if (memcmp(s, "user", 4) == 0)
+					tag = ARCHIVE_ENTRY_ACL_USER;
+				break;
+			case 5:
+				if (memcmp(s, "group", 5) == 0)
+					tag = ARCHIVE_ENTRY_ACL_GROUP;
+				break;
+			case 6:
+				if (memcmp(s, "owner@", 6) == 0)
+					tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				else if (memcmp(s, "group@", 6) == 0)
+					tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case 9:
+				if (memcmp(s, "everyone@", 9) == 0)
+					tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+				break;
+			default:
+				break;
+			}
+
+			if (tag == 0) {
+				/* Invalid tag, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			} else if (tag == ARCHIVE_ENTRY_ACL_USER ||
+			    tag == ARCHIVE_ENTRY_ACL_GROUP) {
+				n = 1;
+				name = field[1];
+				isint(name.start, name.end, &id);
+			} else
+				n = 0;
+
+			if (!is_nfs4_perms(field[1 + n].start,
+			    field[1 + n].end, &permset)) {
+				/* Invalid NFSv4 perms, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			if (!is_nfs4_flags(field[2 + n].start,
+			    field[2 + n].end, &permset)) {
+				/* Invalid NFSv4 flags, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			s = field[3 + n].start;
+			len = field[3 + n].end - field[3 + n].start;
+			type = 0;
+			if (len == 4) {
+				if (memcmp(s, "deny", 4) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+			} else if (len == 5) {
+				if (memcmp(s, "allow", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+				else if (memcmp(s, "audit", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+				else if (memcmp(s, "alarm", 5) == 0)
+					type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+			}
+			if (type == 0) {
+				/* Invalid entry type, skip entry */
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+			isint(field[4 + n].start, field[4 + n].end,
+			    &id);
+		}
+
+		/* Add entry to the internal list. */
+		r = archive_acl_add_entry_len_l(acl, type, permset,
+		    tag, id, name.start, name.end - name.start, sc);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if (r != ARCHIVE_OK)
+			ret = ARCHIVE_WARN;
+		types |= type;
+	}
+
+	/* Reset ACL */
+	archive_acl_reset(acl, types);
+
+	return (ret);
+}
+
+/*
+ * 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(const char *start, const char *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 / 10 && (*start - '0') > INT_MAX % 10)) {
+			n = INT_MAX;
+		} else {
+			n *= 10;
+			n += *start - '0';
+		}
+		start++;
+	}
+	*result = n;
+	return (1);
+}
+
+/*
+ * Parse a string as a mode field.  Returns true if
+ * the string is non-empty and consists only of mode characters,
+ * false otherwise.
+ */
+static int
+ismode(const char *start, const char *end, int *permset)
+{
+	const char *p;
+
+	if (start >= end)
+		return (0);
+	p = start;
+	*permset = 0;
+	while (p < 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 (0);
+		}
+	}
+	return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL permission field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * permission characters, false otherwise
+ */
+static int
+is_nfs4_perms(const char *start, const char *end, int *permset)
+{
+	const char *p = start;
+
+	while (p < end) {
+		switch (*p++) {
+		case 'r':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_DATA;
+			break;
+		case 'w':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_DATA;
+			break;
+		case 'x':
+			*permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
+			break;
+		case 'p':
+			*permset |= ARCHIVE_ENTRY_ACL_APPEND_DATA;
+			break;
+		case 'D':
+			*permset |= ARCHIVE_ENTRY_ACL_DELETE_CHILD;
+			break;
+		case 'd':
+			*permset |= ARCHIVE_ENTRY_ACL_DELETE;
+			break;
+		case 'a':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES;
+			break;
+		case 'A':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES;
+			break;
+		case 'R':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS;
+			break;
+		case 'W':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS;
+			break;
+		case 'c':
+			*permset |= ARCHIVE_ENTRY_ACL_READ_ACL;
+			break;
+		case 'C':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_ACL;
+			break;
+		case 'o':
+			*permset |= ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+			break;
+		case 's':
+			*permset |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+			break;
+		case '-':
+			break;
+		default:
+			return(0);
+		}
+	}
+	return (1);
+}
+
+/*
+ * Parse a string as a NFS4 ACL flags field.
+ * Returns true if the string is non-empty and consists only of NFS4 ACL
+ * flag characters, false otherwise
+ */
+static int
+is_nfs4_flags(const char *start, const char *end, int *permset)
+{
+	const char *p = start;
+
+	while (p < end) {
+		switch(*p++) {
+		case 'f':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT;
+			break;
+		case 'd':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT;
+			break;
+		case 'i':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY;
+			break;
+		case 'n':
+			*permset |=
+			    ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT;
+			break;
+		case 'S':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS;
+			break;
+		case 'F':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS;
+			break;
+		case 'I':
+			*permset |= ARCHIVE_ENTRY_ACL_ENTRY_INHERITED;
+			break;
+		case '-':
+			break;
+		default:
+			return (0);
+		}
+	}
+	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(const char **p, const char **start,
+    const char **end, char *sep)
+{
+	/* Skip leading whitespace to find start of field. */
+	while (**p == ' ' || **p == '\t' || **p == '\n') {
+		(*p)++;
+	}
+	*start = *p;
+
+	/* Scan for the separator. */
+	while (**p != '\0' && **p != ',' && **p != ':' && **p != '\n' &&
+	    **p != '#') {
+		(*p)++;
+	}
+	*sep = **p;
+
+	/* Locate end of field, trim trailing whitespace if necessary */
+	if (*p == *start) {
+		*end = *p;
+	} else {
+		*end = *p - 1;
+		while (**end == ' ' || **end == '\t' || **end == '\n') {
+			(*end)--;
+		}
+		(*end)++;
+	}
+
+	/* Handle in-field comments */
+	if (*sep == '#') {
+		while (**p != '\0' && **p != ',' && **p != '\n') {
+			(*p)++;
+		}
+		*sep = **p;
+	}
+
+	/* Adjust scanner location. */
+	if (**p != '\0')
+		(*p)++;
+}
diff --git a/c/archive_acl_private.h b/c/archive_acl_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_acl_private.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_ACL_PRIVATE_H_INCLUDED
+#define ARCHIVE_ACL_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_string.h"
+
+struct archive_acl_entry {
+	struct archive_acl_entry *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 archive_mstring name;		/* uname/gname */
+};
+
+struct archive_acl {
+	mode_t		mode;
+	struct archive_acl_entry	*acl_head;
+	struct archive_acl_entry	*acl_p;
+	int		 acl_state;	/* See acl_next for details. */
+	wchar_t		*acl_text_w;
+	char		*acl_text;
+	int		 acl_types;
+};
+
+void archive_acl_clear(struct archive_acl *);
+void archive_acl_copy(struct archive_acl *, struct archive_acl *);
+int archive_acl_count(struct archive_acl *, int);
+int archive_acl_types(struct archive_acl *);
+int archive_acl_reset(struct archive_acl *, int);
+int archive_acl_next(struct archive *, struct archive_acl *, int,
+    int *, int *, int *, int *, const char **);
+
+int archive_acl_add_entry(struct archive_acl *, int, int, int, int, const char *);
+int archive_acl_add_entry_w_len(struct archive_acl *,
+    int, int, int, int, const wchar_t *, size_t);
+int archive_acl_add_entry_len(struct archive_acl *,
+    int, int, int, int, const char *, size_t);
+
+wchar_t *archive_acl_to_text_w(struct archive_acl *, ssize_t *, int,
+    struct archive *);
+char *archive_acl_to_text_l(struct archive_acl *, ssize_t *, int,
+    struct archive_string_conv *);
+
+/*
+ * ACL text parser.
+ */
+int archive_acl_from_text_w(struct archive_acl *, const wchar_t * /* wtext */,
+    int /* type */);
+int archive_acl_from_text_l(struct archive_acl *, const char * /* text */,
+    int /* type */, struct archive_string_conv *);
+
+#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
diff --git a/c/archive_blake2.h b/c/archive_blake2.h
new file mode 100644
--- /dev/null
+++ b/c/archive_blake2.h
@@ -0,0 +1,195 @@
+/*
+   BLAKE2 reference source code package - reference C implementations
+
+   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the
+   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
+   your option.  The terms of these licenses can be found at:
+
+   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
+   - OpenSSL license   : https://www.openssl.org/source/license.html
+   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0
+
+   More information about the BLAKE2 hash function can be found at
+   https://blake2.net.
+*/
+
+#ifndef ARCHIVE_BLAKE2_H
+#define ARCHIVE_BLAKE2_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined(_MSC_VER)
+#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
+#else
+#define BLAKE2_PACKED(x) x __attribute__((packed))
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+  enum blake2s_constant
+  {
+    BLAKE2S_BLOCKBYTES = 64,
+    BLAKE2S_OUTBYTES   = 32,
+    BLAKE2S_KEYBYTES   = 32,
+    BLAKE2S_SALTBYTES  = 8,
+    BLAKE2S_PERSONALBYTES = 8
+  };
+
+  enum blake2b_constant
+  {
+    BLAKE2B_BLOCKBYTES = 128,
+    BLAKE2B_OUTBYTES   = 64,
+    BLAKE2B_KEYBYTES   = 64,
+    BLAKE2B_SALTBYTES  = 16,
+    BLAKE2B_PERSONALBYTES = 16
+  };
+
+  typedef struct blake2s_state__
+  {
+    uint32_t h[8];
+    uint32_t t[2];
+    uint32_t f[2];
+    uint8_t  buf[BLAKE2S_BLOCKBYTES];
+    size_t   buflen;
+    size_t   outlen;
+    uint8_t  last_node;
+  } blake2s_state;
+
+  typedef struct blake2b_state__
+  {
+    uint64_t h[8];
+    uint64_t t[2];
+    uint64_t f[2];
+    uint8_t  buf[BLAKE2B_BLOCKBYTES];
+    size_t   buflen;
+    size_t   outlen;
+    uint8_t  last_node;
+  } blake2b_state;
+
+  typedef struct blake2sp_state__
+  {
+    blake2s_state S[8][1];
+    blake2s_state R[1];
+    uint8_t       buf[8 * BLAKE2S_BLOCKBYTES];
+    size_t        buflen;
+    size_t        outlen;
+  } blake2sp_state;
+
+  typedef struct blake2bp_state__
+  {
+    blake2b_state S[4][1];
+    blake2b_state R[1];
+    uint8_t       buf[4 * BLAKE2B_BLOCKBYTES];
+    size_t        buflen;
+    size_t        outlen;
+  } blake2bp_state;
+
+  BLAKE2_PACKED(struct blake2s_param__
+  {
+    uint8_t  digest_length; /* 1 */
+    uint8_t  key_length;    /* 2 */
+    uint8_t  fanout;        /* 3 */
+    uint8_t  depth;         /* 4 */
+    uint32_t leaf_length;   /* 8 */
+    uint32_t node_offset;  /* 12 */
+    uint16_t xof_length;    /* 14 */
+    uint8_t  node_depth;    /* 15 */
+    uint8_t  inner_length;  /* 16 */
+    /* uint8_t  reserved[0]; */
+    uint8_t  salt[BLAKE2S_SALTBYTES]; /* 24 */
+    uint8_t  personal[BLAKE2S_PERSONALBYTES];  /* 32 */
+  });
+
+  typedef struct blake2s_param__ blake2s_param;
+
+  BLAKE2_PACKED(struct blake2b_param__
+  {
+    uint8_t  digest_length; /* 1 */
+    uint8_t  key_length;    /* 2 */
+    uint8_t  fanout;        /* 3 */
+    uint8_t  depth;         /* 4 */
+    uint32_t leaf_length;   /* 8 */
+    uint32_t node_offset;   /* 12 */
+    uint32_t xof_length;    /* 16 */
+    uint8_t  node_depth;    /* 17 */
+    uint8_t  inner_length;  /* 18 */
+    uint8_t  reserved[14];  /* 32 */
+    uint8_t  salt[BLAKE2B_SALTBYTES]; /* 48 */
+    uint8_t  personal[BLAKE2B_PERSONALBYTES];  /* 64 */
+  });
+
+  typedef struct blake2b_param__ blake2b_param;
+
+  typedef struct blake2xs_state__
+  {
+    blake2s_state S[1];
+    blake2s_param P[1];
+  } blake2xs_state;
+
+  typedef struct blake2xb_state__
+  {
+    blake2b_state S[1];
+    blake2b_param P[1];
+  } blake2xb_state;
+
+  /* Padded structs result in a compile-time error */
+  enum {
+    BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES),
+    BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES)
+  };
+
+  /* Streaming API */
+  int blake2s_init( blake2s_state *S, size_t outlen );
+  int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );
+  int blake2s_init_param( blake2s_state *S, const blake2s_param *P );
+  int blake2s_update( blake2s_state *S, const void *in, size_t inlen );
+  int blake2s_final( blake2s_state *S, void *out, size_t outlen );
+
+  int blake2b_init( blake2b_state *S, size_t outlen );
+  int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );
+  int blake2b_init_param( blake2b_state *S, const blake2b_param *P );
+  int blake2b_update( blake2b_state *S, const void *in, size_t inlen );
+  int blake2b_final( blake2b_state *S, void *out, size_t outlen );
+
+  int blake2sp_init( blake2sp_state *S, size_t outlen );
+  int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );
+  int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );
+  int blake2sp_final( blake2sp_state *S, void *out, size_t outlen );
+
+  int blake2bp_init( blake2bp_state *S, size_t outlen );
+  int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );
+  int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );
+  int blake2bp_final( blake2bp_state *S, void *out, size_t outlen );
+
+  /* Variable output length API */
+  int blake2xs_init( blake2xs_state *S, const size_t outlen );
+  int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );
+  int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );
+  int blake2xs_final(blake2xs_state *S, void *out, size_t outlen);
+
+  int blake2xb_init( blake2xb_state *S, const size_t outlen );
+  int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );
+  int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );
+  int blake2xb_final(blake2xb_state *S, void *out, size_t outlen);
+
+  /* Simple API */
+  int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+
+  int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+
+  int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+  int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+
+  /* This is simply an alias for blake2b */
+  int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/c/archive_blake2_impl.h b/c/archive_blake2_impl.h
new file mode 100644
--- /dev/null
+++ b/c/archive_blake2_impl.h
@@ -0,0 +1,161 @@
+/*
+   BLAKE2 reference source code package - reference C implementations
+
+   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the
+   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
+   your option.  The terms of these licenses can be found at:
+
+   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
+   - OpenSSL license   : https://www.openssl.org/source/license.html
+   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0
+
+   More information about the BLAKE2 hash function can be found at
+   https://blake2.net.
+*/
+
+#ifndef ARCHIVE_BLAKE2_IMPL_H
+#define ARCHIVE_BLAKE2_IMPL_H
+
+#include <stdint.h>
+#include <string.h>
+
+#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
+  #if   defined(_MSC_VER)
+    #define BLAKE2_INLINE __inline
+  #elif defined(__GNUC__)
+    #define BLAKE2_INLINE __inline__
+  #else
+    #define BLAKE2_INLINE
+  #endif
+#else
+  #define BLAKE2_INLINE inline
+#endif
+
+static BLAKE2_INLINE uint32_t load32( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  uint32_t w;
+  memcpy(&w, src, sizeof w);
+  return w;
+#else
+  const uint8_t *p = ( const uint8_t * )src;
+  return (( uint32_t )( p[0] ) <<  0) |
+         (( uint32_t )( p[1] ) <<  8) |
+         (( uint32_t )( p[2] ) << 16) |
+         (( uint32_t )( p[3] ) << 24) ;
+#endif
+}
+
+static BLAKE2_INLINE uint64_t load64( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  uint64_t w;
+  memcpy(&w, src, sizeof w);
+  return w;
+#else
+  const uint8_t *p = ( const uint8_t * )src;
+  return (( uint64_t )( p[0] ) <<  0) |
+         (( uint64_t )( p[1] ) <<  8) |
+         (( uint64_t )( p[2] ) << 16) |
+         (( uint64_t )( p[3] ) << 24) |
+         (( uint64_t )( p[4] ) << 32) |
+         (( uint64_t )( p[5] ) << 40) |
+         (( uint64_t )( p[6] ) << 48) |
+         (( uint64_t )( p[7] ) << 56) ;
+#endif
+}
+
+static BLAKE2_INLINE uint16_t load16( const void *src )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  uint16_t w;
+  memcpy(&w, src, sizeof w);
+  return w;
+#else
+  const uint8_t *p = ( const uint8_t * )src;
+  return ( uint16_t )((( uint32_t )( p[0] ) <<  0) |
+                      (( uint32_t )( p[1] ) <<  8));
+#endif
+}
+
+static BLAKE2_INLINE void store16( void *dst, uint16_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  memcpy(dst, &w, sizeof w);
+#else
+  uint8_t *p = ( uint8_t * )dst;
+  *p++ = ( uint8_t )w; w >>= 8;
+  *p++ = ( uint8_t )w;
+#endif
+}
+
+static BLAKE2_INLINE void store32( void *dst, uint32_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  memcpy(dst, &w, sizeof w);
+#else
+  uint8_t *p = ( uint8_t * )dst;
+  p[0] = (uint8_t)(w >>  0);
+  p[1] = (uint8_t)(w >>  8);
+  p[2] = (uint8_t)(w >> 16);
+  p[3] = (uint8_t)(w >> 24);
+#endif
+}
+
+static BLAKE2_INLINE void store64( void *dst, uint64_t w )
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+  memcpy(dst, &w, sizeof w);
+#else
+  uint8_t *p = ( uint8_t * )dst;
+  p[0] = (uint8_t)(w >>  0);
+  p[1] = (uint8_t)(w >>  8);
+  p[2] = (uint8_t)(w >> 16);
+  p[3] = (uint8_t)(w >> 24);
+  p[4] = (uint8_t)(w >> 32);
+  p[5] = (uint8_t)(w >> 40);
+  p[6] = (uint8_t)(w >> 48);
+  p[7] = (uint8_t)(w >> 56);
+#endif
+}
+
+static BLAKE2_INLINE uint64_t load48( const void *src )
+{
+  const uint8_t *p = ( const uint8_t * )src;
+  return (( uint64_t )( p[0] ) <<  0) |
+         (( uint64_t )( p[1] ) <<  8) |
+         (( uint64_t )( p[2] ) << 16) |
+         (( uint64_t )( p[3] ) << 24) |
+         (( uint64_t )( p[4] ) << 32) |
+         (( uint64_t )( p[5] ) << 40) ;
+}
+
+static BLAKE2_INLINE void store48( void *dst, uint64_t w )
+{
+  uint8_t *p = ( uint8_t * )dst;
+  p[0] = (uint8_t)(w >>  0);
+  p[1] = (uint8_t)(w >>  8);
+  p[2] = (uint8_t)(w >> 16);
+  p[3] = (uint8_t)(w >> 24);
+  p[4] = (uint8_t)(w >> 32);
+  p[5] = (uint8_t)(w >> 40);
+}
+
+static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c )
+{
+  return ( w >> c ) | ( w << ( 32 - c ) );
+}
+
+static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c )
+{
+  return ( w >> c ) | ( w << ( 64 - c ) );
+}
+
+/* prevents compiler optimizing out memset() */
+static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n)
+{
+  static void *(*const volatile memset_v)(void *, int, size_t) = &memset;
+  memset_v(v, 0, n);
+}
+
+#endif
diff --git a/c/archive_blake2s_ref.c b/c/archive_blake2s_ref.c
new file mode 100644
--- /dev/null
+++ b/c/archive_blake2s_ref.c
@@ -0,0 +1,367 @@
+/*
+   BLAKE2 reference source code package - reference C implementations
+
+   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the
+   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
+   your option.  The terms of these licenses can be found at:
+
+   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
+   - OpenSSL license   : https://www.openssl.org/source/license.html
+   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0
+
+   More information about the BLAKE2 hash function can be found at
+   https://blake2.net.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "archive_blake2.h"
+#include "archive_blake2_impl.h"
+
+static const uint32_t blake2s_IV[8] =
+{
+  0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+  0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] =
+{
+  {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,
+  { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 } ,
+  { 11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4 } ,
+  {  7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8 } ,
+  {  9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13 } ,
+  {  2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9 } ,
+  { 12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11 } ,
+  { 13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10 } ,
+  {  6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5 } ,
+  { 10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13 , 0 } ,
+};
+
+static void blake2s_set_lastnode( blake2s_state *S )
+{
+  S->f[1] = (uint32_t)-1;
+}
+
+/* Some helper functions, not necessarily useful */
+static int blake2s_is_lastblock( const blake2s_state *S )
+{
+  return S->f[0] != 0;
+}
+
+static void blake2s_set_lastblock( blake2s_state *S )
+{
+  if( S->last_node ) blake2s_set_lastnode( S );
+
+  S->f[0] = (uint32_t)-1;
+}
+
+static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc )
+{
+  S->t[0] += inc;
+  S->t[1] += ( S->t[0] < inc );
+}
+
+static void blake2s_init0( blake2s_state *S )
+{
+  size_t i;
+  memset( S, 0, sizeof( blake2s_state ) );
+
+  for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i];
+}
+
+/* init2 xors IV with input parameter block */
+int blake2s_init_param( blake2s_state *S, const blake2s_param *P )
+{
+  const unsigned char *p = ( const unsigned char * )( P );
+  size_t i;
+
+  blake2s_init0( S );
+
+  /* IV XOR ParamBlock */
+  for( i = 0; i < 8; ++i )
+    S->h[i] ^= load32( &p[i * 4] );
+
+  S->outlen = P->digest_length;
+  return 0;
+}
+
+
+/* Sequential blake2s initialization */
+int blake2s_init( blake2s_state *S, size_t outlen )
+{
+  blake2s_param P[1];
+
+  /* Move interval verification here? */
+  if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+  P->digest_length = (uint8_t)outlen;
+  P->key_length    = 0;
+  P->fanout        = 1;
+  P->depth         = 1;
+  store32( &P->leaf_length, 0 );
+  store32( &P->node_offset, 0 );
+  store16( &P->xof_length, 0 );
+  P->node_depth    = 0;
+  P->inner_length  = 0;
+  /* memset(P->reserved, 0, sizeof(P->reserved) ); */
+  memset( P->salt,     0, sizeof( P->salt ) );
+  memset( P->personal, 0, sizeof( P->personal ) );
+  return blake2s_init_param( S, P );
+}
+
+int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen )
+{
+  blake2s_param P[1];
+
+  if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1;
+
+  if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
+
+  P->digest_length = (uint8_t)outlen;
+  P->key_length    = (uint8_t)keylen;
+  P->fanout        = 1;
+  P->depth         = 1;
+  store32( &P->leaf_length, 0 );
+  store32( &P->node_offset, 0 );
+  store16( &P->xof_length, 0 );
+  P->node_depth    = 0;
+  P->inner_length  = 0;
+  /* memset(P->reserved, 0, sizeof(P->reserved) ); */
+  memset( P->salt,     0, sizeof( P->salt ) );
+  memset( P->personal, 0, sizeof( P->personal ) );
+
+  if( blake2s_init_param( S, P ) < 0 ) return -1;
+
+  {
+    uint8_t block[BLAKE2S_BLOCKBYTES];
+    memset( block, 0, BLAKE2S_BLOCKBYTES );
+    memcpy( block, key, keylen );
+    blake2s_update( S, block, BLAKE2S_BLOCKBYTES );
+    secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+  }
+  return 0;
+}
+
+#define G(r,i,a,b,c,d)                      \
+  do {                                      \
+    a = a + b + m[blake2s_sigma[r][2*i+0]]; \
+    d = rotr32(d ^ a, 16);                  \
+    c = c + d;                              \
+    b = rotr32(b ^ c, 12);                  \
+    a = a + b + m[blake2s_sigma[r][2*i+1]]; \
+    d = rotr32(d ^ a, 8);                   \
+    c = c + d;                              \
+    b = rotr32(b ^ c, 7);                   \
+  } while(0)
+
+#define ROUND(r)                    \
+  do {                              \
+    G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+    G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+    G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+    G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+    G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+    G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+    G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+    G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+  } while(0)
+
+static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] )
+{
+  uint32_t m[16];
+  uint32_t v[16];
+  size_t i;
+
+  for( i = 0; i < 16; ++i ) {
+    m[i] = load32( in + i * sizeof( m[i] ) );
+  }
+
+  for( i = 0; i < 8; ++i ) {
+    v[i] = S->h[i];
+  }
+
+  v[ 8] = blake2s_IV[0];
+  v[ 9] = blake2s_IV[1];
+  v[10] = blake2s_IV[2];
+  v[11] = blake2s_IV[3];
+  v[12] = S->t[0] ^ blake2s_IV[4];
+  v[13] = S->t[1] ^ blake2s_IV[5];
+  v[14] = S->f[0] ^ blake2s_IV[6];
+  v[15] = S->f[1] ^ blake2s_IV[7];
+
+  ROUND( 0 );
+  ROUND( 1 );
+  ROUND( 2 );
+  ROUND( 3 );
+  ROUND( 4 );
+  ROUND( 5 );
+  ROUND( 6 );
+  ROUND( 7 );
+  ROUND( 8 );
+  ROUND( 9 );
+
+  for( i = 0; i < 8; ++i ) {
+    S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+  }
+}
+
+#undef G
+#undef ROUND
+
+int blake2s_update( blake2s_state *S, const void *pin, size_t inlen )
+{
+  const unsigned char * in = (const unsigned char *)pin;
+  if( inlen > 0 )
+  {
+    size_t left = S->buflen;
+    size_t fill = BLAKE2S_BLOCKBYTES - left;
+    if( inlen > fill )
+    {
+      S->buflen = 0;
+      memcpy( S->buf + left, in, fill ); /* Fill buffer */
+      blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+      blake2s_compress( S, S->buf ); /* Compress */
+      in += fill; inlen -= fill;
+      while(inlen > BLAKE2S_BLOCKBYTES) {
+        blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES);
+        blake2s_compress( S, in );
+        in += BLAKE2S_BLOCKBYTES;
+        inlen -= BLAKE2S_BLOCKBYTES;
+      }
+    }
+    memcpy( S->buf + S->buflen, in, inlen );
+    S->buflen += inlen;
+  }
+  return 0;
+}
+
+int blake2s_final( blake2s_state *S, void *out, size_t outlen )
+{
+  uint8_t buffer[BLAKE2S_OUTBYTES] = {0};
+  size_t i;
+
+  if( out == NULL || outlen < S->outlen )
+    return -1;
+
+  if( blake2s_is_lastblock( S ) )
+    return -1;
+
+  blake2s_increment_counter( S, ( uint32_t )S->buflen );
+  blake2s_set_lastblock( S );
+  memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
+  blake2s_compress( S, S->buf );
+
+  for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
+    store32( buffer + sizeof( S->h[i] ) * i, S->h[i] );
+
+  memcpy( out, buffer, outlen );
+  secure_zero_memory(buffer, sizeof(buffer));
+  return 0;
+}
+
+int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
+{
+  blake2s_state S[1];
+
+  /* Verify parameters */
+  if ( NULL == in && inlen > 0 ) return -1;
+
+  if ( NULL == out ) return -1;
+
+  if ( NULL == key && keylen > 0) return -1;
+
+  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+  if( keylen > BLAKE2S_KEYBYTES ) return -1;
+
+  if( keylen > 0 )
+  {
+    if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1;
+  }
+  else
+  {
+    if( blake2s_init( S, outlen ) < 0 ) return -1;
+  }
+
+  blake2s_update( S, ( const uint8_t * )in, inlen );
+  blake2s_final( S, out, outlen );
+  return 0;
+}
+
+#if defined(SUPERCOP)
+int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )
+{
+  return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 );
+}
+#endif
+
+#if defined(BLAKE2S_SELFTEST)
+#include <string.h>
+#include "blake2-kat.h"
+int main( void )
+{
+  uint8_t key[BLAKE2S_KEYBYTES];
+  uint8_t buf[BLAKE2_KAT_LENGTH];
+  size_t i, step;
+
+  for( i = 0; i < BLAKE2S_KEYBYTES; ++i )
+    key[i] = ( uint8_t )i;
+
+  for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
+    buf[i] = ( uint8_t )i;
+
+  /* Test simple API */
+  for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
+  {
+    uint8_t hash[BLAKE2S_OUTBYTES];
+    blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
+
+    if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) )
+    {
+      goto fail;
+    }
+  }
+
+  /* Test streaming API */
+  for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {
+    for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
+      uint8_t hash[BLAKE2S_OUTBYTES];
+      blake2s_state S;
+      uint8_t * p = buf;
+      size_t mlen = i;
+      int err = 0;
+
+      if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
+        goto fail;
+      }
+
+      while (mlen >= step) {
+        if ( (err = blake2s_update(&S, p, step)) < 0 ) {
+          goto fail;
+        }
+        mlen -= step;
+        p += step;
+      }
+      if ( (err = blake2s_update(&S, p, mlen)) < 0) {
+        goto fail;
+      }
+      if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
+        goto fail;
+      }
+
+      if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) {
+        goto fail;
+      }
+    }
+  }
+
+  puts( "ok" );
+  return 0;
+fail:
+  puts("error");
+  return -1;
+}
+#endif
diff --git a/c/archive_blake2sp_ref.c b/c/archive_blake2sp_ref.c
new file mode 100644
--- /dev/null
+++ b/c/archive_blake2sp_ref.c
@@ -0,0 +1,359 @@
+/*
+   BLAKE2 reference source code package - reference C implementations
+
+   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the
+   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
+   your option.  The terms of these licenses can be found at:
+
+   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
+   - OpenSSL license   : https://www.openssl.org/source/license.html
+   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0
+
+   More information about the BLAKE2 hash function can be found at
+   https://blake2.net.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#if defined(_OPENMP)
+#include <omp.h>
+#endif
+
+#include "archive_blake2.h"
+#include "archive_blake2_impl.h"
+
+#define PARALLELISM_DEGREE 8
+
+/*
+  blake2sp_init_param defaults to setting the expecting output length
+  from the digest_length parameter block field.
+
+  In some cases, however, we do not want this, as the output length
+  of these instances is given by inner_length instead.
+*/
+static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P )
+{
+  int err = blake2s_init_param(S, P);
+  S->outlen = P->inner_length;
+  return err;
+}
+
+static int blake2sp_init_leaf( blake2s_state *S, size_t outlen, size_t keylen, uint32_t offset )
+{
+  blake2s_param P[1];
+  P->digest_length = (uint8_t)outlen;
+  P->key_length = (uint8_t)keylen;
+  P->fanout = PARALLELISM_DEGREE;
+  P->depth = 2;
+  store32( &P->leaf_length, 0 );
+  store32( &P->node_offset, offset );
+  store16( &P->xof_length, 0 );
+  P->node_depth = 0;
+  P->inner_length = BLAKE2S_OUTBYTES;
+  memset( P->salt, 0, sizeof( P->salt ) );
+  memset( P->personal, 0, sizeof( P->personal ) );
+  return blake2sp_init_leaf_param( S, P );
+}
+
+static int blake2sp_init_root( blake2s_state *S, size_t outlen, size_t keylen )
+{
+  blake2s_param P[1];
+  P->digest_length = (uint8_t)outlen;
+  P->key_length = (uint8_t)keylen;
+  P->fanout = PARALLELISM_DEGREE;
+  P->depth = 2;
+  store32( &P->leaf_length, 0 );
+  store32( &P->node_offset, 0 );
+  store16( &P->xof_length, 0 );
+  P->node_depth = 1;
+  P->inner_length = BLAKE2S_OUTBYTES;
+  memset( P->salt, 0, sizeof( P->salt ) );
+  memset( P->personal, 0, sizeof( P->personal ) );
+  return blake2s_init_param( S, P );
+}
+
+
+int blake2sp_init( blake2sp_state *S, size_t outlen )
+{
+  size_t i;
+
+  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+  memset( S->buf, 0, sizeof( S->buf ) );
+  S->buflen = 0;
+  S->outlen = outlen;
+
+  if( blake2sp_init_root( S->R, outlen, 0 ) < 0 )
+    return -1;
+
+  for( i = 0; i < PARALLELISM_DEGREE; ++i )
+    if( blake2sp_init_leaf( S->S[i], outlen, 0, (uint32_t)i ) < 0 ) return -1;
+
+  S->R->last_node = 1;
+  S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+  return 0;
+}
+
+int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen )
+{
+  size_t i;
+
+  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+  if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1;
+
+  memset( S->buf, 0, sizeof( S->buf ) );
+  S->buflen = 0;
+  S->outlen = outlen;
+
+  if( blake2sp_init_root( S->R, outlen, keylen ) < 0 )
+    return -1;
+
+  for( i = 0; i < PARALLELISM_DEGREE; ++i )
+    if( blake2sp_init_leaf( S->S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1;
+
+  S->R->last_node = 1;
+  S->S[PARALLELISM_DEGREE - 1]->last_node = 1;
+  {
+    uint8_t block[BLAKE2S_BLOCKBYTES];
+    memset( block, 0, BLAKE2S_BLOCKBYTES );
+    memcpy( block, key, keylen );
+
+    for( i = 0; i < PARALLELISM_DEGREE; ++i )
+      blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES );
+
+    secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+  }
+  return 0;
+}
+
+
+int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen )
+{
+  const unsigned char * in = (const unsigned char *)pin;
+  size_t left = S->buflen;
+  size_t fill = sizeof( S->buf ) - left;
+  size_t i;
+
+  if( left && inlen >= fill )
+  {
+    memcpy( S->buf + left, in, fill );
+
+    for( i = 0; i < PARALLELISM_DEGREE; ++i )
+      blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
+
+    in += fill;
+    inlen -= fill;
+    left = 0;
+  }
+
+#if defined(_OPENMP)
+  #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE)
+#else
+  for( i = 0; i < PARALLELISM_DEGREE; ++i )
+#endif
+  {
+#if defined(_OPENMP)
+    size_t      i = omp_get_thread_num();
+#endif
+    size_t inlen__ = inlen;
+    const unsigned char *in__ = ( const unsigned char * )in;
+    in__ += i * BLAKE2S_BLOCKBYTES;
+
+    while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+    {
+      blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES );
+      in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+      inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+    }
+  }
+
+  in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
+  inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+
+  if( inlen > 0 )
+    memcpy( S->buf + left, in, inlen );
+
+  S->buflen = left + inlen;
+  return 0;
+}
+
+
+int blake2sp_final( blake2sp_state *S, void *out, size_t outlen )
+{
+  uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+  size_t i;
+
+  if(out == NULL || outlen < S->outlen) {
+    return -1;
+  }
+
+  for( i = 0; i < PARALLELISM_DEGREE; ++i )
+  {
+    if( S->buflen > i * BLAKE2S_BLOCKBYTES )
+    {
+      size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
+
+      if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
+
+      blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
+    }
+
+    blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES );
+  }
+
+  for( i = 0; i < PARALLELISM_DEGREE; ++i )
+    blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES );
+
+  return blake2s_final( S->R, out, S->outlen );
+}
+
+
+int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )
+{
+  uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+  blake2s_state S[PARALLELISM_DEGREE][1];
+  blake2s_state FS[1];
+  size_t i;
+
+  /* Verify parameters */
+  if ( NULL == in && inlen > 0 ) return -1;
+
+  if ( NULL == out ) return -1;
+
+  if ( NULL == key && keylen > 0) return -1;
+
+  if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1;
+
+  if( keylen > BLAKE2S_KEYBYTES ) return -1;
+
+  for( i = 0; i < PARALLELISM_DEGREE; ++i )
+    if( blake2sp_init_leaf( S[i], outlen, keylen, (uint32_t)i ) < 0 ) return -1;
+
+  S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */
+
+  if( keylen > 0 )
+  {
+    uint8_t block[BLAKE2S_BLOCKBYTES];
+    memset( block, 0, BLAKE2S_BLOCKBYTES );
+    memcpy( block, key, keylen );
+
+    for( i = 0; i < PARALLELISM_DEGREE; ++i )
+      blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES );
+
+    secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */
+  }
+
+#if defined(_OPENMP)
+  #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE)
+#else
+
+  for( i = 0; i < PARALLELISM_DEGREE; ++i )
+#endif
+  {
+#if defined(_OPENMP)
+    size_t      i = omp_get_thread_num();
+#endif
+    size_t inlen__ = inlen;
+    const unsigned char *in__ = ( const unsigned char * )in;
+    in__ += i * BLAKE2S_BLOCKBYTES;
+
+    while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+    {
+      blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES );
+      in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+      inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+    }
+
+    if( inlen__ > i * BLAKE2S_BLOCKBYTES )
+    {
+      const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES;
+      const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES;
+      blake2s_update( S[i], in__, len );
+    }
+
+    blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES );
+  }
+
+  if( blake2sp_init_root( FS, outlen, keylen ) < 0 )
+    return -1;
+
+  FS->last_node = 1;
+
+  for( i = 0; i < PARALLELISM_DEGREE; ++i )
+    blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES );
+
+  return blake2s_final( FS, out, outlen );
+}
+
+
+
+#if defined(BLAKE2SP_SELFTEST)
+#include <string.h>
+#include "blake2-kat.h"
+int main( void )
+{
+  uint8_t key[BLAKE2S_KEYBYTES];
+  uint8_t buf[BLAKE2_KAT_LENGTH];
+  size_t i, step;
+
+  for( i = 0; i < BLAKE2S_KEYBYTES; ++i )
+    key[i] = ( uint8_t )i;
+
+  for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
+    buf[i] = ( uint8_t )i;
+
+  /* Test simple API */
+  for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )
+  {
+    uint8_t hash[BLAKE2S_OUTBYTES];
+    blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES );
+
+    if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) )
+    {
+      goto fail;
+    }
+  }
+
+  /* Test streaming API */
+  for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) {
+    for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {
+      uint8_t hash[BLAKE2S_OUTBYTES];
+      blake2sp_state S;
+      uint8_t * p = buf;
+      size_t mlen = i;
+      int err = 0;
+
+      if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) {
+        goto fail;
+      }
+
+      while (mlen >= step) {
+        if ( (err = blake2sp_update(&S, p, step)) < 0 ) {
+          goto fail;
+        }
+        mlen -= step;
+        p += step;
+      }
+      if ( (err = blake2sp_update(&S, p, mlen)) < 0) {
+        goto fail;
+      }
+      if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) {
+        goto fail;
+      }
+
+      if (0 != memcmp(hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES)) {
+        goto fail;
+      }
+    }
+  }
+
+  puts( "ok" );
+  return 0;
+fail:
+  puts("error");
+  return -1;
+}
+#endif
diff --git a/c/archive_check_magic.c b/c/archive_check_magic.c
new file mode 100644
--- /dev/null
+++ b/c/archive_check_magic.c
@@ -0,0 +1,175 @@
+/*-
+ * Copyright (c) 2003-2010 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: head/lib/libarchive/archive_check_magic.c 201089 2009-12-28 02:20:23Z kientzle $");
+
+#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
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#include <winbase.h>
+#endif
+
+#include "archive_private.h"
+
+static void
+errmsg(const char *m)
+{
+	size_t s = strlen(m);
+	ssize_t written;
+
+	while (s > 0) {
+		written = write(2, m, strlen(m));
+		if (written <= 0)
+			return;
+		m += written;
+		s -= written;
+	}
+}
+
+static __LA_DEAD void
+diediedie(void)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
+	/* Cause a breakpoint exception  */
+	DebugBreak();
+#endif
+	abort();        /* Terminate the program abnormally. */
+}
+
+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 const char *
+archive_handle_type_name(unsigned m)
+{
+	switch (m) {
+	case ARCHIVE_WRITE_MAGIC:	return ("archive_write");
+	case ARCHIVE_READ_MAGIC:	return ("archive_read");
+	case ARCHIVE_WRITE_DISK_MAGIC:	return ("archive_write_disk");
+	case ARCHIVE_READ_DISK_MAGIC:	return ("archive_read_disk");
+	case ARCHIVE_MATCH_MAGIC:	return ("archive_match");
+	default:			return NULL;
+	}
+}
+
+
+static char *
+write_all_states(char *buff, unsigned int states)
+{
+	unsigned int lowbit;
+
+	buff[0] = '\0';
+
+	/* A trick for computing the lowest set bit. */
+	while ((lowbit = states & (1 + ~states)) != 0) {
+		states &= ~lowbit;		/* Clear the low bit. */
+		strcat(buff, state_name(lowbit));
+		if (states != 0)
+			strcat(buff, "/");
+	}
+	return buff;
+}
+
+/*
+ * Check magic value and current state.
+ *   Magic value mismatches are fatal and result in calls to abort().
+ *   State mismatches return ARCHIVE_FATAL.
+ *   Otherwise, returns ARCHIVE_OK.
+ *
+ * This is designed to catch serious programming errors that violate
+ * the libarchive API.
+ */
+int
+__archive_check_magic(struct archive *a, unsigned int magic,
+    unsigned int state, const char *function)
+{
+	char states1[64];
+	char states2[64];
+	const char *handle_type;
+
+	/*
+	 * If this isn't some form of archive handle,
+	 * then the library user has screwed up so bad that
+	 * we don't even have a reliable way to report an error.
+	 */
+	handle_type = archive_handle_type_name(a->magic);
+
+	if (!handle_type) {
+		errmsg("PROGRAMMER ERROR: Function ");
+		errmsg(function);
+		errmsg(" invoked with invalid archive handle.\n");
+		diediedie();
+	}
+
+	if (a->magic != magic) {
+		archive_set_error(a, -1,
+		    "PROGRAMMER ERROR: Function '%s' invoked"
+		    " on '%s' archive object, which is not supported.",
+		    function,
+		    handle_type);
+		a->state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+
+	if ((a->state & state) == 0) {
+		/* If we're already FATAL, don't overwrite the error. */
+		if (a->state != ARCHIVE_STATE_FATAL)
+			archive_set_error(a, -1,
+			    "INTERNAL ERROR: Function '%s' invoked with"
+			    " archive structure in state '%s',"
+			    " should be in state '%s'",
+			    function,
+			    write_all_states(states1, a->state),
+			    write_all_states(states2, state));
+		a->state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+	return ARCHIVE_OK;
+}
diff --git a/c/archive_cmdline.c b/c/archive_cmdline.c
new file mode 100644
--- /dev/null
+++ b/c/archive_cmdline.c
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA 
+ * 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$");
+
+#ifdef HAVE_STRING_H
+#  include <string.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_cmdline_private.h"
+#include "archive_string.h"
+
+static int cmdline_set_path(struct archive_cmdline *, const char *);
+static int cmdline_add_arg(struct archive_cmdline *, const char *);
+
+static ssize_t
+extract_quotation(struct archive_string *as, const char *p)
+{
+	const char *s;
+
+	for (s = p + 1; *s;) {
+		if (*s == '\\') {
+			if (s[1] != '\0') {
+				archive_strappend_char(as, s[1]);
+				s += 2;
+			} else
+				s++;
+		} else if (*s == '"')
+			break;
+		else {
+			archive_strappend_char(as, s[0]);
+			s++;
+		}
+	}
+	if (*s != '"')
+		return (ARCHIVE_FAILED);/* Invalid sequence. */
+	return ((ssize_t)(s + 1 - p));
+}
+
+static ssize_t
+get_argument(struct archive_string *as, const char *p)
+{
+	const char *s = p;
+
+	archive_string_empty(as);
+
+	/* Skip beginning space characters. */
+	while (*s != '\0' && *s == ' ')
+		s++;
+	/* Copy non-space characters. */
+	while (*s != '\0' && *s != ' ') {
+		if (*s == '\\') {
+			if (s[1] != '\0') {
+				archive_strappend_char(as, s[1]);
+				s += 2;
+			} else {
+				s++;/* Ignore this character.*/
+				break;
+			}
+		} else if (*s == '"') {
+			ssize_t q = extract_quotation(as, s);
+			if (q < 0)
+				return (ARCHIVE_FAILED);/* Invalid sequence. */
+			s += q;
+		} else {
+			archive_strappend_char(as, s[0]);
+			s++;
+		}
+	}
+	return ((ssize_t)(s - p));
+}
+
+/*
+ * Set up command line arguments.
+ * Returns ARCHIVE_OK if everything okey.
+ * Returns ARCHIVE_FAILED if there is a lack of the `"' terminator or an
+ * empty command line.
+ * Returns ARCHIVE_FATAL if no memory.
+ */
+int
+__archive_cmdline_parse(struct archive_cmdline *data, const char *cmd)
+{
+	struct archive_string as;
+	const char *p;
+	ssize_t al;
+	int r;
+
+	archive_string_init(&as);
+
+	/* Get first argument as a command path. */
+	al = get_argument(&as, cmd);
+	if (al < 0) {
+		r = ARCHIVE_FAILED;/* Invalid sequence. */
+		goto exit_function;
+	}
+	if (archive_strlen(&as) == 0) {
+		r = ARCHIVE_FAILED;/* An empty command path. */
+		goto exit_function;
+	}
+	r = cmdline_set_path(data, as.s);
+	if (r != ARCHIVE_OK)
+		goto exit_function;
+	p = strrchr(as.s, '/');
+	if (p == NULL)
+		p = as.s;
+	else
+		p++;
+	r = cmdline_add_arg(data, p);
+	if (r != ARCHIVE_OK)
+		goto exit_function;
+	cmd += al;
+
+	for (;;) {
+		al = get_argument(&as, cmd);
+		if (al < 0) {
+			r = ARCHIVE_FAILED;/* Invalid sequence. */
+			goto exit_function;
+		}
+		if (al == 0)
+			break;
+		cmd += al;
+		if (archive_strlen(&as) == 0 && *cmd == '\0')
+			break;
+		r = cmdline_add_arg(data, as.s);
+		if (r != ARCHIVE_OK)
+			goto exit_function;
+	}
+	r = ARCHIVE_OK;
+exit_function:
+	archive_string_free(&as);
+	return (r);
+}
+
+/*
+ * Set the program path.
+ */
+static int
+cmdline_set_path(struct archive_cmdline *data, const char *path)
+{
+	char *newptr;
+
+	newptr = realloc(data->path, strlen(path) + 1);
+	if (newptr == NULL)
+		return (ARCHIVE_FATAL);
+	data->path = newptr;
+	strcpy(data->path, path);
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Add a argument for the program.
+ */
+static int
+cmdline_add_arg(struct archive_cmdline *data, const char *arg)
+{
+	char **newargv;
+
+	if (data->path == NULL)
+		return (ARCHIVE_FAILED);
+
+	newargv = realloc(data->argv, (data->argc + 2) * sizeof(char *));
+	if (newargv == NULL)
+		return (ARCHIVE_FATAL);
+	data->argv = newargv;
+	data->argv[data->argc] = strdup(arg);
+	if (data->argv[data->argc] == NULL)
+		return (ARCHIVE_FATAL);
+	/* Set the terminator of argv. */
+	data->argv[++data->argc] = NULL;
+	return (ARCHIVE_OK);
+}
+
+struct archive_cmdline *
+__archive_cmdline_allocate(void)
+{
+	return (struct archive_cmdline *)
+		calloc(1, sizeof(struct archive_cmdline));
+}
+
+/*
+ * Release the resources.
+ */
+int
+__archive_cmdline_free(struct archive_cmdline *data)
+{
+
+	if (data) {
+		free(data->path);
+		if (data->argv != NULL) {
+			int i;
+			for (i = 0; data->argv[i] != NULL; i++)
+				free(data->argv[i]);
+			free(data->argv);
+		}
+		free(data);
+	}
+	return (ARCHIVE_OK);
+}
+
diff --git a/c/archive_cmdline_private.h b/c/archive_cmdline_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_cmdline_private.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_CMDLINE_PRIVATE_H
+#define ARCHIVE_CMDLINE_PRIVATE_H
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+struct archive_cmdline {
+        char            *path;
+        char            **argv;
+        int              argc;
+};
+
+struct archive_cmdline *__archive_cmdline_allocate(void);
+int __archive_cmdline_parse(struct archive_cmdline *, const char *);
+int __archive_cmdline_free(struct archive_cmdline *);
+
+#endif
diff --git a/c/archive_crc32.h b/c/archive_crc32.h
new file mode 100644
--- /dev/null
+++ b/c/archive_crc32.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2009 Joerg  Sonnenberger
+ * 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: head/lib/libarchive/archive_crc32.h 201102 2009-12-28 03:11:36Z kientzle $
+ */
+
+#ifndef ARCHIVE_CRC32_H
+#define ARCHIVE_CRC32_H
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/*
+ * When zlib is unavailable, we should still be able to validate
+ * uncompressed zip archives.  That requires us to be able to compute
+ * the CRC32 check value.  This is a drop-in compatible replacement
+ * for crc32() from zlib.  It's slower than the zlib implementation,
+ * but still pretty fast: This runs about 300MB/s on my 3GHz P4
+ * compared to about 800MB/s for the zlib implementation.
+ */
+static unsigned long
+crc32(unsigned long crc, const void *_p, size_t len)
+{
+	unsigned long crc2, b, i;
+	const unsigned char *p = _p;
+	static volatile int crc_tbl_inited = 0;
+	static unsigned long crc_tbl[256];
+
+	if (!crc_tbl_inited) {
+		for (b = 0; b < 256; ++b) {
+			crc2 = b;
+			for (i = 8; i > 0; --i) {
+				if (crc2 & 1)
+					crc2 = (crc2 >> 1) ^ 0xedb88320UL;
+				else    
+					crc2 = (crc2 >> 1);
+			}
+			crc_tbl[b] = crc2;
+		}
+		crc_tbl_inited = 1;
+	}
+
+	crc = crc ^ 0xffffffffUL;
+	/* A use of this loop is about 20% - 30% faster than
+	 * no use version in any optimization option of gcc.  */
+	for (;len >= 8; len -= 8) {
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+	}
+	while (len--)
+		crc = crc_tbl[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+	return (crc ^ 0xffffffffUL);
+}
+
+#endif
diff --git a/c/archive_cryptor.c b/c/archive_cryptor.c
new file mode 100644
--- /dev/null
+++ b/c/archive_cryptor.c
@@ -0,0 +1,519 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* 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"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include "archive.h"
+#include "archive_cryptor_private.h"
+
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * this file will normally define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist.  This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_cryptor_build_hack(void) {
+	return 0;
+}
+
+#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+    size_t salt_len, unsigned rounds, uint8_t *derived_key,
+    size_t derived_key_len)
+{
+	CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pw,
+	    pw_len, salt, salt_len, kCCPRFHmacAlgSHA1, rounds,
+	    derived_key, derived_key_len);
+	return 0;
+}
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+#ifdef _MSC_VER
+#pragma comment(lib, "Bcrypt.lib")
+#endif
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+	size_t salt_len, unsigned rounds, uint8_t *derived_key,
+	size_t derived_key_len)
+{
+	NTSTATUS status;
+	BCRYPT_ALG_HANDLE hAlg;
+
+	status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM,
+		MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG);
+	if (!BCRYPT_SUCCESS(status))
+		return -1;
+
+	status = BCryptDeriveKeyPBKDF2(hAlg,
+		(PUCHAR)(uintptr_t)pw, (ULONG)pw_len,
+		(PUCHAR)(uintptr_t)salt, (ULONG)salt_len, rounds,
+		(PUCHAR)derived_key, (ULONG)derived_key_len, 0);
+
+	BCryptCloseAlgorithmProvider(hAlg, 0);
+
+	return (BCRYPT_SUCCESS(status)) ? 0: -1;
+}
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_PKCS5_H)
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+    size_t salt_len, unsigned rounds, uint8_t *derived_key,
+    size_t derived_key_len)
+{
+	mbedtls_md_context_t ctx;
+	const mbedtls_md_info_t *info;
+	int ret;
+
+	mbedtls_md_init(&ctx);
+	info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+	if (info == NULL) {
+		mbedtls_md_free(&ctx);
+		return (-1);
+	}
+	ret = mbedtls_md_setup(&ctx, info, 1);
+	if (ret != 0) {
+		mbedtls_md_free(&ctx);
+		return (-1);
+	}
+	ret = mbedtls_pkcs5_pbkdf2_hmac(&ctx, (const unsigned char *)pw,
+	    pw_len, salt, salt_len, rounds, derived_key_len, derived_key);
+
+	mbedtls_md_free(&ctx);
+	return (ret);
+}
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_PBKDF2_H)
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+    size_t salt_len, unsigned rounds, uint8_t *derived_key,
+    size_t derived_key_len) {
+	pbkdf2_hmac_sha1((unsigned)pw_len, (const uint8_t *)pw, rounds,
+	    salt_len, salt, derived_key_len, derived_key);
+	return 0;
+}
+
+#elif defined(HAVE_LIBCRYPTO) && defined(HAVE_PKCS5_PBKDF2_HMAC_SHA1)
+
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+    size_t salt_len, unsigned rounds, uint8_t *derived_key,
+    size_t derived_key_len) {
+
+	PKCS5_PBKDF2_HMAC_SHA1(pw, pw_len, salt, salt_len, rounds,
+	    derived_key_len, derived_key);
+	return 0;
+}
+
+#else
+
+/* Stub */
+static int
+pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt,
+    size_t salt_len, unsigned rounds, uint8_t *derived_key,
+    size_t derived_key_len) {
+	(void)pw; /* UNUSED */
+	(void)pw_len; /* UNUSED */
+	(void)salt; /* UNUSED */
+	(void)salt_len; /* UNUSED */
+	(void)rounds; /* UNUSED */
+	(void)derived_key; /* UNUSED */
+	(void)derived_key_len; /* UNUSED */
+	return -1; /* UNSUPPORTED */
+}
+
+#endif
+
+#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+# if MAC_OS_X_VERSION_MAX_ALLOWED < 1090
+#  define kCCAlgorithmAES kCCAlgorithmAES128
+# endif
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+	CCCryptorStatus r;
+
+	ctx->key_len = key_len;
+	memcpy(ctx->key, key, key_len);
+	memset(ctx->nonce, 0, sizeof(ctx->nonce));
+	ctx->encr_pos = AES_BLOCK_SIZE;
+	r = CCCryptorCreateWithMode(kCCEncrypt, kCCModeECB, kCCAlgorithmAES,
+	    ccNoPadding, NULL, key, key_len, NULL, 0, 0, 0, &ctx->ctx);
+	return (r == kCCSuccess)? 0: -1;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+	CCCryptorRef ref = ctx->ctx;
+	CCCryptorStatus r;
+
+	r = CCCryptorReset(ref, NULL);
+	if (r != kCCSuccess && r != kCCUnimplemented)
+		return -1;
+	r = CCCryptorUpdate(ref, ctx->nonce, AES_BLOCK_SIZE, ctx->encr_buf,
+	    AES_BLOCK_SIZE, NULL);
+	return (r == kCCSuccess)? 0: -1;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+	memset(ctx->key, 0, ctx->key_len);
+	memset(ctx->nonce, 0, sizeof(ctx->nonce));
+	return 0;
+}
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+	BCRYPT_ALG_HANDLE hAlg;
+	BCRYPT_KEY_HANDLE hKey;
+	DWORD keyObj_len, aes_key_len;
+	PBYTE keyObj;
+	ULONG result;
+	NTSTATUS status;
+	BCRYPT_KEY_LENGTHS_STRUCT key_lengths;
+
+	ctx->hAlg = NULL;
+	ctx->hKey = NULL;
+	ctx->keyObj = NULL;
+	switch (key_len) {
+	case 16: aes_key_len = 128; break;
+	case 24: aes_key_len = 192; break;
+	case 32: aes_key_len = 256; break;
+	default: return -1;
+	}
+	status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM,
+		MS_PRIMITIVE_PROVIDER, 0);
+	if (!BCRYPT_SUCCESS(status))
+		return -1;
+	status = BCryptGetProperty(hAlg, BCRYPT_KEY_LENGTHS, (PUCHAR)&key_lengths,
+		sizeof(key_lengths), &result, 0);
+	if (!BCRYPT_SUCCESS(status)) {
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+		return -1;
+	}
+	if (key_lengths.dwMinLength > aes_key_len
+		|| key_lengths.dwMaxLength < aes_key_len) {
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+		return -1;
+	}
+	status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&keyObj_len,
+		sizeof(keyObj_len), &result, 0);
+	if (!BCRYPT_SUCCESS(status)) {
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+		return -1;
+	}
+	keyObj = (PBYTE)HeapAlloc(GetProcessHeap(), 0, keyObj_len);
+	if (keyObj == NULL) {
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+		return -1;
+	}
+	status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE,
+		(PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0);
+	if (!BCRYPT_SUCCESS(status)) {
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+		HeapFree(GetProcessHeap(), 0, keyObj);
+		return -1;
+	}
+	status = BCryptGenerateSymmetricKey(hAlg, &hKey,
+		keyObj, keyObj_len,
+		(PUCHAR)(uintptr_t)key, (ULONG)key_len, 0);
+	if (!BCRYPT_SUCCESS(status)) {
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+		HeapFree(GetProcessHeap(), 0, keyObj);
+		return -1;
+	}
+
+	ctx->hAlg = hAlg;
+	ctx->hKey = hKey;
+	ctx->keyObj = keyObj;
+	ctx->keyObj_len = keyObj_len;
+	ctx->encr_pos = AES_BLOCK_SIZE;
+
+	return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+	NTSTATUS status;
+	ULONG result;
+
+	status = BCryptEncrypt(ctx->hKey, (PUCHAR)ctx->nonce, AES_BLOCK_SIZE,
+		NULL, NULL, 0, (PUCHAR)ctx->encr_buf, AES_BLOCK_SIZE,
+		&result, 0);
+	return BCRYPT_SUCCESS(status) ? 0 : -1;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+
+	if (ctx->hAlg != NULL) {
+		BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+		ctx->hAlg = NULL;
+		BCryptDestroyKey(ctx->hKey);
+		ctx->hKey = NULL;
+		HeapFree(GetProcessHeap(), 0, ctx->keyObj);
+		ctx->keyObj = NULL;
+	}
+	memset(ctx, 0, sizeof(*ctx));
+	return 0;
+}
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+	mbedtls_aes_init(&ctx->ctx);
+	ctx->key_len = key_len;
+	memcpy(ctx->key, key, key_len);
+	memset(ctx->nonce, 0, sizeof(ctx->nonce));
+	ctx->encr_pos = AES_BLOCK_SIZE;
+	return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+	if (mbedtls_aes_setkey_enc(&ctx->ctx, ctx->key,
+	    ctx->key_len * 8) != 0)
+		return (-1);
+	if (mbedtls_aes_crypt_ecb(&ctx->ctx, MBEDTLS_AES_ENCRYPT, ctx->nonce,
+	    ctx->encr_buf) != 0)
+		return (-1);
+	return 0;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+	mbedtls_aes_free(&ctx->ctx);
+	memset(ctx, 0, sizeof(*ctx));
+	return 0;
+}
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+	ctx->key_len = key_len;
+	memcpy(ctx->key, key, key_len);
+	memset(ctx->nonce, 0, sizeof(ctx->nonce));
+	ctx->encr_pos = AES_BLOCK_SIZE;
+	memset(&ctx->ctx, 0, sizeof(ctx->ctx));
+	return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+	aes_set_encrypt_key(&ctx->ctx, ctx->key_len, ctx->key);
+	aes_encrypt(&ctx->ctx, AES_BLOCK_SIZE, ctx->encr_buf, ctx->nonce);
+	return 0;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+	memset(ctx, 0, sizeof(*ctx));
+	return 0;
+}
+
+#elif defined(HAVE_LIBCRYPTO)
+
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+	if ((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL)
+		return -1;
+
+	switch (key_len) {
+	case 16: ctx->type = EVP_aes_128_ecb(); break;
+	case 24: ctx->type = EVP_aes_192_ecb(); break;
+	case 32: ctx->type = EVP_aes_256_ecb(); break;
+	default: ctx->type = NULL; return -1;
+	}
+
+	ctx->key_len = key_len;
+	memcpy(ctx->key, key, key_len);
+	memset(ctx->nonce, 0, sizeof(ctx->nonce));
+	ctx->encr_pos = AES_BLOCK_SIZE;
+#if OPENSSL_VERSION_NUMBER  >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+	if (!EVP_CIPHER_CTX_reset(ctx->ctx)) {
+		EVP_CIPHER_CTX_free(ctx->ctx);
+		ctx->ctx = NULL;
+	}
+#else
+	EVP_CIPHER_CTX_init(ctx->ctx);
+#endif
+	return 0;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+	int outl = 0;
+	int r;
+
+	r = EVP_EncryptInit_ex(ctx->ctx, ctx->type, NULL, ctx->key, NULL);
+	if (r == 0)
+		return -1;
+	r = EVP_EncryptUpdate(ctx->ctx, ctx->encr_buf, &outl, ctx->nonce,
+	    AES_BLOCK_SIZE);
+	if (r == 0 || outl != AES_BLOCK_SIZE)
+		return -1;
+	return 0;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+	EVP_CIPHER_CTX_free(ctx->ctx);
+	memset(ctx->key, 0, ctx->key_len);
+	memset(ctx->nonce, 0, sizeof(ctx->nonce));
+	return 0;
+}
+
+#else
+
+#define ARCHIVE_CRYPTOR_STUB
+/* Stub */
+static int
+aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+	(void)ctx; /* UNUSED */
+	(void)key; /* UNUSED */
+	(void)key_len; /* UNUSED */
+	return -1;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+	(void)ctx; /* UNUSED */
+	return -1;
+}
+
+static int
+aes_ctr_release(archive_crypto_ctx *ctx)
+{
+	(void)ctx; /* UNUSED */
+	return 0;
+}
+
+#endif
+
+#ifdef ARCHIVE_CRYPTOR_STUB
+static int
+aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in,
+    size_t in_len, uint8_t * const out, size_t *out_len)
+{
+	(void)ctx; /* UNUSED */
+	(void)in; /* UNUSED */
+	(void)in_len; /* UNUSED */
+	(void)out; /* UNUSED */
+	(void)out_len; /* UNUSED */
+	aes_ctr_encrypt_counter(ctx); /* UNUSED */ /* Fix unused function warning */
+	return -1;
+}
+
+#else
+static void
+aes_ctr_increase_counter(archive_crypto_ctx *ctx)
+{
+	uint8_t *const nonce = ctx->nonce;
+	int j;
+
+	for (j = 0; j < 8; j++) {
+		if (++nonce[j])
+			break;
+	}
+}
+
+static int
+aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in,
+    size_t in_len, uint8_t * const out, size_t *out_len)
+{
+	uint8_t *const ebuf = ctx->encr_buf;
+	unsigned pos = ctx->encr_pos;
+	unsigned max = (unsigned)((in_len < *out_len)? in_len: *out_len);
+	unsigned i;
+
+	for (i = 0; i < max; ) {
+		if (pos == AES_BLOCK_SIZE) {
+			aes_ctr_increase_counter(ctx);
+			if (aes_ctr_encrypt_counter(ctx) != 0)
+				return -1;
+			while (max -i >= AES_BLOCK_SIZE) {
+				for (pos = 0; pos < AES_BLOCK_SIZE; pos++)
+					out[i+pos] = in[i+pos] ^ ebuf[pos];
+				i += AES_BLOCK_SIZE;
+				aes_ctr_increase_counter(ctx);
+				if (aes_ctr_encrypt_counter(ctx) != 0)
+					return -1;
+			}
+			pos = 0;
+			if (i >= max)
+				break;
+		}
+		out[i] = in[i] ^ ebuf[pos++];
+		i++;
+	}
+	ctx->encr_pos = pos;
+	*out_len = i;
+
+	return 0;
+}
+#endif /* ARCHIVE_CRYPTOR_STUB */
+
+
+const struct archive_cryptor __archive_cryptor =
+{
+  &pbkdf2_sha1,
+  &aes_ctr_init,
+  &aes_ctr_update,
+  &aes_ctr_release,
+  &aes_ctr_init,
+  &aes_ctr_update,
+  &aes_ctr_release,
+};
diff --git a/c/archive_cryptor_private.h b/c/archive_cryptor_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_cryptor_private.h
@@ -0,0 +1,179 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* 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.
+*/
+
+#ifndef ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED
+#define ARCHIVE_CRYPTOR_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * the archive_cryptor.c file will normally define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist.  This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_cryptor_build_hack(void);
+
+#ifdef __APPLE__
+# include <AvailabilityMacros.h>
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
+#  define ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+# endif
+#endif
+
+#ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto
+#include <CommonCrypto/CommonCryptor.h>
+#include <CommonCrypto/CommonKeyDerivation.h>
+#define AES_BLOCK_SIZE	16
+#define AES_MAX_KEY_SIZE kCCKeySizeAES256
+
+typedef struct {
+	CCCryptorRef	ctx;
+	uint8_t		key[AES_MAX_KEY_SIZE];
+	unsigned	key_len;
+	uint8_t		nonce[AES_BLOCK_SIZE];
+	uint8_t		encr_buf[AES_BLOCK_SIZE];
+	unsigned	encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#define AES_MAX_KEY_SIZE 32
+#define AES_BLOCK_SIZE 16
+typedef struct {
+	BCRYPT_ALG_HANDLE hAlg;
+	BCRYPT_KEY_HANDLE hKey;
+	PBYTE		keyObj;
+	DWORD		keyObj_len;
+	uint8_t		nonce[AES_BLOCK_SIZE];
+	uint8_t		encr_buf[AES_BLOCK_SIZE];
+	unsigned	encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_AES_H)
+#include <mbedtls/aes.h>
+#include <mbedtls/md.h>
+#include <mbedtls/pkcs5.h>
+
+#define AES_MAX_KEY_SIZE 32
+#define AES_BLOCK_SIZE 16
+
+typedef struct {
+	mbedtls_aes_context	ctx;
+	uint8_t		key[AES_MAX_KEY_SIZE];
+	unsigned	key_len;
+	uint8_t		nonce[AES_BLOCK_SIZE];
+	uint8_t		encr_buf[AES_BLOCK_SIZE];
+	unsigned	encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_AES_H)
+#if defined(HAVE_NETTLE_PBKDF2_H)
+#include <nettle/pbkdf2.h>
+#endif
+#include <nettle/aes.h>
+
+typedef struct {
+	struct aes_ctx	ctx;
+	uint8_t		key[AES_MAX_KEY_SIZE];
+	unsigned	key_len;
+	uint8_t		nonce[AES_BLOCK_SIZE];
+	uint8_t		encr_buf[AES_BLOCK_SIZE];
+	unsigned	encr_pos;
+} archive_crypto_ctx;
+
+#elif defined(HAVE_LIBCRYPTO)
+#include "archive_openssl_evp_private.h"
+#define AES_BLOCK_SIZE	16
+#define AES_MAX_KEY_SIZE 32
+
+typedef struct {
+	EVP_CIPHER_CTX	*ctx;
+	const EVP_CIPHER *type;
+	uint8_t		key[AES_MAX_KEY_SIZE];
+	unsigned	key_len;
+	uint8_t		nonce[AES_BLOCK_SIZE];
+	uint8_t		encr_buf[AES_BLOCK_SIZE];
+	unsigned	encr_pos;
+} archive_crypto_ctx;
+
+#else
+
+#define AES_BLOCK_SIZE	16
+#define AES_MAX_KEY_SIZE 32
+typedef int archive_crypto_ctx;
+
+#endif
+
+/* defines */
+#define archive_pbkdf2_sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len)\
+  __archive_cryptor.pbkdf2sha1(pw, pw_len, salt, salt_len, rounds, dk, dk_len)
+
+#define archive_decrypto_aes_ctr_init(ctx, key, key_len) \
+  __archive_cryptor.decrypto_aes_ctr_init(ctx, key, key_len)
+#define archive_decrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \
+  __archive_cryptor.decrypto_aes_ctr_update(ctx, in, in_len, out, out_len)
+#define archive_decrypto_aes_ctr_release(ctx) \
+  __archive_cryptor.decrypto_aes_ctr_release(ctx)
+
+#define archive_encrypto_aes_ctr_init(ctx, key, key_len) \
+  __archive_cryptor.encrypto_aes_ctr_init(ctx, key, key_len)
+#define archive_encrypto_aes_ctr_update(ctx, in, in_len, out, out_len) \
+  __archive_cryptor.encrypto_aes_ctr_update(ctx, in, in_len, out, out_len)
+#define archive_encrypto_aes_ctr_release(ctx) \
+  __archive_cryptor.encrypto_aes_ctr_release(ctx)
+
+/* Minimal interface to cryptographic functionality for internal use in
+ * libarchive */
+struct archive_cryptor
+{
+  /* PKCS5 PBKDF2 HMAC-SHA1 */
+  int (*pbkdf2sha1)(const char *pw, size_t pw_len, const uint8_t *salt,
+    size_t salt_len, unsigned rounds, uint8_t *derived_key,
+    size_t derived_key_len);
+  /* AES CTR mode(little endian version) */
+  int (*decrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t);
+  int (*decrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *,
+    size_t, uint8_t *, size_t *);
+  int (*decrypto_aes_ctr_release)(archive_crypto_ctx *);
+  int (*encrypto_aes_ctr_init)(archive_crypto_ctx *, const uint8_t *, size_t);
+  int (*encrypto_aes_ctr_update)(archive_crypto_ctx *, const uint8_t *,
+    size_t, uint8_t *, size_t *);
+  int (*encrypto_aes_ctr_release)(archive_crypto_ctx *);
+};
+
+extern const struct archive_cryptor __archive_cryptor;
+
+#endif
diff --git a/c/archive_digest.c b/c/archive_digest.c
new file mode 100644
--- /dev/null
+++ b/c/archive_digest.c
@@ -0,0 +1,1499 @@
+/*-
+* Copyright (c) 2003-2007 Tim Kientzle
+* Copyright (c) 2011 Andres Mejia
+* Copyright (c) 2011 Michihiro NAKAJIMA
+* 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"
+
+#include "archive.h"
+#include "archive_digest_private.h"
+
+/* In particular, force the configure probe to break if it tries
+ * to test a combination of OpenSSL and libmd. */
+#if defined(ARCHIVE_CRYPTO_OPENSSL) && defined(ARCHIVE_CRYPTO_LIBMD)
+#error Cannot use both OpenSSL and libmd.
+#endif
+
+/*
+ * Message digest functions for Windows platform.
+ */
+#if defined(ARCHIVE_CRYPTO_MD5_WIN)    ||\
+	defined(ARCHIVE_CRYPTO_SHA1_WIN)   ||\
+	defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\
+	defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\
+	defined(ARCHIVE_CRYPTO_SHA512_WIN)
+
+/*
+ * Initialize a Message digest.
+ */
+static int
+win_crypto_init(Digest_CTX *ctx, ALG_ID algId)
+{
+
+	ctx->valid = 0;
+	if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
+	    PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+		if (GetLastError() != (DWORD)NTE_BAD_KEYSET)
+			return (ARCHIVE_FAILED);
+		if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
+		    PROV_RSA_FULL, CRYPT_NEWKEYSET))
+			return (ARCHIVE_FAILED);
+	}
+
+	if (!CryptCreateHash(ctx->cryptProv, algId, 0, 0, &ctx->hash)) {
+		CryptReleaseContext(ctx->cryptProv, 0);
+		return (ARCHIVE_FAILED);
+	}
+
+	ctx->valid = 1;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Update a Message digest.
+ */
+static int
+win_crypto_Update(Digest_CTX *ctx, const unsigned char *buf, size_t len)
+{
+
+	if (!ctx->valid)
+		return (ARCHIVE_FAILED);
+
+	CryptHashData(ctx->hash,
+		      (unsigned char *)(uintptr_t)buf,
+		      (DWORD)len, 0);
+	return (ARCHIVE_OK);
+}
+
+static int
+win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx)
+{
+	DWORD siglen = (DWORD)bufsize;
+
+	if (!ctx->valid)
+		return (ARCHIVE_FAILED);
+
+	CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0);
+	CryptDestroyHash(ctx->hash);
+	CryptReleaseContext(ctx->cryptProv, 0);
+	ctx->valid = 0;
+	return (ARCHIVE_OK);
+}
+
+#endif /* defined(ARCHIVE_CRYPTO_*_WIN) */
+
+
+/* MD5 implementations */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+  MD5Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  MD5Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+  MD5Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBMD)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+  MD5Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  MD5Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+  MD5Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+  CC_MD5_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  CC_MD5_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+  CC_MD5_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+  mbedtls_md5_init(ctx);
+  if (mbedtls_md5_starts_ret(ctx) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  if (mbedtls_md5_update_ret(ctx, indata, insize) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+  if (mbedtls_md5_finish_ret(ctx, md) == 0) {
+    mbedtls_md5_free(ctx);
+    return (ARCHIVE_OK);
+  } else {
+    mbedtls_md5_free(ctx);
+    return (ARCHIVE_FATAL);
+  }
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+  md5_init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  md5_update(ctx, insize, indata);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+  md5_digest(ctx, MD5_DIGEST_SIZE, md);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+  if ((*ctx = EVP_MD_CTX_new()) == NULL)
+	return (ARCHIVE_FAILED);
+  EVP_DigestInit(*ctx, EVP_md5());
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  EVP_DigestUpdate(*ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+  /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so
+   * this is meant to cope with that. Real fix is probably to fix
+   * archive_write_set_format_xar.c
+   */
+  if (*ctx) {
+    EVP_DigestFinal(*ctx, md, NULL);
+    EVP_MD_CTX_free(*ctx);
+    *ctx = NULL;
+  }
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_WIN)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+  return (win_crypto_init(ctx, CALG_MD5));
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+  return (win_crypto_Final(md, 16, ctx));
+}
+
+#else
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+	(void)ctx; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+    size_t insize)
+{
+	(void)ctx; /* UNUSED */
+	(void)indata; /* UNUSED */
+	(void)insize; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+	(void)ctx; /* UNUSED */
+	(void)md; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* RIPEMD160 implementations */
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+  RMD160Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  RMD160Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+  RMD160Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+  RIPEMD160_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  RIPEMD160_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+  RIPEMD160_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+  mbedtls_ripemd160_init(ctx);
+  if (mbedtls_ripemd160_starts_ret(ctx) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  if (mbedtls_ripemd160_update_ret(ctx, indata, insize) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+  if (mbedtls_ripemd160_finish_ret(ctx, md) == 0) {
+    mbedtls_ripemd160_free(ctx);
+    return (ARCHIVE_OK);
+  } else {
+    mbedtls_ripemd160_free(ctx);
+    return (ARCHIVE_FATAL);
+  }
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+  ripemd160_init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  ripemd160_update(ctx, insize, indata);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+  ripemd160_digest(ctx, RIPEMD160_DIGEST_SIZE, md);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+  if ((*ctx = EVP_MD_CTX_new()) == NULL)
+	return (ARCHIVE_FAILED);
+  EVP_DigestInit(*ctx, EVP_ripemd160());
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  EVP_DigestUpdate(*ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+  if (*ctx) {
+    EVP_DigestFinal(*ctx, md, NULL);
+    EVP_MD_CTX_free(*ctx);
+    *ctx = NULL;
+  }
+  return (ARCHIVE_OK);
+}
+
+#else
+
+static int
+__archive_ripemd160init(archive_rmd160_ctx *ctx)
+{
+	(void)ctx; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+    size_t insize)
+{
+	(void)ctx; /* UNUSED */
+	(void)indata; /* UNUSED */
+	(void)insize; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+	(void)ctx; /* UNUSED */
+	(void)md; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA1 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+  SHA1Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA1Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+  SHA1Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+  SHA1_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA1_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+  SHA1_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+  CC_SHA1_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  CC_SHA1_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+  CC_SHA1_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+  mbedtls_sha1_init(ctx);
+  if (mbedtls_sha1_starts_ret(ctx) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  if (mbedtls_sha1_update_ret(ctx, indata, insize) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+  if (mbedtls_sha1_finish_ret(ctx, md) == 0) {
+    mbedtls_sha1_free(ctx);
+    return (ARCHIVE_OK);
+  } else {
+    mbedtls_sha1_free(ctx);
+    return (ARCHIVE_FATAL);
+  }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+  sha1_init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  sha1_update(ctx, insize, indata);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+  sha1_digest(ctx, SHA1_DIGEST_SIZE, md);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+  if ((*ctx = EVP_MD_CTX_new()) == NULL)
+	return (ARCHIVE_FAILED);
+  EVP_DigestInit(*ctx, EVP_sha1());
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  EVP_DigestUpdate(*ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+  /* HACK: archive_write_set_format_xar.c is finalizing empty contexts, so
+   * this is meant to cope with that. Real fix is probably to fix
+   * archive_write_set_format_xar.c
+   */
+  if (*ctx) {
+    EVP_DigestFinal(*ctx, md, NULL);
+    EVP_MD_CTX_free(*ctx);
+    *ctx = NULL;
+  }
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_WIN)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+  return (win_crypto_init(ctx, CALG_SHA1));
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+  return (win_crypto_Final(md, 20, ctx));
+}
+
+#else
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+	(void)ctx; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+    size_t insize)
+{
+	(void)ctx; /* UNUSED */
+	(void)indata; /* UNUSED */
+	(void)insize; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+	(void)ctx; /* UNUSED */
+	(void)md; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA256 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+  SHA256_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA256_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+  SHA256_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+  SHA256Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA256Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+  SHA256Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+  SHA256Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA256Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+  SHA256Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+  SHA256_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA256_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+  SHA256_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+  CC_SHA256_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  CC_SHA256_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+  CC_SHA256_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+  mbedtls_sha256_init(ctx);
+  if (mbedtls_sha256_starts_ret(ctx, 0) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  if (mbedtls_sha256_update_ret(ctx, indata, insize) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+  if (mbedtls_sha256_finish_ret(ctx, md) == 0) {
+    mbedtls_sha256_free(ctx);
+    return (ARCHIVE_OK);
+  } else {
+    mbedtls_sha256_free(ctx);
+    return (ARCHIVE_FATAL);
+  }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+  sha256_init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  sha256_update(ctx, insize, indata);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+  sha256_digest(ctx, SHA256_DIGEST_SIZE, md);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+  if ((*ctx = EVP_MD_CTX_new()) == NULL)
+	return (ARCHIVE_FAILED);
+  EVP_DigestInit(*ctx, EVP_sha256());
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  EVP_DigestUpdate(*ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+  if (*ctx) {
+    EVP_DigestFinal(*ctx, md, NULL);
+    EVP_MD_CTX_free(*ctx);
+    *ctx = NULL;
+  }
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_WIN)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+  return (win_crypto_init(ctx, CALG_SHA_256));
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+  return (win_crypto_Final(md, 32, ctx));
+}
+
+#else
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+	(void)ctx; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+	(void)ctx; /* UNUSED */
+	(void)indata; /* UNUSED */
+	(void)insize; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+	(void)ctx; /* UNUSED */
+	(void)md; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA384 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA384_LIBC)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+  SHA384_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA384_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+  SHA384_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+  SHA384Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA384Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+  SHA384Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+  SHA384Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA384Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+  SHA384Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+  CC_SHA384_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  CC_SHA384_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+  CC_SHA384_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+  mbedtls_sha512_init(ctx);
+  if (mbedtls_sha512_starts_ret(ctx, 1) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+  if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
+    mbedtls_sha512_free(ctx);
+    return (ARCHIVE_OK);
+  } else {
+    mbedtls_sha512_free(ctx);
+    return (ARCHIVE_FATAL);
+  }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+  sha384_init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  sha384_update(ctx, insize, indata);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+  sha384_digest(ctx, SHA384_DIGEST_SIZE, md);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+  if ((*ctx = EVP_MD_CTX_new()) == NULL)
+	return (ARCHIVE_FAILED);
+  EVP_DigestInit(*ctx, EVP_sha384());
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  EVP_DigestUpdate(*ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+  if (*ctx) {
+    EVP_DigestFinal(*ctx, md, NULL);
+    EVP_MD_CTX_free(*ctx);
+    *ctx = NULL;
+  }
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_WIN)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+  return (win_crypto_init(ctx, CALG_SHA_384));
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+  return (win_crypto_Final(md, 48, ctx));
+}
+
+#else
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+	(void)ctx; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+    size_t insize)
+{
+	(void)ctx; /* UNUSED */
+	(void)indata; /* UNUSED */
+	(void)insize; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+	(void)ctx; /* UNUSED */
+	(void)md; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* SHA512 implementations */
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBC)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+  SHA512_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA512_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+  SHA512_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+  SHA512Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA512Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+  SHA512Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+  SHA512Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA512Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+  SHA512Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+  SHA512_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  SHA512_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+  SHA512_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+  CC_SHA512_Init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  CC_SHA512_Update(ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+  CC_SHA512_Final(md, ctx);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+  mbedtls_sha512_init(ctx);
+  if (mbedtls_sha512_starts_ret(ctx, 0) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+  if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
+    mbedtls_sha512_free(ctx);
+    return (ARCHIVE_OK);
+  } else {
+    mbedtls_sha512_free(ctx);
+    return (ARCHIVE_FATAL);
+  }
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+  sha512_init(ctx);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  sha512_update(ctx, insize, indata);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+  sha512_digest(ctx, SHA512_DIGEST_SIZE, md);
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+  if ((*ctx = EVP_MD_CTX_new()) == NULL)
+	return (ARCHIVE_FAILED);
+  EVP_DigestInit(*ctx, EVP_sha512());
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  EVP_DigestUpdate(*ctx, indata, insize);
+  return (ARCHIVE_OK);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+  if (*ctx) {
+    EVP_DigestFinal(*ctx, md, NULL);
+    EVP_MD_CTX_free(*ctx);
+    *ctx = NULL;
+  }
+  return (ARCHIVE_OK);
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_WIN)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+  return (win_crypto_init(ctx, CALG_SHA_512));
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+  return (win_crypto_Update(ctx, indata, insize));
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+  return (win_crypto_Final(md, 64, ctx));
+}
+
+#else
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+	(void)ctx; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+	(void)ctx; /* UNUSED */
+	(void)indata; /* UNUSED */
+	(void)insize; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+	(void)ctx; /* UNUSED */
+	(void)md; /* UNUSED */
+	return (ARCHIVE_FAILED);
+}
+
+#endif
+
+/* NOTE: Message Digest functions are set based on availability and by the
+ * following order of preference.
+ * 1. libc
+ * 2. libc2
+ * 3. libc3
+ * 4. libSystem
+ * 5. Nettle
+ * 6. OpenSSL
+ * 7. libmd
+ * 8. Windows API
+ */
+const struct archive_digest __archive_digest =
+{
+/* MD5 */
+  &__archive_md5init,
+  &__archive_md5update,
+  &__archive_md5final,
+
+/* RIPEMD160 */
+  &__archive_ripemd160init,
+  &__archive_ripemd160update,
+  &__archive_ripemd160final,
+
+/* SHA1 */
+  &__archive_sha1init,
+  &__archive_sha1update,
+  &__archive_sha1final,
+
+/* SHA256 */
+  &__archive_sha256init,
+  &__archive_sha256update,
+  &__archive_sha256final,
+
+/* SHA384 */
+  &__archive_sha384init,
+  &__archive_sha384update,
+  &__archive_sha384final,
+
+/* SHA512 */
+  &__archive_sha512init,
+  &__archive_sha512update,
+  &__archive_sha512final
+};
diff --git a/c/archive_digest_private.h b/c/archive_digest_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_digest_private.h
@@ -0,0 +1,412 @@
+/*-
+* Copyright (c) 2003-2007 Tim Kientzle
+* Copyright (c) 2011 Andres Mejia
+* 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.
+*/
+
+#ifndef ARCHIVE_DIGEST_PRIVATE_H_INCLUDED
+#define ARCHIVE_DIGEST_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+/*
+ * Crypto support in various Operating Systems:
+ *
+ * NetBSD:
+ * - MD5 and SHA1 in libc: without _ after algorithm name
+ * - SHA2 in libc: with _ after algorithm name
+ *
+ * OpenBSD:
+ * - MD5, SHA1 and SHA2 in libc: without _ after algorithm name
+ * - OpenBSD 4.4 and earlier have SHA2 in libc with _ after algorithm name
+ *
+ * DragonFly and FreeBSD:
+ * - MD5 libmd: without _ after algorithm name
+ * - SHA1, SHA256 and SHA512 in libmd: with _ after algorithm name
+ *
+ * Mac OS X (10.4 and later):
+ * - MD5, SHA1 and SHA2 in libSystem: with CC_ prefix and _ after algorithm name
+ *
+ * OpenSSL:
+ * - MD5, SHA1 and SHA2 in libcrypto: with _ after algorithm name
+ *
+ * Windows:
+ * - MD5, SHA1 and SHA2 in archive_crypto.c using Windows crypto API
+ */
+
+/* libc crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
+#include <md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
+#include <rmd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
+#include <sha1.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
+#include <sha2.h>
+#endif
+
+/* libmd crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBMD) ||\
+  defined(ARCHIVE_CRYPTO_RMD160_LIBMD) ||\
+  defined(ARCHIVE_CRYPTO_SHA1_LIBMD) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+#define	ARCHIVE_CRYPTO_LIBMD 1
+#endif
+
+#if defined(ARCHIVE_CRYPTO_MD5_LIBMD)
+#include <md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
+#include <ripemd.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
+#include <sha.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
+#include <sha256.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+#include <sha512.h>
+#endif
+
+/* libSystem crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\
+  defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
+#include <CommonCrypto/CommonDigest.h>
+#endif
+
+/* mbed TLS crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+#include <mbedtls/md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
+#include <mbedtls/ripemd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+#include <mbedtls/sha1.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+#include <mbedtls/sha256.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+#include <mbedtls/sha512.h>
+#endif
+
+/* Nettle crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+#include <nettle/md5.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+#include <nettle/ripemd160.h>
+#endif
+#if defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
+#include <nettle/sha.h>
+#endif
+
+/* OpenSSL crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\
+  defined(ARCHIVE_CRYPTO_RMD160_OPENSSL) ||\
+  defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
+#define	ARCHIVE_CRYPTO_OPENSSL 1
+#include "archive_openssl_evp_private.h"
+#endif
+
+/* Windows crypto headers */
+#if defined(ARCHIVE_CRYPTO_MD5_WIN)    ||\
+  defined(ARCHIVE_CRYPTO_SHA1_WIN)   ||\
+  defined(ARCHIVE_CRYPTO_SHA256_WIN) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_WIN) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_WIN)
+#include <windows.h>
+#include <wincrypt.h>
+typedef struct {
+  int   valid;
+  HCRYPTPROV  cryptProv;
+  HCRYPTHASH  hash;
+} Digest_CTX;
+#endif
+
+/* typedefs */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC)
+typedef MD5_CTX archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBMD)
+typedef MD5_CTX archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM)
+typedef CC_MD5_CTX archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+typedef mbedtls_md5_context archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+typedef struct md5_ctx archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)
+typedef EVP_MD_CTX *archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_WIN)
+typedef Digest_CTX archive_md5_ctx;
+#else
+typedef unsigned char archive_md5_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC)
+typedef RMD160_CTX archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_LIBMD)
+typedef RIPEMD160_CTX archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS)
+typedef mbedtls_ripemd160_context archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+typedef struct ripemd160_ctx archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
+typedef EVP_MD_CTX *archive_rmd160_ctx;
+#else
+typedef unsigned char archive_rmd160_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC)
+typedef SHA1_CTX archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBMD)
+typedef SHA1_CTX archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM)
+typedef CC_SHA1_CTX archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+typedef mbedtls_sha1_context archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
+typedef struct sha1_ctx archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)
+typedef EVP_MD_CTX *archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_WIN)
+typedef Digest_CTX archive_sha1_ctx;
+#else
+typedef unsigned char archive_sha1_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC)
+typedef SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC2)
+typedef SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBC3)
+typedef SHA2_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBMD)
+typedef SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM)
+typedef CC_SHA256_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+typedef mbedtls_sha256_context archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
+typedef struct sha256_ctx archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)
+typedef EVP_MD_CTX *archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_WIN)
+typedef Digest_CTX archive_sha256_ctx;
+#else
+typedef unsigned char archive_sha256_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA384_LIBC)
+typedef SHA384_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC2)
+typedef SHA384_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBC3)
+typedef SHA2_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM)
+typedef CC_SHA512_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
+typedef mbedtls_sha512_context archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
+typedef struct sha384_ctx archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)
+typedef EVP_MD_CTX *archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_WIN)
+typedef Digest_CTX archive_sha384_ctx;
+#else
+typedef unsigned char archive_sha384_ctx;
+#endif
+
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBC)
+typedef SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC2)
+typedef SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBC3)
+typedef SHA2_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBMD)
+typedef SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM)
+typedef CC_SHA512_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+typedef mbedtls_sha512_context archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
+typedef struct sha512_ctx archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
+typedef EVP_MD_CTX *archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_WIN)
+typedef Digest_CTX archive_sha512_ctx;
+#else
+typedef unsigned char archive_sha512_ctx;
+#endif
+
+/* defines */
+#if defined(ARCHIVE_CRYPTO_MD5_LIBC) ||\
+  defined(ARCHIVE_CRYPTO_MD5_LIBMD) ||	\
+  defined(ARCHIVE_CRYPTO_MD5_LIBSYSTEM) ||\
+  defined(ARCHIVE_CRYPTO_MD5_MBEDTLS) ||\
+  defined(ARCHIVE_CRYPTO_MD5_NETTLE) ||\
+  defined(ARCHIVE_CRYPTO_MD5_OPENSSL) ||\
+  defined(ARCHIVE_CRYPTO_MD5_WIN)
+#define ARCHIVE_HAS_MD5
+#endif
+#define archive_md5_init(ctx)\
+  __archive_digest.md5init(ctx)
+#define archive_md5_final(ctx, md)\
+  __archive_digest.md5final(ctx, md)
+#define archive_md5_update(ctx, buf, n)\
+  __archive_digest.md5update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_RMD160_LIBC) ||\
+  defined(ARCHIVE_CRYPTO_RMD160_MBEDTLS) ||\
+  defined(ARCHIVE_CRYPTO_RMD160_NETTLE) ||\
+  defined(ARCHIVE_CRYPTO_RMD160_OPENSSL)
+#define ARCHIVE_HAS_RMD160
+#endif
+#define archive_rmd160_init(ctx)\
+  __archive_digest.rmd160init(ctx)
+#define archive_rmd160_final(ctx, md)\
+  __archive_digest.rmd160final(ctx, md)
+#define archive_rmd160_update(ctx, buf, n)\
+  __archive_digest.rmd160update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA1_LIBC) ||\
+  defined(ARCHIVE_CRYPTO_SHA1_LIBMD) ||	\
+  defined(ARCHIVE_CRYPTO_SHA1_LIBSYSTEM) ||\
+  defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS) ||\
+  defined(ARCHIVE_CRYPTO_SHA1_NETTLE) ||\
+  defined(ARCHIVE_CRYPTO_SHA1_OPENSSL) ||\
+  defined(ARCHIVE_CRYPTO_SHA1_WIN)
+#define ARCHIVE_HAS_SHA1
+#endif
+#define archive_sha1_init(ctx)\
+  __archive_digest.sha1init(ctx)
+#define archive_sha1_final(ctx, md)\
+  __archive_digest.sha1final(ctx, md)
+#define archive_sha1_update(ctx, buf, n)\
+  __archive_digest.sha1update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA256_LIBC) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_LIBC2) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_LIBC3) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_LIBMD) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_LIBSYSTEM) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_NETTLE) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_OPENSSL) ||\
+  defined(ARCHIVE_CRYPTO_SHA256_WIN)
+#define ARCHIVE_HAS_SHA256
+#endif
+#define archive_sha256_init(ctx)\
+  __archive_digest.sha256init(ctx)
+#define archive_sha256_final(ctx, md)\
+  __archive_digest.sha256final(ctx, md)
+#define archive_sha256_update(ctx, buf, n)\
+  __archive_digest.sha256update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA384_LIBC) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_LIBC2) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_LIBC3) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_LIBSYSTEM) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_NETTLE) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_OPENSSL) ||\
+  defined(ARCHIVE_CRYPTO_SHA384_WIN)
+#define ARCHIVE_HAS_SHA384
+#endif
+#define archive_sha384_init(ctx)\
+  __archive_digest.sha384init(ctx)
+#define archive_sha384_final(ctx, md)\
+  __archive_digest.sha384final(ctx, md)
+#define archive_sha384_update(ctx, buf, n)\
+  __archive_digest.sha384update(ctx, buf, n)
+
+#if defined(ARCHIVE_CRYPTO_SHA512_LIBC) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_LIBC2) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_LIBC3) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_LIBMD) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_LIBSYSTEM) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_NETTLE) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_OPENSSL) ||\
+  defined(ARCHIVE_CRYPTO_SHA512_WIN)
+#define ARCHIVE_HAS_SHA512
+#endif
+#define archive_sha512_init(ctx)\
+  __archive_digest.sha512init(ctx)
+#define archive_sha512_final(ctx, md)\
+  __archive_digest.sha512final(ctx, md)
+#define archive_sha512_update(ctx, buf, n)\
+  __archive_digest.sha512update(ctx, buf, n)
+
+/* Minimal interface to digest functionality for internal use in libarchive */
+struct archive_digest
+{
+  /* Message Digest */
+  int (*md5init)(archive_md5_ctx *ctx);
+  int (*md5update)(archive_md5_ctx *, const void *, size_t);
+  int (*md5final)(archive_md5_ctx *, void *);
+  int (*rmd160init)(archive_rmd160_ctx *);
+  int (*rmd160update)(archive_rmd160_ctx *, const void *, size_t);
+  int (*rmd160final)(archive_rmd160_ctx *, void *);
+  int (*sha1init)(archive_sha1_ctx *);
+  int (*sha1update)(archive_sha1_ctx *, const void *, size_t);
+  int (*sha1final)(archive_sha1_ctx *, void *);
+  int (*sha256init)(archive_sha256_ctx *);
+  int (*sha256update)(archive_sha256_ctx *, const void *, size_t);
+  int (*sha256final)(archive_sha256_ctx *, void *);
+  int (*sha384init)(archive_sha384_ctx *);
+  int (*sha384update)(archive_sha384_ctx *, const void *, size_t);
+  int (*sha384final)(archive_sha384_ctx *, void *);
+  int (*sha512init)(archive_sha512_ctx *);
+  int (*sha512update)(archive_sha512_ctx *, const void *, size_t);
+  int (*sha512final)(archive_sha512_ctx *, void *);
+};
+
+extern const struct archive_digest __archive_digest;
+
+#endif
diff --git a/c/archive_disk_acl_darwin.c b/c/archive_disk_acl_darwin.c
new file mode 100644
--- /dev/null
+++ b/c/archive_disk_acl_darwin.c
@@ -0,0 +1,559 @@
+/*-
+ * Copyright (c) 2017 Martin Matuska
+ * 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"
+
+#if ARCHIVE_ACL_DARWIN
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_MEMBERSHIP_H
+#include <membership.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#define _ACL_PRIVATE /* For debugging */
+#include <sys/acl.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+#include "archive_write_disk_private.h"
+
+typedef struct {
+	const int a_perm;	/* Libarchive permission or flag */
+	const int p_perm;	/* Platform permission or flag */
+} acl_perm_map_t;
+
+static const acl_perm_map_t acl_nfs4_perm_map[] = {
+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER},
+#if HAVE_DECL_ACL_SYNCHRONIZE
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
+#endif
+};
+
+static const int acl_nfs4_perm_map_size =
+    (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
+
+static const acl_perm_map_t acl_nfs4_flag_map[] = {
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED},
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT}
+};
+
+static const int acl_nfs4_flag_map_size =
+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+
+static int translate_guid(struct archive *a, acl_entry_t acl_entry,
+    int *ae_id, int *ae_tag, const char **ae_name)
+{
+	void *q;
+	uid_t ugid;
+	int r, idtype;
+
+	q = acl_get_qualifier(acl_entry);
+	if (q == NULL)
+		return (1);
+	r = mbr_uuid_to_id((const unsigned char *)q, &ugid, &idtype);
+	if (r != 0) {
+		acl_free(q);
+		return (1);
+	}
+	if (idtype == ID_TYPE_UID) {
+		*ae_tag = ARCHIVE_ENTRY_ACL_USER;
+		*ae_id = ugid;
+		*ae_name = archive_read_disk_uname(a, *ae_id);
+	} else if (idtype == ID_TYPE_GID) {
+		*ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+		*ae_id = ugid;
+		*ae_name = archive_read_disk_gname(a, *ae_id);
+	} else
+		r = 1;
+
+	acl_free(q);
+	return (r);
+}
+
+static void
+add_trivial_nfs4_acl(struct archive_entry *entry)
+{
+	mode_t mode;
+	int i;
+	const int rperm = ARCHIVE_ENTRY_ACL_READ_DATA;
+	const int wperm = ARCHIVE_ENTRY_ACL_WRITE_DATA |
+	    ARCHIVE_ENTRY_ACL_APPEND_DATA;
+	const int eperm = ARCHIVE_ENTRY_ACL_EXECUTE;
+	const int pubset = ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_READ_ACL |
+	    ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+	const int ownset = pubset | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |
+	    ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |
+	    ARCHIVE_ENTRY_ACL_WRITE_ACL |
+	    ARCHIVE_ENTRY_ACL_WRITE_OWNER;
+
+	struct {
+	    const int type;
+	    const int tag;
+	    int permset;
+	} tacl_entry[] = {
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},
+	    {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},
+	    {ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0},
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, ownset},
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_GROUP_OBJ, pubset},
+	    {ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EVERYONE, pubset}
+	};
+
+	mode = archive_entry_mode(entry);
+
+	/* Permissions for everyone@ */
+	if (mode & 0004)
+		tacl_entry[5].permset |= rperm;
+	if (mode & 0002)
+		tacl_entry[5].permset |= wperm;
+	if (mode & 0001)
+		tacl_entry[5].permset |= eperm;
+
+	/* Permissions for group@ */
+	if (mode & 0040)
+		tacl_entry[4].permset |= rperm;
+	else if (mode & 0004)
+		tacl_entry[2].permset |= rperm;
+	if (mode & 0020)
+		tacl_entry[4].permset |= wperm;
+	else if (mode & 0002)
+		tacl_entry[2].permset |= wperm;
+	if (mode & 0010)
+		tacl_entry[4].permset |= eperm;
+	else if (mode & 0001)
+		tacl_entry[2].permset |= eperm;
+
+	/* Permissions for owner@ */
+	if (mode & 0400) {
+		tacl_entry[3].permset |= rperm;
+		if (!(mode & 0040) && (mode & 0004))
+			tacl_entry[0].permset |= rperm;
+	} else if ((mode & 0040) || (mode & 0004))
+		tacl_entry[1].permset |= rperm;
+	if (mode & 0200) {
+		tacl_entry[3].permset |= wperm;
+		if (!(mode & 0020) && (mode & 0002))
+			tacl_entry[0].permset |= wperm;
+	} else if ((mode & 0020) || (mode & 0002))
+		tacl_entry[1].permset |= wperm;
+	if (mode & 0100) {
+		tacl_entry[3].permset |= eperm;
+		if (!(mode & 0010) && (mode & 0001))
+			tacl_entry[0].permset |= eperm;
+	} else if ((mode & 0010) || (mode & 0001))
+		tacl_entry[1].permset |= eperm;
+
+	for (i = 0; i < 6; i++) {
+		if (tacl_entry[i].permset != 0) {
+			archive_entry_acl_add_entry(entry,
+			    tacl_entry[i].type, tacl_entry[i].permset,
+			    tacl_entry[i].tag, -1, NULL);
+		}
+	}
+
+	return;
+}
+
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, acl_t acl)
+{
+	acl_tag_t	 acl_tag;
+	acl_flagset_t	 acl_flagset;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+	int		 i, entry_acl_type;
+	int		 r, s, ae_id, ae_tag, ae_perm;
+	const char	*ae_name;
+
+	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
+	if (s == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to get first ACL entry");
+		return (ARCHIVE_WARN);
+	}
+
+	while (s == 0) {
+		ae_id = -1;
+		ae_name = NULL;
+		ae_perm = 0;
+
+		if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL tag type");
+			return (ARCHIVE_WARN);
+		}
+		switch (acl_tag) {
+		case ACL_EXTENDED_ALLOW:
+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+			r = translate_guid(&a->archive, acl_entry,
+			    &ae_id, &ae_tag, &ae_name);
+			break;
+		case ACL_EXTENDED_DENY:
+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+			r = translate_guid(&a->archive, acl_entry,
+			    &ae_id, &ae_tag, &ae_name);
+			break;
+		default:
+			/* Skip types that libarchive can't support. */
+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+			continue;
+		}
+
+		/* Skip if translate_guid() above failed */
+		if (r != 0) {
+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+			continue;
+		}
+
+		/*
+		 * Libarchive stores "flag" (NFSv4 inheritance bits)
+		 * in the ae_perm bitmap.
+		 *
+		 * acl_get_flagset_np() fails with non-NFSv4 ACLs
+		 */
+		if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get flagset from a NFSv4 ACL entry");
+			return (ARCHIVE_WARN);
+		}
+		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+			r = acl_get_flag_np(acl_flagset,
+			    acl_nfs4_flag_map[i].p_perm);
+			if (r == -1) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to check flag in a NFSv4 "
+				    "ACL flagset");
+				return (ARCHIVE_WARN);
+			} else if (r)
+				ae_perm |= acl_nfs4_flag_map[i].a_perm;
+		}
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL permission set");
+			return (ARCHIVE_WARN);
+		}
+
+		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+			/*
+			 * acl_get_perm() is spelled differently on different
+			 * platforms; see above.
+			 */
+			r = acl_get_perm_np(acl_permset,
+			    acl_nfs4_perm_map[i].p_perm);
+			if (r == -1) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to check permission in an ACL "
+				    "permission set");
+				return (ARCHIVE_WARN);
+			} else if (r)
+				ae_perm |= acl_nfs4_perm_map[i].a_perm;
+		}
+
+#if !HAVE_DECL_ACL_SYNCHRONIZE
+		/* On Mac OS X without ACL_SYNCHRONIZE assume it is set */
+		ae_perm |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;
+#endif
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+					    ae_perm, ae_tag,
+					    ae_id, ae_name);
+
+		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+set_acl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl,
+    int ae_requested_type, const char *tname)
+{
+	acl_t		 acl;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+	acl_flagset_t	 acl_flagset;
+	int		 ret;
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	uuid_t		 ae_uuid;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	int		 i;
+
+	ret = ARCHIVE_OK;
+	entries = archive_acl_reset(abstract_acl, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+
+	if (ae_requested_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+		errno = ENOENT;
+		archive_set_error(a, errno, "Unsupported ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	acl = acl_init(entries);
+	if (acl == (acl_t)NULL) {
+		archive_set_error(a, errno,
+		    "Failed to initialize ACL working storage");
+		return (ARCHIVE_FAILED);
+	}
+
+	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+		/*
+		 * Mac OS doesn't support NFSv4 ACLs for
+		 * owner@, group@ and everyone@.
+		 * We skip any of these ACLs found.
+		 */
+		if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ ||
+		    ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ ||
+		    ae_tag == ARCHIVE_ENTRY_ACL_EVERYONE)
+			continue;
+
+		if (acl_create_entry(&acl, &acl_entry) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to create a new ACL entry");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		switch (ae_type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			acl_set_tag_type(acl_entry, ACL_EXTENDED_ALLOW);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			acl_set_tag_type(acl_entry, ACL_EXTENDED_DENY);
+			break;
+		default:
+			/* We don't support any other types on MacOS */
+			continue;
+		}
+
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			if (mbr_uid_to_uuid(ae_uid, ae_uuid) != 0)
+				continue;
+			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
+				continue;
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			if (mbr_gid_to_uuid(ae_gid, ae_uuid) != 0)
+				continue;
+			if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)
+				continue;
+			break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL tag");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to get ACL permission set");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+		if (acl_clear_perms(acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to clear ACL permissions");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+			if (ae_permset & acl_nfs4_perm_map[i].a_perm) {
+				if (acl_add_perm(acl_permset,
+				    acl_nfs4_perm_map[i].p_perm) != 0) {
+					archive_set_error(a, errno,
+					    "Failed to add ACL permission");
+					ret = ARCHIVE_FAILED;
+					goto exit_free;
+				}
+			}
+		}
+
+		/*
+		 * acl_get_flagset_np() fails with non-NFSv4 ACLs
+		 */
+		if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to get flagset from an NFSv4 ACL entry");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+		if (acl_clear_flags_np(acl_flagset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to clear flags from an NFSv4 ACL flagset");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+			if (ae_permset & acl_nfs4_flag_map[i].a_perm) {
+				if (acl_add_flag_np(acl_flagset,
+				    acl_nfs4_flag_map[i].p_perm) != 0) {
+					archive_set_error(a, errno,
+					    "Failed to add flag to "
+					    "NFSv4 ACL flagset");
+					ret = ARCHIVE_FAILED;
+					goto exit_free;
+				}
+			}
+		}
+	}
+
+	if (fd >= 0) {
+		if (acl_set_fd_np(fd, acl, ACL_TYPE_EXTENDED) == 0)
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set acl on fd: %s", tname);
+				ret = ARCHIVE_WARN;
+			}
+		}
+	} else if (acl_set_link_np(name, ACL_TYPE_EXTENDED, acl) != 0) {
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set acl: %s",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
+exit_free:
+	acl_free(acl);
+	return (ret);
+}
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	const char	*accpath;
+	acl_t		acl;
+	int		r;
+
+	accpath = NULL;
+
+	if (*fd < 0) {
+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (accpath == NULL)
+			return (ARCHIVE_WARN);
+	}
+
+	archive_entry_acl_clear(entry);
+
+	acl = NULL;
+
+	if (*fd >= 0)
+		acl = acl_get_fd_np(*fd, ACL_TYPE_EXTENDED);
+	else if (!a->follow_symlinks)
+		acl = acl_get_link_np(accpath, ACL_TYPE_EXTENDED);
+	else
+		acl = acl_get_file(accpath, ACL_TYPE_EXTENDED);
+
+	if (acl != NULL) {
+		r = translate_acl(a, entry, acl);
+		acl_free(acl);
+		acl = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate NFSv4 ACLs");
+		}
+
+		/*
+		 * Because Mac OS doesn't support owner@, group@ and everyone@
+		 * ACLs we need to add NFSv4 ACLs mirroring the file mode to
+		 * the archive entry. Otherwise extraction on non-Mac platforms
+		 * would lead to an invalid file mode.
+		 */
+		if ((archive_entry_acl_types(entry) &
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)
+			add_trivial_nfs4_acl(entry);
+
+		return (r);
+	}
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+	int		ret = ARCHIVE_OK;
+
+	(void)mode;	/* UNUSED */
+
+	if ((archive_acl_types(abstract_acl) &
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		ret = set_acl(a, fd, name, abstract_acl,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+	}
+	return (ret);
+}
+#endif	/* ARCHIVE_ACL_DARWIN */
diff --git a/c/archive_disk_acl_freebsd.c b/c/archive_disk_acl_freebsd.c
new file mode 100644
--- /dev/null
+++ b/c/archive_disk_acl_freebsd.c
@@ -0,0 +1,702 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2017 Martin Matuska
+ * 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"
+
+#if ARCHIVE_ACL_FREEBSD
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#define _ACL_PRIVATE /* For debugging */
+#include <sys/acl.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+#include "archive_write_disk_private.h"
+
+typedef struct {
+	const int a_perm;	/* Libarchive permission or flag */
+	const int p_perm;	/* Platform permission or flag */
+} acl_perm_map_t;
+
+static const acl_perm_map_t acl_posix_perm_map[] = {
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
+	{ARCHIVE_ENTRY_ACL_READ, ACL_READ},
+};
+
+static const int acl_posix_perm_map_size =
+    (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+static const acl_perm_map_t acl_nfs4_perm_map[] = {
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_WRITE_OWNER},
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}
+};
+
+static const int acl_nfs4_perm_map_size =
+    (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
+
+static const acl_perm_map_t acl_nfs4_flag_map[] = {
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_NO_PROPAGATE_INHERIT},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_INHERIT_ONLY},
+	{ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACL_ENTRY_SUCCESSFUL_ACCESS},
+	{ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACL_ENTRY_FAILED_ACCESS},
+#ifdef ACL_ENTRY_INHERITED
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED}
+#endif
+};
+
+static const int acl_nfs4_flag_map_size =
+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+#endif /* ARCHIVE_ACL_FREEBSD_NFS4 */
+
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
+{
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	int brand;
+	acl_flagset_t	 acl_flagset;
+	acl_entry_type_t acl_type;
+#endif
+	acl_tag_t	 acl_tag;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+	int		 i, entry_acl_type, perm_map_size;
+	const acl_perm_map_t	*perm_map;
+	int		 r, s, ae_id, ae_tag, ae_perm;
+	void		*q;
+	const char	*ae_name;
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	// FreeBSD "brands" ACLs as POSIX.1e or NFSv4
+	// Make sure the "brand" on this ACL is consistent
+	// with the default_entry_acl_type bits provided.
+	if (acl_get_brand_np(acl, &brand) != 0) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to read ACL brand");
+		return (ARCHIVE_WARN);
+	}
+	switch (brand) {
+	case ACL_BRAND_POSIX:
+		switch (default_entry_acl_type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+			break;
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Invalid ACL entry type for POSIX.1e ACL");
+			return (ARCHIVE_WARN);
+		}
+		break;
+	case ACL_BRAND_NFS4:
+		if (default_entry_acl_type & ~ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Invalid ACL entry type for NFSv4 ACL");
+			return (ARCHIVE_WARN);
+		}
+		break;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Unknown ACL brand");
+		return (ARCHIVE_WARN);
+	}
+#endif
+
+	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
+	if (s == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to get first ACL entry");
+		return (ARCHIVE_WARN);
+	}
+
+	while (s == 1) {
+		ae_id = -1;
+		ae_name = NULL;
+		ae_perm = 0;
+
+		if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL tag type");
+			return (ARCHIVE_WARN);
+		}
+		switch (acl_tag) {
+		case ACL_USER:
+			q = acl_get_qualifier(acl_entry);
+			if (q != NULL) {
+				ae_id = (int)*(uid_t *)q;
+				acl_free(q);
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+			}
+			ae_tag = ARCHIVE_ENTRY_ACL_USER;
+			break;
+		case ACL_GROUP:
+			q = acl_get_qualifier(acl_entry);
+			if (q != NULL) {
+				ae_id = (int)*(gid_t *)q;
+				acl_free(q);
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+			}
+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+			break;
+		case ACL_MASK:
+			ae_tag = ARCHIVE_ENTRY_ACL_MASK;
+			break;
+		case ACL_USER_OBJ:
+			ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+			break;
+		case ACL_GROUP_OBJ:
+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+			break;
+		case ACL_OTHER:
+			ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
+			break;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		case ACL_EVERYONE:
+			ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+			break;
+#endif
+		default:
+			/* Skip types that libarchive can't support. */
+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+			continue;
+		}
+
+		// XXX acl_type maps to allow/deny/audit/YYYY bits
+		entry_acl_type = default_entry_acl_type;
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			/*
+			 * acl_get_entry_type_np() fails with non-NFSv4 ACLs
+			 */
+			if (acl_get_entry_type_np(acl_entry, &acl_type) != 0) {
+				archive_set_error(&a->archive, errno, "Failed "
+				    "to get ACL type from a NFSv4 ACL entry");
+				return (ARCHIVE_WARN);
+			}
+			switch (acl_type) {
+			case ACL_ENTRY_TYPE_ALLOW:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+				break;
+			case ACL_ENTRY_TYPE_DENY:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+				break;
+			case ACL_ENTRY_TYPE_AUDIT:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_AUDIT;
+				break;
+			case ACL_ENTRY_TYPE_ALARM:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+				break;
+			default:
+				archive_set_error(&a->archive, errno,
+				    "Invalid NFSv4 ACL entry type");
+				return (ARCHIVE_WARN);
+			}
+
+			/*
+			 * Libarchive stores "flag" (NFSv4 inheritance bits)
+			 * in the ae_perm bitmap.
+			 *
+			 * acl_get_flagset_np() fails with non-NFSv4 ACLs
+			 */
+			if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to get flagset from a NFSv4 "
+				    "ACL entry");
+				return (ARCHIVE_WARN);
+			}
+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+				r = acl_get_flag_np(acl_flagset,
+				    acl_nfs4_flag_map[i].p_perm);
+				if (r == -1) {
+					archive_set_error(&a->archive, errno,
+					    "Failed to check flag in a NFSv4 "
+					    "ACL flagset");
+					return (ARCHIVE_WARN);
+				} else if (r)
+					ae_perm |= acl_nfs4_flag_map[i].a_perm;
+			}
+		}
+#endif
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL permission set");
+			return (ARCHIVE_WARN);
+		}
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		if (default_entry_acl_type & ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			perm_map_size = acl_nfs4_perm_map_size;
+			perm_map = acl_nfs4_perm_map;
+		} else {
+#endif
+			perm_map_size = acl_posix_perm_map_size;
+			perm_map = acl_posix_perm_map;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		}
+#endif
+
+		for (i = 0; i < perm_map_size; ++i) {
+			r = acl_get_perm_np(acl_permset, perm_map[i].p_perm);
+			if (r == -1) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to check permission in an ACL "
+				    "permission set");
+				return (ARCHIVE_WARN);
+			} else if (r)
+				ae_perm |= perm_map[i].a_perm;
+		}
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+					    ae_perm, ae_tag,
+					    ae_id, ae_name);
+
+		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+		if (s == -1) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get next ACL entry");
+			return (ARCHIVE_WARN);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+set_acl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl,
+    int ae_requested_type, const char *tname)
+{
+	int		 acl_type = 0;
+	acl_t		 acl;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	acl_flagset_t	 acl_flagset;
+	int		 r;
+#endif
+	int		 ret;
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	int		 perm_map_size;
+	const acl_perm_map_t	*perm_map;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	int		 i;
+
+	ret = ARCHIVE_OK;
+	entries = archive_acl_reset(abstract_acl, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+
+
+	switch (ae_requested_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+		acl_type = ACL_TYPE_ACCESS;
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+		acl_type = ACL_TYPE_DEFAULT;
+		break;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		acl_type = ACL_TYPE_NFS4;
+		break;
+#endif
+	default:
+		errno = ENOENT;
+		archive_set_error(a, errno, "Unsupported ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	acl = acl_init(entries);
+	if (acl == (acl_t)NULL) {
+		archive_set_error(a, errno,
+		    "Failed to initialize ACL working storage");
+		return (ARCHIVE_FAILED);
+	}
+
+	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+		if (acl_create_entry(&acl, &acl_entry) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to create a new ACL entry");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			acl_set_tag_type(acl_entry, ACL_USER);
+			acl_set_qualifier(acl_entry, &ae_uid);
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			acl_set_tag_type(acl_entry, ACL_GROUP);
+			acl_set_qualifier(acl_entry, &ae_gid);
+			break;
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			acl_set_tag_type(acl_entry, ACL_USER_OBJ);
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
+			break;
+		case ARCHIVE_ENTRY_ACL_MASK:
+			acl_set_tag_type(acl_entry, ACL_MASK);
+			break;
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			acl_set_tag_type(acl_entry, ACL_OTHER);
+			break;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		case ARCHIVE_ENTRY_ACL_EVERYONE:
+			acl_set_tag_type(acl_entry, ACL_EVERYONE);
+			break;
+#endif
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL tag");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		r = 0;
+		switch (ae_type) {
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			r = acl_set_entry_type_np(acl_entry,
+			    ACL_ENTRY_TYPE_ALLOW);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			r = acl_set_entry_type_np(acl_entry,
+			    ACL_ENTRY_TYPE_DENY);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			r = acl_set_entry_type_np(acl_entry,
+			    ACL_ENTRY_TYPE_AUDIT);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+			r = acl_set_entry_type_np(acl_entry,
+			    ACL_ENTRY_TYPE_ALARM);
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+			// These don't translate directly into the system ACL.
+			break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL entry type");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		if (r != 0) {
+			archive_set_error(a, errno,
+			    "Failed to set ACL entry type");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+#endif
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to get ACL permission set");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+		if (acl_clear_perms(acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to clear ACL permissions");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			perm_map_size = acl_nfs4_perm_map_size;
+			perm_map = acl_nfs4_perm_map;
+		} else {
+#endif
+			perm_map_size = acl_posix_perm_map_size;
+			perm_map = acl_posix_perm_map;
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		}
+#endif
+
+		for (i = 0; i < perm_map_size; ++i) {
+			if (ae_permset & perm_map[i].a_perm) {
+				if (acl_add_perm(acl_permset,
+				    perm_map[i].p_perm) != 0) {
+					archive_set_error(a, errno,
+					    "Failed to add ACL permission");
+					ret = ARCHIVE_FAILED;
+					goto exit_free;
+				}
+			}
+		}
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			/*
+			 * acl_get_flagset_np() fails with non-NFSv4 ACLs
+			 */
+			if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {
+				archive_set_error(a, errno,
+				    "Failed to get flagset from an NFSv4 "
+				    "ACL entry");
+				ret = ARCHIVE_FAILED;
+				goto exit_free;
+			}
+			if (acl_clear_flags_np(acl_flagset) != 0) {
+				archive_set_error(a, errno,
+				    "Failed to clear flags from an NFSv4 "
+				    "ACL flagset");
+				ret = ARCHIVE_FAILED;
+				goto exit_free;
+			}
+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+				if (ae_permset & acl_nfs4_flag_map[i].a_perm) {
+					if (acl_add_flag_np(acl_flagset,
+					    acl_nfs4_flag_map[i].p_perm) != 0) {
+						archive_set_error(a, errno,
+						    "Failed to add flag to "
+						    "NFSv4 ACL flagset");
+						ret = ARCHIVE_FAILED;
+						goto exit_free;
+					}
+				}
+			}
+		}
+#endif
+	}
+
+	/* Try restoring the ACL through 'fd' if we can. */
+	if (fd >= 0) {
+		if (acl_set_fd_np(fd, acl, acl_type) == 0)
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set acl on fd: %s", tname);
+				ret = ARCHIVE_WARN;
+			}
+		}
+	}
+#if HAVE_ACL_SET_LINK_NP
+	else if (acl_set_link_np(name, acl_type, acl) != 0)
+#else
+	/* FreeBSD older than 8.0 */
+	else if (acl_set_file(name, acl_type, acl) != 0)
+#endif
+	{
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set acl: %s",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
+exit_free:
+	acl_free(acl);
+	return (ret);
+}
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	const char	*accpath;
+	acl_t		acl;
+	int		r;
+
+	accpath = NULL;
+
+	if (*fd < 0) {
+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (accpath == NULL)
+			return (ARCHIVE_WARN);
+	}
+
+	archive_entry_acl_clear(entry);
+
+	acl = NULL;
+
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	/* Try NFSv4 ACL first. */
+	if (*fd >= 0)
+		acl = acl_get_fd_np(*fd, ACL_TYPE_NFS4);
+	else if (!a->follow_symlinks)
+		acl = acl_get_link_np(accpath, ACL_TYPE_NFS4);
+	else
+		acl = acl_get_file(accpath, ACL_TYPE_NFS4);
+
+	/* Ignore "trivial" ACLs that just mirror the file mode. */
+	if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {
+		acl_free(acl);
+		acl = NULL;
+		return (ARCHIVE_OK);
+	}
+
+	if (acl != NULL) {
+		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+		acl_free(acl);
+		acl = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate NFSv4 ACLs");
+		}
+
+		return (r);
+	}
+#endif
+
+	/* Retrieve access ACL from file. */
+	if (*fd >= 0)
+		acl = acl_get_fd_np(*fd, ACL_TYPE_ACCESS);
+#if HAVE_ACL_GET_LINK_NP
+	else if (!a->follow_symlinks)
+		acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS);
+#else
+	else if ((!a->follow_symlinks)
+	    && (archive_entry_filetype(entry) == AE_IFLNK))
+		/* We can't get the ACL of a symlink, so we assume it can't
+		   have one. */
+		acl = NULL;
+#endif
+	else
+		acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
+
+#if HAVE_ACL_IS_TRIVIAL_NP
+	/* Ignore "trivial" ACLs that just mirror the file mode. */
+	if (acl != NULL && acl_is_trivial_np(acl, &r) == 0 && r == 1) {
+		acl_free(acl);
+		acl = NULL;
+	}
+#endif
+
+	if (acl != NULL) {
+		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+		acl_free(acl);
+		acl = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate access ACLs");
+			return (r);
+		}
+	}
+
+	/* Only directories can have default ACLs. */
+	if (S_ISDIR(archive_entry_mode(entry))) {
+		if (*fd >= 0)
+			acl = acl_get_fd_np(*fd, ACL_TYPE_DEFAULT);
+		else
+			acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
+		if (acl != NULL) {
+			r = translate_acl(a, entry, acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+			acl_free(acl);
+			if (r != ARCHIVE_OK) {
+				archive_set_error(&a->archive, errno,
+				    "Couldn't translate default ACLs");
+				return (r);
+			}
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+	int		ret = ARCHIVE_OK;
+
+	(void)mode;	/* UNUSED */
+
+	if ((archive_acl_types(abstract_acl)
+	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+		if ((archive_acl_types(abstract_acl)
+		    & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+			ret = set_acl(a, fd, name, abstract_acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
+			if (ret != ARCHIVE_OK)
+				return (ret);
+		}
+		if ((archive_acl_types(abstract_acl)
+		    & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+			ret = set_acl(a, fd, name, abstract_acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
+
+		/* Simultaneous POSIX.1e and NFSv4 is not supported */
+		return (ret);
+	}
+#if ARCHIVE_ACL_FREEBSD_NFS4
+	else if ((archive_acl_types(abstract_acl) &
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		ret = set_acl(a, fd, name, abstract_acl,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+	}
+#endif
+	return (ret);
+}
+#endif	/* ARCHIVE_ACL_FREEBSD */
diff --git a/c/archive_disk_acl_linux.c b/c/archive_disk_acl_linux.c
new file mode 100644
--- /dev/null
+++ b/c/archive_disk_acl_linux.c
@@ -0,0 +1,743 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2017 Martin Matuska
+ * 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"
+
+#if ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_LIBRICHACL
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#if HAVE_ACL_LIBACL_H
+#include <acl/libacl.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_SYS_RICHACL_H
+#include <sys/richacl.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+#include "archive_write_disk_private.h"
+
+typedef struct {
+	const int a_perm;	/* Libarchive permission or flag */
+	const int p_perm;	/* Platform permission or flag */
+} acl_perm_map_t;
+
+#if ARCHIVE_ACL_LIBACL
+static const acl_perm_map_t acl_posix_perm_map[] = {
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_WRITE, ACL_WRITE},
+	{ARCHIVE_ENTRY_ACL_READ, ACL_READ},
+};
+
+static const int acl_posix_perm_map_size =
+    (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));
+#endif /* ARCHIVE_ACL_LIBACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+static const acl_perm_map_t acl_nfs4_perm_map[] = {
+	{ARCHIVE_ENTRY_ACL_EXECUTE, RICHACE_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_READ_DATA, RICHACE_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, RICHACE_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, RICHACE_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, RICHACE_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, RICHACE_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, RICHACE_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, RICHACE_READ_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, RICHACE_WRITE_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, RICHACE_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, RICHACE_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, RICHACE_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_DELETE, RICHACE_DELETE},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, RICHACE_READ_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, RICHACE_WRITE_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, RICHACE_WRITE_OWNER},
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, RICHACE_SYNCHRONIZE}
+};
+
+static const int acl_nfs4_perm_map_size =
+    (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
+
+static const acl_perm_map_t acl_nfs4_flag_map[] = {
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, RICHACE_FILE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, RICHACE_DIRECTORY_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, RICHACE_NO_PROPAGATE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, RICHACE_INHERIT_ONLY_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, RICHACE_INHERITED_ACE}
+};
+
+static const int acl_nfs4_flag_map_size =
+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+#endif /* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBACL
+/*
+ * Translate POSIX.1e ACLs into libarchive internal structure
+ */
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, acl_t acl, int default_entry_acl_type)
+{
+	acl_tag_t	 acl_tag;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+	int		 i, entry_acl_type;
+	int		 r, s, ae_id, ae_tag, ae_perm;
+	void		*q;
+	const char	*ae_name;
+
+	s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);
+	if (s == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to get first ACL entry");
+		return (ARCHIVE_WARN);
+	}
+
+	while (s == 1) {
+		ae_id = -1;
+		ae_name = NULL;
+		ae_perm = 0;
+
+		if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL tag type");
+			return (ARCHIVE_WARN);
+		}
+		switch (acl_tag) {
+		case ACL_USER:
+			q = acl_get_qualifier(acl_entry);
+			if (q != NULL) {
+				ae_id = (int)*(uid_t *)q;
+				acl_free(q);
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+			}
+			ae_tag = ARCHIVE_ENTRY_ACL_USER;
+			break;
+		case ACL_GROUP:
+			q = acl_get_qualifier(acl_entry);
+			if (q != NULL) {
+				ae_id = (int)*(gid_t *)q;
+				acl_free(q);
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+			}
+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+			break;
+		case ACL_MASK:
+			ae_tag = ARCHIVE_ENTRY_ACL_MASK;
+			break;
+		case ACL_USER_OBJ:
+			ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+			break;
+		case ACL_GROUP_OBJ:
+			ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+			break;
+		case ACL_OTHER:
+			ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
+			break;
+		default:
+			/* Skip types that libarchive can't support. */
+			s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+			continue;
+		}
+
+		// XXX acl_type maps to allow/deny/audit/YYYY bits
+		entry_acl_type = default_entry_acl_type;
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get ACL permission set");
+			return (ARCHIVE_WARN);
+		}
+
+		for (i = 0; i < acl_posix_perm_map_size; ++i) {
+			r = acl_get_perm(acl_permset,
+			    acl_posix_perm_map[i].p_perm);
+			if (r == -1) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to check permission in an ACL "
+				    "permission set");
+				return (ARCHIVE_WARN);
+			} else if (r)
+				ae_perm |= acl_posix_perm_map[i].a_perm;
+		}
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+					    ae_perm, ae_tag,
+					    ae_id, ae_name);
+
+		s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);
+		if (s == -1) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get next ACL entry");
+			return (ARCHIVE_WARN);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+#endif /* ARCHIVE_ACL_LIBACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+/*
+ * Translate RichACL into libarchive internal ACL
+ */
+static int
+translate_richacl(struct archive_read_disk *a, struct archive_entry *entry,
+    struct richacl *richacl)
+{
+	int ae_id, ae_tag, ae_perm;
+	int entry_acl_type, i;
+	const char *ae_name;
+
+	struct richace *richace;
+
+	richacl_for_each_entry(richace, richacl) {
+		ae_name = NULL;
+		ae_tag = 0;
+		ae_perm = 0;
+		ae_id = -1;
+
+		switch (richace->e_type) {
+		case RICHACE_ACCESS_ALLOWED_ACE_TYPE:
+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+			break;
+		case RICHACE_ACCESS_DENIED_ACE_TYPE:
+			entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+			break;
+		default: /* Unknown entry type, skip */
+			continue;
+		}
+
+		/* Unsupported */
+		if (richace->e_flags & RICHACE_UNMAPPED_WHO)
+			continue;
+
+		if (richace->e_flags & RICHACE_SPECIAL_WHO) {
+			switch (richace->e_id) {
+			case RICHACE_OWNER_SPECIAL_ID:
+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				break;
+			case RICHACE_GROUP_SPECIAL_ID:
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case RICHACE_EVERYONE_SPECIAL_ID:
+				ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+				break;
+			default: /* Unknown special ID type */
+				continue;
+			}
+		} else {
+			ae_id = richace->e_id;
+			if (richace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+				ae_name = archive_read_disk_gname(&a->archive,
+				    (gid_t)(richace->e_id));
+			} else {
+				ae_tag = ARCHIVE_ENTRY_ACL_USER;
+				ae_name = archive_read_disk_uname(&a->archive,
+				    (uid_t)(richace->e_id));
+			}
+		}
+		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+			if ((richace->e_flags &
+			    acl_nfs4_flag_map[i].p_perm) != 0)
+				ae_perm |= acl_nfs4_flag_map[i].a_perm;
+		}
+		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+			if ((richace->e_mask &
+			    acl_nfs4_perm_map[i].p_perm) != 0)
+				ae_perm |=
+				    acl_nfs4_perm_map[i].a_perm;
+		}
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+		    ae_perm, ae_tag, ae_id, ae_name);
+	}
+	return (ARCHIVE_OK);
+}
+#endif	/* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+static int
+_richacl_mode_to_mask(short mode)
+{
+	int mask = 0;
+
+	if (mode & S_IROTH)
+		mask |= RICHACE_POSIX_MODE_READ;
+	if (mode & S_IWOTH)
+		mask |= RICHACE_POSIX_MODE_WRITE;
+	if (mode & S_IXOTH)
+		mask |= RICHACE_POSIX_MODE_EXEC;
+
+	return (mask);
+}
+
+static void
+_richacl_mode_to_masks(struct richacl *richacl, __LA_MODE_T mode)
+{
+	richacl->a_owner_mask = _richacl_mode_to_mask((mode & 0700) >> 6);
+	richacl->a_group_mask = _richacl_mode_to_mask((mode & 0070) >> 3);
+	richacl->a_other_mask = _richacl_mode_to_mask(mode & 0007);
+}
+#endif /* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBRICHACL
+static int
+set_richacl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode,
+    int ae_requested_type, const char *tname)
+{
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	int		 i;
+	int		 ret;
+	int		 e = 0;
+	struct richacl  *richacl = NULL;
+	struct richace  *richace;
+
+	ret = ARCHIVE_OK;
+	entries = archive_acl_reset(abstract_acl, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+
+	if (ae_requested_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+		errno = ENOENT;
+		archive_set_error(a, errno, "Unsupported ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	richacl = richacl_alloc(entries);
+	if (richacl == NULL) {
+		archive_set_error(a, errno,
+			"Failed to initialize RichACL working storage");
+		return (ARCHIVE_FAILED);
+	}
+
+	e = 0;
+
+	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+		richace = &(richacl->a_entries[e]);
+
+		richace->e_flags = 0;
+		richace->e_mask = 0;
+
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			richace->e_id = ae_uid;
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			richace->e_id = ae_gid;
+			richace->e_flags |= RICHACE_IDENTIFIER_GROUP;
+			break;
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			richace->e_flags |= RICHACE_SPECIAL_WHO;
+			richace->e_id = RICHACE_OWNER_SPECIAL_ID;
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			richace->e_flags |= RICHACE_SPECIAL_WHO;
+			richace->e_id = RICHACE_GROUP_SPECIAL_ID;
+			break;
+		case ARCHIVE_ENTRY_ACL_EVERYONE:
+			richace->e_flags |= RICHACE_SPECIAL_WHO;
+			richace->e_id = RICHACE_EVERYONE_SPECIAL_ID;
+			break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL tag");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		switch (ae_type) {
+			case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+				richace->e_type =
+				    RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+				break;
+			case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+				richace->e_type =
+				    RICHACE_ACCESS_DENIED_ACE_TYPE;
+				break;
+			case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+				break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL entry type");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+			if (ae_permset & acl_nfs4_perm_map[i].a_perm)
+				richace->e_mask |= acl_nfs4_perm_map[i].p_perm;
+		}
+
+		for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+			if (ae_permset &
+			    acl_nfs4_flag_map[i].a_perm)
+				richace->e_flags |= acl_nfs4_flag_map[i].p_perm;
+		}
+	e++;
+	}
+
+	/* Fill RichACL masks */
+	_richacl_mode_to_masks(richacl, mode);
+
+	if (fd >= 0) {
+		if (richacl_set_fd(fd, richacl) == 0)
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set richacl on fd: %s", tname);
+				ret = ARCHIVE_WARN;
+			}
+		}
+	} else if (richacl_set_file(name, richacl) != 0) {
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set richacl: %s",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
+exit_free:
+	richacl_free(richacl);
+	return (ret);
+}
+#endif /* ARCHIVE_ACL_RICHACL */
+
+#if ARCHIVE_ACL_LIBACL
+static int
+set_acl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl,
+    int ae_requested_type, const char *tname)
+{
+	int		 acl_type = 0;
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	int		 i;
+	int		 ret;
+	acl_t		 acl = NULL;
+	acl_entry_t	 acl_entry;
+	acl_permset_t	 acl_permset;
+
+	ret = ARCHIVE_OK;
+	entries = archive_acl_reset(abstract_acl, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+
+	switch (ae_requested_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+		acl_type = ACL_TYPE_ACCESS;
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+		acl_type = ACL_TYPE_DEFAULT;
+		break;
+	default:
+		errno = ENOENT;
+		archive_set_error(a, errno, "Unsupported ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	acl = acl_init(entries);
+	if (acl == (acl_t)NULL) {
+		archive_set_error(a, errno,
+		    "Failed to initialize ACL working storage");
+		return (ARCHIVE_FAILED);
+	}
+
+	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+
+		if (acl_create_entry(&acl, &acl_entry) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to create a new ACL entry");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			acl_set_tag_type(acl_entry, ACL_USER);
+			acl_set_qualifier(acl_entry, &ae_uid);
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			acl_set_tag_type(acl_entry, ACL_GROUP);
+			acl_set_qualifier(acl_entry, &ae_gid);
+			break;
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			acl_set_tag_type(acl_entry, ACL_USER_OBJ);
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
+			break;
+		case ARCHIVE_ENTRY_ACL_MASK:
+			acl_set_tag_type(acl_entry, ACL_MASK);
+			break;
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			acl_set_tag_type(acl_entry, ACL_OTHER);
+			break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL tag");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		if (acl_get_permset(acl_entry, &acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to get ACL permission set");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+		if (acl_clear_perms(acl_permset) != 0) {
+			archive_set_error(a, errno,
+			    "Failed to clear ACL permissions");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		for (i = 0; i < acl_posix_perm_map_size; ++i) {
+			if (ae_permset & acl_posix_perm_map[i].a_perm) {
+				if (acl_add_perm(acl_permset,
+				    acl_posix_perm_map[i].p_perm) != 0) {
+					archive_set_error(a, errno,
+					    "Failed to add ACL permission");
+					ret = ARCHIVE_FAILED;
+					goto exit_free;
+				}
+			}
+		}
+
+	}
+
+	if (fd >= 0 && ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+		if (acl_set_fd(fd, acl) == 0)
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set acl on fd: %s", tname);
+				ret = ARCHIVE_WARN;
+			}
+		}
+	} else if (acl_set_file(name, acl_type, acl) != 0) {
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set acl: %s",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
+exit_free:
+	acl_free(acl);
+	return (ret);
+}
+#endif /* ARCHIVE_ACL_LIBACL */
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	const char	*accpath;
+	int		r;
+#if ARCHIVE_ACL_LIBACL
+	acl_t		acl;
+#endif
+#if ARCHIVE_ACL_LIBRICHACL
+	struct richacl *richacl;
+	mode_t		mode;
+#endif
+
+	accpath = NULL;
+	r = ARCHIVE_OK;
+
+	/* For default ACLs we need reachable accpath */
+	if (*fd < 0 || S_ISDIR(archive_entry_mode(entry))) {
+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (accpath == NULL)
+			return (ARCHIVE_WARN);
+	}
+
+	archive_entry_acl_clear(entry);
+
+#if ARCHIVE_ACL_LIBACL
+	acl = NULL;
+#endif
+#if ARCHIVE_ACL_LIBRICHACL
+	richacl = NULL;
+#endif
+
+#if ARCHIVE_ACL_LIBRICHACL
+	/* Try NFSv4 ACL first. */
+	if (*fd >= 0)
+		richacl = richacl_get_fd(*fd);
+	else if ((!a->follow_symlinks)
+	    && (archive_entry_filetype(entry) == AE_IFLNK))
+		/* We can't get the ACL of a symlink, so we assume it can't
+		   have one */
+		richacl = NULL;
+	else
+		richacl = richacl_get_file(accpath);
+
+	/* Ignore "trivial" ACLs that just mirror the file mode. */
+	if (richacl != NULL) {
+		mode = archive_entry_mode(entry);
+		if (richacl_equiv_mode(richacl, &mode) == 0) {
+			richacl_free(richacl);
+			richacl = NULL;
+			return (ARCHIVE_OK);
+		}
+	}
+
+	if (richacl != NULL) {
+		r = translate_richacl(a, entry, richacl);
+		richacl_free(richacl);
+		richacl = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			"Couldn't translate NFSv4 ACLs");
+		}
+
+		return (r);
+	}
+#endif	/* ARCHIVE_ACL_LIBRICHACL */
+
+#if ARCHIVE_ACL_LIBACL
+	/* Retrieve access ACL from file. */
+	if (*fd >= 0)
+		acl = acl_get_fd(*fd);
+	else if ((!a->follow_symlinks)
+	    && (archive_entry_filetype(entry) == AE_IFLNK))
+		/* We can't get the ACL of a symlink, so we assume it can't
+		   have one. */
+		acl = NULL;
+	else
+		acl = acl_get_file(accpath, ACL_TYPE_ACCESS);
+
+	if (acl != NULL) {
+		r = translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+		acl_free(acl);
+		acl = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate access ACLs");
+			return (r);
+		}
+	}
+
+	/* Only directories can have default ACLs. */
+	if (S_ISDIR(archive_entry_mode(entry))) {
+		acl = acl_get_file(accpath, ACL_TYPE_DEFAULT);
+		if (acl != NULL) {
+			r = translate_acl(a, entry, acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+			acl_free(acl);
+			if (r != ARCHIVE_OK) {
+				archive_set_error(&a->archive, errno,
+				    "Couldn't translate default ACLs");
+				return (r);
+			}
+		}
+	}
+#endif	/* ARCHIVE_ACL_LIBACL */
+	return (r);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+	int		ret = ARCHIVE_OK;
+
+#if !ARCHIVE_ACL_LIBRICHACL
+	(void)mode;	/* UNUSED */
+#endif
+
+#if ARCHIVE_ACL_LIBRICHACL
+	if ((archive_acl_types(abstract_acl)
+	    & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		ret = set_richacl(a, fd, name, abstract_acl, mode,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+	}
+#if ARCHIVE_ACL_LIBACL
+	else
+#endif
+#endif	/* ARCHIVE_ACL_LIBRICHACL */
+#if ARCHIVE_ACL_LIBACL
+	if ((archive_acl_types(abstract_acl)
+	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+		if ((archive_acl_types(abstract_acl)
+		    & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
+			ret = set_acl(a, fd, name, abstract_acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
+			if (ret != ARCHIVE_OK)
+				return (ret);
+		}
+		if ((archive_acl_types(abstract_acl)
+		    & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+			ret = set_acl(a, fd, name, abstract_acl,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
+	}
+#endif	/* ARCHIVE_ACL_LIBACL */
+	return (ret);
+}
+#endif /* ARCHIVE_ACL_LIBACL || ARCHIVE_ACL_LIBRICHACL */
diff --git a/c/archive_disk_acl_sunos.c b/c/archive_disk_acl_sunos.c
new file mode 100644
--- /dev/null
+++ b/c/archive_disk_acl_sunos.c
@@ -0,0 +1,819 @@
+/*-
+ * Copyright (c) 2017 Martin Matuska
+ * 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"
+
+#if ARCHIVE_ACL_SUNOS
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#define _ACL_PRIVATE /* For debugging */
+#include <sys/acl.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+#include "archive_write_disk_private.h"
+
+typedef struct {
+	const int a_perm;	/* Libarchive permission or flag */
+	const int p_perm;	/* Platform permission or flag */
+} acl_perm_map_t;
+
+static const acl_perm_map_t acl_posix_perm_map[] = {
+	{ARCHIVE_ENTRY_ACL_EXECUTE, S_IXOTH },
+	{ARCHIVE_ENTRY_ACL_WRITE, S_IWOTH },
+	{ARCHIVE_ENTRY_ACL_READ, S_IROTH }
+};
+
+static const int acl_posix_perm_map_size =
+    (int)(sizeof(acl_posix_perm_map)/sizeof(acl_posix_perm_map[0]));
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+static const acl_perm_map_t acl_nfs4_perm_map[] = {
+	{ARCHIVE_ENTRY_ACL_EXECUTE, ACE_EXECUTE},
+	{ARCHIVE_ENTRY_ACL_READ_DATA, ACE_READ_DATA},
+	{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACE_LIST_DIRECTORY},
+	{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACE_WRITE_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_FILE, ACE_ADD_FILE},
+	{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACE_APPEND_DATA},
+	{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACE_ADD_SUBDIRECTORY},
+	{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACE_READ_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACE_WRITE_NAMED_ATTRS},
+	{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACE_DELETE_CHILD},
+	{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACE_READ_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACE_WRITE_ATTRIBUTES},
+	{ARCHIVE_ENTRY_ACL_DELETE, ACE_DELETE},
+	{ARCHIVE_ENTRY_ACL_READ_ACL, ACE_READ_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACE_WRITE_ACL},
+	{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACE_WRITE_OWNER},
+	{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACE_SYNCHRONIZE}
+};
+
+static const int acl_nfs4_perm_map_size =
+    (int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));
+
+static const acl_perm_map_t acl_nfs4_flag_map[] = {
+	{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACE_FILE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACE_DIRECTORY_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACE_NO_PROPAGATE_INHERIT_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACE_INHERIT_ONLY_ACE},
+	{ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS, ACE_SUCCESSFUL_ACCESS_ACE_FLAG},
+	{ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS, ACE_FAILED_ACCESS_ACE_FLAG},
+#ifdef ACE_INHERITED_ACE
+	{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACE_INHERITED_ACE}
+#endif
+};
+
+const int acl_nfs4_flag_map_size =
+    (int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));
+
+#endif /* ARCHIVE_ACL_SUNOS_NFS4 */
+
+static void *
+sunacl_get(int cmd, int *aclcnt, int fd, const char *path)
+{
+	int cnt, cntcmd;
+	size_t size;
+	void *aclp;
+
+	if (cmd == GETACL) {
+		cntcmd = GETACLCNT;
+		size = sizeof(aclent_t);
+	}
+#if ARCHIVE_ACL_SUNOS_NFS4
+	else if (cmd == ACE_GETACL) {
+		cntcmd = ACE_GETACLCNT;
+		size = sizeof(ace_t);
+	}
+#endif
+	else {
+		errno = EINVAL;
+		*aclcnt = -1;
+		return (NULL);
+	}
+
+	aclp = NULL;
+	cnt = -2;
+
+	while (cnt == -2 || (cnt == -1 && errno == ENOSPC)) {
+		if (path != NULL)
+			cnt = acl(path, cntcmd, 0, NULL);
+		else
+			cnt = facl(fd, cntcmd, 0, NULL);
+
+		if (cnt > 0) {
+			if (aclp == NULL)
+				aclp = malloc(cnt * size);
+			else
+				aclp = realloc(NULL, cnt * size);
+			if (aclp != NULL) {
+				if (path != NULL)
+					cnt = acl(path, cmd, cnt, aclp);
+				else
+					cnt = facl(fd, cmd, cnt, aclp);
+			}
+		} else {
+			free(aclp);
+			aclp = NULL;
+			break;
+		}
+	}
+
+	*aclcnt = cnt;
+	return (aclp);
+}
+
+/*
+ * Check if acl is trivial
+ * This is a FreeBSD acl_is_trivial_np() implementation for Solaris
+ */
+static int
+sun_acl_is_trivial(void *aclp, int aclcnt, mode_t mode, int is_nfs4,
+    int is_dir, int *trivialp)
+{
+#if ARCHIVE_ACL_SUNOS_NFS4
+	int i, p;
+	const uint32_t rperm = ACE_READ_DATA;
+	const uint32_t wperm = ACE_WRITE_DATA | ACE_APPEND_DATA;
+	const uint32_t eperm = ACE_EXECUTE;
+	const uint32_t pubset = ACE_READ_ATTRIBUTES | ACE_READ_NAMED_ATTRS |
+	    ACE_READ_ACL | ACE_SYNCHRONIZE;
+	const uint32_t ownset = pubset | ACE_WRITE_ATTRIBUTES |
+	    ACE_WRITE_NAMED_ATTRS | ACE_WRITE_ACL | ACE_WRITE_OWNER;
+
+	ace_t *ace;
+	ace_t tace[6];
+#endif
+
+	if (aclp == NULL || trivialp == NULL)
+		return (-1);
+
+	*trivialp = 0;
+
+	/*
+	 * POSIX.1e ACLs marked with ACL_IS_TRIVIAL are compatible with
+	 * FreeBSD acl_is_trivial_np(). On Solaris they have 4 entries,
+	 * including mask.
+	 */
+	if (!is_nfs4) {
+		if (aclcnt == 4)
+			*trivialp = 1;
+		return (0);
+	}
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+	/*
+	 * Continue with checking NFSv4 ACLs
+	 *
+	 * Create list of trivial ace's to be compared
+	 */
+
+	/* owner@ allow pre */
+	tace[0].a_flags = ACE_OWNER;
+	tace[0].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[0].a_access_mask = 0;
+
+	/* owner@ deny */
+	tace[1].a_flags = ACE_OWNER;
+	tace[1].a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+	tace[1].a_access_mask = 0;
+
+	/* group@ deny */
+	tace[2].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;
+	tace[2].a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+	tace[2].a_access_mask = 0;
+
+	/* owner@ allow */
+	tace[3].a_flags = ACE_OWNER;
+	tace[3].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[3].a_access_mask = ownset;
+
+	/* group@ allow */
+	tace[4].a_flags = ACE_GROUP | ACE_IDENTIFIER_GROUP;
+	tace[4].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[4].a_access_mask = pubset;
+
+	/* everyone@ allow */
+	tace[5].a_flags = ACE_EVERYONE;
+	tace[5].a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+	tace[5].a_access_mask = pubset;
+
+	/* Permissions for everyone@ */
+	if (mode & 0004)
+		tace[5].a_access_mask |= rperm;
+	if (mode & 0002)
+		tace[5].a_access_mask |= wperm;
+	if (mode & 0001)
+		tace[5].a_access_mask |= eperm;
+
+	/* Permissions for group@ */
+	if (mode & 0040)
+		tace[4].a_access_mask |= rperm;
+	else if (mode & 0004)
+		tace[2].a_access_mask |= rperm;
+	if (mode & 0020)
+		tace[4].a_access_mask |= wperm;
+	else if (mode & 0002)
+		tace[2].a_access_mask |= wperm;
+	if (mode & 0010)
+		tace[4].a_access_mask |= eperm;
+	else if (mode & 0001)
+		tace[2].a_access_mask |= eperm;
+
+	/* Permissions for owner@ */
+	if (mode & 0400) {
+		tace[3].a_access_mask |= rperm;
+		if (!(mode & 0040) && (mode & 0004))
+			tace[0].a_access_mask |= rperm;
+	} else if ((mode & 0040) || (mode & 0004))
+		tace[1].a_access_mask |= rperm;
+	if (mode & 0200) {
+		tace[3].a_access_mask |= wperm;
+		if (!(mode & 0020) && (mode & 0002))
+			tace[0].a_access_mask |= wperm;
+	} else if ((mode & 0020) || (mode & 0002))
+		tace[1].a_access_mask |= wperm;
+	if (mode & 0100) {
+		tace[3].a_access_mask |= eperm;
+		if (!(mode & 0010) && (mode & 0001))
+			tace[0].a_access_mask |= eperm;
+	} else if ((mode & 0010) || (mode & 0001))
+		tace[1].a_access_mask |= eperm;
+
+	/* Check if the acl count matches */
+	p = 3;
+	for (i = 0; i < 3; i++) {
+		if (tace[i].a_access_mask != 0)
+			p++;
+	}
+	if (aclcnt != p)
+		return (0);
+
+	p = 0;
+	for (i = 0; i < 6; i++) {
+		if (tace[i].a_access_mask != 0) {
+			ace = &((ace_t *)aclp)[p];
+			/*
+			 * Illumos added ACE_DELETE_CHILD to write perms for
+			 * directories. We have to check against that, too.
+			 */
+			if (ace->a_flags != tace[i].a_flags ||
+			    ace->a_type != tace[i].a_type ||
+			    (ace->a_access_mask != tace[i].a_access_mask &&
+			    (!is_dir || (tace[i].a_access_mask & wperm) == 0 ||
+			    ace->a_access_mask !=
+			    (tace[i].a_access_mask | ACE_DELETE_CHILD))))
+				return (0);
+			p++;
+		}
+	}
+
+	*trivialp = 1;
+#else	/* !ARCHIVE_ACL_SUNOS_NFS4 */
+	(void)is_dir;	/* UNUSED */
+	(void)aclp;	/* UNUSED */
+#endif	/* !ARCHIVE_ACL_SUNOS_NFS4 */
+	return (0);
+}
+
+/*
+ * Translate Solaris POSIX.1e and NFSv4 ACLs into libarchive internal ACL
+ */
+static int
+translate_acl(struct archive_read_disk *a,
+    struct archive_entry *entry, void *aclp, int aclcnt,
+    int default_entry_acl_type)
+{
+	int e, i;
+	int ae_id, ae_tag, ae_perm;
+	int entry_acl_type;
+	const char *ae_name;
+	aclent_t *aclent;
+#if ARCHIVE_ACL_SUNOS_NFS4
+	ace_t *ace;
+#endif
+
+	if (aclcnt <= 0)
+		return (ARCHIVE_OK);
+
+	for (e = 0; e < aclcnt; e++) {
+		ae_name = NULL;
+		ae_tag = 0;
+		ae_perm = 0;
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+		if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			ace = &((ace_t *)aclp)[e];
+			ae_id = ace->a_who;
+
+			switch(ace->a_type) {
+			case ACE_ACCESS_ALLOWED_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;
+				break;
+			case ACE_ACCESS_DENIED_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;
+				break;
+			case ACE_SYSTEM_AUDIT_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+				break;
+			case ACE_SYSTEM_ALARM_ACE_TYPE:
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALARM;
+				break;
+			default:
+				/* Unknown entry type, skip */
+				continue;
+			}
+
+			if ((ace->a_flags & ACE_OWNER) != 0)
+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+			else if ((ace->a_flags & ACE_GROUP) != 0)
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+			else if ((ace->a_flags & ACE_EVERYONE) != 0)
+				ae_tag = ARCHIVE_ENTRY_ACL_EVERYONE;
+			else if ((ace->a_flags & ACE_IDENTIFIER_GROUP) != 0) {
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+			} else {
+				ae_tag = ARCHIVE_ENTRY_ACL_USER;
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+			}
+
+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+				if ((ace->a_flags &
+				    acl_nfs4_flag_map[i].p_perm) != 0)
+					ae_perm |= acl_nfs4_flag_map[i].a_perm;
+			}
+
+			for (i = 0; i < acl_nfs4_perm_map_size; ++i) {
+				if ((ace->a_access_mask &
+				    acl_nfs4_perm_map[i].p_perm) != 0)
+					ae_perm |= acl_nfs4_perm_map[i].a_perm;
+			}
+		} else
+#endif	/* ARCHIVE_ACL_SUNOS_NFS4 */
+		if (default_entry_acl_type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+			aclent = &((aclent_t *)aclp)[e];
+			if ((aclent->a_type & ACL_DEFAULT) != 0)
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
+			else
+				entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+			ae_id = aclent->a_id;
+
+			switch(aclent->a_type) {
+			case DEF_USER:
+			case USER:
+				ae_name = archive_read_disk_uname(&a->archive,
+				    ae_id);
+				ae_tag = ARCHIVE_ENTRY_ACL_USER;
+				break;
+			case DEF_GROUP:
+			case GROUP:
+				ae_name = archive_read_disk_gname(&a->archive,
+				    ae_id);
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP;
+				break;
+			case DEF_CLASS_OBJ:
+			case CLASS_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_MASK;
+				break;
+			case DEF_USER_OBJ:
+			case USER_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
+				break;
+			case DEF_GROUP_OBJ:
+			case GROUP_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
+				break;
+			case DEF_OTHER_OBJ:
+			case OTHER_OBJ:
+				ae_tag = ARCHIVE_ENTRY_ACL_OTHER;
+				break;
+			default:
+				/* Unknown tag type, skip */
+				continue;
+			}
+
+			for (i = 0; i < acl_posix_perm_map_size; ++i) {
+				if ((aclent->a_perm &
+				    acl_posix_perm_map[i].p_perm) != 0)
+					ae_perm |= acl_posix_perm_map[i].a_perm;
+			}
+		} else
+			return (ARCHIVE_WARN);
+
+		archive_entry_acl_add_entry(entry, entry_acl_type,
+		    ae_perm, ae_tag, ae_id, ae_name);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+set_acl(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl,
+    int ae_requested_type, const char *tname)
+{
+	aclent_t	 *aclent;
+#if ARCHIVE_ACL_SUNOS_NFS4
+	ace_t		 *ace;
+#endif
+	int		 cmd, e, r;
+	void		 *aclp;
+	int		 ret;
+	int		 ae_type, ae_permset, ae_tag, ae_id;
+	int		 perm_map_size;
+	const acl_perm_map_t	*perm_map;
+	uid_t		 ae_uid;
+	gid_t		 ae_gid;
+	const char	*ae_name;
+	int		 entries;
+	int		 i;
+
+	ret = ARCHIVE_OK;
+	entries = archive_acl_reset(abstract_acl, ae_requested_type);
+	if (entries == 0)
+		return (ARCHIVE_OK);
+
+
+	switch (ae_requested_type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E:
+		cmd = SETACL;
+		aclp = malloc(entries * sizeof(aclent_t));
+		break;
+#if ARCHIVE_ACL_SUNOS_NFS4
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		cmd = ACE_SETACL;
+		aclp = malloc(entries * sizeof(ace_t));
+
+		break;
+#endif
+	default:
+		errno = ENOENT;
+		archive_set_error(a, errno, "Unsupported ACL type");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (aclp == NULL) {
+		archive_set_error(a, errno,
+		    "Can't allocate memory for acl buffer");
+		return (ARCHIVE_FAILED);
+	}
+
+	e = 0;
+
+	while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,
+		   &ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
+		aclent = NULL;
+#if ARCHIVE_ACL_SUNOS_NFS4
+		ace = NULL;
+#endif
+		if (cmd == SETACL) {
+			aclent = &((aclent_t *)aclp)[e];
+			aclent->a_id = -1;
+			aclent->a_type = 0;
+			aclent->a_perm = 0;
+		}
+#if ARCHIVE_ACL_SUNOS_NFS4
+		else {	/* cmd == ACE_SETACL */
+			ace = &((ace_t *)aclp)[e];
+			ace->a_who = -1;
+			ace->a_access_mask = 0;
+			ace->a_flags = 0;
+		}
+#endif	/* ARCHIVE_ACL_SUNOS_NFS4 */
+
+		switch (ae_tag) {
+		case ARCHIVE_ENTRY_ACL_USER:
+			ae_uid = archive_write_disk_uid(a, ae_name, ae_id);
+			if (aclent != NULL) {
+				aclent->a_id = ae_uid;
+				aclent->a_type |= USER;
+			}
+#if ARCHIVE_ACL_SUNOS_NFS4
+			else {
+				ace->a_who = ae_uid;
+			}
+#endif
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP:
+			ae_gid = archive_write_disk_gid(a, ae_name, ae_id);
+			if (aclent != NULL) {
+				aclent->a_id = ae_gid;
+				aclent->a_type |= GROUP;
+			}
+#if ARCHIVE_ACL_SUNOS_NFS4
+			else {
+				ace->a_who = ae_gid;
+				ace->a_flags |= ACE_IDENTIFIER_GROUP;
+			}
+#endif
+			break;
+		case ARCHIVE_ENTRY_ACL_USER_OBJ:
+			if (aclent != NULL)
+				aclent->a_type |= USER_OBJ;
+#if ARCHIVE_ACL_SUNOS_NFS4
+			else {
+				ace->a_flags |= ACE_OWNER;
+			}
+#endif
+			break;
+		case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
+			if (aclent != NULL)
+				aclent->a_type |= GROUP_OBJ;
+#if ARCHIVE_ACL_SUNOS_NFS4
+			else {
+				ace->a_flags |= ACE_GROUP;
+				ace->a_flags |= ACE_IDENTIFIER_GROUP;
+			}
+#endif
+			break;
+		case ARCHIVE_ENTRY_ACL_MASK:
+			if (aclent != NULL)
+				aclent->a_type |= CLASS_OBJ;
+			break;
+		case ARCHIVE_ENTRY_ACL_OTHER:
+			if (aclent != NULL)
+				aclent->a_type |= OTHER_OBJ;
+			break;
+#if ARCHIVE_ACL_SUNOS_NFS4
+		case ARCHIVE_ENTRY_ACL_EVERYONE:
+			if (ace != NULL)
+				ace->a_flags |= ACE_EVERYONE;
+			break;
+#endif
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL tag");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		r = 0;
+		switch (ae_type) {
+#if ARCHIVE_ACL_SUNOS_NFS4
+		case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+			if (ace != NULL)
+				ace->a_type = ACE_ACCESS_ALLOWED_ACE_TYPE;
+			else
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+			if (ace != NULL)
+				ace->a_type = ACE_ACCESS_DENIED_ACE_TYPE;
+			else
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+			if (ace != NULL)
+				ace->a_type = ACE_SYSTEM_AUDIT_ACE_TYPE;
+			else
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+			if (ace != NULL)
+				ace->a_type = ACE_SYSTEM_ALARM_ACE_TYPE;
+			else
+				r = -1;
+			break;
+#endif
+		case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+			if (aclent == NULL)
+				r = -1;
+			break;
+		case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+			if (aclent != NULL)
+				aclent->a_type |= ACL_DEFAULT;
+			else
+				r = -1;
+			break;
+		default:
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unsupported ACL entry type");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+		if (r != 0) {
+			errno = EINVAL;
+			archive_set_error(a, errno,
+			    "Failed to set ACL entry type");
+			ret = ARCHIVE_FAILED;
+			goto exit_free;
+		}
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			perm_map_size = acl_nfs4_perm_map_size;
+			perm_map = acl_nfs4_perm_map;
+		} else {
+#endif
+			perm_map_size = acl_posix_perm_map_size;
+			perm_map = acl_posix_perm_map;
+#if ARCHIVE_ACL_SUNOS_NFS4
+		}
+#endif
+		for (i = 0; i < perm_map_size; ++i) {
+			if (ae_permset & perm_map[i].a_perm) {
+#if ARCHIVE_ACL_SUNOS_NFS4
+				if (ae_requested_type ==
+				    ARCHIVE_ENTRY_ACL_TYPE_NFS4)
+					ace->a_access_mask |=
+					    perm_map[i].p_perm;
+				else
+#endif
+					aclent->a_perm |= perm_map[i].p_perm;
+			}
+		}
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+		if (ae_requested_type == ARCHIVE_ENTRY_ACL_TYPE_NFS4) {
+			for (i = 0; i < acl_nfs4_flag_map_size; ++i) {
+				if (ae_permset & acl_nfs4_flag_map[i].a_perm) {
+					ace->a_flags |=
+					    acl_nfs4_flag_map[i].p_perm;
+				}
+			}
+		}
+#endif
+	e++;
+	}
+
+	/* Try restoring the ACL through 'fd' if we can. */
+	if (fd >= 0) {
+		if (facl(fd, cmd, entries, aclp) == 0)
+			ret = ARCHIVE_OK;
+		else {
+			if (errno == EOPNOTSUPP) {
+				/* Filesystem doesn't support ACLs */
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(a, errno,
+				    "Failed to set acl on fd: %s", tname);
+				ret = ARCHIVE_WARN;
+			}
+		}
+	} else if (acl(name, cmd, entries, aclp) != 0) {
+		if (errno == EOPNOTSUPP) {
+			/* Filesystem doesn't support ACLs */
+			ret = ARCHIVE_OK;
+		} else {
+			archive_set_error(a, errno, "Failed to set acl: %s",
+			    tname);
+			ret = ARCHIVE_WARN;
+		}
+	}
+exit_free:
+	free(aclp);
+	return (ret);
+}
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	const char	*accpath;
+	void		*aclp;
+	int		aclcnt;
+	int		r;
+
+	accpath = NULL;
+
+	if (*fd < 0) {
+		accpath = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (accpath == NULL)
+			return (ARCHIVE_WARN);
+	}
+
+	archive_entry_acl_clear(entry);
+
+	aclp = NULL;
+
+#if ARCHIVE_ACL_SUNOS_NFS4
+	if (*fd >= 0)
+		aclp = sunacl_get(ACE_GETACL, &aclcnt, *fd, NULL);
+	else if ((!a->follow_symlinks)
+	    && (archive_entry_filetype(entry) == AE_IFLNK))
+		/* We can't get the ACL of a symlink, so we assume it can't
+		   have one. */
+		aclp = NULL;
+	else
+		aclp = sunacl_get(ACE_GETACL, &aclcnt, 0, accpath);
+
+	if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt,
+	    archive_entry_mode(entry), 1, S_ISDIR(archive_entry_mode(entry)),
+	    &r) == 0 && r == 1) {
+		free(aclp);
+		aclp = NULL;
+		return (ARCHIVE_OK);
+	}
+
+	if (aclp != NULL) {
+		r = translate_acl(a, entry, aclp, aclcnt,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+		free(aclp);
+		aclp = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate NFSv4 ACLs");
+		}
+		return (r);
+	}
+#endif	/* ARCHIVE_ACL_SUNOS_NFS4 */
+
+	/* Retrieve POSIX.1e ACLs from file. */
+	if (*fd >= 0)
+		aclp = sunacl_get(GETACL, &aclcnt, *fd, NULL);
+	else if ((!a->follow_symlinks)
+	    && (archive_entry_filetype(entry) == AE_IFLNK))
+		/* We can't get the ACL of a symlink, so we assume it can't
+		   have one. */
+		aclp = NULL;
+	else
+		aclp = sunacl_get(GETACL, &aclcnt, 0, accpath);
+
+	/* Ignore "trivial" ACLs that just mirror the file mode. */
+	if (aclp != NULL && sun_acl_is_trivial(aclp, aclcnt,
+	    archive_entry_mode(entry), 0, S_ISDIR(archive_entry_mode(entry)),
+	    &r) == 0 && r == 1) {
+		free(aclp);
+		aclp = NULL;
+	}
+
+	if (aclp != NULL)
+	{
+		r = translate_acl(a, entry, aclp, aclcnt,
+		    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+		free(aclp);
+		aclp = NULL;
+
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't translate access ACLs");
+			return (r);
+		}
+	}
+
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+	int		ret = ARCHIVE_OK;
+
+	(void)mode;	/* UNUSED */
+
+	if ((archive_acl_types(abstract_acl)
+	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
+		/* Solaris writes POSIX.1e access and default ACLs together */
+		ret = set_acl(a, fd, name, abstract_acl,
+		    ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e");
+
+		/* Simultaneous POSIX.1e and NFSv4 is not supported */
+		return (ret);
+	}
+#if ARCHIVE_ACL_SUNOS_NFS4
+	else if ((archive_acl_types(abstract_acl) &
+	    ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+		ret = set_acl(a, fd, name, abstract_acl,
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");
+	}
+#endif
+	return (ret);
+}
+#endif	/* ARCHIVE_ACL_SUNOS */
diff --git a/c/archive_endian.h b/c/archive_endian.h
new file mode 100644
--- /dev/null
+++ b/c/archive_endian.h
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 2002 Thomas Moestl <tmm@FreeBSD.org>
+ * 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: head/lib/libarchive/archive_endian.h 201085 2009-12-28 02:17:15Z kientzle $
+ *
+ * Borrowed from FreeBSD's <sys/endian.h>
+ */
+
+#ifndef ARCHIVE_ENDIAN_H_INCLUDED
+#define ARCHIVE_ENDIAN_H_INCLUDED
+
+/* Note:  This is a purely internal header! */
+/* Do not use this outside of libarchive internal code! */
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/*
+ * Disabling inline keyword for compilers known to choke on it:
+ * - Watcom C++ in C code.  (For any version?)
+ * - SGI MIPSpro
+ * - Microsoft Visual C++ 6.0 (supposedly newer versions too)
+ * - IBM VisualAge 6 (XL v6)
+ * - Sun WorkShop C (SunPro) before 5.9
+ */
+#if defined(__WATCOMC__) || defined(__sgi) || defined(__hpux) || defined(__BORLANDC__)
+#define	inline
+#elif defined(__IBMC__) && __IBMC__ < 700
+#define	inline
+#elif defined(__SUNPRO_C) && __SUNPRO_C < 0x590
+#define inline
+#elif defined(_MSC_VER) || defined(__osf__)
+#define inline __inline
+#endif
+
+/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */
+
+static inline uint16_t
+archive_be16dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	/* Store into unsigned temporaries before left shifting, to avoid
+	promotion to signed int and then left shifting into the sign bit,
+	which is undefined behaviour. */
+	unsigned int p1 = p[1];
+	unsigned int p0 = p[0];
+
+	return ((p0 << 8) | p1);
+}
+
+static inline uint32_t
+archive_be32dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	/* Store into unsigned temporaries before left shifting, to avoid
+	promotion to signed int and then left shifting into the sign bit,
+	which is undefined behaviour. */
+	unsigned int p3 = p[3];
+	unsigned int p2 = p[2];
+	unsigned int p1 = p[1];
+	unsigned int p0 = p[0];
+
+	return ((p0 << 24) | (p1 << 16) | (p2 << 8) | p3);
+}
+
+static inline uint64_t
+archive_be64dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	return (((uint64_t)archive_be32dec(p) << 32) | archive_be32dec(p + 4));
+}
+
+static inline uint16_t
+archive_le16dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	/* Store into unsigned temporaries before left shifting, to avoid
+	promotion to signed int and then left shifting into the sign bit,
+	which is undefined behaviour. */
+	unsigned int p1 = p[1];
+	unsigned int p0 = p[0];
+
+	return ((p1 << 8) | p0);
+}
+
+static inline uint32_t
+archive_le32dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	/* Store into unsigned temporaries before left shifting, to avoid
+	promotion to signed int and then left shifting into the sign bit,
+	which is undefined behaviour. */
+	unsigned int p3 = p[3];
+	unsigned int p2 = p[2];
+	unsigned int p1 = p[1];
+	unsigned int p0 = p[0];
+
+	return ((p3 << 24) | (p2 << 16) | (p1 << 8) | p0);
+}
+
+static inline uint64_t
+archive_le64dec(const void *pp)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	return (((uint64_t)archive_le32dec(p + 4) << 32) | archive_le32dec(p));
+}
+
+static inline void
+archive_be16enc(void *pp, uint16_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	p[0] = (u >> 8) & 0xff;
+	p[1] = u & 0xff;
+}
+
+static inline void
+archive_be32enc(void *pp, uint32_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	p[0] = (u >> 24) & 0xff;
+	p[1] = (u >> 16) & 0xff;
+	p[2] = (u >> 8) & 0xff;
+	p[3] = u & 0xff;
+}
+
+static inline void
+archive_be64enc(void *pp, uint64_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	archive_be32enc(p, (uint32_t)(u >> 32));
+	archive_be32enc(p + 4, (uint32_t)(u & 0xffffffff));
+}
+
+static inline void
+archive_le16enc(void *pp, uint16_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	p[0] = u & 0xff;
+	p[1] = (u >> 8) & 0xff;
+}
+
+static inline void
+archive_le32enc(void *pp, uint32_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	p[0] = u & 0xff;
+	p[1] = (u >> 8) & 0xff;
+	p[2] = (u >> 16) & 0xff;
+	p[3] = (u >> 24) & 0xff;
+}
+
+static inline void
+archive_le64enc(void *pp, uint64_t u)
+{
+	unsigned char *p = (unsigned char *)pp;
+
+	archive_le32enc(p, (uint32_t)(u & 0xffffffff));
+	archive_le32enc(p + 4, (uint32_t)(u >> 32));
+}
+
+#endif
diff --git a/c/archive_entry.c b/c/archive_entry.c
new file mode 100644
--- /dev/null
+++ b/c/archive_entry.c
@@ -0,0 +1,2066 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * 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: head/lib/libarchive/archive_entry.c 201096 2009-12-28 02:41:27Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#define HAVE_MAJOR
+#elif MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#define HAVE_MAJOR
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>	/* for Linux file flags */
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>	/* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#include <ext2fs/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
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive.h"
+#include "archive_acl_private.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_entry_private.h"
+
+#if !defined(HAVE_MAJOR) && !defined(major)
+/* Replacement for major/minor/makedev. */
+#define	major(x) ((int)(0x00ff & ((x) >> 8)))
+#define	minor(x) ((int)(0xffff00ff & (x)))
+#define	makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min)))
+#endif
+
+/* Play games to come up with a suitable makedev() definition. */
+#ifdef __QNXNTO__
+/* QNX.  <sigh> */
+#include <sys/netmgr.h>
+#define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))
+#elif defined makedev
+/* There's a "makedev" macro. */
+#define ae_makedev(maj, min) makedev((maj), (min))
+#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__))
+/* Windows. <sigh> */
+#define ae_makedev(maj, min) mkdev((maj), (min))
+#else
+/* There's a "makedev" function. */
+#define ae_makedev(maj, min) makedev((maj), (min))
+#endif
+
+/*
+ * This adjustment is needed to support the following idiom for adding
+ * 1000ns to the stored time:
+ * archive_entry_set_atime(archive_entry_atime(),
+ *                         archive_entry_atime_nsec() + 1000)
+ * The additional if() here compensates for ambiguity in the C standard,
+ * which permits two possible interpretations of a % b when a is negative.
+ */
+#define FIX_NS(t,ns) \
+	do {	\
+		t += ns / 1000000000; \
+		ns %= 1000000000; \
+		if (ns < 0) { --t; ns += 1000000000; } \
+	} while (0)
+
+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 const char	*ae_strtofflags(const char *stringp,
+		    unsigned long *setp, unsigned long *clrp);
+
+#ifndef HAVE_WCSCPY
+static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
+{
+	wchar_t *dest = s1;
+	while ((*s1 = *s2) != L'\0')
+		++s1, ++s2;
+	return dest;
+}
+#endif
+#ifndef HAVE_WCSLEN
+static size_t wcslen(const wchar_t *s)
+{
+	const wchar_t *p = s;
+	while (*p != L'\0')
+		++p;
+	return p - s;
+}
+#endif
+#ifndef HAVE_WMEMCMP
+/* Good enough for simple equality testing, but not for sorting. */
+#define wmemcmp(a,b,i)  memcmp((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+/****************************************************************************
+ *
+ * Public Interface
+ *
+ ****************************************************************************/
+
+struct archive_entry *
+archive_entry_clear(struct archive_entry *entry)
+{
+	if (entry == NULL)
+		return (NULL);
+	archive_mstring_clean(&entry->ae_fflags_text);
+	archive_mstring_clean(&entry->ae_gname);
+	archive_mstring_clean(&entry->ae_hardlink);
+	archive_mstring_clean(&entry->ae_pathname);
+	archive_mstring_clean(&entry->ae_sourcepath);
+	archive_mstring_clean(&entry->ae_symlink);
+	archive_mstring_clean(&entry->ae_uname);
+	archive_entry_copy_mac_metadata(entry, NULL, 0);
+	archive_acl_clear(&entry->acl);
+	archive_entry_xattr_clear(entry);
+	archive_entry_sparse_clear(entry);
+	free(entry->stat);
+	entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED;
+	memset(entry, 0, sizeof(*entry));
+	return entry;
+}
+
+struct archive_entry *
+archive_entry_clone(struct archive_entry *entry)
+{
+	struct archive_entry *entry2;
+	struct ae_xattr *xp;
+	struct ae_sparse *sp;
+	size_t s;
+	const void *p;
+
+	/* Allocate new structure and copy over all of the fields. */
+	/* TODO: Should we copy the archive over?  Or require a new archive
+	 * as an argument? */
+	entry2 = archive_entry_new2(entry->archive);
+	if (entry2 == NULL)
+		return (NULL);
+	entry2->ae_stat = entry->ae_stat;
+	entry2->ae_fflags_set = entry->ae_fflags_set;
+	entry2->ae_fflags_clear = entry->ae_fflags_clear;
+
+	/* TODO: XXX If clone can have a different archive, what do we do here if
+	 * character sets are different? XXX */
+	archive_mstring_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
+	archive_mstring_copy(&entry2->ae_gname, &entry->ae_gname);
+	archive_mstring_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
+	archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname);
+	archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath);
+	archive_mstring_copy(&entry2->ae_symlink, &entry->ae_symlink);
+	entry2->ae_set = entry->ae_set;
+	archive_mstring_copy(&entry2->ae_uname, &entry->ae_uname);
+
+	/* Copy symlink type */
+	entry2->ae_symlink_type = entry->ae_symlink_type;
+
+	/* Copy encryption status */
+	entry2->encryption = entry->encryption;
+	
+	/* Copy ACL data over. */
+	archive_acl_copy(&entry2->acl, &entry->acl);
+
+	/* Copy Mac OS metadata. */
+	p = archive_entry_mac_metadata(entry, &s);
+	archive_entry_copy_mac_metadata(entry2, p, s);
+
+	/* Copy xattr data over. */
+	xp = entry->xattr_head;
+	while (xp != NULL) {
+		archive_entry_xattr_add_entry(entry2,
+		    xp->name, xp->value, xp->size);
+		xp = xp->next;
+	}
+
+	/* Copy sparse data over. */
+	sp = entry->sparse_head;
+	while (sp != NULL) {
+		archive_entry_sparse_add_entry(entry2,
+		    sp->offset, sp->length);
+		sp = sp->next;
+	}
+
+	return (entry2);
+}
+
+void
+archive_entry_free(struct archive_entry *entry)
+{
+	archive_entry_clear(entry);
+	free(entry);
+}
+
+struct archive_entry *
+archive_entry_new(void)
+{
+	return archive_entry_new2(NULL);
+}
+
+struct archive_entry *
+archive_entry_new2(struct archive *a)
+{
+	struct archive_entry *entry;
+
+	entry = (struct archive_entry *)calloc(1, sizeof(*entry));
+	if (entry == NULL)
+		return (NULL);
+	entry->archive = a;
+	entry->ae_symlink_type = AE_SYMLINK_TYPE_UNDEFINED;
+	return (entry);
+}
+
+/*
+ * Functions for reading fields from an archive_entry.
+ */
+
+time_t
+archive_entry_atime(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_atime);
+}
+
+long
+archive_entry_atime_nsec(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_atime_nsec);
+}
+
+int
+archive_entry_atime_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_ATIME);
+}
+
+time_t
+archive_entry_birthtime(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_birthtime);
+}
+
+long
+archive_entry_birthtime_nsec(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_birthtime_nsec);
+}
+
+int
+archive_entry_birthtime_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_BIRTHTIME);
+}
+
+time_t
+archive_entry_ctime(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_ctime);
+}
+
+int
+archive_entry_ctime_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_CTIME);
+}
+
+long
+archive_entry_ctime_nsec(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_ctime_nsec);
+}
+
+dev_t
+archive_entry_dev(struct archive_entry *entry)
+{
+	if (entry->ae_stat.aest_dev_is_broken_down)
+		return ae_makedev(entry->ae_stat.aest_devmajor,
+		    entry->ae_stat.aest_devminor);
+	else
+		return (entry->ae_stat.aest_dev);
+}
+
+int
+archive_entry_dev_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_DEV);
+}
+
+dev_t
+archive_entry_devmajor(struct archive_entry *entry)
+{
+	if (entry->ae_stat.aest_dev_is_broken_down)
+		return (entry->ae_stat.aest_devmajor);
+	else
+		return major(entry->ae_stat.aest_dev);
+}
+
+dev_t
+archive_entry_devminor(struct archive_entry *entry)
+{
+	if (entry->ae_stat.aest_dev_is_broken_down)
+		return (entry->ae_stat.aest_devminor);
+	else
+		return minor(entry->ae_stat.aest_dev);
+}
+
+__LA_MODE_T
+archive_entry_filetype(struct archive_entry *entry)
+{
+	return (AE_IFMT & entry->acl.mode);
+}
+
+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;
+
+	if (archive_mstring_get_mbs(entry->archive,
+	    &entry->ae_fflags_text, &f) == 0) {
+		if (f != NULL)
+			return (f);
+	} else if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+
+	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);
+
+	archive_mstring_copy_mbs(&entry->ae_fflags_text, p);
+	free(p);
+	if (archive_mstring_get_mbs(entry->archive,
+	    &entry->ae_fflags_text, &f) == 0)
+		return (f);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+la_int64_t
+archive_entry_gid(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_gid);
+}
+
+const char *
+archive_entry_gname(struct archive_entry *entry)
+{
+	const char *p;
+	if (archive_mstring_get_mbs(entry->archive, &entry->ae_gname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+const char *
+archive_entry_gname_utf8(struct archive_entry *entry)
+{
+	const char *p;
+	if (archive_mstring_get_utf8(entry->archive, &entry->ae_gname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+
+const wchar_t *
+archive_entry_gname_w(struct archive_entry *entry)
+{
+	const wchar_t *p;
+	if (archive_mstring_get_wcs(entry->archive, &entry->ae_gname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+int
+_archive_entry_gname_l(struct archive_entry *entry,
+    const char **p, size_t *len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_get_mbs_l(&entry->ae_gname, p, len, sc));
+}
+
+const char *
+archive_entry_hardlink(struct archive_entry *entry)
+{
+	const char *p;
+	if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+		return (NULL);
+	if (archive_mstring_get_mbs(
+	    entry->archive, &entry->ae_hardlink, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+const char *
+archive_entry_hardlink_utf8(struct archive_entry *entry)
+{
+	const char *p;
+	if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+		return (NULL);
+	if (archive_mstring_get_utf8(
+	    entry->archive, &entry->ae_hardlink, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+const wchar_t *
+archive_entry_hardlink_w(struct archive_entry *entry)
+{
+	const wchar_t *p;
+	if ((entry->ae_set & AE_SET_HARDLINK) == 0)
+		return (NULL);
+	if (archive_mstring_get_wcs(
+	    entry->archive, &entry->ae_hardlink, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+int
+_archive_entry_hardlink_l(struct archive_entry *entry,
+    const char **p, size_t *len, struct archive_string_conv *sc)
+{
+	if ((entry->ae_set & AE_SET_HARDLINK) == 0) {
+		*p = NULL;
+		*len = 0;
+		return (0);
+	}
+	return (archive_mstring_get_mbs_l(&entry->ae_hardlink, p, len, sc));
+}
+
+la_int64_t
+archive_entry_ino(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_ino);
+}
+
+int
+archive_entry_ino_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_INO);
+}
+
+la_int64_t
+archive_entry_ino64(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_ino);
+}
+
+__LA_MODE_T
+archive_entry_mode(struct archive_entry *entry)
+{
+	return (entry->acl.mode);
+}
+
+time_t
+archive_entry_mtime(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_mtime);
+}
+
+long
+archive_entry_mtime_nsec(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_mtime_nsec);
+}
+
+int
+archive_entry_mtime_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_MTIME);
+}
+
+unsigned int
+archive_entry_nlink(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_nlink);
+}
+
+const char *
+archive_entry_pathname(struct archive_entry *entry)
+{
+	const char *p;
+	if (archive_mstring_get_mbs(
+	    entry->archive, &entry->ae_pathname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+const char *
+archive_entry_pathname_utf8(struct archive_entry *entry)
+{
+	const char *p;
+	if (archive_mstring_get_utf8(
+	    entry->archive, &entry->ae_pathname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+const wchar_t *
+archive_entry_pathname_w(struct archive_entry *entry)
+{
+	const wchar_t *p;
+	if (archive_mstring_get_wcs(
+	    entry->archive, &entry->ae_pathname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+int
+_archive_entry_pathname_l(struct archive_entry *entry,
+    const char **p, size_t *len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_get_mbs_l(&entry->ae_pathname, p, len, sc));
+}
+
+__LA_MODE_T
+archive_entry_perm(struct archive_entry *entry)
+{
+	return (~AE_IFMT & entry->acl.mode);
+}
+
+dev_t
+archive_entry_rdev(struct archive_entry *entry)
+{
+	if (entry->ae_stat.aest_rdev_is_broken_down)
+		return ae_makedev(entry->ae_stat.aest_rdevmajor,
+		    entry->ae_stat.aest_rdevminor);
+	else
+		return (entry->ae_stat.aest_rdev);
+}
+
+dev_t
+archive_entry_rdevmajor(struct archive_entry *entry)
+{
+	if (entry->ae_stat.aest_rdev_is_broken_down)
+		return (entry->ae_stat.aest_rdevmajor);
+	else
+		return major(entry->ae_stat.aest_rdev);
+}
+
+dev_t
+archive_entry_rdevminor(struct archive_entry *entry)
+{
+	if (entry->ae_stat.aest_rdev_is_broken_down)
+		return (entry->ae_stat.aest_rdevminor);
+	else
+		return minor(entry->ae_stat.aest_rdev);
+}
+
+la_int64_t
+archive_entry_size(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_size);
+}
+
+int
+archive_entry_size_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_SIZE);
+}
+
+const char *
+archive_entry_sourcepath(struct archive_entry *entry)
+{
+	const char *p;
+	if (archive_mstring_get_mbs(
+	    entry->archive, &entry->ae_sourcepath, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+const wchar_t *
+archive_entry_sourcepath_w(struct archive_entry *entry)
+{
+	const wchar_t *p;
+	if (archive_mstring_get_wcs(
+	    entry->archive, &entry->ae_sourcepath, &p) == 0)
+		return (p);
+	return (NULL);
+}
+
+const char *
+archive_entry_symlink(struct archive_entry *entry)
+{
+	const char *p;
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+		return (NULL);
+	if (archive_mstring_get_mbs(
+	    entry->archive, &entry->ae_symlink, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+int
+archive_entry_symlink_type(struct archive_entry *entry)
+{
+	return (entry->ae_symlink_type);
+}
+
+const char *
+archive_entry_symlink_utf8(struct archive_entry *entry)
+{
+	const char *p;
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+		return (NULL);
+	if (archive_mstring_get_utf8(
+	    entry->archive, &entry->ae_symlink, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+const wchar_t *
+archive_entry_symlink_w(struct archive_entry *entry)
+{
+	const wchar_t *p;
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0)
+		return (NULL);
+	if (archive_mstring_get_wcs(
+	    entry->archive, &entry->ae_symlink, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+int
+_archive_entry_symlink_l(struct archive_entry *entry,
+    const char **p, size_t *len, struct archive_string_conv *sc)
+{
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0) {
+		*p = NULL;
+		*len = 0;
+		return (0);
+	}
+	return (archive_mstring_get_mbs_l( &entry->ae_symlink, p, len, sc));
+}
+
+la_int64_t
+archive_entry_uid(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_uid);
+}
+
+const char *
+archive_entry_uname(struct archive_entry *entry)
+{
+	const char *p;
+	if (archive_mstring_get_mbs(entry->archive, &entry->ae_uname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+const char *
+archive_entry_uname_utf8(struct archive_entry *entry)
+{
+	const char *p;
+	if (archive_mstring_get_utf8(entry->archive, &entry->ae_uname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+const wchar_t *
+archive_entry_uname_w(struct archive_entry *entry)
+{
+	const wchar_t *p;
+	if (archive_mstring_get_wcs(entry->archive, &entry->ae_uname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+int
+_archive_entry_uname_l(struct archive_entry *entry,
+    const char **p, size_t *len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_get_mbs_l(&entry->ae_uname, p, len, sc));
+}
+
+int
+archive_entry_is_data_encrypted(struct archive_entry *entry)
+{
+	return ((entry->encryption & AE_ENCRYPTION_DATA) == AE_ENCRYPTION_DATA);
+}
+
+int
+archive_entry_is_metadata_encrypted(struct archive_entry *entry)
+{
+	return ((entry->encryption & AE_ENCRYPTION_METADATA) == AE_ENCRYPTION_METADATA);
+}
+
+int
+archive_entry_is_encrypted(struct archive_entry *entry)
+{
+	return (entry->encryption & (AE_ENCRYPTION_DATA|AE_ENCRYPTION_METADATA));
+}
+
+/*
+ * Functions to set archive_entry properties.
+ */
+
+void
+archive_entry_set_filetype(struct archive_entry *entry, unsigned int type)
+{
+	entry->stat_valid = 0;
+	entry->acl.mode &= ~AE_IFMT;
+	entry->acl.mode |= AE_IFMT & type;
+}
+
+void
+archive_entry_set_fflags(struct archive_entry *entry,
+    unsigned long set, unsigned long clear)
+{
+	archive_mstring_clean(&entry->ae_fflags_text);
+	entry->ae_fflags_set = set;
+	entry->ae_fflags_clear = clear;
+}
+
+const char *
+archive_entry_copy_fflags_text(struct archive_entry *entry,
+    const char *flags)
+{
+	archive_mstring_copy_mbs(&entry->ae_fflags_text, flags);
+	return (ae_strtofflags(flags,
+		    &entry->ae_fflags_set, &entry->ae_fflags_clear));
+}
+
+const wchar_t *
+archive_entry_copy_fflags_text_w(struct archive_entry *entry,
+    const wchar_t *flags)
+{
+	archive_mstring_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, la_int64_t g)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_gid = g;
+}
+
+void
+archive_entry_set_gname(struct archive_entry *entry, const char *name)
+{
+	archive_mstring_copy_mbs(&entry->ae_gname, name);
+}
+
+void
+archive_entry_set_gname_utf8(struct archive_entry *entry, const char *name)
+{
+	archive_mstring_copy_utf8(&entry->ae_gname, name);
+}
+
+void
+archive_entry_copy_gname(struct archive_entry *entry, const char *name)
+{
+	archive_mstring_copy_mbs(&entry->ae_gname, name);
+}
+
+void
+archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
+{
+	archive_mstring_copy_wcs(&entry->ae_gname, name);
+}
+
+int
+archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
+{
+	if (archive_mstring_update_utf8(entry->archive,
+	    &entry->ae_gname, name) == 0)
+		return (1);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (0);
+}
+
+int
+_archive_entry_copy_gname_l(struct archive_entry *entry,
+    const char *name, size_t len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_copy_mbs_len_l(&entry->ae_gname, name, len, sc));
+}
+
+void
+archive_entry_set_ino(struct archive_entry *entry, la_int64_t ino)
+{
+	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_INO;
+	entry->ae_stat.aest_ino = ino;
+}
+
+void
+archive_entry_set_ino64(struct archive_entry *entry, la_int64_t ino)
+{
+	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_INO;
+	entry->ae_stat.aest_ino = ino;
+}
+
+void
+archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
+{
+	archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+	if (target != NULL)
+		entry->ae_set |= AE_SET_HARDLINK;
+	else
+		entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+void
+archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target)
+{
+	archive_mstring_copy_utf8(&entry->ae_hardlink, target);
+	if (target != NULL)
+		entry->ae_set |= AE_SET_HARDLINK;
+	else
+		entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+void
+archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
+{
+	archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+	if (target != NULL)
+		entry->ae_set |= AE_SET_HARDLINK;
+	else
+		entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+void
+archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
+{
+	archive_mstring_copy_wcs(&entry->ae_hardlink, target);
+	if (target != NULL)
+		entry->ae_set |= AE_SET_HARDLINK;
+	else
+		entry->ae_set &= ~AE_SET_HARDLINK;
+}
+
+int
+archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target)
+{
+	if (target != NULL)
+		entry->ae_set |= AE_SET_HARDLINK;
+	else
+		entry->ae_set &= ~AE_SET_HARDLINK;
+	if (archive_mstring_update_utf8(entry->archive,
+	    &entry->ae_hardlink, target) == 0)
+		return (1);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (0);
+}
+
+int
+_archive_entry_copy_hardlink_l(struct archive_entry *entry,
+    const char *target, size_t len, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink,
+	    target, len, sc);
+	if (target != NULL && r == 0)
+		entry->ae_set |= AE_SET_HARDLINK;
+	else
+		entry->ae_set &= ~AE_SET_HARDLINK;
+	return (r);
+}
+
+void
+archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
+{
+	FIX_NS(t, ns);
+	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_ATIME;
+	entry->ae_stat.aest_atime = t;
+	entry->ae_stat.aest_atime_nsec = ns;
+}
+
+void
+archive_entry_unset_atime(struct archive_entry *entry)
+{
+	archive_entry_set_atime(entry, 0, 0);
+	entry->ae_set &= ~AE_SET_ATIME;
+}
+
+void
+archive_entry_set_birthtime(struct archive_entry *entry, time_t t, long ns)
+{
+	FIX_NS(t, ns);
+	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_BIRTHTIME;
+	entry->ae_stat.aest_birthtime = t;
+	entry->ae_stat.aest_birthtime_nsec = ns;
+}
+
+void
+archive_entry_unset_birthtime(struct archive_entry *entry)
+{
+	archive_entry_set_birthtime(entry, 0, 0);
+	entry->ae_set &= ~AE_SET_BIRTHTIME;
+}
+
+void
+archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
+{
+	FIX_NS(t, ns);
+	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_CTIME;
+	entry->ae_stat.aest_ctime = t;
+	entry->ae_stat.aest_ctime_nsec = ns;
+}
+
+void
+archive_entry_unset_ctime(struct archive_entry *entry)
+{
+	archive_entry_set_ctime(entry, 0, 0);
+	entry->ae_set &= ~AE_SET_CTIME;
+}
+
+void
+archive_entry_set_dev(struct archive_entry *entry, dev_t d)
+{
+	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_DEV;
+	entry->ae_stat.aest_dev_is_broken_down = 0;
+	entry->ae_stat.aest_dev = d;
+}
+
+void
+archive_entry_set_devmajor(struct archive_entry *entry, dev_t m)
+{
+	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_DEV;
+	entry->ae_stat.aest_dev_is_broken_down = 1;
+	entry->ae_stat.aest_devmajor = m;
+}
+
+void
+archive_entry_set_devminor(struct archive_entry *entry, dev_t m)
+{
+	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_DEV;
+	entry->ae_stat.aest_dev_is_broken_down = 1;
+	entry->ae_stat.aest_devminor = m;
+}
+
+/* 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_set & AE_SET_SYMLINK)
+		archive_mstring_copy_mbs(&entry->ae_symlink, target);
+	else
+		archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+}
+
+void
+archive_entry_set_link_utf8(struct archive_entry *entry, const char *target)
+{
+	if (entry->ae_set & AE_SET_SYMLINK)
+		archive_mstring_copy_utf8(&entry->ae_symlink, target);
+	else
+		archive_mstring_copy_utf8(&entry->ae_hardlink, target);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link(struct archive_entry *entry, const char *target)
+{
+	if (entry->ae_set & AE_SET_SYMLINK)
+		archive_mstring_copy_mbs(&entry->ae_symlink, target);
+	else
+		archive_mstring_copy_mbs(&entry->ae_hardlink, target);
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
+{
+	if (entry->ae_set & AE_SET_SYMLINK)
+		archive_mstring_copy_wcs(&entry->ae_symlink, target);
+	else
+		archive_mstring_copy_wcs(&entry->ae_hardlink, target);
+}
+
+int
+archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
+{
+	int r;
+	if (entry->ae_set & AE_SET_SYMLINK)
+		r = archive_mstring_update_utf8(entry->archive,
+		    &entry->ae_symlink, target);
+	else
+		r = archive_mstring_update_utf8(entry->archive,
+		    &entry->ae_hardlink, target);
+	if (r == 0)
+		return (1);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (0);
+}
+
+int
+_archive_entry_copy_link_l(struct archive_entry *entry,
+    const char *target, size_t len, struct archive_string_conv *sc)
+{
+	int r;
+
+	if (entry->ae_set & AE_SET_SYMLINK)
+		r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink,
+		    target, len, sc);
+	else
+		r = archive_mstring_copy_mbs_len_l(&entry->ae_hardlink,
+		    target, len, sc);
+	return (r);
+}
+
+void
+archive_entry_set_mode(struct archive_entry *entry, mode_t m)
+{
+	entry->stat_valid = 0;
+	entry->acl.mode = m;
+}
+
+void
+archive_entry_set_mtime(struct archive_entry *entry, time_t t, long ns)
+{
+	FIX_NS(t, ns);
+	entry->stat_valid = 0;
+	entry->ae_set |= AE_SET_MTIME;
+	entry->ae_stat.aest_mtime = t;
+	entry->ae_stat.aest_mtime_nsec = ns;
+}
+
+void
+archive_entry_unset_mtime(struct archive_entry *entry)
+{
+	archive_entry_set_mtime(entry, 0, 0);
+	entry->ae_set &= ~AE_SET_MTIME;
+}
+
+void
+archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_nlink = nlink;
+}
+
+void
+archive_entry_set_pathname(struct archive_entry *entry, const char *name)
+{
+	archive_mstring_copy_mbs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_set_pathname_utf8(struct archive_entry *entry, const char *name)
+{
+	archive_mstring_copy_utf8(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_copy_pathname(struct archive_entry *entry, const char *name)
+{
+	archive_mstring_copy_mbs(&entry->ae_pathname, name);
+}
+
+void
+archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
+{
+	archive_mstring_copy_wcs(&entry->ae_pathname, name);
+}
+
+int
+archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name)
+{
+	if (archive_mstring_update_utf8(entry->archive,
+	    &entry->ae_pathname, name) == 0)
+		return (1);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (0);
+}
+
+int
+_archive_entry_copy_pathname_l(struct archive_entry *entry,
+    const char *name, size_t len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_copy_mbs_len_l(&entry->ae_pathname,
+	    name, len, sc));
+}
+
+void
+archive_entry_set_perm(struct archive_entry *entry, mode_t p)
+{
+	entry->stat_valid = 0;
+	entry->acl.mode &= AE_IFMT;
+	entry->acl.mode |= ~AE_IFMT & p;
+}
+
+void
+archive_entry_set_rdev(struct archive_entry *entry, dev_t m)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_rdev = m;
+	entry->ae_stat.aest_rdev_is_broken_down = 0;
+}
+
+void
+archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_rdev_is_broken_down = 1;
+	entry->ae_stat.aest_rdevmajor = m;
+}
+
+void
+archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_rdev_is_broken_down = 1;
+	entry->ae_stat.aest_rdevminor = m;
+}
+
+void
+archive_entry_set_size(struct archive_entry *entry, la_int64_t s)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_size = s;
+	entry->ae_set |= AE_SET_SIZE;
+}
+
+void
+archive_entry_unset_size(struct archive_entry *entry)
+{
+	archive_entry_set_size(entry, 0);
+	entry->ae_set &= ~AE_SET_SIZE;
+}
+
+void
+archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path)
+{
+	archive_mstring_copy_mbs(&entry->ae_sourcepath, path);
+}
+
+void
+archive_entry_copy_sourcepath_w(struct archive_entry *entry, const wchar_t *path)
+{
+	archive_mstring_copy_wcs(&entry->ae_sourcepath, path);
+}
+
+void
+archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
+{
+	archive_mstring_copy_mbs(&entry->ae_symlink, linkname);
+	if (linkname != NULL)
+		entry->ae_set |= AE_SET_SYMLINK;
+	else
+		entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+void
+archive_entry_set_symlink_type(struct archive_entry *entry, int type)
+{
+	entry->ae_symlink_type = type;
+}
+
+void
+archive_entry_set_symlink_utf8(struct archive_entry *entry, const char *linkname)
+{
+	archive_mstring_copy_utf8(&entry->ae_symlink, linkname);
+	if (linkname != NULL)
+		entry->ae_set |= AE_SET_SYMLINK;
+	else
+		entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+void
+archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)
+{
+	archive_mstring_copy_mbs(&entry->ae_symlink, linkname);
+	if (linkname != NULL)
+		entry->ae_set |= AE_SET_SYMLINK;
+	else
+		entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+void
+archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
+{
+	archive_mstring_copy_wcs(&entry->ae_symlink, linkname);
+	if (linkname != NULL)
+		entry->ae_set |= AE_SET_SYMLINK;
+	else
+		entry->ae_set &= ~AE_SET_SYMLINK;
+}
+
+int
+archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname)
+{
+	if (linkname != NULL)
+		entry->ae_set |= AE_SET_SYMLINK;
+	else
+		entry->ae_set &= ~AE_SET_SYMLINK;
+	if (archive_mstring_update_utf8(entry->archive,
+	    &entry->ae_symlink, linkname) == 0)
+		return (1);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (0);
+}
+
+int
+_archive_entry_copy_symlink_l(struct archive_entry *entry,
+    const char *linkname, size_t len, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_mstring_copy_mbs_len_l(&entry->ae_symlink,
+	    linkname, len, sc);
+	if (linkname != NULL && r == 0)
+		entry->ae_set |= AE_SET_SYMLINK;
+	else
+		entry->ae_set &= ~AE_SET_SYMLINK;
+	return (r);
+}
+
+void
+archive_entry_set_uid(struct archive_entry *entry, la_int64_t u)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_uid = u;
+}
+
+void
+archive_entry_set_uname(struct archive_entry *entry, const char *name)
+{
+	archive_mstring_copy_mbs(&entry->ae_uname, name);
+}
+
+void
+archive_entry_set_uname_utf8(struct archive_entry *entry, const char *name)
+{
+	archive_mstring_copy_utf8(&entry->ae_uname, name);
+}
+
+void
+archive_entry_copy_uname(struct archive_entry *entry, const char *name)
+{
+	archive_mstring_copy_mbs(&entry->ae_uname, name);
+}
+
+void
+archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
+{
+	archive_mstring_copy_wcs(&entry->ae_uname, name);
+}
+
+int
+archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)
+{
+	if (archive_mstring_update_utf8(entry->archive,
+	    &entry->ae_uname, name) == 0)
+		return (1);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (0);
+}
+
+void
+archive_entry_set_is_data_encrypted(struct archive_entry *entry, char is_encrypted)
+{
+	if (is_encrypted) {
+		entry->encryption |= AE_ENCRYPTION_DATA;
+	} else {
+		entry->encryption &= ~AE_ENCRYPTION_DATA;
+	}
+}
+
+void
+archive_entry_set_is_metadata_encrypted(struct archive_entry *entry, char is_encrypted)
+{
+	if (is_encrypted) {
+		entry->encryption |= AE_ENCRYPTION_METADATA;
+	} else {
+		entry->encryption &= ~AE_ENCRYPTION_METADATA;
+	}
+}
+
+int
+_archive_entry_copy_uname_l(struct archive_entry *entry,
+    const char *name, size_t len, struct archive_string_conv *sc)
+{
+	return (archive_mstring_copy_mbs_len_l(&entry->ae_uname,
+	    name, len, sc));
+}
+
+const void *
+archive_entry_mac_metadata(struct archive_entry *entry, size_t *s)
+{
+  *s = entry->mac_metadata_size;
+  return entry->mac_metadata;
+}
+
+void
+archive_entry_copy_mac_metadata(struct archive_entry *entry,
+    const void *p, size_t s)
+{
+  free(entry->mac_metadata);
+  if (p == NULL || s == 0) {
+    entry->mac_metadata = NULL;
+    entry->mac_metadata_size = 0;
+  } else {
+    entry->mac_metadata_size = s;
+    entry->mac_metadata = malloc(s);
+    if (entry->mac_metadata == NULL)
+      abort();
+    memcpy(entry->mac_metadata, p, s);
+  }
+}
+
+/*
+ * 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.
+ */
+
+struct archive_acl *
+archive_entry_acl(struct archive_entry *entry)
+{
+	return &entry->acl;
+}
+
+void
+archive_entry_acl_clear(struct archive_entry *entry)
+{
+	archive_acl_clear(&entry->acl);
+}
+
+/*
+ * Add a single ACL entry to the internal list of ACL data.
+ */
+int
+archive_entry_acl_add_entry(struct archive_entry *entry,
+    int type, int permset, int tag, int id, const char *name)
+{
+	return archive_acl_add_entry(&entry->acl, type, permset, tag, id, name);
+}
+
+/*
+ * As above, but with a wide-character name.
+ */
+int
+archive_entry_acl_add_entry_w(struct archive_entry *entry,
+    int type, int permset, int tag, int id, const wchar_t *name)
+{
+	return archive_acl_add_entry_w_len(&entry->acl,
+	    type, permset, tag, id, name, wcslen(name));
+}
+
+/*
+ * Return a bitmask of ACL types in an archive entry ACL list
+ */
+int
+archive_entry_acl_types(struct archive_entry *entry)
+{
+	return (archive_acl_types(&entry->acl));
+}
+
+/*
+ * Return a count of entries matching "want_type".
+ */
+int
+archive_entry_acl_count(struct archive_entry *entry, int want_type)
+{
+	return archive_acl_count(&entry->acl, want_type);
+}
+
+/*
+ * 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)
+{
+	return archive_acl_reset(&entry->acl, want_type);
+}
+
+/*
+ * 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)
+{
+	int r;
+	r = archive_acl_next(entry->archive, &entry->acl, want_type, type,
+		permset, tag, id, name);
+	if (r == ARCHIVE_FATAL && errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (r);
+}
+
+/*
+ * Generate a text version of the ACL. The flags parameter controls
+ * the style of the generated ACL.
+ */
+wchar_t *
+archive_entry_acl_to_text_w(struct archive_entry *entry, la_ssize_t *len,
+    int flags)
+{
+	return (archive_acl_to_text_w(&entry->acl, len, flags,
+	    entry->archive));
+}
+
+char *
+archive_entry_acl_to_text(struct archive_entry *entry, la_ssize_t *len,
+    int flags)
+{
+	return (archive_acl_to_text_l(&entry->acl, len, flags, NULL));
+}
+
+char *
+_archive_entry_acl_to_text_l(struct archive_entry *entry, ssize_t *len,
+   int flags, struct archive_string_conv *sc)
+{
+	return (archive_acl_to_text_l(&entry->acl, len, flags, sc));
+}
+
+/*
+ * ACL text parser.
+ */
+int
+archive_entry_acl_from_text_w(struct archive_entry *entry,
+    const wchar_t *wtext, int type)
+{
+	return (archive_acl_from_text_w(&entry->acl, wtext, type));
+}
+
+int
+archive_entry_acl_from_text(struct archive_entry *entry,
+    const char *text, int type)
+{
+	return (archive_acl_from_text_l(&entry->acl, text, type, NULL));
+}
+
+int
+_archive_entry_acl_from_text_l(struct archive_entry *entry, const char *text,
+    int type, struct archive_string_conv *sc)
+{
+	return (archive_acl_from_text_l(&entry->acl, text, type, sc));
+}
+
+/* Deprecated */
+static int
+archive_entry_acl_text_compat(int *flags)
+{
+	if ((*flags & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) == 0)
+		return (1);
+
+	/* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID */
+	if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID) != 0)
+		*flags |= ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID;
+
+	/* ABI compat with old ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT */
+	if ((*flags & OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) != 0)
+		*flags |=  ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT;
+
+	*flags |= ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA;
+
+	return (0);
+}
+
+/* Deprecated */
+const wchar_t *
+archive_entry_acl_text_w(struct archive_entry *entry, int flags)
+{
+	free(entry->acl.acl_text_w);
+	entry->acl.acl_text_w = NULL;
+	if (archive_entry_acl_text_compat(&flags) == 0)
+		entry->acl.acl_text_w = archive_acl_to_text_w(&entry->acl,
+		    NULL, flags, entry->archive);
+	return (entry->acl.acl_text_w);
+}
+
+/* Deprecated */
+const char *
+archive_entry_acl_text(struct archive_entry *entry, int flags)
+{
+	free(entry->acl.acl_text);
+	entry->acl.acl_text = NULL;
+	if (archive_entry_acl_text_compat(&flags) == 0)
+		entry->acl.acl_text = archive_acl_to_text_l(&entry->acl, NULL,
+		    flags, NULL);
+
+	return (entry->acl.acl_text);
+}
+
+/* Deprecated */
+int
+_archive_entry_acl_text_l(struct archive_entry *entry, int flags,
+    const char **acl_text, size_t *len, struct archive_string_conv *sc)
+{
+	free(entry->acl.acl_text);
+	entry->acl.acl_text = NULL;
+
+	if (archive_entry_acl_text_compat(&flags) == 0)
+		entry->acl.acl_text = archive_acl_to_text_l(&entry->acl,
+		    (ssize_t *)len, flags, sc);
+
+	*acl_text = entry->acl.acl_text;
+
+	return (0);
+}
+
+/*
+ * 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.
+ */
+
+/*
+ * Supported file flags on FreeBSD and Mac OS:
+ * sappnd,sappend		SF_APPEND
+ * arch,archived		SF_ARCHIVED
+ * schg,schange,simmutable	SF_IMMUTABLE
+ * sunlnk,sunlink		SF_NOUNLINK	(FreeBSD only)
+ * uappnd,uappend		UF_APPEND
+ * compressed			UF_COMPRESSED	(Mac OS only)
+ * hidden,uhidden		UF_HIDDEN
+ * uchg,uchange,uimmutable	UF_IMMUTABLE
+ * nodump			UF_NODUMP
+ * uunlnk,uunlink		UF_NOUNLINK	(FreeBSD only)
+ * offline,uoffline		UF_OFFLINE	(FreeBSD only)
+ * opaque			UF_OPAQUE
+ * rdonly,urdonly,readonly	UF_READONLY	(FreeBSD only)
+ * reparse,ureparse		UF_REPARSE	(FreeBSD only)
+ * sparse,usparse		UF_SPARSE	(FreeBSD only)
+ * system,usystem		UF_SYSTEM	(FreeBSD only)
+ *
+ * See chflags(2) for more information
+ *
+ * Supported file attributes on Linux:
+ * a	append only			FS_APPEND_FL		sappnd
+ * A	no atime updates		FS_NOATIME_FL		atime
+ * c	compress			FS_COMPR_FL		compress
+ * C	no copy on write		FS_NOCOW_FL		cow
+ * d	no dump				FS_NODUMP_FL		dump
+ * D	synchronous directory updates	FS_DIRSYNC_FL		dirsync
+ * i	immutable			FS_IMMUTABLE_FL		schg
+ * j	data journalling		FS_JOURNAL_DATA_FL	journal
+ * P	project hierarchy		FS_PROJINHERIT_FL	projinherit
+ * s	secure deletion			FS_SECRM_FL		securedeletion
+ * S	synchronous updates		FS_SYNC_FL		sync
+ * t	no tail-merging			FS_NOTAIL_FL		tail
+ * T	top of directory hierarchy	FS_TOPDIR_FL		topdir
+ * u	undeletable			FS_UNRM_FL		undel
+ *
+ * See ioctl_iflags(2) for more information
+ *
+ * Equivalent file flags supported on FreeBSD / Mac OS and Linux:
+ * SF_APPEND		FS_APPEND_FL		sappnd
+ * SF_IMMUTABLE		FS_IMMUTABLE_FL		schg
+ * UF_NODUMP		FS_NODUMP_FL		nodump
+ */
+
+static const struct flag {
+	const char	*name;
+	const wchar_t	*wname;
+	unsigned long	 set;
+	unsigned long	 clear;
+} fileflags[] = {
+	/* 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
+#if defined(FS_APPEND_FL)			/* 'a' */
+	{ "nosappnd",	L"nosappnd",		FS_APPEND_FL,	0},
+	{ "nosappend",	L"nosappend",		FS_APPEND_FL,	0},
+#elif defined(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
+#if defined(FS_IMMUTABLE_FL)			/* 'i' */
+	{ "noschg",	L"noschg",		FS_IMMUTABLE_FL,	0},
+	{ "noschange",	L"noschange",		FS_IMMUTABLE_FL,	0},
+	{ "nosimmutable",	L"nosimmutable",	FS_IMMUTABLE_FL,	0},
+#elif defined(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 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
+#if defined(FS_NODUMP_FL)	/* 'd' */
+	{ "nodump",	L"nodump",		0,		FS_NODUMP_FL},
+#elif defined(EXT2_NODUMP_FL)
+	{ "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 UF_COMPRESSED
+	/* Mac OS */
+	{ "nocompressed",	L"nocompressed",	UF_COMPRESSED,	0},
+#endif
+#ifdef UF_HIDDEN
+	{ "nohidden",	L"nohidden",		UF_HIDDEN,	0},
+	{ "nouhidden",	L"nouhidden",		UF_HIDDEN,	0},
+#endif
+#ifdef FILE_ATTRIBUTE_HIDDEN
+	{ "nohidden",	L"nohidden",	FILE_ATTRIBUTE_HIDDEN,	0},
+	{ "nouhidden",	L"nouhidden",	FILE_ATTRIBUTE_HIDDEN,	0},
+#endif
+#ifdef UF_OFFLINE
+	{ "nooffline",	L"nooffline",		UF_OFFLINE,	0},
+	{ "nouoffline",	L"nouoffline",		UF_OFFLINE,	0},
+#endif
+#ifdef UF_READONLY
+	{ "nordonly",	L"nordonly",		UF_READONLY,	0},
+	{ "nourdonly",	L"nourdonly",		UF_READONLY,	0},
+	{ "noreadonly",	L"noreadonly",		UF_READONLY,	0},
+#endif
+#ifdef FILE_ATTRIBUTE_READONLY
+	{ "nordonly",	L"nordonly",	FILE_ATTRIBUTE_READONLY,	0},
+	{ "nourdonly",	L"nourdonly",	FILE_ATTRIBUTE_READONLY,	0},
+	{ "noreadonly",	L"noreadonly",	FILE_ATTRIBUTE_READONLY,	0},
+#endif
+#ifdef UF_SPARSE
+	{ "nosparse",	L"nosparse",		UF_SPARSE,	0},
+	{ "nousparse",	L"nousparse",		UF_SPARSE,	0},
+#endif
+#ifdef UF_REPARSE
+	{ "noreparse",	L"noreparse",		UF_REPARSE,	0},
+	{ "noureparse",	L"noureparse",		UF_REPARSE,	0},
+#endif
+#ifdef UF_SYSTEM
+	{ "nosystem",	L"nosystem",		UF_SYSTEM,	0},
+	{ "nousystem",	L"nousystem",		UF_SYSTEM,	0},
+#endif
+#ifdef FILE_ATTRIBUTE_SYSTEM
+	{ "nosystem",	L"nosystem",	FILE_ATTRIBUTE_SYSTEM,	0},
+	{ "nousystem",	L"nousystem",	FILE_ATTRIBUTE_SYSTEM,	0},
+#endif
+#if defined(FS_UNRM_FL)		/* 'u' */
+	{ "noundel",	L"noundel",		FS_UNRM_FL,	0},
+#elif defined(EXT2_UNRM_FL)
+	{ "noundel",	L"noundel",		EXT2_UNRM_FL,	0},
+#endif
+
+#if defined(FS_COMPR_FL)	/* 'c' */
+	{ "nocompress",	L"nocompress",       	FS_COMPR_FL,	0},
+#elif defined(EXT2_COMPR_FL)
+	{ "nocompress",	L"nocompress",       	EXT2_COMPR_FL,	0},
+#endif
+
+#if defined(FS_NOATIME_FL)	/* 'A' */
+	{ "noatime",	L"noatime",		0,		FS_NOATIME_FL},
+#elif defined(EXT2_NOATIME_FL)
+	{ "noatime",	L"noatime",		0,		EXT2_NOATIME_FL},
+#endif
+#if defined(FS_DIRSYNC_FL)	/* 'D' */
+	{ "nodirsync",	L"nodirsync",		FS_DIRSYNC_FL,		0},
+#elif defined(EXT2_DIRSYNC_FL)
+	{ "nodirsync",	L"nodirsync",		EXT2_DIRSYNC_FL,	0},
+#endif
+#if defined(FS_JOURNAL_DATA_FL)	/* 'j' */
+	{ "nojournal-data",L"nojournal-data",	FS_JOURNAL_DATA_FL,	0},
+	{ "nojournal",	L"nojournal",		FS_JOURNAL_DATA_FL,	0},
+#elif defined(EXT3_JOURNAL_DATA_FL)
+	{ "nojournal-data",L"nojournal-data",	EXT3_JOURNAL_DATA_FL,	0},
+	{ "nojournal",	L"nojournal",		EXT3_JOURNAL_DATA_FL,	0},
+#endif
+#if defined(FS_SECRM_FL)	/* 's' */
+	{ "nosecdel",	L"nosecdel",		FS_SECRM_FL,		0},
+	{ "nosecuredeletion",L"nosecuredeletion",FS_SECRM_FL,		0},
+#elif defined(EXT2_SECRM_FL)
+	{ "nosecdel",	L"nosecdel",		EXT2_SECRM_FL,		0},
+	{ "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL,		0},
+#endif
+#if defined(FS_SYNC_FL)		/* 'S' */
+	{ "nosync",	L"nosync",		FS_SYNC_FL,		0},
+#elif defined(EXT2_SYNC_FL)
+	{ "nosync",	L"nosync",		EXT2_SYNC_FL,		0},
+#endif
+#if defined(FS_NOTAIL_FL)	/* 't' */
+	{ "notail",	L"notail",		0,		FS_NOTAIL_FL},
+#elif defined(EXT2_NOTAIL_FL)
+	{ "notail",	L"notail",		0,		EXT2_NOTAIL_FL},
+#endif
+#if defined(FS_TOPDIR_FL)	/* 'T' */
+	{ "notopdir",	L"notopdir",		FS_TOPDIR_FL,		0},
+#elif defined(EXT2_TOPDIR_FL)
+	{ "notopdir",	L"notopdir",		EXT2_TOPDIR_FL,		0},
+#endif
+#ifdef FS_NOCOW_FL	/* 'C' */
+	{ "nocow",	L"nocow",		0,	FS_NOCOW_FL},
+#endif
+#ifdef FS_PROJINHERIT_FL	/* 'P' */
+	{ "noprojinherit",L"noprojinherit",	FS_PROJINHERIT_FL,	0},
+#endif
+	{ NULL,		NULL,			0,		0}
+};
+
+/*
+ * fflagstostr --
+ *	Convert file flags to a comma-separated string.  If no flags
+ *	are set, return the empty string.
+ */
+static char *
+ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
+{
+	char *string, *dp;
+	const char *sp;
+	unsigned long bits;
+	const struct flag *flag;
+	size_t	length;
+
+	bits = bitset | bitclear;
+	length = 0;
+	for (flag = fileflags; 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 = fileflags; 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);
+}
+
+/*
+ * strtofflags --
+ *	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.
+ */
+static const char *
+ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp)
+{
+	const char *start, *end;
+	const struct flag *flag;
+	unsigned long set, clear;
+	const char *failed;
+
+	set = clear = 0;
+	start = s;
+	failed = NULL;
+	/* Find start of first token. */
+	while (*start == '\t'  ||  *start == ' '  ||  *start == ',')
+		start++;
+	while (*start != '\0') {
+		size_t length;
+		/* Locate end of token. */
+		end = start;
+		while (*end != '\0'  &&  *end != '\t'  &&
+		    *end != ' '  &&  *end != ',')
+			end++;
+		length = end - start;
+		for (flag = fileflags; flag->name != NULL; flag++) {
+			size_t flag_length = strlen(flag->name);
+			if (length == flag_length
+			    && memcmp(start, flag->name, length) == 0) {
+				/* Matched "noXXXX", so reverse the sense. */
+				clear |= flag->set;
+				set |= flag->clear;
+				break;
+			} else if (length == flag_length - 2
+			    && memcmp(start, flag->name + 2, length) == 0) {
+				/* Matched "XXXX", so don't reverse. */
+				set |= flag->set;
+				clear |= flag->clear;
+				break;
+			}
+		}
+		/* Ignore unknown flag names. */
+		if (flag->name == NULL  &&  failed == NULL)
+			failed = start;
+
+		/* Find start of next token. */
+		start = end;
+		while (*start == '\t'  ||  *start == ' '  ||  *start == ',')
+			start++;
+
+	}
+
+	if (setp)
+		*setp = set;
+	if (clrp)
+		*clrp = clear;
+
+	/* Return location of first failure. */
+	return (failed);
+}
+
+/*
+ * 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.
+ */
+static const wchar_t *
+ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
+{
+	const wchar_t *start, *end;
+	const 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') {
+		size_t length;
+		/* Locate end of token. */
+		end = start;
+		while (*end != L'\0'  &&  *end != L'\t'  &&
+		    *end != L' '  &&  *end != L',')
+			end++;
+		length = end - start;
+		for (flag = fileflags; flag->wname != NULL; flag++) {
+			size_t flag_length = wcslen(flag->wname);
+			if (length == flag_length
+			    && wmemcmp(start, flag->wname, length) == 0) {
+				/* Matched "noXXXX", so reverse the sense. */
+				clear |= flag->set;
+				set |= flag->clear;
+				break;
+			} else if (length == flag_length - 2
+			    && wmemcmp(start, flag->wname + 2, length) == 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/c/archive_entry.h b/c/archive_entry.h
new file mode 100644
--- /dev/null
+++ b/c/archive_entry.h
@@ -0,0 +1,708 @@
+/*-
+ * Copyright (c) 2003-2008 Tim Kientzle
+ * Copyright (c) 2016 Martin Matuska
+ * 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: head/lib/libarchive/archive_entry.h 201096 2009-12-28 02:41:27Z kientzle $
+ */
+
+#ifndef ARCHIVE_ENTRY_H_INCLUDED
+#define	ARCHIVE_ENTRY_H_INCLUDED
+
+/* Note: Compiler will complain if this does not match archive.h! */
+#define	ARCHIVE_VERSION_NUMBER 3004003
+
+/*
+ * Note: archive_entry.h is for use outside of libarchive; the
+ * configuration headers (config.h, archive_platform.h, etc.) are
+ * purely internal.  Do NOT use HAVE_XXX configuration macros to
+ * control the behavior of this header!  If you must conditionalize,
+ * use predefined compiler and/or platform macros.
+ */
+
+#include <sys/types.h>
+#include <stddef.h>  /* for wchar_t */
+#include <stdint.h>
+#include <time.h>
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#endif
+
+/* Get a suitable 64-bit integer type. */
+#if !defined(__LA_INT64_T_DEFINED)
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_INT64_T la_int64_t
+# endif
+#define __LA_INT64_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+typedef __int64 la_int64_t;
+# else
+#include <unistd.h>
+#  if defined(_SCO_DS) || defined(__osf__)
+typedef long long la_int64_t;
+#  else
+typedef int64_t la_int64_t;
+#  endif
+# endif
+#endif
+
+/* The la_ssize_t should match the type used in 'struct stat' */
+#if !defined(__LA_SSIZE_T_DEFINED)
+/* Older code relied on the __LA_SSIZE_T macro; after 4.0 we'll switch to the typedef exclusively. */
+# if ARCHIVE_VERSION_NUMBER < 4000000
+#define __LA_SSIZE_T la_ssize_t
+# endif
+#define __LA_SSIZE_T_DEFINED
+# if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WATCOMC__)
+#  if defined(_SSIZE_T_DEFINED) || defined(_SSIZE_T_)
+typedef ssize_t la_ssize_t;
+#  elif defined(_WIN64)
+typedef __int64 la_ssize_t;
+#  else
+typedef long la_ssize_t;
+#  endif
+# else
+# include <unistd.h>  /* ssize_t */
+typedef ssize_t la_ssize_t;
+# endif
+#endif
+
+/* Get a suitable definition for mode_t */
+#if ARCHIVE_VERSION_NUMBER >= 3999000
+/* Switch to plain 'int' for libarchive 4.0.  It's less broken than 'mode_t' */
+# define	__LA_MODE_T	int
+#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) && !defined(__WATCOMC__)
+# define	__LA_MODE_T	unsigned short
+#else
+# define	__LA_MODE_T	mode_t
+#endif
+
+/* Large file support for Android */
+#ifdef __ANDROID__
+#include "android_lf.h"
+#endif
+
+/*
+ * On Windows, define LIBARCHIVE_STATIC if you're building or using a
+ * .lib.  The default here assumes you're building a DLL.  Only
+ * libarchive source should ever define __LIBARCHIVE_BUILD.
+ */
+#if ((defined __WIN32__) || (defined _WIN32) || defined(__CYGWIN__)) && (!defined LIBARCHIVE_STATIC)
+# ifdef __LIBARCHIVE_BUILD
+#  ifdef __GNUC__
+#   define __LA_DECL	__attribute__((dllexport)) extern
+#  else
+#   define __LA_DECL	__declspec(dllexport)
+#  endif
+# else
+#  ifdef __GNUC__
+#   define __LA_DECL
+#  else
+#   define __LA_DECL	__declspec(dllimport)
+#  endif
+# endif
+#else
+/* Static libraries on all platforms and shared libraries on non-Windows. */
+# define __LA_DECL
+#endif
+
+#if defined(__GNUC__) && __GNUC__ >= 3 && __GNUC_MINOR__ >= 1
+# define __LA_DEPRECATED __attribute__((deprecated))
+#else
+# define __LA_DEPRECATED
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Description of an archive entry.
+ *
+ * You can think of this as "struct stat" with some 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;
+struct archive_entry;
+
+/*
+ * File-type constants.  These are returned from archive_entry_filetype()
+ * and passed to archive_entry_set_filetype().
+ *
+ * These values match S_XXX defines on every platform I've checked,
+ * including Windows, AIX, Linux, Solaris, and BSD.  They're
+ * (re)defined here because platforms generally don't define the ones
+ * they don't support.  For example, Windows doesn't define S_IFLNK or
+ * S_IFBLK.  Instead of having a mass of conditional logic and system
+ * checks to define any S_XXX values that aren't supported locally,
+ * I've just defined a new set of such constants so that
+ * libarchive-based applications can manipulate and identify archive
+ * entries properly even if the hosting platform can't store them on
+ * disk.
+ *
+ * These values are also used directly within some portable formats,
+ * such as cpio.  If you find a platform that varies from these, the
+ * correct solution is to leave these alone and translate from these
+ * portable values to platform-native values when entries are read from
+ * or written to disk.
+ */
+/*
+ * In libarchive 4.0, we can drop the casts here.
+ * They're needed to work around Borland C's broken mode_t.
+ */
+#define AE_IFMT		((__LA_MODE_T)0170000)
+#define AE_IFREG	((__LA_MODE_T)0100000)
+#define AE_IFLNK	((__LA_MODE_T)0120000)
+#define AE_IFSOCK	((__LA_MODE_T)0140000)
+#define AE_IFCHR	((__LA_MODE_T)0020000)
+#define AE_IFBLK	((__LA_MODE_T)0060000)
+#define AE_IFDIR	((__LA_MODE_T)0040000)
+#define AE_IFIFO	((__LA_MODE_T)0010000)
+
+/*
+ * Symlink types
+ */
+#define AE_SYMLINK_TYPE_UNDEFINED	0
+#define AE_SYMLINK_TYPE_FILE		1
+#define AE_SYMLINK_TYPE_DIRECTORY	2
+
+/*
+ * Basic object manipulation
+ */
+
+__LA_DECL struct archive_entry	*archive_entry_clear(struct archive_entry *);
+/* The 'clone' function does a deep copy; all of the strings are copied too. */
+__LA_DECL struct archive_entry	*archive_entry_clone(struct archive_entry *);
+__LA_DECL void			 archive_entry_free(struct archive_entry *);
+__LA_DECL struct archive_entry	*archive_entry_new(void);
+
+/*
+ * This form of archive_entry_new2() will pull character-set
+ * conversion information from the specified archive handle.  The
+ * older archive_entry_new(void) form is equivalent to calling
+ * archive_entry_new2(NULL) and will result in the use of an internal
+ * default character-set conversion.
+ */
+__LA_DECL struct archive_entry	*archive_entry_new2(struct archive *);
+
+/*
+ * Retrieve fields from an archive_entry.
+ *
+ * There are a number of implicit conversions among these fields.  For
+ * example, if a regular string field is set and you read the _w wide
+ * character field, the entry will implicitly convert narrow-to-wide
+ * using the current locale.  Similarly, dev values are automatically
+ * updated when you write devmajor or devminor and vice versa.
+ *
+ * In addition, fields can be "set" or "unset."  Unset string fields
+ * return NULL, non-string fields have _is_set() functions to test
+ * whether they've been set.  You can "unset" a string field by
+ * assigning NULL; non-string fields have _unset() functions to
+ * unset them.
+ *
+ * Note: There is one ambiguity in the above; string fields will
+ * also return NULL when implicit character set conversions fail.
+ * This is usually what you want.
+ */
+__LA_DECL time_t	 archive_entry_atime(struct archive_entry *);
+__LA_DECL long		 archive_entry_atime_nsec(struct archive_entry *);
+__LA_DECL int		 archive_entry_atime_is_set(struct archive_entry *);
+__LA_DECL time_t	 archive_entry_birthtime(struct archive_entry *);
+__LA_DECL long		 archive_entry_birthtime_nsec(struct archive_entry *);
+__LA_DECL int		 archive_entry_birthtime_is_set(struct archive_entry *);
+__LA_DECL time_t	 archive_entry_ctime(struct archive_entry *);
+__LA_DECL long		 archive_entry_ctime_nsec(struct archive_entry *);
+__LA_DECL int		 archive_entry_ctime_is_set(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_dev(struct archive_entry *);
+__LA_DECL int		 archive_entry_dev_is_set(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_devmajor(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_devminor(struct archive_entry *);
+__LA_DECL __LA_MODE_T	 archive_entry_filetype(struct archive_entry *);
+__LA_DECL void		 archive_entry_fflags(struct archive_entry *,
+			    unsigned long * /* set */,
+			    unsigned long * /* clear */);
+__LA_DECL const char	*archive_entry_fflags_text(struct archive_entry *);
+__LA_DECL la_int64_t	 archive_entry_gid(struct archive_entry *);
+__LA_DECL const char	*archive_entry_gname(struct archive_entry *);
+__LA_DECL const char	*archive_entry_gname_utf8(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_gname_w(struct archive_entry *);
+__LA_DECL const char	*archive_entry_hardlink(struct archive_entry *);
+__LA_DECL const char	*archive_entry_hardlink_utf8(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_hardlink_w(struct archive_entry *);
+__LA_DECL la_int64_t	 archive_entry_ino(struct archive_entry *);
+__LA_DECL la_int64_t	 archive_entry_ino64(struct archive_entry *);
+__LA_DECL int		 archive_entry_ino_is_set(struct archive_entry *);
+__LA_DECL __LA_MODE_T	 archive_entry_mode(struct archive_entry *);
+__LA_DECL time_t	 archive_entry_mtime(struct archive_entry *);
+__LA_DECL long		 archive_entry_mtime_nsec(struct archive_entry *);
+__LA_DECL int		 archive_entry_mtime_is_set(struct archive_entry *);
+__LA_DECL unsigned int	 archive_entry_nlink(struct archive_entry *);
+__LA_DECL const char	*archive_entry_pathname(struct archive_entry *);
+__LA_DECL const char	*archive_entry_pathname_utf8(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_pathname_w(struct archive_entry *);
+__LA_DECL __LA_MODE_T	 archive_entry_perm(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_rdev(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_rdevmajor(struct archive_entry *);
+__LA_DECL dev_t		 archive_entry_rdevminor(struct archive_entry *);
+__LA_DECL const char	*archive_entry_sourcepath(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_sourcepath_w(struct archive_entry *);
+__LA_DECL la_int64_t	 archive_entry_size(struct archive_entry *);
+__LA_DECL int		 archive_entry_size_is_set(struct archive_entry *);
+__LA_DECL const char	*archive_entry_strmode(struct archive_entry *);
+__LA_DECL const char	*archive_entry_symlink(struct archive_entry *);
+__LA_DECL const char	*archive_entry_symlink_utf8(struct archive_entry *);
+__LA_DECL int		 archive_entry_symlink_type(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_symlink_w(struct archive_entry *);
+__LA_DECL la_int64_t	 archive_entry_uid(struct archive_entry *);
+__LA_DECL const char	*archive_entry_uname(struct archive_entry *);
+__LA_DECL const char	*archive_entry_uname_utf8(struct archive_entry *);
+__LA_DECL const wchar_t	*archive_entry_uname_w(struct archive_entry *);
+__LA_DECL int archive_entry_is_data_encrypted(struct archive_entry *);
+__LA_DECL int archive_entry_is_metadata_encrypted(struct archive_entry *);
+__LA_DECL int archive_entry_is_encrypted(struct archive_entry *);
+
+/*
+ * Set fields in an archive_entry.
+ *
+ * Note: Before libarchive 2.4, there were 'set' and 'copy' versions
+ * of the string setters.  'copy' copied the actual string, 'set' just
+ * stored the pointer.  In libarchive 2.4 and later, strings are
+ * always copied.
+ */
+
+__LA_DECL void	archive_entry_set_atime(struct archive_entry *, time_t, long);
+__LA_DECL void  archive_entry_unset_atime(struct archive_entry *);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+__LA_DECL void archive_entry_copy_bhfi(struct archive_entry *, BY_HANDLE_FILE_INFORMATION *);
+#endif
+__LA_DECL void	archive_entry_set_birthtime(struct archive_entry *, time_t, long);
+__LA_DECL void  archive_entry_unset_birthtime(struct archive_entry *);
+__LA_DECL void	archive_entry_set_ctime(struct archive_entry *, time_t, long);
+__LA_DECL void  archive_entry_unset_ctime(struct archive_entry *);
+__LA_DECL void	archive_entry_set_dev(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_devmajor(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_devminor(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_filetype(struct archive_entry *, unsigned int);
+__LA_DECL 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. */
+__LA_DECL const char *archive_entry_copy_fflags_text(struct archive_entry *,
+	    const char *);
+__LA_DECL const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *,
+	    const wchar_t *);
+__LA_DECL void	archive_entry_set_gid(struct archive_entry *, la_int64_t);
+__LA_DECL void	archive_entry_set_gname(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_gname_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_gname(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int	archive_entry_update_gname_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_hardlink(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_hardlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_hardlink(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_hardlink_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int	archive_entry_update_hardlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_ino(struct archive_entry *, la_int64_t);
+__LA_DECL void	archive_entry_set_ino64(struct archive_entry *, la_int64_t);
+__LA_DECL void	archive_entry_set_link(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_link_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_link(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_link_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int	archive_entry_update_link_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_mode(struct archive_entry *, __LA_MODE_T);
+__LA_DECL void	archive_entry_set_mtime(struct archive_entry *, time_t, long);
+__LA_DECL void  archive_entry_unset_mtime(struct archive_entry *);
+__LA_DECL void	archive_entry_set_nlink(struct archive_entry *, unsigned int);
+__LA_DECL void	archive_entry_set_pathname(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_pathname_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_pathname(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_pathname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int	archive_entry_update_pathname_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_perm(struct archive_entry *, __LA_MODE_T);
+__LA_DECL void	archive_entry_set_rdev(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_rdevmajor(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_rdevminor(struct archive_entry *, dev_t);
+__LA_DECL void	archive_entry_set_size(struct archive_entry *, la_int64_t);
+__LA_DECL void	archive_entry_unset_size(struct archive_entry *);
+__LA_DECL void	archive_entry_copy_sourcepath(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_sourcepath_w(struct archive_entry *, const wchar_t *);
+__LA_DECL void	archive_entry_set_symlink(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_symlink_type(struct archive_entry *, int);
+__LA_DECL void	archive_entry_set_symlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_symlink(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_symlink_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int	archive_entry_update_symlink_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_uid(struct archive_entry *, la_int64_t);
+__LA_DECL void	archive_entry_set_uname(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_uname_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_uname(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
+__LA_DECL int	archive_entry_update_uname_utf8(struct archive_entry *, const char *);
+__LA_DECL void	archive_entry_set_is_data_encrypted(struct archive_entry *, char is_encrypted);
+__LA_DECL void	archive_entry_set_is_metadata_encrypted(struct archive_entry *, char is_encrypted);
+/*
+ * Routines to bulk copy fields to/from a platform-native "struct
+ * stat."  Libarchive used to just store a struct stat inside of each
+ * archive_entry object, but this created issues when trying to
+ * manipulate archives on systems different than the ones they were
+ * created on.
+ *
+ * TODO: On Linux and other LFS systems, provide both stat32 and
+ * stat64 versions of these functions and all of the macro glue so
+ * that archive_entry_stat is magically defined to
+ * archive_entry_stat32 or archive_entry_stat64 as appropriate.
+ */
+__LA_DECL const struct stat	*archive_entry_stat(struct archive_entry *);
+__LA_DECL void	archive_entry_copy_stat(struct archive_entry *, const struct stat *);
+
+/*
+ * Storage for Mac OS-specific AppleDouble metadata information.
+ * Apple-format tar files store a separate binary blob containing
+ * encoded metadata with ACL, extended attributes, etc.
+ * This provides a place to store that blob.
+ */
+
+__LA_DECL const void * archive_entry_mac_metadata(struct archive_entry *, size_t *);
+__LA_DECL void archive_entry_copy_mac_metadata(struct archive_entry *, const void *, size_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
+ *
+ *  This last point, in particular, forces me to implement a reasonably
+ *  complete set of ACL support routines.
+ */
+
+/*
+ * Permission bits.
+ */
+#define	ARCHIVE_ENTRY_ACL_EXECUTE             0x00000001
+#define	ARCHIVE_ENTRY_ACL_WRITE               0x00000002
+#define	ARCHIVE_ENTRY_ACL_READ                0x00000004
+#define	ARCHIVE_ENTRY_ACL_READ_DATA           0x00000008
+#define	ARCHIVE_ENTRY_ACL_LIST_DIRECTORY      0x00000008
+#define	ARCHIVE_ENTRY_ACL_WRITE_DATA          0x00000010
+#define	ARCHIVE_ENTRY_ACL_ADD_FILE            0x00000010
+#define	ARCHIVE_ENTRY_ACL_APPEND_DATA         0x00000020
+#define	ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY    0x00000020
+#define	ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS    0x00000040
+#define	ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS   0x00000080
+#define	ARCHIVE_ENTRY_ACL_DELETE_CHILD        0x00000100
+#define	ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES     0x00000200
+#define	ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES    0x00000400
+#define	ARCHIVE_ENTRY_ACL_DELETE              0x00000800
+#define	ARCHIVE_ENTRY_ACL_READ_ACL            0x00001000
+#define	ARCHIVE_ENTRY_ACL_WRITE_ACL           0x00002000
+#define	ARCHIVE_ENTRY_ACL_WRITE_OWNER         0x00004000
+#define	ARCHIVE_ENTRY_ACL_SYNCHRONIZE         0x00008000
+
+#define	ARCHIVE_ENTRY_ACL_PERMS_POSIX1E			\
+	(ARCHIVE_ENTRY_ACL_EXECUTE			\
+	    | ARCHIVE_ENTRY_ACL_WRITE			\
+	    | ARCHIVE_ENTRY_ACL_READ)
+
+#define ARCHIVE_ENTRY_ACL_PERMS_NFS4			\
+	(ARCHIVE_ENTRY_ACL_EXECUTE			\
+	    | ARCHIVE_ENTRY_ACL_READ_DATA		\
+	    | ARCHIVE_ENTRY_ACL_LIST_DIRECTORY 		\
+	    | ARCHIVE_ENTRY_ACL_WRITE_DATA		\
+	    | ARCHIVE_ENTRY_ACL_ADD_FILE		\
+	    | ARCHIVE_ENTRY_ACL_APPEND_DATA		\
+	    | ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY	\
+	    | ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS	\
+	    | ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS	\
+	    | ARCHIVE_ENTRY_ACL_DELETE_CHILD		\
+	    | ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES		\
+	    | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES	\
+	    | ARCHIVE_ENTRY_ACL_DELETE			\
+	    | ARCHIVE_ENTRY_ACL_READ_ACL		\
+	    | ARCHIVE_ENTRY_ACL_WRITE_ACL		\
+	    | ARCHIVE_ENTRY_ACL_WRITE_OWNER		\
+	    | ARCHIVE_ENTRY_ACL_SYNCHRONIZE)
+
+/*
+ * Inheritance values (NFS4 ACLs only); included in permset.
+ */
+#define	ARCHIVE_ENTRY_ACL_ENTRY_INHERITED                   0x01000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT                0x02000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT           0x04000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT        0x08000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY                0x10000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS           0x20000000
+#define	ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS               0x40000000
+
+#define	ARCHIVE_ENTRY_ACL_INHERITANCE_NFS4			\
+	(ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT			\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT		\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT	\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY		\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_SUCCESSFUL_ACCESS		\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_FAILED_ACCESS		\
+	    | ARCHIVE_ENTRY_ACL_ENTRY_INHERITED)
+
+/* We need to be able to specify combinations of these. */
+#define	ARCHIVE_ENTRY_ACL_TYPE_ACCESS	0x00000100  /* POSIX.1e only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_DEFAULT	0x00000200  /* POSIX.1e only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_ALLOW	0x00000400 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_DENY	0x00000800 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_AUDIT	0x00001000 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_ALARM	0x00002000 /* NFS4 only */
+#define	ARCHIVE_ENTRY_ACL_TYPE_POSIX1E	(ARCHIVE_ENTRY_ACL_TYPE_ACCESS \
+	    | ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
+#define	ARCHIVE_ENTRY_ACL_TYPE_NFS4	(ARCHIVE_ENTRY_ACL_TYPE_ALLOW \
+	    | ARCHIVE_ENTRY_ACL_TYPE_DENY \
+	    | ARCHIVE_ENTRY_ACL_TYPE_AUDIT \
+	    | ARCHIVE_ENTRY_ACL_TYPE_ALARM)
+
+/* 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 (POSIX.1e only) */
+#define	ARCHIVE_ENTRY_ACL_OTHER		10006	/* Public (POSIX.1e only) */
+#define	ARCHIVE_ENTRY_ACL_EVERYONE	10107   /* Everyone (NFS4 only) */
+
+/*
+ * 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.
+ */
+__LA_DECL void	 archive_entry_acl_clear(struct archive_entry *);
+__LA_DECL int	 archive_entry_acl_add_entry(struct archive_entry *,
+	    int /* type */, int /* permset */, int /* tag */,
+	    int /* qual */, const char * /* name */);
+__LA_DECL int	 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
+ * certain types of entries.
+ */
+__LA_DECL int	 archive_entry_acl_reset(struct archive_entry *, int /* want_type */);
+__LA_DECL int	 archive_entry_acl_next(struct archive_entry *, int /* want_type */,
+	    int * /* type */, int * /* permset */, int * /* tag */,
+	    int * /* qual */, const char ** /* name */);
+
+/*
+ * Construct a text-format ACL.  The flags argument is a bitmask that
+ * can include any of the following:
+ *
+ * Flags only for archive entries with POSIX.1e ACL:
+ * ARCHIVE_ENTRY_ACL_TYPE_ACCESS - Include POSIX.1e "access" entries.
+ * ARCHIVE_ENTRY_ACL_TYPE_DEFAULT - Include POSIX.1e "default" entries.
+ * ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT - Include "default:" before each
+ *    default ACL entry.
+ * ARCHIVE_ENTRY_ACL_STYLE_SOLARIS - Output only one colon after "other" and
+ *    "mask" entries.
+ *
+ * Flags only for archive entries with NFSv4 ACL:
+ * ARCHIVE_ENTRY_ACL_STYLE_COMPACT - Do not output the minus character for
+ *    unset permissions and flags in NFSv4 ACL permission and flag fields
+ *
+ * Flags for for archive entries with POSIX.1e ACL or NFSv4 ACL:
+ * ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID - Include extra numeric ID field in
+ *    each ACL entry.
+ * ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA - Separate entries with comma
+ *    instead of newline.
+ */
+#define	ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID	0x00000001
+#define	ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	0x00000002
+#define	ARCHIVE_ENTRY_ACL_STYLE_SOLARIS		0x00000004
+#define	ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA	0x00000008
+#define	ARCHIVE_ENTRY_ACL_STYLE_COMPACT		0x00000010
+
+__LA_DECL wchar_t *archive_entry_acl_to_text_w(struct archive_entry *,
+	    la_ssize_t * /* len */, int /* flags */);
+__LA_DECL char *archive_entry_acl_to_text(struct archive_entry *,
+	    la_ssize_t * /* len */, int /* flags */);
+__LA_DECL int archive_entry_acl_from_text_w(struct archive_entry *,
+	    const wchar_t * /* wtext */, int /* type */);
+__LA_DECL int archive_entry_acl_from_text(struct archive_entry *,
+	    const char * /* text */, int /* type */);
+
+/* Deprecated constants */
+#define	OLD_ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID		1024
+#define	OLD_ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT	2048
+
+/* Deprecated functions */
+__LA_DECL const wchar_t	*archive_entry_acl_text_w(struct archive_entry *,
+		    int /* flags */) __LA_DEPRECATED;
+__LA_DECL const char *archive_entry_acl_text(struct archive_entry *,
+		    int /* flags */) __LA_DEPRECATED;
+
+/* Return bitmask of ACL types in an archive entry */
+__LA_DECL int	 archive_entry_acl_types(struct archive_entry *);
+
+/* Return a count of entries matching 'want_type' */
+__LA_DECL int	 archive_entry_acl_count(struct archive_entry *, int /* want_type */);
+
+/* Return an opaque ACL object. */
+/* There's not yet anything clients can actually do with this... */
+struct archive_acl;
+__LA_DECL struct archive_acl *archive_entry_acl(struct archive_entry *);
+
+/*
+ * extended attributes
+ */
+
+__LA_DECL void	 archive_entry_xattr_clear(struct archive_entry *);
+__LA_DECL 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.
+ */
+
+__LA_DECL int	archive_entry_xattr_count(struct archive_entry *);
+__LA_DECL int	archive_entry_xattr_reset(struct archive_entry *);
+__LA_DECL int	archive_entry_xattr_next(struct archive_entry *,
+	    const char ** /* name */, const void ** /* value */, size_t *);
+
+/*
+ * sparse
+ */
+
+__LA_DECL void	 archive_entry_sparse_clear(struct archive_entry *);
+__LA_DECL void	 archive_entry_sparse_add_entry(struct archive_entry *,
+	    la_int64_t /* offset */, la_int64_t /* length */);
+
+/*
+ * To retrieve the xattr list, first "reset", then repeatedly ask for the
+ * "next" entry.
+ */
+
+__LA_DECL int	archive_entry_sparse_count(struct archive_entry *);
+__LA_DECL int	archive_entry_sparse_reset(struct archive_entry *);
+__LA_DECL int	archive_entry_sparse_next(struct archive_entry *,
+	    la_int64_t * /* offset */, la_int64_t * /* length */);
+
+/*
+ * Utility to match up hardlinks.
+ *
+ * The 'struct archive_entry_linkresolver' is a cache of archive entries
+ * for files with multiple links.  Here's how to use it:
+ *   1. Create a lookup object with archive_entry_linkresolver_new()
+ *   2. Tell it the archive format you're using.
+ *   3. Hand each archive_entry to archive_entry_linkify().
+ *      That function will return 0, 1, or 2 entries that should
+ *      be written.
+ *   4. Call archive_entry_linkify(resolver, NULL) until
+ *      no more entries are returned.
+ *   5. Call archive_entry_linkresolver_free(resolver) to free resources.
+ *
+ * The entries returned have their hardlink and size fields updated
+ * appropriately.  If an entry is passed in that does not refer to
+ * a file with multiple links, it is returned unchanged.  The intention
+ * is that you should be able to simply filter all entries through
+ * this machine.
+ *
+ * To make things more efficient, be sure that each entry has a valid
+ * nlinks value.  The hardlink cache uses this to track when all links
+ * have been found.  If the nlinks value is zero, it will keep every
+ * name in the cache indefinitely, which can use a lot of memory.
+ *
+ * Note that archive_entry_size() is reset to zero if the file
+ * body should not be written to the archive.  Pay attention!
+ */
+struct archive_entry_linkresolver;
+
+/*
+ * There are three different strategies for marking hardlinks.
+ * The descriptions below name them after the best-known
+ * formats that rely on each strategy:
+ *
+ * "Old cpio" is the simplest, it always returns any entry unmodified.
+ *    As far as I know, only cpio formats use this.  Old cpio archives
+ *    store every link with the full body; the onus is on the dearchiver
+ *    to detect and properly link the files as they are restored.
+ * "tar" is also pretty simple; it caches a copy the first time it sees
+ *    any link.  Subsequent appearances are modified to be hardlink
+ *    references to the first one without any body.  Used by all tar
+ *    formats, although the newest tar formats permit the "old cpio" strategy
+ *    as well.  This strategy is very simple for the dearchiver,
+ *    and reasonably straightforward for the archiver.
+ * "new cpio" is trickier.  It stores the body only with the last
+ *    occurrence.  The complication is that we might not
+ *    see every link to a particular file in a single session, so
+ *    there's no easy way to know when we've seen the last occurrence.
+ *    The solution here is to queue one link until we see the next.
+ *    At the end of the session, you can enumerate any remaining
+ *    entries by calling archive_entry_linkify(NULL) and store those
+ *    bodies.  If you have a file with three links l1, l2, and l3,
+ *    you'll get the following behavior if you see all three links:
+ *           linkify(l1) => NULL   (the resolver stores l1 internally)
+ *           linkify(l2) => l1     (resolver stores l2, you write l1)
+ *           linkify(l3) => l2, l3 (all links seen, you can write both).
+ *    If you only see l1 and l2, you'll get this behavior:
+ *           linkify(l1) => NULL
+ *           linkify(l2) => l1
+ *           linkify(NULL) => l2   (at end, you retrieve remaining links)
+ *    As the name suggests, this strategy is used by newer cpio variants.
+ *    It's noticeably more complex for the archiver, slightly more complex
+ *    for the dearchiver than the tar strategy, but makes it straightforward
+ *    to restore a file using any link by simply continuing to scan until
+ *    you see a link that is stored with a body.  In contrast, the tar
+ *    strategy requires you to rescan the archive from the beginning to
+ *    correctly extract an arbitrary link.
+ */
+
+__LA_DECL struct archive_entry_linkresolver *archive_entry_linkresolver_new(void);
+__LA_DECL void archive_entry_linkresolver_set_strategy(
+	struct archive_entry_linkresolver *, int /* format_code */);
+__LA_DECL void archive_entry_linkresolver_free(struct archive_entry_linkresolver *);
+__LA_DECL void archive_entry_linkify(struct archive_entry_linkresolver *,
+    struct archive_entry **, struct archive_entry **);
+__LA_DECL struct archive_entry *archive_entry_partial_links(
+    struct archive_entry_linkresolver *res, unsigned int *links);
+#ifdef __cplusplus
+}
+#endif
+
+/* This is meaningless outside of this header. */
+#undef __LA_DECL
+
+#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
diff --git a/c/archive_entry_copy_bhfi.c b/c/archive_entry_copy_bhfi.c
new file mode 100644
--- /dev/null
+++ b/c/archive_entry_copy_bhfi.c
@@ -0,0 +1,75 @@
+/*-
+ * 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$");
+
+#include "archive_private.h"
+#include "archive_entry.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+
+__inline static void
+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)
+{
+	ULARGE_INTEGER utc;
+
+	utc.HighPart = filetime->dwHighDateTime;
+	utc.LowPart  = filetime->dwLowDateTime;
+	if (utc.QuadPart >= EPOC_TIME) {
+		utc.QuadPart -= EPOC_TIME;
+		*t = (time_t)(utc.QuadPart / 10000000);	/* milli seconds base */
+		*ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
+	} else {
+		*t = 0;
+		*ns = 0;
+	}
+}
+
+void
+archive_entry_copy_bhfi(struct archive_entry *entry,
+			BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+	time_t secs;
+	long nsecs;
+
+	fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs);
+	archive_entry_set_atime(entry, secs, nsecs);
+	fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs);
+	archive_entry_set_mtime(entry, secs, nsecs);
+	fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs);
+	archive_entry_set_birthtime(entry, secs, nsecs);
+	archive_entry_set_ctime(entry, secs, nsecs);
+	archive_entry_set_dev(entry, bhfi->dwVolumeSerialNumber);
+	archive_entry_set_ino64(entry, (((int64_t)bhfi->nFileIndexHigh) << 32)
+		+ bhfi->nFileIndexLow);
+	archive_entry_set_nlink(entry, bhfi->nNumberOfLinks);
+	archive_entry_set_size(entry, (((int64_t)bhfi->nFileSizeHigh) << 32)
+		+ bhfi->nFileSizeLow);
+	/* archive_entry_set_mode(entry, st->st_mode); */
+}
+#endif
diff --git a/c/archive_entry_copy_stat.c b/c/archive_entry_copy_stat.c
new file mode 100644
--- /dev/null
+++ b/c/archive_entry_copy_stat.c
@@ -0,0 +1,83 @@
+/*-
+ * 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: head/lib/libarchive/archive_entry_copy_stat.c 189466 2009-03-07 00:52:02Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+
+void
+archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
+{
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+	archive_entry_set_atime(entry, st->st_atime, st->st_atimespec.tv_nsec);
+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctimespec.tv_nsec);
+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+	archive_entry_set_atime(entry, st->st_atime, st->st_atim.tv_nsec);
+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctim.tv_nsec);
+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtim.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIME_NSEC
+	archive_entry_set_atime(entry, st->st_atime, st->st_atime_nsec);
+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_nsec);
+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+	archive_entry_set_atime(entry, st->st_atime, st->st_atime_n);
+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_n);
+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_n);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+	archive_entry_set_atime(entry, st->st_atime, st->st_uatime * 1000);
+	archive_entry_set_ctime(entry, st->st_ctime, st->st_uctime * 1000);
+	archive_entry_set_mtime(entry, st->st_mtime, st->st_umtime * 1000);
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+	archive_entry_set_atime(entry, st->st_atime, st->st_atime_usec * 1000);
+	archive_entry_set_ctime(entry, st->st_ctime, st->st_ctime_usec * 1000);
+	archive_entry_set_mtime(entry, st->st_mtime, st->st_mtime_usec * 1000);
+#else
+	archive_entry_set_atime(entry, st->st_atime, 0);
+	archive_entry_set_ctime(entry, st->st_ctime, 0);
+	archive_entry_set_mtime(entry, st->st_mtime, 0);
+#endif
+#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
+	archive_entry_set_birthtime(entry, st->st_birthtime, st->st_birthtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_BIRTHTIME
+	archive_entry_set_birthtime(entry, st->st_birthtime, 0);
+#else
+	archive_entry_unset_birthtime(entry);
+#endif
+	archive_entry_set_dev(entry, st->st_dev);
+	archive_entry_set_gid(entry, st->st_gid);
+	archive_entry_set_uid(entry, st->st_uid);
+	archive_entry_set_ino(entry, st->st_ino);
+	archive_entry_set_nlink(entry, st->st_nlink);
+	archive_entry_set_rdev(entry, st->st_rdev);
+	archive_entry_set_size(entry, st->st_size);
+	archive_entry_set_mode(entry, st->st_mode);
+}
diff --git a/c/archive_entry_link_resolver.c b/c/archive_entry_link_resolver.c
new file mode 100644
--- /dev/null
+++ b/c/archive_entry_link_resolver.c
@@ -0,0 +1,447 @@
+/*-
+ * 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: head/lib/libarchive/archive_entry_link_resolver.c 201100 2009-12-28 03:05:31Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#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
+
+#include "archive.h"
+#include "archive_entry.h"
+
+/*
+ * This is mostly a pretty straightforward hash table implementation.
+ * The only interesting bit is the different strategies used to
+ * match up links.  These strategies match those used by various
+ * archiving formats:
+ *   tar - content stored with first link, remainder refer back to it.
+ *       This requires us to match each subsequent link up with the
+ *       first appearance.
+ *   cpio - Old cpio just stored body with each link, match-ups were
+ *       implicit.  This is trivial.
+ *   new cpio - New cpio only stores body with last link, match-ups
+ *       are implicit.  This is actually quite tricky; see the notes
+ *       below.
+ */
+
+/* Users pass us a format code, we translate that into a strategy here. */
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_TAR	0
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE 1
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO 2
+#define ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO 3
+
+/* Initial size of link cache. */
+#define	links_cache_initial_size 1024
+
+struct links_entry {
+	struct links_entry	*next;
+	struct links_entry	*previous;
+	struct archive_entry	*canonical;
+	struct archive_entry	*entry;
+	size_t			 hash;
+	unsigned int		 links; /* # links not yet seen */
+};
+
+struct archive_entry_linkresolver {
+	struct links_entry	**buckets;
+	struct links_entry	 *spare;
+	unsigned long		  number_entries;
+	size_t			  number_buckets;
+	int			  strategy;
+};
+
+#define	NEXT_ENTRY_DEFERRED	1
+#define	NEXT_ENTRY_PARTIAL	2
+#define	NEXT_ENTRY_ALL		(NEXT_ENTRY_DEFERRED | NEXT_ENTRY_PARTIAL)
+
+static struct links_entry *find_entry(struct archive_entry_linkresolver *,
+		    struct archive_entry *);
+static void grow_hash(struct archive_entry_linkresolver *);
+static struct links_entry *insert_entry(struct archive_entry_linkresolver *,
+		    struct archive_entry *);
+static struct links_entry *next_entry(struct archive_entry_linkresolver *,
+    int);
+
+struct archive_entry_linkresolver *
+archive_entry_linkresolver_new(void)
+{
+	struct archive_entry_linkresolver *res;
+
+	/* Check for positive power-of-two */
+	if (links_cache_initial_size == 0 ||
+	    (links_cache_initial_size & (links_cache_initial_size - 1)) != 0)
+		return (NULL);
+
+	res = calloc(1, sizeof(struct archive_entry_linkresolver));
+	if (res == NULL)
+		return (NULL);
+	res->number_buckets = links_cache_initial_size;
+	res->buckets = calloc(res->number_buckets, sizeof(res->buckets[0]));
+	if (res->buckets == NULL) {
+		free(res);
+		return (NULL);
+	}
+	return (res);
+}
+
+void
+archive_entry_linkresolver_set_strategy(struct archive_entry_linkresolver *res,
+    int fmt)
+{
+	int fmtbase = fmt & ARCHIVE_FORMAT_BASE_MASK;
+
+	switch (fmtbase) {
+	case ARCHIVE_FORMAT_7ZIP:
+	case ARCHIVE_FORMAT_AR:
+	case ARCHIVE_FORMAT_ZIP:
+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+		break;
+	case ARCHIVE_FORMAT_CPIO:
+		switch (fmt) {
+		case ARCHIVE_FORMAT_CPIO_SVR4_NOCRC:
+		case ARCHIVE_FORMAT_CPIO_SVR4_CRC:
+			res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO;
+			break;
+		default:
+			res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+			break;
+		}
+		break;
+	case ARCHIVE_FORMAT_MTREE:
+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE;
+		break;
+	case ARCHIVE_FORMAT_ISO9660:
+	case ARCHIVE_FORMAT_SHAR:
+	case ARCHIVE_FORMAT_TAR:
+	case ARCHIVE_FORMAT_XAR:
+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_TAR;
+		break;
+	default:
+		res->strategy = ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO;
+		break;
+	}
+}
+
+void
+archive_entry_linkresolver_free(struct archive_entry_linkresolver *res)
+{
+	struct links_entry *le;
+
+	if (res == NULL)
+		return;
+
+	while ((le = next_entry(res, NEXT_ENTRY_ALL)) != NULL)
+		archive_entry_free(le->entry);
+	free(res->buckets);
+	free(res);
+}
+
+void
+archive_entry_linkify(struct archive_entry_linkresolver *res,
+    struct archive_entry **e, struct archive_entry **f)
+{
+	struct links_entry *le;
+	struct archive_entry *t;
+
+	*f = NULL; /* Default: Don't return a second entry. */
+
+	if (*e == NULL) {
+		le = next_entry(res, NEXT_ENTRY_DEFERRED);
+		if (le != NULL) {
+			*e = le->entry;
+			le->entry = NULL;
+		}
+		return;
+	}
+
+	/* If it has only one link, then we're done. */
+	if (archive_entry_nlink(*e) == 1)
+		return;
+	/* Directories, devices never have hardlinks. */
+	if (archive_entry_filetype(*e) == AE_IFDIR
+	    || archive_entry_filetype(*e) == AE_IFBLK
+	    || archive_entry_filetype(*e) == AE_IFCHR)
+		return;
+
+	switch (res->strategy) {
+	case ARCHIVE_ENTRY_LINKIFY_LIKE_TAR:
+		le = find_entry(res, *e);
+		if (le != NULL) {
+			archive_entry_unset_size(*e);
+			archive_entry_copy_hardlink(*e,
+			    archive_entry_pathname(le->canonical));
+		} else
+			insert_entry(res, *e);
+		return;
+	case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
+		le = find_entry(res, *e);
+		if (le != NULL) {
+			archive_entry_copy_hardlink(*e,
+			    archive_entry_pathname(le->canonical));
+		} else
+			insert_entry(res, *e);
+		return;
+	case ARCHIVE_ENTRY_LINKIFY_LIKE_OLD_CPIO:
+		/* This one is trivial. */
+		return;
+	case ARCHIVE_ENTRY_LINKIFY_LIKE_NEW_CPIO:
+		le = find_entry(res, *e);
+		if (le != NULL) {
+			/*
+			 * Put the new entry in le, return the
+			 * old entry from le.
+			 */
+			t = *e;
+			*e = le->entry;
+			le->entry = t;
+			/* Make the old entry into a hardlink. */
+			archive_entry_unset_size(*e);
+			archive_entry_copy_hardlink(*e,
+			    archive_entry_pathname(le->canonical));
+			/* If we ran out of links, return the
+			 * final entry as well. */
+			if (le->links == 0) {
+				*f = le->entry;
+				le->entry = NULL;
+			}
+		} else {
+			/*
+			 * If we haven't seen it, tuck it away
+			 * for future use.
+			 */
+			le = insert_entry(res, *e);
+			if (le == NULL)
+				/* XXX We should return an error code XXX */
+				return;
+			le->entry = *e;
+			*e = NULL;
+		}
+		return;
+	default:
+		break;
+	}
+	return;
+}
+
+static struct links_entry *
+find_entry(struct archive_entry_linkresolver *res,
+    struct archive_entry *entry)
+{
+	struct links_entry	*le;
+	size_t			 hash, bucket;
+	dev_t			 dev;
+	int64_t			 ino;
+
+	/* Free a held entry. */
+	if (res->spare != NULL) {
+		archive_entry_free(res->spare->canonical);
+		archive_entry_free(res->spare->entry);
+		free(res->spare);
+		res->spare = NULL;
+	}
+
+	dev = archive_entry_dev(entry);
+	ino = archive_entry_ino64(entry);
+	hash = (size_t)(dev ^ ino);
+
+	/* Try to locate this entry in the links cache. */
+	bucket = hash & (res->number_buckets - 1);
+	for (le = res->buckets[bucket]; le != NULL; le = le->next) {
+		if (le->hash == hash
+		    && dev == archive_entry_dev(le->canonical)
+		    && ino == archive_entry_ino64(le->canonical)) {
+			/*
+			 * Decrement link count each time and release
+			 * the entry if it hits zero.  This saves
+			 * memory and is necessary for detecting
+			 * missed links.
+			 */
+			--le->links;
+			if (le->links > 0)
+				return (le);
+			/* Remove it from this hash bucket. */
+			if (le->previous != NULL)
+				le->previous->next = le->next;
+			if (le->next != NULL)
+				le->next->previous = le->previous;
+			if (res->buckets[bucket] == le)
+				res->buckets[bucket] = le->next;
+			res->number_entries--;
+			/* Defer freeing this entry. */
+			res->spare = le;
+			return (le);
+		}
+	}
+	return (NULL);
+}
+
+static struct links_entry *
+next_entry(struct archive_entry_linkresolver *res, int mode)
+{
+	struct links_entry	*le;
+	size_t			 bucket;
+
+	/* Free a held entry. */
+	if (res->spare != NULL) {
+		archive_entry_free(res->spare->canonical);
+		archive_entry_free(res->spare->entry);
+		free(res->spare);
+		res->spare = NULL;
+	}
+
+	/* Look for next non-empty bucket in the links cache. */
+	for (bucket = 0; bucket < res->number_buckets; bucket++) {
+		for (le = res->buckets[bucket]; le != NULL; le = le->next) {
+			if (le->entry != NULL &&
+			    (mode & NEXT_ENTRY_DEFERRED) == 0)
+				continue;
+			if (le->entry == NULL &&
+			    (mode & NEXT_ENTRY_PARTIAL) == 0)
+				continue;
+			/* Remove it from this hash bucket. */
+			if (le->next != NULL)
+				le->next->previous = le->previous;
+			if (le->previous != NULL)
+				le->previous->next = le->next;
+			else
+				res->buckets[bucket] = le->next;
+			res->number_entries--;
+			/* Defer freeing this entry. */
+			res->spare = le;
+			return (le);
+		}
+	}
+	return (NULL);
+}
+
+static struct links_entry *
+insert_entry(struct archive_entry_linkresolver *res,
+    struct archive_entry *entry)
+{
+	struct links_entry *le;
+	size_t hash, bucket;
+
+	/* Add this entry to the links cache. */
+	le = calloc(1, sizeof(struct links_entry));
+	if (le == NULL)
+		return (NULL);
+	le->canonical = archive_entry_clone(entry);
+
+	/* If the links cache is getting too full, enlarge the hash table. */
+	if (res->number_entries > res->number_buckets * 2)
+		grow_hash(res);
+
+	hash = (size_t)(archive_entry_dev(entry) ^ archive_entry_ino64(entry));
+	bucket = hash & (res->number_buckets - 1);
+
+	/* If we could allocate the entry, record it. */
+	if (res->buckets[bucket] != NULL)
+		res->buckets[bucket]->previous = le;
+	res->number_entries++;
+	le->next = res->buckets[bucket];
+	le->previous = NULL;
+	res->buckets[bucket] = le;
+	le->hash = hash;
+	le->links = archive_entry_nlink(entry) - 1;
+	return (le);
+}
+
+static void
+grow_hash(struct archive_entry_linkresolver *res)
+{
+	struct links_entry *le, **new_buckets;
+	size_t new_size;
+	size_t i, bucket;
+
+	/* Try to enlarge the bucket list. */
+	new_size = res->number_buckets * 2;
+	if (new_size < res->number_buckets)
+		return;
+	new_buckets = calloc(new_size, sizeof(struct links_entry *));
+
+	if (new_buckets == NULL)
+		return;
+
+	for (i = 0; i < res->number_buckets; i++) {
+		while (res->buckets[i] != NULL) {
+			/* Remove entry from old bucket. */
+			le = res->buckets[i];
+			res->buckets[i] = le->next;
+
+			/* Add entry to new bucket. */
+			bucket = le->hash & (new_size - 1);
+
+			if (new_buckets[bucket] != NULL)
+				new_buckets[bucket]->previous = le;
+			le->next = new_buckets[bucket];
+			le->previous = NULL;
+			new_buckets[bucket] = le;
+		}
+	}
+	free(res->buckets);
+	res->buckets = new_buckets;
+	res->number_buckets = new_size;
+}
+
+struct archive_entry *
+archive_entry_partial_links(struct archive_entry_linkresolver *res,
+    unsigned int *links)
+{
+	struct archive_entry	*e;
+	struct links_entry	*le;
+
+	/* Free a held entry. */
+	if (res->spare != NULL) {
+		archive_entry_free(res->spare->canonical);
+		archive_entry_free(res->spare->entry);
+		free(res->spare);
+		res->spare = NULL;
+	}
+
+	le = next_entry(res, NEXT_ENTRY_PARTIAL);
+	if (le != NULL) {
+		e = le->canonical;
+		if (links != NULL)
+			*links = le->links;
+		le->canonical = NULL;
+	} else {
+		e = NULL;
+		if (links != NULL)
+			*links = 0;
+	}
+	return (e);
+}
diff --git a/c/archive_entry_locale.h b/c/archive_entry_locale.h
new file mode 100644
--- /dev/null
+++ b/c/archive_entry_locale.h
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2011 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_ENTRY_LOCALE_H_INCLUDED
+#define ARCHIVE_ENTRY_LOCALE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+struct archive_entry;
+struct archive_string_conv;
+
+/*
+ * Utility functions to set and get entry attributes by translating
+ * character-set. These are designed for use in format readers and writers.
+ *
+ * The return code and interface of these are quite different from other
+ * functions for archive_entry defined in archive_entry.h.
+ * Common return code are:
+ *   Return 0 if the string conversion succeeded.
+ *   Return -1 if the string conversion failed.
+ */
+
+#define archive_entry_gname_l	_archive_entry_gname_l
+int _archive_entry_gname_l(struct archive_entry *,
+    const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_hardlink_l	_archive_entry_hardlink_l
+int _archive_entry_hardlink_l(struct archive_entry *,
+    const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_pathname_l	_archive_entry_pathname_l
+int _archive_entry_pathname_l(struct archive_entry *,
+    const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_symlink_l	_archive_entry_symlink_l
+int _archive_entry_symlink_l(struct archive_entry *,
+    const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_uname_l	_archive_entry_uname_l
+int _archive_entry_uname_l(struct archive_entry *,
+    const char **, size_t *, struct archive_string_conv *);
+#define archive_entry_acl_text_l _archive_entry_acl_text_l
+int _archive_entry_acl_text_l(struct archive_entry *, int,
+const char **, size_t *, struct archive_string_conv *) __LA_DEPRECATED;
+#define archive_entry_acl_to_text_l _archive_entry_acl_to_text_l
+char *_archive_entry_acl_to_text_l(struct archive_entry *, ssize_t *, int,
+    struct archive_string_conv *);
+#define archive_entry_acl_from_text_l _archive_entry_acl_from_text_l
+int _archive_entry_acl_from_text_l(struct archive_entry *, const char* text,
+    int type, struct archive_string_conv *);
+#define archive_entry_copy_gname_l	_archive_entry_copy_gname_l
+int _archive_entry_copy_gname_l(struct archive_entry *,
+    const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_hardlink_l	_archive_entry_copy_hardlink_l
+int _archive_entry_copy_hardlink_l(struct archive_entry *,
+    const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_link_l	_archive_entry_copy_link_l
+int _archive_entry_copy_link_l(struct archive_entry *,
+    const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_pathname_l	_archive_entry_copy_pathname_l
+int _archive_entry_copy_pathname_l(struct archive_entry *,
+    const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_symlink_l	_archive_entry_copy_symlink_l
+int _archive_entry_copy_symlink_l(struct archive_entry *,
+    const char *, size_t, struct archive_string_conv *);
+#define archive_entry_copy_uname_l	_archive_entry_copy_uname_l
+int _archive_entry_copy_uname_l(struct archive_entry *,
+    const char *, size_t, struct archive_string_conv *);
+
+#endif /* ARCHIVE_ENTRY_LOCALE_H_INCLUDED */
diff --git a/c/archive_entry_private.h b/c/archive_entry_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_entry_private.h
@@ -0,0 +1,184 @@
+/*-
+ * 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: head/lib/libarchive/archive_entry_private.h 201096 2009-12-28 02:41:27Z kientzle $
+ */
+
+#ifndef ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
+#define ARCHIVE_ENTRY_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_acl_private.h"
+#include "archive_string.h"
+
+struct ae_xattr {
+	struct ae_xattr *next;
+
+	char	*name;
+	void	*value;
+	size_t	size;
+};
+
+struct ae_sparse {
+	struct ae_sparse *next;
+
+	int64_t	 offset;
+	int64_t	 length;
+};
+
+/*
+ * 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 {
+	struct archive *archive;
+
+	/*
+	 * Note that ae_stat.st_mode & AE_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.
+	 */
+
+	/*
+	 * We have a "struct aest" for holding file metadata rather than just
+	 * a "struct stat" because on some platforms the "struct stat" has
+	 * fields which are too narrow to hold the range of possible values;
+	 * we don't want to lose information if we read an archive and write
+	 * out another (e.g., in "tar -cf new.tar @old.tar").
+	 *
+	 * The "stat" pointer points to some form of platform-specific struct
+	 * stat; it is declared as a void * rather than a struct stat * as
+	 * some platforms have multiple varieties of stat structures.
+	 */
+	void *stat;
+	int  stat_valid; /* Set to 0 whenever a field in aest changes. */
+
+	struct aest {
+		int64_t		aest_atime;
+		uint32_t	aest_atime_nsec;
+		int64_t		aest_ctime;
+		uint32_t	aest_ctime_nsec;
+		int64_t		aest_mtime;
+		uint32_t	aest_mtime_nsec;
+		int64_t		aest_birthtime;
+		uint32_t	aest_birthtime_nsec;
+		int64_t		aest_gid;
+		int64_t		aest_ino;
+		uint32_t	aest_nlink;
+		uint64_t	aest_size;
+		int64_t		aest_uid;
+		/*
+		 * Because converting between device codes and
+		 * major/minor values is platform-specific and
+		 * inherently a bit risky, we only do that conversion
+		 * lazily.  That way, we will do a better job of
+		 * preserving information in those cases where no
+		 * conversion is actually required.
+		 */
+		int		aest_dev_is_broken_down;
+		dev_t		aest_dev;
+		dev_t		aest_devmajor;
+		dev_t		aest_devminor;
+		int		aest_rdev_is_broken_down;
+		dev_t		aest_rdev;
+		dev_t		aest_rdevmajor;
+		dev_t		aest_rdevminor;
+	} ae_stat;
+
+	int ae_set; /* bitmap of fields that are currently set */
+#define	AE_SET_HARDLINK	1
+#define	AE_SET_SYMLINK	2
+#define	AE_SET_ATIME	4
+#define	AE_SET_CTIME	8
+#define	AE_SET_MTIME	16
+#define	AE_SET_BIRTHTIME 32
+#define	AE_SET_SIZE	64
+#define	AE_SET_INO	128
+#define	AE_SET_DEV	256
+
+	/*
+	 * Use aes here so that we get transparent mbs<->wcs conversions.
+	 */
+	struct archive_mstring ae_fflags_text;	/* Text fflags per fflagstostr(3) */
+	unsigned long ae_fflags_set;		/* Bitmap fflags */
+	unsigned long ae_fflags_clear;
+	struct archive_mstring ae_gname;		/* Name of owning group */
+	struct archive_mstring ae_hardlink;	/* Name of target for hardlink */
+	struct archive_mstring ae_pathname;	/* Name of entry */
+	struct archive_mstring ae_symlink;		/* symlink contents */
+	struct archive_mstring ae_uname;		/* Name of owner */
+
+	/* Not used within libarchive; useful for some clients. */
+	struct archive_mstring ae_sourcepath;	/* Path this entry is sourced from. */
+
+#define AE_ENCRYPTION_NONE 0
+#define AE_ENCRYPTION_DATA 1
+#define AE_ENCRYPTION_METADATA 2
+	char encryption;
+	
+	void *mac_metadata;
+	size_t mac_metadata_size;
+
+	/* ACL support. */
+	struct archive_acl    acl;
+
+	/* extattr support. */
+	struct ae_xattr *xattr_head;
+	struct ae_xattr *xattr_p;
+
+	/* sparse support. */
+	struct ae_sparse *sparse_head;
+	struct ae_sparse *sparse_tail;
+	struct ae_sparse *sparse_p;
+
+	/* Miscellaneous. */
+	char		 strmode[12];
+
+	/* Symlink type support */
+	int ae_symlink_type;
+};
+
+#endif /* ARCHIVE_ENTRY_PRIVATE_H_INCLUDED */
diff --git a/c/archive_entry_sparse.c b/c/archive_entry_sparse.c
new file mode 100644
--- /dev/null
+++ b/c/archive_entry_sparse.c
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2011 Michihiro NAKAJIMA
+ * 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$");
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_entry_private.h"
+
+/*
+ * sparse handling
+ */
+
+void
+archive_entry_sparse_clear(struct archive_entry *entry)
+{
+	struct ae_sparse *sp;
+
+	while (entry->sparse_head != NULL) {
+		sp = entry->sparse_head->next;
+		free(entry->sparse_head);
+		entry->sparse_head = sp;
+	}
+	entry->sparse_tail = NULL;
+}
+
+void
+archive_entry_sparse_add_entry(struct archive_entry *entry,
+	la_int64_t offset, la_int64_t length)
+{
+	struct ae_sparse *sp;
+
+	if (offset < 0 || length < 0)
+		/* Invalid value */
+		return;
+	if (offset > INT64_MAX - length ||
+	    offset + length > archive_entry_size(entry))
+		/* A value of "length" parameter is too large. */
+		return;
+	if ((sp = entry->sparse_tail) != NULL) {
+		if (sp->offset + sp->length > offset)
+			/* Invalid value. */
+			return;
+		if (sp->offset + sp->length == offset) {
+			if (sp->offset + sp->length + length < 0)
+				/* A value of "length" parameter is
+				 * too large. */
+				return;
+			/* Expand existing sparse block size. */
+			sp->length += length;
+			return;
+		}
+	}
+
+	if ((sp = (struct ae_sparse *)malloc(sizeof(*sp))) == NULL)
+		/* XXX Error XXX */
+		return;
+
+	sp->offset = offset;
+	sp->length = length;
+	sp->next = NULL;
+
+	if (entry->sparse_head == NULL)
+		entry->sparse_head = entry->sparse_tail = sp;
+	else {
+		/* Add a new sparse block to the tail of list. */
+		if (entry->sparse_tail != NULL)
+			entry->sparse_tail->next = sp;
+		entry->sparse_tail = sp;
+	}
+}
+
+
+/*
+ * returns number of the sparse entries
+ */
+int
+archive_entry_sparse_count(struct archive_entry *entry)
+{
+	struct ae_sparse *sp;
+	int count = 0;
+
+	for (sp = entry->sparse_head; sp != NULL; sp = sp->next)
+		count++;
+
+	/*
+	 * Sanity check if this entry is exactly sparse.
+	 * If amount of sparse blocks is just one and it indicates the whole
+	 * file data, we should remove it and return zero.
+	 */
+	if (count == 1) {
+		sp = entry->sparse_head;
+		if (sp->offset == 0 &&
+		    sp->length >= archive_entry_size(entry)) {
+			count = 0;
+			archive_entry_sparse_clear(entry);
+		}
+	}
+
+	return (count);
+}
+
+int
+archive_entry_sparse_reset(struct archive_entry * entry)
+{
+	entry->sparse_p = entry->sparse_head;
+
+	return archive_entry_sparse_count(entry);
+}
+
+int
+archive_entry_sparse_next(struct archive_entry * entry,
+	la_int64_t *offset, la_int64_t *length)
+{
+	if (entry->sparse_p) {
+		*offset = entry->sparse_p->offset;
+		*length = entry->sparse_p->length;
+
+		entry->sparse_p = entry->sparse_p->next;
+
+		return (ARCHIVE_OK);
+	} else {
+		*offset = 0;
+		*length = 0;
+		return (ARCHIVE_WARN);
+	}
+}
+
+/*
+ * end of sparse handling
+ */
diff --git a/c/archive_entry_stat.c b/c/archive_entry_stat.c
new file mode 100644
--- /dev/null
+++ b/c/archive_entry_stat.c
@@ -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: head/lib/libarchive/archive_entry_stat.c 201100 2009-12-28 03:05:31Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+
+const struct stat *
+archive_entry_stat(struct archive_entry *entry)
+{
+	struct stat *st;
+	if (entry->stat == NULL) {
+		entry->stat = calloc(1, sizeof(*st));
+		if (entry->stat == NULL)
+			return (NULL);
+		entry->stat_valid = 0;
+	}
+
+	/*
+	 * If none of the underlying fields have been changed, we
+	 * don't need to regenerate.  In theory, we could use a bitmap
+	 * here to flag only those items that have changed, but the
+	 * extra complexity probably isn't worth it.  It will be very
+	 * rare for anyone to change just one field then request a new
+	 * stat structure.
+	 */
+	if (entry->stat_valid)
+		return (entry->stat);
+
+	st = entry->stat;
+	/*
+	 * Use the public interfaces to extract items, so that
+	 * the appropriate conversions get invoked.
+	 */
+	st->st_atime = archive_entry_atime(entry);
+#if HAVE_STRUCT_STAT_ST_BIRTHTIME
+	st->st_birthtime = archive_entry_birthtime(entry);
+#endif
+	st->st_ctime = archive_entry_ctime(entry);
+	st->st_mtime = archive_entry_mtime(entry);
+	st->st_dev = archive_entry_dev(entry);
+	st->st_gid = (gid_t)archive_entry_gid(entry);
+	st->st_uid = (uid_t)archive_entry_uid(entry);
+	st->st_ino = (ino_t)archive_entry_ino64(entry);
+	st->st_nlink = archive_entry_nlink(entry);
+	st->st_rdev = archive_entry_rdev(entry);
+	st->st_size = (off_t)archive_entry_size(entry);
+	st->st_mode = archive_entry_mode(entry);
+
+	/*
+	 * On systems that support high-res timestamps, copy that
+	 * information into struct stat.
+	 */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+	st->st_atimespec.tv_nsec = archive_entry_atime_nsec(entry);
+	st->st_ctimespec.tv_nsec = archive_entry_ctime_nsec(entry);
+	st->st_mtimespec.tv_nsec = archive_entry_mtime_nsec(entry);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+	st->st_atim.tv_nsec = archive_entry_atime_nsec(entry);
+	st->st_ctim.tv_nsec = archive_entry_ctime_nsec(entry);
+	st->st_mtim.tv_nsec = archive_entry_mtime_nsec(entry);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+	st->st_atime_n = archive_entry_atime_nsec(entry);
+	st->st_ctime_n = archive_entry_ctime_nsec(entry);
+	st->st_mtime_n = archive_entry_mtime_nsec(entry);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+	st->st_uatime = archive_entry_atime_nsec(entry) / 1000;
+	st->st_uctime = archive_entry_ctime_nsec(entry) / 1000;
+	st->st_umtime = archive_entry_mtime_nsec(entry) / 1000;
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+	st->st_atime_usec = archive_entry_atime_nsec(entry) / 1000;
+	st->st_ctime_usec = archive_entry_ctime_nsec(entry) / 1000;
+	st->st_mtime_usec = archive_entry_mtime_nsec(entry) / 1000;
+#endif
+#if HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC
+	st->st_birthtimespec.tv_nsec = archive_entry_birthtime_nsec(entry);
+#endif
+
+	/*
+	 * TODO: On Linux, store 32 or 64 here depending on whether
+	 * the cached stat structure is a stat32 or a stat64.  This
+	 * will allow us to support both variants interchangeably.
+	 */
+	entry->stat_valid = 1;
+
+	return (st);
+}
diff --git a/c/archive_entry_strmode.c b/c/archive_entry_strmode.c
new file mode 100644
--- /dev/null
+++ b/c/archive_entry_strmode.c
@@ -0,0 +1,87 @@
+/*-
+ * 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_strmode.c,v 1.4 2008/06/15 05:14:01 kientzle Exp $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_private.h"
+
+const char *
+archive_entry_strmode(struct archive_entry *entry)
+{
+	static const mode_t permbits[] =
+	    { 0400, 0200, 0100, 0040, 0020, 0010, 0004, 0002, 0001 };
+	char *bp = entry->strmode;
+	mode_t mode;
+	int i;
+
+	/* Fill in a default string, then selectively override. */
+	strcpy(bp, "?rwxrwxrwx ");
+
+	mode = archive_entry_mode(entry);
+	switch (archive_entry_filetype(entry)) {
+	case AE_IFREG:  bp[0] = '-'; break;
+	case AE_IFBLK:  bp[0] = 'b'; break;
+	case AE_IFCHR:  bp[0] = 'c'; break;
+	case AE_IFDIR:  bp[0] = 'd'; break;
+	case AE_IFLNK:  bp[0] = 'l'; break;
+	case AE_IFSOCK: bp[0] = 's'; break;
+	case AE_IFIFO:  bp[0] = 'p'; break;
+	default:
+		if (archive_entry_hardlink(entry) != NULL) {
+			bp[0] = 'h';
+			break;
+		}
+	}
+
+	for (i = 0; i < 9; i++)
+		if (!(mode & permbits[i]))
+			bp[i+1] = '-';
+
+	if (mode & S_ISUID) {
+		if (mode & 0100) bp[3] = 's';
+		else bp[3] = 'S';
+	}
+	if (mode & S_ISGID) {
+		if (mode & 0010) bp[6] = 's';
+		else bp[6] = 'S';
+	}
+	if (mode & S_ISVTX) {
+		if (mode & 0001) bp[9] = 't';
+		else bp[9] = 'T';
+	}
+	if (archive_entry_acl_types(entry) != 0)
+		bp[10] = '+';
+
+	return (bp);
+}
diff --git a/c/archive_entry_xattr.c b/c/archive_entry_xattr.c
new file mode 100644
--- /dev/null
+++ b/c/archive_entry_xattr.c
@@ -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: head/lib/libarchive/archive_entry_xattr.c 201096 2009-12-28 02:41:27Z kientzle $");
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>	/* for Linux file flags */
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>	/* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#include <ext2fs/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
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_entry_private.h"
+
+/*
+ * 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;
+
+	if ((xp = (struct ae_xattr *)malloc(sizeof(struct ae_xattr))) == NULL)
+		__archive_errx(1, "Out of memory");
+
+	if ((xp->name = strdup(name)) == NULL)
+		__archive_errx(1, "Out of memory");
+
+	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;
+		*value = NULL;
+		*size = (size_t)0;
+		return (ARCHIVE_WARN);
+	}
+}
+
+/*
+ * end of xattr handling
+ */
diff --git a/c/archive_getdate.c b/c/archive_getdate.c
new file mode 100644
--- /dev/null
+++ b/c/archive_getdate.c
@@ -0,0 +1,1165 @@
+/*
+ * This code is in the public domain and has no copyright.
+ *
+ * This is a plain C recursive-descent translation of an old
+ * public-domain YACC grammar that has been used for parsing dates in
+ * very many open-source projects.
+ *
+ * Since the original authors were generous enough to donate their
+ * work to the public domain, I feel compelled to match their
+ * generosity.
+ *
+ * Tim Kientzle, February 2009.
+ */
+
+/*
+ * Header comment from original getdate.y:
+ */
+
+/*
+**  Originally written by Steven M. Bellovin <smb@research.att.com> while
+**  at the University of North Carolina at Chapel Hill.  Later tweaked by
+**  a couple of people on Usenet.  Completely overhauled by Rich $alz
+**  <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+**
+**  This grammar has 10 shift/reduce conflicts.
+**
+**  This code is in the public domain and has no copyright.
+*/
+
+#include "archive_platform.h"
+#ifdef __FreeBSD__
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#define __LIBARCHIVE_BUILD 1
+#include "archive_getdate.h"
+
+/* Basic time units. */
+#define	EPOCH		1970
+#define	MINUTE		(60L)
+#define	HOUR		(60L * MINUTE)
+#define	DAY		(24L * HOUR)
+
+/* Daylight-savings mode:  on, off, or not yet known. */
+enum DSTMODE { DSTon, DSToff, DSTmaybe };
+/* Meridian:  am or pm. */
+enum { tAM, tPM };
+/* Token types returned by nexttoken() */
+enum { tAGO = 260, tDAY, tDAYZONE, tAMPM, tMONTH, tMONTH_UNIT, tSEC_UNIT,
+       tUNUMBER, tZONE, tDST };
+struct token { int token; time_t value; };
+
+/*
+ * Parser state.
+ */
+struct gdstate {
+	struct token *tokenp; /* Pointer to next token. */
+	/* HaveXxxx counts how many of this kind of phrase we've seen;
+	 * it's a fatal error to have more than one time, zone, day,
+	 * or date phrase. */
+	int	HaveYear;
+	int	HaveMonth;
+	int	HaveDay;
+	int	HaveWeekDay; /* Day of week */
+	int	HaveTime; /* Hour/minute/second */
+	int	HaveZone; /* timezone and/or DST info */
+	int	HaveRel; /* time offset; we can have more than one */
+	/* Absolute time values. */
+	time_t	Timezone;  /* Seconds offset from GMT */
+	time_t	Day;
+	time_t	Hour;
+	time_t	Minutes;
+	time_t	Month;
+	time_t	Seconds;
+	time_t	Year;
+	/* DST selection */
+	enum DSTMODE	DSTmode;
+	/* Day of week accounting, e.g., "3rd Tuesday" */
+	time_t	DayOrdinal; /* "3" in "3rd Tuesday" */
+	time_t	DayNumber; /* "Tuesday" in "3rd Tuesday" */
+	/* Relative time values: hour/day/week offsets are measured in
+	 * seconds, month/year are counted in months. */
+	time_t	RelMonth;
+	time_t	RelSeconds;
+};
+
+/*
+ * A series of functions that recognize certain common time phrases.
+ * Each function returns 1 if it managed to make sense of some of the
+ * tokens, zero otherwise.
+ */
+
+/*
+ *  hour:minute or hour:minute:second with optional AM, PM, or numeric
+ *  timezone offset
+ */
+static int
+timephrase(struct gdstate *gds)
+{
+	if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == ':'
+	    && gds->tokenp[2].token == tUNUMBER
+	    && gds->tokenp[3].token == ':'
+	    && gds->tokenp[4].token == tUNUMBER) {
+		/* "12:14:18" or "22:08:07" */
+		++gds->HaveTime;
+		gds->Hour = gds->tokenp[0].value;
+		gds->Minutes = gds->tokenp[2].value;
+		gds->Seconds = gds->tokenp[4].value;
+		gds->tokenp += 5;
+	}
+	else if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == ':'
+	    && gds->tokenp[2].token == tUNUMBER) {
+		/* "12:14" or "22:08" */
+		++gds->HaveTime;
+		gds->Hour = gds->tokenp[0].value;
+		gds->Minutes = gds->tokenp[2].value;
+		gds->Seconds = 0;
+		gds->tokenp += 3;
+	}
+	else if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == tAMPM) {
+		/* "7" is a time if it's followed by "am" or "pm" */
+		++gds->HaveTime;
+		gds->Hour = gds->tokenp[0].value;
+		gds->Minutes = gds->Seconds = 0;
+		/* We'll handle the AM/PM below. */
+		gds->tokenp += 1;
+	} else {
+		/* We can't handle this. */
+		return 0;
+	}
+
+	if (gds->tokenp[0].token == tAMPM) {
+		/* "7:12pm", "12:20:13am" */
+		if (gds->Hour == 12)
+			gds->Hour = 0;
+		if (gds->tokenp[0].value == tPM)
+			gds->Hour += 12;
+		gds->tokenp += 1;
+	}
+	if (gds->tokenp[0].token == '+'
+	    && gds->tokenp[1].token == tUNUMBER) {
+		/* "7:14+0700" */
+		gds->HaveZone++;
+		gds->DSTmode = DSToff;
+		gds->Timezone = - ((gds->tokenp[1].value / 100) * HOUR
+		    + (gds->tokenp[1].value % 100) * MINUTE);
+		gds->tokenp += 2;
+	}
+	if (gds->tokenp[0].token == '-'
+	    && gds->tokenp[1].token == tUNUMBER) {
+		/* "19:14:12-0530" */
+		gds->HaveZone++;
+		gds->DSTmode = DSToff;
+		gds->Timezone = + ((gds->tokenp[1].value / 100) * HOUR
+		    + (gds->tokenp[1].value % 100) * MINUTE);
+		gds->tokenp += 2;
+	}
+	return 1;
+}
+
+/*
+ * Timezone name, possibly including DST.
+ */
+static int
+zonephrase(struct gdstate *gds)
+{
+	if (gds->tokenp[0].token == tZONE
+	    && gds->tokenp[1].token == tDST) {
+		gds->HaveZone++;
+		gds->Timezone = gds->tokenp[0].value;
+		gds->DSTmode = DSTon;
+		gds->tokenp += 1;
+		return 1;
+	}
+
+	if (gds->tokenp[0].token == tZONE) {
+		gds->HaveZone++;
+		gds->Timezone = gds->tokenp[0].value;
+		gds->DSTmode = DSToff;
+		gds->tokenp += 1;
+		return 1;
+	}
+
+	if (gds->tokenp[0].token == tDAYZONE) {
+		gds->HaveZone++;
+		gds->Timezone = gds->tokenp[0].value;
+		gds->DSTmode = DSTon;
+		gds->tokenp += 1;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Year/month/day in various combinations.
+ */
+static int
+datephrase(struct gdstate *gds)
+{
+	if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == '/'
+	    && gds->tokenp[2].token == tUNUMBER
+	    && gds->tokenp[3].token == '/'
+	    && gds->tokenp[4].token == tUNUMBER) {
+		gds->HaveYear++;
+		gds->HaveMonth++;
+		gds->HaveDay++;
+		if (gds->tokenp[0].value >= 13) {
+			/* First number is big:  2004/01/29, 99/02/17 */
+			gds->Year = gds->tokenp[0].value;
+			gds->Month = gds->tokenp[2].value;
+			gds->Day = gds->tokenp[4].value;
+		} else if ((gds->tokenp[4].value >= 13)
+		    || (gds->tokenp[2].value >= 13)) {
+			/* Last number is big:  01/07/98 */
+			/* Middle number is big:  01/29/04 */
+			gds->Month = gds->tokenp[0].value;
+			gds->Day = gds->tokenp[2].value;
+			gds->Year = gds->tokenp[4].value;
+		} else {
+			/* No significant clues: 02/03/04 */
+			gds->Month = gds->tokenp[0].value;
+			gds->Day = gds->tokenp[2].value;
+			gds->Year = gds->tokenp[4].value;
+		}
+		gds->tokenp += 5;
+		return 1;
+	}
+
+	if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == '/'
+	    && gds->tokenp[2].token == tUNUMBER) {
+		/* "1/15" */
+		gds->HaveMonth++;
+		gds->HaveDay++;
+		gds->Month = gds->tokenp[0].value;
+		gds->Day = gds->tokenp[2].value;
+		gds->tokenp += 3;
+		return 1;
+	}
+
+	if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == '-'
+	    && gds->tokenp[2].token == tUNUMBER
+	    && gds->tokenp[3].token == '-'
+	    && gds->tokenp[4].token == tUNUMBER) {
+		/* ISO 8601 format.  yyyy-mm-dd.  */
+		gds->HaveYear++;
+		gds->HaveMonth++;
+		gds->HaveDay++;
+		gds->Year = gds->tokenp[0].value;
+		gds->Month = gds->tokenp[2].value;
+		gds->Day = gds->tokenp[4].value;
+		gds->tokenp += 5;
+		return 1;
+	}
+
+	if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == '-'
+	    && gds->tokenp[2].token == tMONTH
+	    && gds->tokenp[3].token == '-'
+	    && gds->tokenp[4].token == tUNUMBER) {
+		gds->HaveYear++;
+		gds->HaveMonth++;
+		gds->HaveDay++;
+		if (gds->tokenp[0].value > 31) {
+			/* e.g. 1992-Jun-17 */
+			gds->Year = gds->tokenp[0].value;
+			gds->Month = gds->tokenp[2].value;
+			gds->Day = gds->tokenp[4].value;
+		} else {
+			/* e.g. 17-JUN-1992.  */
+			gds->Day = gds->tokenp[0].value;
+			gds->Month = gds->tokenp[2].value;
+			gds->Year = gds->tokenp[4].value;
+		}
+		gds->tokenp += 5;
+		return 1;
+	}
+
+	if (gds->tokenp[0].token == tMONTH
+	    && gds->tokenp[1].token == tUNUMBER
+	    && gds->tokenp[2].token == ','
+	    && gds->tokenp[3].token == tUNUMBER) {
+		/* "June 17, 2001" */
+		gds->HaveYear++;
+		gds->HaveMonth++;
+		gds->HaveDay++;
+		gds->Month = gds->tokenp[0].value;
+		gds->Day = gds->tokenp[1].value;
+		gds->Year = gds->tokenp[3].value;
+		gds->tokenp += 4;
+		return 1;
+	}
+
+	if (gds->tokenp[0].token == tMONTH
+	    && gds->tokenp[1].token == tUNUMBER) {
+		/* "May 3" */
+		gds->HaveMonth++;
+		gds->HaveDay++;
+		gds->Month = gds->tokenp[0].value;
+		gds->Day = gds->tokenp[1].value;
+		gds->tokenp += 2;
+		return 1;
+	}
+
+	if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == tMONTH
+	    && gds->tokenp[2].token == tUNUMBER) {
+		/* "12 Sept 1997" */
+		gds->HaveYear++;
+		gds->HaveMonth++;
+		gds->HaveDay++;
+		gds->Day = gds->tokenp[0].value;
+		gds->Month = gds->tokenp[1].value;
+		gds->Year = gds->tokenp[2].value;
+		gds->tokenp += 3;
+		return 1;
+	}
+
+	if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == tMONTH) {
+		/* "12 Sept" */
+		gds->HaveMonth++;
+		gds->HaveDay++;
+		gds->Day = gds->tokenp[0].value;
+		gds->Month = gds->tokenp[1].value;
+		gds->tokenp += 2;
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * Relative time phrase: "tomorrow", "yesterday", "+1 hour", etc.
+ */
+static int
+relunitphrase(struct gdstate *gds)
+{
+	if (gds->tokenp[0].token == '-'
+	    && gds->tokenp[1].token == tUNUMBER
+	    && gds->tokenp[2].token == tSEC_UNIT) {
+		/* "-3 hours" */
+		gds->HaveRel++;
+		gds->RelSeconds -= gds->tokenp[1].value * gds->tokenp[2].value;
+		gds->tokenp += 3;
+		return 1;
+	}
+	if (gds->tokenp[0].token == '+'
+	    && gds->tokenp[1].token == tUNUMBER
+	    && gds->tokenp[2].token == tSEC_UNIT) {
+		/* "+1 minute" */
+		gds->HaveRel++;
+		gds->RelSeconds += gds->tokenp[1].value * gds->tokenp[2].value;
+		gds->tokenp += 3;
+		return 1;
+	}
+	if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == tSEC_UNIT) {
+		/* "1 day" */
+		gds->HaveRel++;
+		gds->RelSeconds += gds->tokenp[0].value * gds->tokenp[1].value;
+		gds->tokenp += 2;
+		return 1;
+	}
+	if (gds->tokenp[0].token == '-'
+	    && gds->tokenp[1].token == tUNUMBER
+	    && gds->tokenp[2].token == tMONTH_UNIT) {
+		/* "-3 months" */
+		gds->HaveRel++;
+		gds->RelMonth -= gds->tokenp[1].value * gds->tokenp[2].value;
+		gds->tokenp += 3;
+		return 1;
+	}
+	if (gds->tokenp[0].token == '+'
+	    && gds->tokenp[1].token == tUNUMBER
+	    && gds->tokenp[2].token == tMONTH_UNIT) {
+		/* "+5 years" */
+		gds->HaveRel++;
+		gds->RelMonth += gds->tokenp[1].value * gds->tokenp[2].value;
+		gds->tokenp += 3;
+		return 1;
+	}
+	if (gds->tokenp[0].token == tUNUMBER
+	    && gds->tokenp[1].token == tMONTH_UNIT) {
+		/* "2 years" */
+		gds->HaveRel++;
+		gds->RelMonth += gds->tokenp[0].value * gds->tokenp[1].value;
+		gds->tokenp += 2;
+		return 1;
+	}
+	if (gds->tokenp[0].token == tSEC_UNIT) {
+		/* "now", "tomorrow" */
+		gds->HaveRel++;
+		gds->RelSeconds += gds->tokenp[0].value;
+		gds->tokenp += 1;
+		return 1;
+	}
+	if (gds->tokenp[0].token == tMONTH_UNIT) {
+		/* "month" */
+		gds->HaveRel++;
+		gds->RelMonth += gds->tokenp[0].value;
+		gds->tokenp += 1;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Day of the week specification.
+ */
+static int
+dayphrase(struct gdstate *gds)
+{
+	if (gds->tokenp[0].token == tDAY) {
+		/* "tues", "wednesday," */
+		gds->HaveWeekDay++;
+		gds->DayOrdinal = 1;
+		gds->DayNumber = gds->tokenp[0].value;
+		gds->tokenp += 1;
+		if (gds->tokenp[0].token == ',')
+			gds->tokenp += 1;
+		return 1;
+	}
+	if (gds->tokenp[0].token == tUNUMBER
+		&& gds->tokenp[1].token == tDAY) {
+		/* "second tues" "3 wed" */
+		gds->HaveWeekDay++;
+		gds->DayOrdinal = gds->tokenp[0].value;
+		gds->DayNumber = gds->tokenp[1].value;
+		gds->tokenp += 2;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Try to match a phrase using one of the above functions.
+ * This layer also deals with a couple of generic issues.
+ */
+static int
+phrase(struct gdstate *gds)
+{
+	if (timephrase(gds))
+		return 1;
+	if (zonephrase(gds))
+		return 1;
+	if (datephrase(gds))
+		return 1;
+	if (dayphrase(gds))
+		return 1;
+	if (relunitphrase(gds)) {
+		if (gds->tokenp[0].token == tAGO) {
+			gds->RelSeconds = -gds->RelSeconds;
+			gds->RelMonth = -gds->RelMonth;
+			gds->tokenp += 1;
+		}
+		return 1;
+	}
+
+	/* Bare numbers sometimes have meaning. */
+	if (gds->tokenp[0].token == tUNUMBER) {
+		if (gds->HaveTime && !gds->HaveYear && !gds->HaveRel) {
+			gds->HaveYear++;
+			gds->Year = gds->tokenp[0].value;
+			gds->tokenp += 1;
+			return 1;
+		}
+
+		if(gds->tokenp[0].value > 10000) {
+			/* "20040301" */
+			gds->HaveYear++;
+			gds->HaveMonth++;
+			gds->HaveDay++;
+			gds->Day= (gds->tokenp[0].value)%100;
+			gds->Month= (gds->tokenp[0].value/100)%100;
+			gds->Year = gds->tokenp[0].value/10000;
+			gds->tokenp += 1;
+			return 1;
+		}
+
+		if (gds->tokenp[0].value < 24) {
+			gds->HaveTime++;
+			gds->Hour = gds->tokenp[0].value;
+			gds->Minutes = 0;
+			gds->Seconds = 0;
+			gds->tokenp += 1;
+			return 1;
+		}
+
+		if ((gds->tokenp[0].value / 100 < 24)
+		    && (gds->tokenp[0].value % 100 < 60)) {
+			/* "513" is same as "5:13" */
+			gds->Hour = gds->tokenp[0].value / 100;
+			gds->Minutes = gds->tokenp[0].value % 100;
+			gds->Seconds = 0;
+			gds->tokenp += 1;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * A dictionary of time words.
+ */
+static struct LEXICON {
+	size_t		abbrev;
+	const char	*name;
+	int		type;
+	time_t		value;
+} const TimeWords[] = {
+	/* am/pm */
+	{ 0, "am",		tAMPM,	tAM },
+	{ 0, "pm",		tAMPM,	tPM },
+
+	/* Month names. */
+	{ 3, "january",		tMONTH,  1 },
+	{ 3, "february",	tMONTH,  2 },
+	{ 3, "march",		tMONTH,  3 },
+	{ 3, "april",		tMONTH,  4 },
+	{ 3, "may",		tMONTH,  5 },
+	{ 3, "june",		tMONTH,  6 },
+	{ 3, "july",		tMONTH,  7 },
+	{ 3, "august",		tMONTH,  8 },
+	{ 3, "september",	tMONTH,  9 },
+	{ 3, "october",		tMONTH, 10 },
+	{ 3, "november",	tMONTH, 11 },
+	{ 3, "december",	tMONTH, 12 },
+
+	/* Days of the week. */
+	{ 2, "sunday",		tDAY, 0 },
+	{ 3, "monday",		tDAY, 1 },
+	{ 2, "tuesday",		tDAY, 2 },
+	{ 3, "wednesday",	tDAY, 3 },
+	{ 2, "thursday",	tDAY, 4 },
+	{ 2, "friday",		tDAY, 5 },
+	{ 2, "saturday",	tDAY, 6 },
+
+	/* Timezones: Offsets are in seconds. */
+	{ 0, "gmt",  tZONE,     0*HOUR }, /* Greenwich Mean */
+	{ 0, "ut",   tZONE,     0*HOUR }, /* Universal (Coordinated) */
+	{ 0, "utc",  tZONE,     0*HOUR },
+	{ 0, "wet",  tZONE,     0*HOUR }, /* Western European */
+	{ 0, "bst",  tDAYZONE,  0*HOUR }, /* British Summer */
+	{ 0, "wat",  tZONE,     1*HOUR }, /* West Africa */
+	{ 0, "at",   tZONE,     2*HOUR }, /* Azores */
+	/* { 0, "bst", tZONE, 3*HOUR }, */ /* Brazil Standard: Conflict */
+	/* { 0, "gst", tZONE, 3*HOUR }, */ /* Greenland Standard: Conflict*/
+	{ 0, "nft",  tZONE,     3*HOUR+30*MINUTE }, /* Newfoundland */
+	{ 0, "nst",  tZONE,     3*HOUR+30*MINUTE }, /* Newfoundland Standard */
+	{ 0, "ndt",  tDAYZONE,  3*HOUR+30*MINUTE }, /* Newfoundland Daylight */
+	{ 0, "ast",  tZONE,     4*HOUR }, /* Atlantic Standard */
+	{ 0, "adt",  tDAYZONE,  4*HOUR }, /* Atlantic Daylight */
+	{ 0, "est",  tZONE,     5*HOUR }, /* Eastern Standard */
+	{ 0, "edt",  tDAYZONE,  5*HOUR }, /* Eastern Daylight */
+	{ 0, "cst",  tZONE,     6*HOUR }, /* Central Standard */
+	{ 0, "cdt",  tDAYZONE,  6*HOUR }, /* Central Daylight */
+	{ 0, "mst",  tZONE,     7*HOUR }, /* Mountain Standard */
+	{ 0, "mdt",  tDAYZONE,  7*HOUR }, /* Mountain Daylight */
+	{ 0, "pst",  tZONE,     8*HOUR }, /* Pacific Standard */
+	{ 0, "pdt",  tDAYZONE,  8*HOUR }, /* Pacific Daylight */
+	{ 0, "yst",  tZONE,     9*HOUR }, /* Yukon Standard */
+	{ 0, "ydt",  tDAYZONE,  9*HOUR }, /* Yukon Daylight */
+	{ 0, "hst",  tZONE,     10*HOUR }, /* Hawaii Standard */
+	{ 0, "hdt",  tDAYZONE,  10*HOUR }, /* Hawaii Daylight */
+	{ 0, "cat",  tZONE,     10*HOUR }, /* Central Alaska */
+	{ 0, "ahst", tZONE,     10*HOUR }, /* Alaska-Hawaii Standard */
+	{ 0, "nt",   tZONE,     11*HOUR }, /* Nome */
+	{ 0, "idlw", tZONE,     12*HOUR }, /* Intl Date Line West */
+	{ 0, "cet",  tZONE,     -1*HOUR }, /* Central European */
+	{ 0, "met",  tZONE,     -1*HOUR }, /* Middle European */
+	{ 0, "mewt", tZONE,     -1*HOUR }, /* Middle European Winter */
+	{ 0, "mest", tDAYZONE,  -1*HOUR }, /* Middle European Summer */
+	{ 0, "swt",  tZONE,     -1*HOUR }, /* Swedish Winter */
+	{ 0, "sst",  tDAYZONE,  -1*HOUR }, /* Swedish Summer */
+	{ 0, "fwt",  tZONE,     -1*HOUR }, /* French Winter */
+	{ 0, "fst",  tDAYZONE,  -1*HOUR }, /* French Summer */
+	{ 0, "eet",  tZONE,     -2*HOUR }, /* Eastern Eur, USSR Zone 1 */
+	{ 0, "bt",   tZONE,     -3*HOUR }, /* Baghdad, USSR Zone 2 */
+	{ 0, "it",   tZONE,     -3*HOUR-30*MINUTE },/* Iran */
+	{ 0, "zp4",  tZONE,     -4*HOUR }, /* USSR Zone 3 */
+	{ 0, "zp5",  tZONE,     -5*HOUR }, /* USSR Zone 4 */
+	{ 0, "ist",  tZONE,     -5*HOUR-30*MINUTE },/* Indian Standard */
+	{ 0, "zp6",  tZONE,     -6*HOUR }, /* USSR Zone 5 */
+	/* { 0, "nst",  tZONE, -6.5*HOUR }, */ /* North Sumatra: Conflict */
+	/* { 0, "sst", tZONE, -7*HOUR }, */ /* So Sumatra, USSR 6: Conflict */
+	{ 0, "wast", tZONE,     -7*HOUR }, /* West Australian Standard */
+	{ 0, "wadt", tDAYZONE,  -7*HOUR }, /* West Australian Daylight */
+	{ 0, "jt",   tZONE,     -7*HOUR-30*MINUTE },/* Java (3pm in Cronusland!)*/
+	{ 0, "cct",  tZONE,     -8*HOUR }, /* China Coast, USSR Zone 7 */
+	{ 0, "jst",  tZONE,     -9*HOUR }, /* Japan Std, USSR Zone 8 */
+	{ 0, "cast", tZONE,     -9*HOUR-30*MINUTE },/* Ctrl Australian Std */
+	{ 0, "cadt", tDAYZONE,  -9*HOUR-30*MINUTE },/* Ctrl Australian Daylt */
+	{ 0, "east", tZONE,     -10*HOUR }, /* Eastern Australian Std */
+	{ 0, "eadt", tDAYZONE,  -10*HOUR }, /* Eastern Australian Daylt */
+	{ 0, "gst",  tZONE,     -10*HOUR }, /* Guam Std, USSR Zone 9 */
+	{ 0, "nzt",  tZONE,     -12*HOUR }, /* New Zealand */
+	{ 0, "nzst", tZONE,     -12*HOUR }, /* New Zealand Standard */
+	{ 0, "nzdt", tDAYZONE,  -12*HOUR }, /* New Zealand Daylight */
+	{ 0, "idle", tZONE,     -12*HOUR }, /* Intl Date Line East */
+
+	{ 0, "dst",  tDST,		0 },
+
+	/* Time units. */
+	{ 4, "years",		tMONTH_UNIT,	12 },
+	{ 5, "months",		tMONTH_UNIT,	1 },
+	{ 9, "fortnights",	tSEC_UNIT,	14 * DAY },
+	{ 4, "weeks",		tSEC_UNIT,	7 * DAY },
+	{ 3, "days",		tSEC_UNIT,	DAY },
+	{ 4, "hours",		tSEC_UNIT,	HOUR },
+	{ 3, "minutes",		tSEC_UNIT,	MINUTE },
+	{ 3, "seconds",		tSEC_UNIT,	1 },
+
+	/* Relative-time words. */
+	{ 0, "tomorrow",	tSEC_UNIT,	DAY },
+	{ 0, "yesterday",	tSEC_UNIT,	-DAY },
+	{ 0, "today",		tSEC_UNIT,	0 },
+	{ 0, "now",		tSEC_UNIT,	0 },
+	{ 0, "last",		tUNUMBER,	-1 },
+	{ 0, "this",		tSEC_UNIT,	0 },
+	{ 0, "next",		tUNUMBER,	2 },
+	{ 0, "first",		tUNUMBER,	1 },
+	{ 0, "1st",		tUNUMBER,	1 },
+/*	{ 0, "second",		tUNUMBER,	2 }, */
+	{ 0, "2nd",		tUNUMBER,	2 },
+	{ 0, "third",		tUNUMBER,	3 },
+	{ 0, "3rd",		tUNUMBER,	3 },
+	{ 0, "fourth",		tUNUMBER,	4 },
+	{ 0, "4th",		tUNUMBER,	4 },
+	{ 0, "fifth",		tUNUMBER,	5 },
+	{ 0, "5th",		tUNUMBER,	5 },
+	{ 0, "sixth",		tUNUMBER,	6 },
+	{ 0, "seventh",		tUNUMBER,	7 },
+	{ 0, "eighth",		tUNUMBER,	8 },
+	{ 0, "ninth",		tUNUMBER,	9 },
+	{ 0, "tenth",		tUNUMBER,	10 },
+	{ 0, "eleventh",	tUNUMBER,	11 },
+	{ 0, "twelfth",		tUNUMBER,	12 },
+	{ 0, "ago",		tAGO,		1 },
+
+	/* Military timezones. */
+	{ 0, "a",	tZONE,	1*HOUR },
+	{ 0, "b",	tZONE,	2*HOUR },
+	{ 0, "c",	tZONE,	3*HOUR },
+	{ 0, "d",	tZONE,	4*HOUR },
+	{ 0, "e",	tZONE,	5*HOUR },
+	{ 0, "f",	tZONE,	6*HOUR },
+	{ 0, "g",	tZONE,	7*HOUR },
+	{ 0, "h",	tZONE,	8*HOUR },
+	{ 0, "i",	tZONE,	9*HOUR },
+	{ 0, "k",	tZONE,	10*HOUR },
+	{ 0, "l",	tZONE,	11*HOUR },
+	{ 0, "m",	tZONE,	12*HOUR },
+	{ 0, "n",	tZONE,	-1*HOUR },
+	{ 0, "o",	tZONE,	-2*HOUR },
+	{ 0, "p",	tZONE,	-3*HOUR },
+	{ 0, "q",	tZONE,	-4*HOUR },
+	{ 0, "r",	tZONE,	-5*HOUR },
+	{ 0, "s",	tZONE,	-6*HOUR },
+	{ 0, "t",	tZONE,	-7*HOUR },
+	{ 0, "u",	tZONE,	-8*HOUR },
+	{ 0, "v",	tZONE,	-9*HOUR },
+	{ 0, "w",	tZONE,	-10*HOUR },
+	{ 0, "x",	tZONE,	-11*HOUR },
+	{ 0, "y",	tZONE,	-12*HOUR },
+	{ 0, "z",	tZONE,	0*HOUR },
+
+	/* End of table. */
+	{ 0, NULL,	0,	0 }
+};
+
+/*
+ * Year is either:
+ *  = A number from 0 to 99, which means a year from 1970 to 2069, or
+ *  = The actual year (>=100).
+ */
+static time_t
+Convert(time_t Month, time_t Day, time_t Year,
+	time_t Hours, time_t Minutes, time_t Seconds,
+	time_t Timezone, enum DSTMODE DSTmode)
+{
+	signed char DaysInMonth[12] = {
+		31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+	};
+	time_t		Julian;
+	int		i;
+	struct tm	*ltime;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+	struct tm	tmbuf;
+#endif
+#if defined(HAVE__LOCALTIME64_S)
+	errno_t		terr;
+	__time64_t	tmptime;
+#endif
+
+	if (Year < 69)
+		Year += 2000;
+	else if (Year < 100)
+		Year += 1900;
+	DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+	    ? 29 : 28;
+	/* Checking for 2038 bogusly assumes that time_t is 32 bits.  But
+	   I'm too lazy to try to check for time_t overflow in another way.  */
+	if (Year < EPOCH || Year > 2038
+	    || Month < 1 || Month > 12
+	    /* Lint fluff:  "conversion from long may lose accuracy" */
+	    || Day < 1 || Day > DaysInMonth[(int)--Month]
+	    || Hours < 0 || Hours > 23
+	    || Minutes < 0 || Minutes > 59
+	    || Seconds < 0 || Seconds > 59)
+		return -1;
+
+	Julian = Day - 1;
+	for (i = 0; i < Month; i++)
+		Julian += DaysInMonth[i];
+	for (i = EPOCH; i < Year; i++)
+		Julian += 365 + (i % 4 == 0);
+	Julian *= DAY;
+	Julian += Timezone;
+	Julian += Hours * HOUR + Minutes * MINUTE + Seconds;
+#if defined(HAVE_LOCALTIME_R)
+	ltime = localtime_r(&Julian, &tmbuf);
+#elif defined(HAVE__LOCALTIME64_S)
+	tmptime = Julian;
+	terr = _localtime64_s(&tmbuf, &tmptime);
+	if (terr)
+		ltime = NULL;
+	else
+		ltime = &tmbuf;
+#else
+	ltime = localtime(&Julian);
+#endif
+	if (DSTmode == DSTon
+	    || (DSTmode == DSTmaybe && ltime->tm_isdst))
+		Julian -= HOUR;
+	return Julian;
+}
+
+static time_t
+DSTcorrect(time_t Start, time_t Future)
+{
+	time_t		StartDay;
+	time_t		FutureDay;
+	struct tm	*ltime;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+	struct tm	tmbuf;
+#endif
+#if defined(HAVE__LOCALTIME64_S)
+	errno_t		terr;
+	__time64_t	tmptime;
+#endif
+
+#if defined(HAVE_LOCALTIME_R)
+	ltime = localtime_r(&Start, &tmbuf);
+#elif defined(HAVE__LOCALTIME64_S)
+	tmptime = Start;
+	terr = _localtime64_s(&tmbuf, &tmptime);
+	if (terr)
+		ltime = NULL;
+	else
+		ltime = &tmbuf;
+#else
+	ltime = localtime(&Start);
+#endif
+	StartDay = (ltime->tm_hour + 1) % 24;
+#if defined(HAVE_LOCALTIME_R)
+	ltime = localtime_r(&Future, &tmbuf);
+#elif defined(HAVE__LOCALTIME64_S)
+	tmptime = Future;
+	terr = _localtime64_s(&tmbuf, &tmptime);
+	if (terr)
+		ltime = NULL;
+	else
+		ltime = &tmbuf;
+#else
+	ltime = localtime(&Future);
+#endif
+	FutureDay = (ltime->tm_hour + 1) % 24;
+	return (Future - Start) + (StartDay - FutureDay) * HOUR;
+}
+
+
+static time_t
+RelativeDate(time_t Start, time_t zone, int dstmode,
+    time_t DayOrdinal, time_t DayNumber)
+{
+	struct tm	*tm;
+	time_t	t, now;
+#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S)
+	struct tm	tmbuf;
+#endif
+#if defined(HAVE__GMTIME64_S)
+	errno_t		terr;
+	__time64_t	tmptime;
+#endif
+
+	t = Start - zone;
+#if defined(HAVE_GMTIME_R)
+	tm = gmtime_r(&t, &tmbuf);
+#elif defined(HAVE__GMTIME64_S)
+	tmptime = t;
+	terr = _gmtime64_s(&tmbuf, &tmptime);
+	if (terr)
+		tm = NULL;
+	else
+		tm = &tmbuf;
+#else
+	tm = gmtime(&t);
+#endif
+	now = Start;
+	now += DAY * ((DayNumber - tm->tm_wday + 7) % 7);
+	now += 7 * DAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+	if (dstmode == DSTmaybe)
+		return DSTcorrect(Start, now);
+	return now - Start;
+}
+
+
+static time_t
+RelativeMonth(time_t Start, time_t Timezone, time_t RelMonth)
+{
+	struct tm	*tm;
+	time_t	Month;
+	time_t	Year;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+	struct tm	tmbuf;
+#endif
+#if defined(HAVE__LOCALTIME64_S)
+	errno_t		terr;
+	__time64_t	tmptime;
+#endif
+
+	if (RelMonth == 0)
+		return 0;
+#if defined(HAVE_LOCALTIME_R)
+	tm = localtime_r(&Start, &tmbuf);
+#elif defined(HAVE__LOCALTIME64_S)
+	tmptime = Start;
+	terr = _localtime64_s(&tmbuf, &tmptime);
+	if (terr)
+		tm = NULL;
+	else
+		tm = &tmbuf;
+#else
+	tm = localtime(&Start);
+#endif
+	Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
+	Year = Month / 12;
+	Month = Month % 12 + 1;
+	return DSTcorrect(Start,
+	    Convert(Month, (time_t)tm->tm_mday, Year,
+		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+		Timezone, DSTmaybe));
+}
+
+/*
+ * Tokenizer.
+ */
+static int
+nexttoken(const char **in, time_t *value)
+{
+	char	c;
+	char	buff[64];
+
+	for ( ; ; ) {
+		while (isspace((unsigned char)**in))
+			++*in;
+
+		/* Skip parenthesized comments. */
+		if (**in == '(') {
+			int Count = 0;
+			do {
+				c = *(*in)++;
+				if (c == '\0')
+					return c;
+				if (c == '(')
+					Count++;
+				else if (c == ')')
+					Count--;
+			} while (Count > 0);
+			continue;
+		}
+
+		/* Try the next token in the word table first. */
+		/* This allows us to match "2nd", for example. */
+		{
+			const char *src = *in;
+			const struct LEXICON *tp;
+			unsigned i = 0;
+
+			/* Force to lowercase and strip '.' characters. */
+			while (*src != '\0'
+			    && (isalnum((unsigned char)*src) || *src == '.')
+			    && i < sizeof(buff)-1) {
+				if (*src != '.') {
+					if (isupper((unsigned char)*src))
+						buff[i++] = tolower((unsigned char)*src);
+					else
+						buff[i++] = *src;
+				}
+				src++;
+			}
+			buff[i] = '\0';
+
+			/*
+			 * Find the first match.  If the word can be
+			 * abbreviated, make sure we match at least
+			 * the minimum abbreviation.
+			 */
+			for (tp = TimeWords; tp->name; tp++) {
+				size_t abbrev = tp->abbrev;
+				if (abbrev == 0)
+					abbrev = strlen(tp->name);
+				if (strlen(buff) >= abbrev
+				    && strncmp(tp->name, buff, strlen(buff))
+				    	== 0) {
+					/* Skip over token. */
+					*in = src;
+					/* Return the match. */
+					*value = tp->value;
+					return tp->type;
+				}
+			}
+		}
+
+		/*
+		 * Not in the word table, maybe it's a number.  Note:
+		 * Because '-' and '+' have other special meanings, I
+		 * don't deal with signed numbers here.
+		 */
+		if (isdigit((unsigned char)(c = **in))) {
+			for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); )
+				*value = 10 * *value + c - '0';
+			(*in)--;
+			return (tUNUMBER);
+		}
+
+		return *(*in)++;
+	}
+}
+
+#define	TM_YEAR_ORIGIN 1900
+
+/* Yield A - B, measured in seconds.  */
+static long
+difftm (struct tm *a, struct tm *b)
+{
+	int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
+	int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
+	int days = (
+		/* difference in day of year */
+		a->tm_yday - b->tm_yday
+		/* + intervening leap days */
+		+  ((ay >> 2) - (by >> 2))
+		-  (ay/100 - by/100)
+		+  ((ay/100 >> 2) - (by/100 >> 2))
+		/* + difference in years * 365 */
+		+  (long)(ay-by) * 365
+		);
+	return (days * DAY + (a->tm_hour - b->tm_hour) * HOUR
+	    + (a->tm_min - b->tm_min) * MINUTE
+	    + (a->tm_sec - b->tm_sec));
+}
+
+/*
+ *
+ * The public function.
+ *
+ * TODO: tokens[] array should be dynamically sized.
+ */
+time_t
+__archive_get_date(time_t now, const char *p)
+{
+	struct token	tokens[256];
+	struct gdstate	_gds;
+	struct token	*lasttoken;
+	struct gdstate	*gds;
+	struct tm	local, *tm;
+	struct tm	gmt, *gmt_ptr;
+	time_t		Start;
+	time_t		tod;
+	long		tzone;
+#if defined(HAVE__LOCALTIME64_S) || defined(HAVE__GMTIME64_S)
+	errno_t		terr;
+	__time64_t	tmptime;
+#endif
+
+	/* Clear out the parsed token array. */
+	memset(tokens, 0, sizeof(tokens));
+	/* Initialize the parser state. */
+	memset(&_gds, 0, sizeof(_gds));
+	gds = &_gds;
+
+	/* Look up the current time. */
+#if defined(HAVE_LOCALTIME_R)
+	tm = localtime_r(&now, &local);
+#elif defined(HAVE__LOCALTIME64_S)
+	tmptime = now;
+	terr = _localtime64_s(&local, &tmptime);
+	if (terr)
+		tm = NULL;
+	else
+		tm = &local;
+#else
+	memset(&local, 0, sizeof(local));
+	tm = localtime(&now);
+#endif
+	if (tm == NULL)
+		return -1;
+#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE__LOCALTIME64_S)
+	local = *tm;
+#endif
+
+	/* Look up UTC if we can and use that to determine the current
+	 * timezone offset. */
+#if defined(HAVE_GMTIME_R)
+	gmt_ptr = gmtime_r(&now, &gmt);
+#elif defined(HAVE__GMTIME64_S)
+	tmptime = now;
+	terr = _gmtime64_s(&gmt, &tmptime);
+	if (terr)
+		gmt_ptr = NULL;
+	else
+		gmt_ptr = &gmt;
+#else
+	memset(&gmt, 0, sizeof(gmt));
+	gmt_ptr = gmtime(&now);
+	if (gmt_ptr != NULL) {
+		/* Copy, in case localtime and gmtime use the same buffer. */
+		gmt = *gmt_ptr;
+	}
+#endif
+	if (gmt_ptr != NULL)
+		tzone = difftm (&gmt, &local);
+	else
+		/* This system doesn't understand timezones; fake it. */
+		tzone = 0;
+	if(local.tm_isdst)
+		tzone += HOUR;
+
+	/* Tokenize the input string. */
+	lasttoken = tokens;
+	while ((lasttoken->token = nexttoken(&p, &lasttoken->value)) != 0) {
+		++lasttoken;
+		if (lasttoken > tokens + 255)
+			return -1;
+	}
+	gds->tokenp = tokens;
+
+	/* Match phrases until we run out of input tokens. */
+	while (gds->tokenp < lasttoken) {
+		if (!phrase(gds))
+			return -1;
+	}
+
+	/* Use current local timezone if none was specified. */
+	if (!gds->HaveZone) {
+		gds->Timezone = tzone;
+		gds->DSTmode = DSTmaybe;
+	}
+
+	/* If a timezone was specified, use that for generating the default
+	 * time components instead of the local timezone. */
+	if (gds->HaveZone && gmt_ptr != NULL) {
+		now -= gds->Timezone;
+#if defined(HAVE_GMTIME_R)
+		gmt_ptr = gmtime_r(&now, &gmt);
+#elif defined(HAVE__GMTIME64_S)
+		tmptime = now;
+		terr = _gmtime64_s(&gmt, &tmptime);
+		if (terr)
+			gmt_ptr = NULL;
+		else
+			gmt_ptr = &gmt;
+#else
+		gmt_ptr = gmtime(&now);
+#endif
+		if (gmt_ptr != NULL)
+			local = *gmt_ptr;
+		now += gds->Timezone;
+	}
+
+	if (!gds->HaveYear)
+		gds->Year = local.tm_year + 1900;
+	if (!gds->HaveMonth)
+		gds->Month = local.tm_mon + 1;
+	if (!gds->HaveDay)
+		gds->Day = local.tm_mday;
+	/* Note: No default for hour/min/sec; a specifier that just
+	 * gives date always refers to 00:00 on that date. */
+
+	/* If we saw more than one time, timezone, weekday, year, month,
+	 * or day, then give up. */
+	if (gds->HaveTime > 1 || gds->HaveZone > 1 || gds->HaveWeekDay > 1
+	    || gds->HaveYear > 1 || gds->HaveMonth > 1 || gds->HaveDay > 1)
+		return -1;
+
+	/* Compute an absolute time based on whatever absolute information
+	 * we collected. */
+	if (gds->HaveYear || gds->HaveMonth || gds->HaveDay
+	    || gds->HaveTime || gds->HaveWeekDay) {
+		Start = Convert(gds->Month, gds->Day, gds->Year,
+		    gds->Hour, gds->Minutes, gds->Seconds,
+		    gds->Timezone, gds->DSTmode);
+		if (Start < 0)
+			return -1;
+	} else {
+		Start = now;
+		if (!gds->HaveRel)
+			Start -= local.tm_hour * HOUR + local.tm_min * MINUTE
+			    + local.tm_sec;
+	}
+
+	/* Add the relative offset. */
+	Start += gds->RelSeconds;
+	Start += RelativeMonth(Start, gds->Timezone, gds->RelMonth);
+
+	/* Adjust for day-of-week offsets. */
+	if (gds->HaveWeekDay
+	    && !(gds->HaveYear || gds->HaveMonth || gds->HaveDay)) {
+		tod = RelativeDate(Start, gds->Timezone,
+		    gds->DSTmode, gds->DayOrdinal, gds->DayNumber);
+		Start += tod;
+	}
+
+	/* -1 is an error indicator, so return 0 instead of -1 if
+	 * that's the actual time. */
+	return Start == -1 ? 0 : Start;
+}
+
+
+#if	defined(TEST)
+
+/* ARGSUSED */
+int
+main(int argc, char **argv)
+{
+    time_t	d;
+    time_t	now = time(NULL);
+
+    while (*++argv != NULL) {
+	    (void)printf("Input: %s\n", *argv);
+	    d = get_date(now, *argv);
+	    if (d == -1)
+		    (void)printf("Bad format - couldn't convert.\n");
+	    else
+		    (void)printf("Output: %s\n", ctime(&d));
+    }
+    exit(0);
+    /* NOTREACHED */
+}
+#endif	/* defined(TEST) */
diff --git a/c/archive_getdate.h b/c/archive_getdate.h
new file mode 100644
--- /dev/null
+++ b/c/archive_getdate.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2003-2015 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_GETDATE_H_INCLUDED
+#define ARCHIVE_GETDATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <time.h>
+
+time_t __archive_get_date(time_t now, const char *);
+
+#endif
diff --git a/c/archive_hmac.c b/c/archive_hmac.c
new file mode 100644
--- /dev/null
+++ b/c/archive_hmac.c
@@ -0,0 +1,305 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* 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"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include "archive.h"
+#include "archive_hmac_private.h"
+
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * the archive_hmac.c file is expected to define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist.  This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_hmac_build_hack(void) {
+	return 0;
+}
+
+
+#ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+	CCHmacInit(ctx, kCCHmacAlgSHA1, key, key_len);
+	return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+    size_t data_len)
+{
+	CCHmacUpdate(ctx, data, data_len);
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+	CCHmacFinal(ctx, out);
+	*out_len = 20;
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+	memset(ctx, 0, sizeof(*ctx));
+}
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+
+#ifndef BCRYPT_HASH_REUSABLE_FLAG
+# define BCRYPT_HASH_REUSABLE_FLAG 0x00000020
+#endif
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+	BCRYPT_ALG_HANDLE hAlg;
+	BCRYPT_HASH_HANDLE hHash;
+	DWORD hash_len;
+	PBYTE hash;
+	ULONG result;
+	NTSTATUS status;
+
+	ctx->hAlg = NULL;
+	status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM,
+		MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG);
+	if (!BCRYPT_SUCCESS(status))
+		return -1;
+	status = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PUCHAR)&hash_len,
+		sizeof(hash_len), &result, 0);
+	if (!BCRYPT_SUCCESS(status)) {
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+		return -1;
+	}
+	hash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hash_len);
+	if (hash == NULL) {
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+		return -1;
+	}
+	status = BCryptCreateHash(hAlg, &hHash, NULL, 0,
+		(PUCHAR)key, (ULONG)key_len, BCRYPT_HASH_REUSABLE_FLAG);
+	if (!BCRYPT_SUCCESS(status)) {
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+		HeapFree(GetProcessHeap(), 0, hash);
+		return -1;
+	}
+
+	ctx->hAlg = hAlg;
+	ctx->hHash = hHash;
+	ctx->hash_len = hash_len;
+	ctx->hash = hash;
+
+	return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+	size_t data_len)
+{
+	BCryptHashData(ctx->hHash, (PUCHAR)(uintptr_t)data, (ULONG)data_len, 0);
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+	BCryptFinishHash(ctx->hHash, ctx->hash, ctx->hash_len, 0);
+	if (ctx->hash_len == *out_len)
+		memcpy(out, ctx->hash, *out_len);
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+	if (ctx->hAlg != NULL) {
+		BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+		HeapFree(GetProcessHeap(), 0, ctx->hash);
+		ctx->hAlg = NULL;
+	}
+}
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H)
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+        const mbedtls_md_info_t *info;
+        int ret;
+
+        mbedtls_md_init(ctx);
+        info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
+        if (info == NULL) {
+                mbedtls_md_free(ctx);
+                return (-1);
+        }
+        ret = mbedtls_md_setup(ctx, info, 1);
+        if (ret != 0) {
+                mbedtls_md_free(ctx);
+                return (-1);
+        }
+	ret = mbedtls_md_hmac_starts(ctx, key, key_len);
+	if (ret != 0) {
+		mbedtls_md_free(ctx);
+		return (-1);
+	}
+	return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+    size_t data_len)
+{
+	mbedtls_md_hmac_update(ctx, data, data_len);
+}
+
+static void __hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+	(void)out_len;	/* UNUSED */
+
+	mbedtls_md_hmac_finish(ctx, out);
+}
+
+static void __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+	mbedtls_md_free(ctx);
+	memset(ctx, 0, sizeof(*ctx));
+}
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+	hmac_sha1_set_key(ctx, key_len, key);
+	return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+    size_t data_len)
+{
+	hmac_sha1_update(ctx, data_len, data);
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+	hmac_sha1_digest(ctx, (unsigned)*out_len, out);
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+	memset(ctx, 0, sizeof(*ctx));
+}
+
+#elif defined(HAVE_LIBCRYPTO)
+
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+	*ctx = HMAC_CTX_new();
+	if (*ctx == NULL)
+		return -1;
+	HMAC_Init_ex(*ctx, key, key_len, EVP_sha1(), NULL);
+	return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+    size_t data_len)
+{
+	HMAC_Update(*ctx, data, data_len);
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+	unsigned int len = (unsigned int)*out_len;
+
+	HMAC_Final(*ctx, out, &len);
+	*out_len = len;
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+	HMAC_CTX_free(*ctx);
+	*ctx = NULL;
+}
+
+#else
+
+/* Stub */
+static int
+__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len)
+{
+	(void)ctx;/* UNUSED */
+	(void)key;/* UNUSED */
+	(void)key_len;/* UNUSED */
+	return -1;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+    size_t data_len)
+{
+	(void)ctx;/* UNUSED */
+	(void)data;/* UNUSED */
+	(void)data_len;/* UNUSED */
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+	(void)ctx;/* UNUSED */
+	(void)out;/* UNUSED */
+	(void)out_len;/* UNUSED */
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+	(void)ctx;/* UNUSED */
+}
+
+#endif
+
+const struct archive_hmac __archive_hmac = {
+	&__hmac_sha1_init,
+	&__hmac_sha1_update,
+	&__hmac_sha1_final,
+	&__hmac_sha1_cleanup,
+};
diff --git a/c/archive_hmac_private.h b/c/archive_hmac_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_hmac_private.h
@@ -0,0 +1,110 @@
+/*-
+* Copyright (c) 2014 Michihiro NAKAJIMA
+* 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.
+*/
+
+#ifndef ARCHIVE_HMAC_PRIVATE_H_INCLUDED
+#define ARCHIVE_HMAC_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+/*
+ * On systems that do not support any recognized crypto libraries,
+ * the archive_hmac.c file is expected to define no usable symbols.
+ *
+ * But some compilers and linkers choke on empty object files, so
+ * define a public symbol that will always exist.  This could
+ * be removed someday if this file gains another always-present
+ * symbol definition.
+ */
+int __libarchive_hmac_build_hack(void);
+
+#ifdef __APPLE__
+# include <AvailabilityMacros.h>
+# if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+#  define ARCHIVE_HMAC_USE_Apple_CommonCrypto
+# endif
+#endif
+
+#ifdef ARCHIVE_HMAC_USE_Apple_CommonCrypto
+#include <CommonCrypto/CommonHMAC.h>
+
+typedef	CCHmacContext archive_hmac_sha1_ctx;
+
+#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H)
+#include <bcrypt.h>
+
+typedef struct {
+	BCRYPT_ALG_HANDLE	hAlg;
+	BCRYPT_HASH_HANDLE	hHash;
+	DWORD				hash_len;
+	PBYTE				hash;
+
+} archive_hmac_sha1_ctx;
+
+#elif defined(HAVE_LIBMBEDCRYPTO) && defined(HAVE_MBEDTLS_MD_H)
+#include <mbedtls/md.h>
+
+typedef mbedtls_md_context_t archive_hmac_sha1_ctx;
+
+#elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_HMAC_H)
+#include <nettle/hmac.h>
+
+typedef	struct hmac_sha1_ctx archive_hmac_sha1_ctx;
+
+#elif defined(HAVE_LIBCRYPTO)
+#include "archive_openssl_hmac_private.h"
+
+typedef	HMAC_CTX* archive_hmac_sha1_ctx;
+
+#else
+
+typedef int archive_hmac_sha1_ctx;
+
+#endif
+
+
+/* HMAC */
+#define archive_hmac_sha1_init(ctx, key, key_len)\
+	__archive_hmac.__hmac_sha1_init(ctx, key, key_len)
+#define archive_hmac_sha1_update(ctx, data, data_len)\
+	__archive_hmac.__hmac_sha1_update(ctx, data, data_len)
+#define archive_hmac_sha1_final(ctx, out, out_len)\
+  	__archive_hmac.__hmac_sha1_final(ctx, out, out_len)
+#define archive_hmac_sha1_cleanup(ctx)\
+	__archive_hmac.__hmac_sha1_cleanup(ctx)
+
+
+struct archive_hmac {
+	/* HMAC */
+	int (*__hmac_sha1_init)(archive_hmac_sha1_ctx *, const uint8_t *,
+		size_t);
+	void (*__hmac_sha1_update)(archive_hmac_sha1_ctx *, const uint8_t *,
+		size_t);
+	void (*__hmac_sha1_final)(archive_hmac_sha1_ctx *, uint8_t *, size_t *);
+	void (*__hmac_sha1_cleanup)(archive_hmac_sha1_ctx *);
+};
+
+extern const struct archive_hmac __archive_hmac;
+#endif /* ARCHIVE_HMAC_PRIVATE_H_INCLUDED */
diff --git a/c/archive_match.c b/c/archive_match.c
new file mode 100644
--- /dev/null
+++ b/c/archive_match.c
@@ -0,0 +1,1875 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * 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$");
+
+#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_entry.h"
+#include "archive_getdate.h"
+#include "archive_pathmatch.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+
+struct match {
+	struct match		*next;
+	int			 matches;
+	struct archive_mstring	 pattern;
+};
+
+struct match_list {
+	struct match		*first;
+	struct match		**last;
+	int			 count;
+	int			 unmatched_count;
+	struct match		*unmatched_next;
+	int			 unmatched_eof;
+};
+
+struct match_file {
+	struct archive_rb_node	 node;
+	struct match_file	*next;
+	struct archive_mstring	 pathname;
+	int			 flag;
+	time_t			 mtime_sec;
+	long			 mtime_nsec;
+	time_t			 ctime_sec;
+	long			 ctime_nsec;
+};
+
+struct entry_list {
+	struct match_file	*first;
+	struct match_file	**last;
+	int			 count;
+};
+
+struct id_array {
+	size_t			 size;/* Allocated size */
+	size_t			 count;
+	int64_t			*ids;
+};
+
+#define PATTERN_IS_SET		1
+#define TIME_IS_SET		2
+#define ID_IS_SET		4
+
+struct archive_match {
+	struct archive		 archive;
+
+	/* exclusion/inclusion set flag. */
+	int			 setflag;
+
+	/* Recursively include directory content? */
+	int			 recursive_include;
+
+	/*
+	 * Matching filename patterns.
+	 */
+	struct match_list	 exclusions;
+	struct match_list	 inclusions;
+
+	/*
+	 * Matching time stamps.
+	 */
+	time_t			 now;
+	int			 newer_mtime_filter;
+	time_t			 newer_mtime_sec;
+	long			 newer_mtime_nsec;
+	int			 newer_ctime_filter;
+	time_t			 newer_ctime_sec;
+	long			 newer_ctime_nsec;
+	int			 older_mtime_filter;
+	time_t			 older_mtime_sec;
+	long			 older_mtime_nsec;
+	int			 older_ctime_filter;
+	time_t			 older_ctime_sec;
+	long			 older_ctime_nsec;
+	/*
+	 * Matching time stamps with its filename.
+	 */
+	struct archive_rb_tree	 exclusion_tree;
+	struct entry_list 	 exclusion_entry_list;
+
+	/*
+	 * Matching file owners.
+	 */
+	struct id_array 	 inclusion_uids;
+	struct id_array 	 inclusion_gids;
+	struct match_list	 inclusion_unames;
+	struct match_list	 inclusion_gnames;
+};
+
+static int	add_pattern_from_file(struct archive_match *,
+		    struct match_list *, int, const void *, int);
+static int	add_entry(struct archive_match *, int,
+		    struct archive_entry *);
+static int	add_owner_id(struct archive_match *, struct id_array *,
+		    int64_t);
+static int	add_owner_name(struct archive_match *, struct match_list *,
+		    int, const void *);
+static int	add_pattern_mbs(struct archive_match *, struct match_list *,
+		    const char *);
+static int	add_pattern_wcs(struct archive_match *, struct match_list *,
+		    const wchar_t *);
+static int	cmp_key_mbs(const struct archive_rb_node *, const void *);
+static int	cmp_key_wcs(const struct archive_rb_node *, const void *);
+static int	cmp_node_mbs(const struct archive_rb_node *,
+		    const struct archive_rb_node *);
+static int	cmp_node_wcs(const struct archive_rb_node *,
+		    const struct archive_rb_node *);
+static void	entry_list_add(struct entry_list *, struct match_file *);
+static void	entry_list_free(struct entry_list *);
+static void	entry_list_init(struct entry_list *);
+static int	error_nomem(struct archive_match *);
+static void	match_list_add(struct match_list *, struct match *);
+static void	match_list_free(struct match_list *);
+static void	match_list_init(struct match_list *);
+static int	match_list_unmatched_inclusions_next(struct archive_match *,
+		    struct match_list *, int, const void **);
+static int	match_owner_id(struct id_array *, int64_t);
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int	match_owner_name_mbs(struct archive_match *,
+		    struct match_list *, const char *);
+#else
+static int	match_owner_name_wcs(struct archive_match *,
+		    struct match_list *, const wchar_t *);
+#endif
+static int	match_path_exclusion(struct archive_match *,
+		    struct match *, int, const void *);
+static int	match_path_inclusion(struct archive_match *,
+		    struct match *, int, const void *);
+static int	owner_excluded(struct archive_match *,
+		    struct archive_entry *);
+static int	path_excluded(struct archive_match *, int, const void *);
+static int	set_timefilter(struct archive_match *, int, time_t, long,
+		    time_t, long);
+static int	set_timefilter_pathname_mbs(struct archive_match *,
+		    int, const char *);
+static int	set_timefilter_pathname_wcs(struct archive_match *,
+		    int, const wchar_t *);
+static int	set_timefilter_date(struct archive_match *, int, const char *);
+static int	set_timefilter_date_w(struct archive_match *, int,
+		    const wchar_t *);
+static int	time_excluded(struct archive_match *,
+		    struct archive_entry *);
+static int	validate_time_flag(struct archive *, int, const char *);
+
+#define get_date __archive_get_date
+
+static const struct archive_rb_tree_ops rb_ops_mbs = {
+	cmp_node_mbs, cmp_key_mbs
+};
+
+static const struct archive_rb_tree_ops rb_ops_wcs = {
+	cmp_node_wcs, cmp_key_wcs
+};
+
+/*
+ * The matching logic here needs to be re-thought.  I started out to
+ * try to mimic gtar's matching logic, but it's not entirely
+ * consistent.  In particular 'tar -t' and 'tar -x' interpret patterns
+ * on the command line as anchored, but --exclude doesn't.
+ */
+
+static int
+error_nomem(struct archive_match *a)
+{
+	archive_set_error(&(a->archive), ENOMEM, "No memory");
+	a->archive.state = ARCHIVE_STATE_FATAL;
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Create an ARCHIVE_MATCH object.
+ */
+struct archive *
+archive_match_new(void)
+{
+	struct archive_match *a;
+
+	a = (struct archive_match *)calloc(1, sizeof(*a));
+	if (a == NULL)
+		return (NULL);
+	a->archive.magic = ARCHIVE_MATCH_MAGIC;
+	a->archive.state = ARCHIVE_STATE_NEW;
+	a->recursive_include = 1;
+	match_list_init(&(a->inclusions));
+	match_list_init(&(a->exclusions));
+	__archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs);
+	entry_list_init(&(a->exclusion_entry_list));
+	match_list_init(&(a->inclusion_unames));
+	match_list_init(&(a->inclusion_gnames));
+	time(&a->now);
+	return (&(a->archive));
+}
+
+/*
+ * Free an ARCHIVE_MATCH object.
+ */
+int
+archive_match_free(struct archive *_a)
+{
+	struct archive_match *a;
+
+	if (_a == NULL)
+		return (ARCHIVE_OK);
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_match_free");
+	a = (struct archive_match *)_a;
+	match_list_free(&(a->inclusions));
+	match_list_free(&(a->exclusions));
+	entry_list_free(&(a->exclusion_entry_list));
+	free(a->inclusion_uids.ids);
+	free(a->inclusion_gids.ids);
+	match_list_free(&(a->inclusion_unames));
+	match_list_free(&(a->inclusion_gnames));
+	free(a);
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Convenience function to perform all exclusion tests.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_excluded(struct archive *_a, struct archive_entry *entry)
+{
+	struct archive_match *a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_excluded_ae");
+
+	a = (struct archive_match *)_a;
+	if (entry == NULL) {
+		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+		return (ARCHIVE_FAILED);
+	}
+
+	r = 0;
+	if (a->setflag & PATTERN_IS_SET) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		r = path_excluded(a, 0, archive_entry_pathname_w(entry));
+#else
+		r = path_excluded(a, 1, archive_entry_pathname(entry));
+#endif
+		if (r != 0)
+			return (r);
+	}
+
+	if (a->setflag & TIME_IS_SET) {
+		r = time_excluded(a, entry);
+		if (r != 0)
+			return (r);
+	}
+
+	if (a->setflag & ID_IS_SET)
+		r = owner_excluded(a, entry);
+	return (r);
+}
+
+/*
+ * Utility functions to manage exclusion/inclusion patterns
+ */
+
+int
+archive_match_exclude_pattern(struct archive *_a, const char *pattern)
+{
+	struct archive_match *a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern");
+	a = (struct archive_match *)_a;
+
+	if (pattern == NULL || *pattern == '\0') {
+		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+		return (ARCHIVE_FAILED);
+	}
+	if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
+		return (r);
+	return (ARCHIVE_OK);
+}
+
+int
+archive_match_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)
+{
+	struct archive_match *a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_w");
+	a = (struct archive_match *)_a;
+
+	if (pattern == NULL || *pattern == L'\0') {
+		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+		return (ARCHIVE_FAILED);
+	}
+	if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
+		return (r);
+	return (ARCHIVE_OK);
+}
+
+int
+archive_match_exclude_pattern_from_file(struct archive *_a,
+    const char *pathname, int nullSeparator)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file");
+	a = (struct archive_match *)_a;
+
+	return add_pattern_from_file(a, &(a->exclusions), 1, pathname,
+		nullSeparator);
+}
+
+int
+archive_match_exclude_pattern_from_file_w(struct archive *_a,
+    const wchar_t *pathname, int nullSeparator)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file_w");
+	a = (struct archive_match *)_a;
+
+	return add_pattern_from_file(a, &(a->exclusions), 0, pathname,
+		nullSeparator);
+}
+
+int
+archive_match_include_pattern(struct archive *_a, const char *pattern)
+{
+	struct archive_match *a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_include_pattern");
+	a = (struct archive_match *)_a;
+
+	if (pattern == NULL || *pattern == '\0') {
+		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+		return (ARCHIVE_FAILED);
+	}
+	if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
+		return (r);
+	return (ARCHIVE_OK);
+}
+
+int
+archive_match_include_pattern_w(struct archive *_a, const wchar_t *pattern)
+{
+	struct archive_match *a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_w");
+	a = (struct archive_match *)_a;
+
+	if (pattern == NULL || *pattern == L'\0') {
+		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
+		return (ARCHIVE_FAILED);
+	}
+	if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
+		return (r);
+	return (ARCHIVE_OK);
+}
+
+int
+archive_match_include_pattern_from_file(struct archive *_a,
+    const char *pathname, int nullSeparator)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file");
+	a = (struct archive_match *)_a;
+
+	return add_pattern_from_file(a, &(a->inclusions), 1, pathname,
+		nullSeparator);
+}
+
+int
+archive_match_include_pattern_from_file_w(struct archive *_a,
+    const wchar_t *pathname, int nullSeparator)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file_w");
+	a = (struct archive_match *)_a;
+
+	return add_pattern_from_file(a, &(a->inclusions), 0, pathname,
+		nullSeparator);
+}
+
+/*
+ * Test functions for pathname patterns.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_path_excluded(struct archive *_a,
+    struct archive_entry *entry)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_path_excluded");
+
+	a = (struct archive_match *)_a;
+	if (entry == NULL) {
+		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* If we don't have exclusion/inclusion pattern set at all,
+	 * the entry is always not excluded. */
+	if ((a->setflag & PATTERN_IS_SET) == 0)
+		return (0);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	return (path_excluded(a, 0, archive_entry_pathname_w(entry)));
+#else
+	return (path_excluded(a, 1, archive_entry_pathname(entry)));
+#endif
+}
+
+/*
+ * When recursive inclusion of directory content is enabled,
+ * an inclusion pattern that matches a directory will also
+ * include everything beneath that directory. Enabled by default.
+ *
+ * For compatibility with GNU tar, exclusion patterns always
+ * match if a subset of the full patch matches (i.e., they are
+ * are not rooted at the beginning of the path) and thus there
+ * is no corresponding non-recursive exclusion mode.
+ */
+int
+archive_match_set_inclusion_recursion(struct archive *_a, int enabled)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion");
+	a = (struct archive_match *)_a;
+	a->recursive_include = enabled;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Utility functions to get statistic information for inclusion patterns.
+ */
+int
+archive_match_path_unmatched_inclusions(struct archive *_a)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions");
+	a = (struct archive_match *)_a;
+
+	return (a->inclusions.unmatched_count);
+}
+
+int
+archive_match_path_unmatched_inclusions_next(struct archive *_a,
+    const char **_p)
+{
+	struct archive_match *a;
+	const void *v;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next");
+	a = (struct archive_match *)_a;
+
+	r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);
+	*_p = (const char *)v;
+	return (r);
+}
+
+int
+archive_match_path_unmatched_inclusions_next_w(struct archive *_a,
+    const wchar_t **_p)
+{
+	struct archive_match *a;
+	const void *v;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next_w");
+	a = (struct archive_match *)_a;
+
+	r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);
+	*_p = (const wchar_t *)v;
+	return (r);
+}
+
+/*
+ * Add inclusion/exclusion patterns.
+ */
+static int
+add_pattern_mbs(struct archive_match *a, struct match_list *list,
+    const char *pattern)
+{
+	struct match *match;
+	size_t len;
+
+	match = calloc(1, sizeof(*match));
+	if (match == NULL)
+		return (error_nomem(a));
+	/* Both "foo/" and "foo" should match "foo/bar". */
+	len = strlen(pattern);
+	if (len && pattern[len - 1] == '/')
+		--len;
+	archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);
+	match_list_add(list, match);
+	a->setflag |= PATTERN_IS_SET;
+	return (ARCHIVE_OK);
+}
+
+static int
+add_pattern_wcs(struct archive_match *a, struct match_list *list,
+    const wchar_t *pattern)
+{
+	struct match *match;
+	size_t len;
+
+	match = calloc(1, sizeof(*match));
+	if (match == NULL)
+		return (error_nomem(a));
+	/* Both "foo/" and "foo" should match "foo/bar". */
+	len = wcslen(pattern);
+	if (len && pattern[len - 1] == L'/')
+		--len;
+	archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);
+	match_list_add(list, match);
+	a->setflag |= PATTERN_IS_SET;
+	return (ARCHIVE_OK);
+}
+
+static int
+add_pattern_from_file(struct archive_match *a, struct match_list *mlist,
+    int mbs, const void *pathname, int nullSeparator)
+{
+	struct archive *ar;
+	struct archive_entry *ae;
+	struct archive_string as;
+	const void *buff;
+	size_t size;
+	int64_t offset;
+	int r;
+
+	ar = archive_read_new(); 
+	if (ar == NULL) {
+		archive_set_error(&(a->archive), ENOMEM, "No memory");
+		return (ARCHIVE_FATAL);
+	}
+	r = archive_read_support_format_raw(ar);
+	r = archive_read_support_format_empty(ar);
+	if (r != ARCHIVE_OK) {
+		archive_copy_error(&(a->archive), ar);
+		archive_read_free(ar);
+		return (r);
+	}
+	if (mbs)
+		r = archive_read_open_filename(ar, pathname, 512*20);
+	else
+		r = archive_read_open_filename_w(ar, pathname, 512*20);
+	if (r != ARCHIVE_OK) {
+		archive_copy_error(&(a->archive), ar);
+		archive_read_free(ar);
+		return (r);
+	}
+	r = archive_read_next_header(ar, &ae);
+	if (r != ARCHIVE_OK) {
+		archive_read_free(ar);
+		if (r == ARCHIVE_EOF) {
+			return (ARCHIVE_OK);
+		} else {
+			archive_copy_error(&(a->archive), ar);
+			return (r);
+		}
+	}
+
+	archive_string_init(&as);
+
+	while ((r = archive_read_data_block(ar, &buff, &size, &offset))
+	    == ARCHIVE_OK) {
+		const char *b = (const char *)buff;
+
+		while (size) {
+			const char *s = (const char *)b;
+			size_t length = 0;
+			int found_separator = 0;
+
+			while (length < size) {
+				if (nullSeparator) {
+					if (*b == '\0') {
+						found_separator = 1;
+						break;
+					}
+				} else {
+			            	if (*b == 0x0d || *b == 0x0a) {
+						found_separator = 1;
+						break;
+					}
+				}
+				b++;
+				length++;
+			}
+			if (!found_separator) {
+				archive_strncat(&as, s, length);
+				/* Read next data block. */
+				break;
+			}
+			b++;
+			size -= length + 1;
+			archive_strncat(&as, s, length);
+
+			/* If the line is not empty, add the pattern. */
+			if (archive_strlen(&as) > 0) {
+				/* Add pattern. */
+				r = add_pattern_mbs(a, mlist, as.s);
+				if (r != ARCHIVE_OK) {
+					archive_read_free(ar);
+					archive_string_free(&as);
+					return (r);
+				}
+				archive_string_empty(&as);
+			}
+		}
+	}
+
+	/* If an error occurred, report it immediately. */
+	if (r < ARCHIVE_OK) {
+		archive_copy_error(&(a->archive), ar);
+		archive_read_free(ar);
+		archive_string_free(&as);
+		return (r);
+	}
+
+	/* If the line is not empty, add the pattern. */
+	if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {
+		/* Add pattern. */
+		r = add_pattern_mbs(a, mlist, as.s);
+		if (r != ARCHIVE_OK) {
+			archive_read_free(ar);
+			archive_string_free(&as);
+			return (r);
+		}
+	}
+	archive_read_free(ar);
+	archive_string_free(&as);
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Test if pathname is excluded by inclusion/exclusion patterns.
+ */
+static int
+path_excluded(struct archive_match *a, int mbs, const void *pathname)
+{
+	struct match *match;
+	struct match *matched;
+	int r;
+
+	if (a == NULL)
+		return (0);
+
+	/* Mark off any unmatched inclusions. */
+	/* In particular, if a filename does appear in the archive and
+	 * is explicitly included and excluded, then we don't report
+	 * it as missing even though we don't extract it.
+	 */
+	matched = NULL;
+	for (match = a->inclusions.first; match != NULL;
+	    match = match->next){
+		if (match->matches == 0 &&
+		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
+			if (r < 0)
+				return (r);
+			a->inclusions.unmatched_count--;
+			match->matches++;
+			matched = match;
+		}
+	}
+
+	/* Exclusions take priority */
+	for (match = a->exclusions.first; match != NULL;
+	    match = match->next){
+		r = match_path_exclusion(a, match, mbs, pathname);
+		if (r)
+			return (r);
+	}
+
+	/* It's not excluded and we found an inclusion above, so it's
+	 * included. */
+	if (matched != NULL)
+		return (0);
+
+
+	/* We didn't find an unmatched inclusion, check the remaining ones. */
+	for (match = a->inclusions.first; match != NULL;
+	    match = match->next){
+		/* We looked at previously-unmatched inclusions already. */
+		if (match->matches > 0 &&
+		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
+			if (r < 0)
+				return (r);
+			match->matches++;
+			return (0);
+		}
+	}
+
+	/* If there were inclusions, default is to exclude. */
+	if (a->inclusions.first != NULL)
+	    return (1);
+
+	/* No explicit inclusions, default is to match. */
+	return (0);
+}
+
+/*
+ * This is a little odd, but it matches the default behavior of
+ * gtar.  In particular, 'a*b' will match 'foo/a1111/222b/bar'
+ *
+ */
+static int
+match_path_exclusion(struct archive_match *a, struct match *m,
+    int mbs, const void *pn)
+{
+	int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;
+	int r;
+
+	if (mbs) {
+		const char *p;
+		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
+		if (r == 0)
+			return (archive_pathmatch(p, (const char *)pn, flag));
+	} else {
+		const wchar_t *p;
+		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
+		if (r == 0)
+			return (archive_pathmatch_w(p, (const wchar_t *)pn,
+				flag));
+	}
+	if (errno == ENOMEM)
+		return (error_nomem(a));
+	return (0);
+}
+
+/*
+ * Again, mimic gtar:  inclusions are always anchored (have to match
+ * the beginning of the path) even though exclusions are not anchored.
+ */
+static int
+match_path_inclusion(struct archive_match *a, struct match *m,
+    int mbs, const void *pn)
+{
+	/* Recursive operation requires only a prefix match. */
+	int flag = a->recursive_include ?
+		PATHMATCH_NO_ANCHOR_END :
+		0;
+	int r;
+
+	if (mbs) {
+		const char *p;
+		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
+		if (r == 0)
+			return (archive_pathmatch(p, (const char *)pn, flag));
+	} else {
+		const wchar_t *p;
+		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
+		if (r == 0)
+			return (archive_pathmatch_w(p, (const wchar_t *)pn,
+				flag));
+	}
+	if (errno == ENOMEM)
+		return (error_nomem(a));
+	return (0);
+}
+
+static void
+match_list_init(struct match_list *list)
+{
+	list->first = NULL;
+	list->last = &(list->first);
+	list->count = 0;
+}
+
+static void
+match_list_free(struct match_list *list)
+{
+	struct match *p, *q;
+
+	for (p = list->first; p != NULL; ) {
+		q = p;
+		p = p->next;
+		archive_mstring_clean(&(q->pattern));
+		free(q);
+	}
+}
+
+static void
+match_list_add(struct match_list *list, struct match *m)
+{
+	*list->last = m;
+	list->last = &(m->next);
+	list->count++;
+	list->unmatched_count++;
+}
+
+static int
+match_list_unmatched_inclusions_next(struct archive_match *a,
+    struct match_list *list, int mbs, const void **vp)
+{
+	struct match *m;
+
+	*vp = NULL;
+	if (list->unmatched_eof) {
+		list->unmatched_eof = 0;
+		return (ARCHIVE_EOF);
+	}
+	if (list->unmatched_next == NULL) {
+		if (list->unmatched_count == 0)
+			return (ARCHIVE_EOF);
+		list->unmatched_next = list->first;
+	}
+
+	for (m = list->unmatched_next; m != NULL; m = m->next) {
+		int r;
+
+		if (m->matches)
+			continue;
+		if (mbs) {
+			const char *p;
+			r = archive_mstring_get_mbs(&(a->archive),
+				&(m->pattern), &p);
+			if (r < 0 && errno == ENOMEM)
+				return (error_nomem(a));
+			if (p == NULL)
+				p = "";
+			*vp = p;
+		} else {
+			const wchar_t *p;
+			r = archive_mstring_get_wcs(&(a->archive),
+				&(m->pattern), &p);
+			if (r < 0 && errno == ENOMEM)
+				return (error_nomem(a));
+			if (p == NULL)
+				p = L"";
+			*vp = p;
+		}
+		list->unmatched_next = m->next;
+		if (list->unmatched_next == NULL)
+			/* To return EOF next time. */
+			list->unmatched_eof = 1;
+		return (ARCHIVE_OK);
+	}
+	list->unmatched_next = NULL;
+	return (ARCHIVE_EOF);
+}
+
+/*
+ * Utility functions to manage inclusion timestamps.
+ */
+int
+archive_match_include_time(struct archive *_a, int flag, time_t sec,
+    long nsec)
+{
+	int r;
+
+	r = validate_time_flag(_a, flag, "archive_match_include_time");
+	if (r != ARCHIVE_OK)
+		return (r);
+	return set_timefilter((struct archive_match *)_a, flag,
+			sec, nsec, sec, nsec);
+}
+
+int
+archive_match_include_date(struct archive *_a, int flag,
+    const char *datestr)
+{
+	int r;
+
+	r = validate_time_flag(_a, flag, "archive_match_include_date");
+	if (r != ARCHIVE_OK)
+		return (r);
+	return set_timefilter_date((struct archive_match *)_a, flag, datestr);
+}
+
+int
+archive_match_include_date_w(struct archive *_a, int flag,
+    const wchar_t *datestr)
+{
+	int r;
+
+	r = validate_time_flag(_a, flag, "archive_match_include_date_w");
+	if (r != ARCHIVE_OK)
+		return (r);
+
+	return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);
+}
+
+int
+archive_match_include_file_time(struct archive *_a, int flag,
+    const char *pathname)
+{
+	int r;
+
+	r = validate_time_flag(_a, flag, "archive_match_include_file_time");
+	if (r != ARCHIVE_OK)
+		return (r);
+	return set_timefilter_pathname_mbs((struct archive_match *)_a,
+			flag, pathname);
+}
+
+int
+archive_match_include_file_time_w(struct archive *_a, int flag,
+    const wchar_t *pathname)
+{
+	int r;
+
+	r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");
+	if (r != ARCHIVE_OK)
+		return (r);
+	return set_timefilter_pathname_wcs((struct archive_match *)_a,
+			flag, pathname);
+}
+
+int
+archive_match_exclude_entry(struct archive *_a, int flag,
+    struct archive_entry *entry)
+{
+	struct archive_match *a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_time_include_entry");
+	a = (struct archive_match *)_a;
+
+	if (entry == NULL) {
+		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+		return (ARCHIVE_FAILED);
+	}
+	r = validate_time_flag(_a, flag, "archive_match_exclude_entry");
+	if (r != ARCHIVE_OK)
+		return (r);
+	return (add_entry(a, flag, entry));
+}
+
+/*
+ * Test function for time stamps.
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_time_excluded(struct archive *_a,
+    struct archive_entry *entry)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");
+
+	a = (struct archive_match *)_a;
+	if (entry == NULL) {
+		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* If we don't have inclusion time set at all, the entry is always
+	 * not excluded. */
+	if ((a->setflag & TIME_IS_SET) == 0)
+		return (0);
+	return (time_excluded(a, entry));
+}
+
+static int
+validate_time_flag(struct archive *_a, int flag, const char *_fn)
+{
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, _fn);
+
+	/* Check a type of time. */
+	if (flag &
+	   ((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {
+		archive_set_error(_a, EINVAL, "Invalid time flag");
+		return (ARCHIVE_FAILED);
+	}
+	if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {
+		archive_set_error(_a, EINVAL, "No time flag");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Check a type of comparison. */
+	if (flag &
+	   ((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
+			| ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {
+		archive_set_error(_a, EINVAL, "Invalid comparison flag");
+		return (ARCHIVE_FAILED);
+	}
+	if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
+	    | ARCHIVE_MATCH_EQUAL)) == 0) {
+		archive_set_error(_a, EINVAL, "No comparison flag");
+		return (ARCHIVE_FAILED);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+#define JUST_EQUAL(t) (((t) &  (ARCHIVE_MATCH_EQUAL |\
+	ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)
+static int
+set_timefilter(struct archive_match *a, int timetype,
+    time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)
+{
+	if (timetype & ARCHIVE_MATCH_MTIME) {
+		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
+			a->newer_mtime_filter = timetype;
+			a->newer_mtime_sec = mtime_sec;
+			a->newer_mtime_nsec = mtime_nsec;
+			a->setflag |= TIME_IS_SET;
+		}
+		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
+			a->older_mtime_filter = timetype;
+			a->older_mtime_sec = mtime_sec;
+			a->older_mtime_nsec = mtime_nsec;
+			a->setflag |= TIME_IS_SET;
+		}
+	}
+	if (timetype & ARCHIVE_MATCH_CTIME) {
+		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
+			a->newer_ctime_filter = timetype;
+			a->newer_ctime_sec = ctime_sec;
+			a->newer_ctime_nsec = ctime_nsec;
+			a->setflag |= TIME_IS_SET;
+		}
+		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
+			a->older_ctime_filter = timetype;
+			a->older_ctime_sec = ctime_sec;
+			a->older_ctime_nsec = ctime_nsec;
+			a->setflag |= TIME_IS_SET;
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+set_timefilter_date(struct archive_match *a, int timetype, const char *datestr)
+{
+	time_t t;
+
+	if (datestr == NULL || *datestr == '\0') {
+		archive_set_error(&(a->archive), EINVAL, "date is empty");
+		return (ARCHIVE_FAILED);
+	}
+	t = get_date(a->now, datestr);
+	if (t == (time_t)-1) {
+		archive_set_error(&(a->archive), EINVAL, "invalid date string");
+		return (ARCHIVE_FAILED);
+	}
+	return set_timefilter(a, timetype, t, 0, t, 0);
+}
+
+static int
+set_timefilter_date_w(struct archive_match *a, int timetype,
+    const wchar_t *datestr)
+{
+	struct archive_string as;
+	time_t t;
+
+	if (datestr == NULL || *datestr == L'\0') {
+		archive_set_error(&(a->archive), EINVAL, "date is empty");
+		return (ARCHIVE_FAILED);
+	}
+
+	archive_string_init(&as);
+	if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {
+		archive_string_free(&as);
+		if (errno == ENOMEM)
+			return (error_nomem(a));
+		archive_set_error(&(a->archive), -1,
+		    "Failed to convert WCS to MBS");
+		return (ARCHIVE_FAILED);
+	}
+	t = get_date(a->now, as.s);
+	archive_string_free(&as);
+	if (t == (time_t)-1) {
+		archive_set_error(&(a->archive), EINVAL, "invalid date string");
+		return (ARCHIVE_FAILED);
+	}
+	return set_timefilter(a, timetype, t, 0, t, 0);
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static int
+set_timefilter_find_data(struct archive_match *a, int timetype,
+    DWORD ftLastWriteTime_dwHighDateTime, DWORD ftLastWriteTime_dwLowDateTime,
+    DWORD ftCreationTime_dwHighDateTime, DWORD ftCreationTime_dwLowDateTime)
+{
+	ULARGE_INTEGER utc;
+	time_t ctime_sec, mtime_sec;
+	long ctime_ns, mtime_ns;
+
+	utc.HighPart = ftCreationTime_dwHighDateTime;
+	utc.LowPart = ftCreationTime_dwLowDateTime;
+	if (utc.QuadPart >= EPOC_TIME) {
+		utc.QuadPart -= EPOC_TIME;
+		ctime_sec = (time_t)(utc.QuadPart / 10000000);
+		ctime_ns = (long)(utc.QuadPart % 10000000) * 100;
+	} else {
+		ctime_sec = 0;
+		ctime_ns = 0;
+	}
+	utc.HighPart = ftLastWriteTime_dwHighDateTime;
+	utc.LowPart = ftLastWriteTime_dwLowDateTime;
+	if (utc.QuadPart >= EPOC_TIME) {
+		utc.QuadPart -= EPOC_TIME;
+		mtime_sec = (time_t)(utc.QuadPart / 10000000);
+		mtime_ns = (long)(utc.QuadPart % 10000000) * 100;
+	} else {
+		mtime_sec = 0;
+		mtime_ns = 0;
+	}
+	return set_timefilter(a, timetype,
+			mtime_sec, mtime_ns, ctime_sec, ctime_ns);
+}
+
+static int
+set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
+    const char *path)
+{
+	/* NOTE: stat() on Windows cannot handle nano seconds. */
+	HANDLE h;
+	WIN32_FIND_DATAA d;
+
+	if (path == NULL || *path == '\0') {
+		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+		return (ARCHIVE_FAILED);
+	}
+	h = FindFirstFileA(path, &d);
+	if (h == INVALID_HANDLE_VALUE) {
+		la_dosmaperr(GetLastError());
+		archive_set_error(&(a->archive), errno,
+		    "Failed to FindFirstFileA");
+		return (ARCHIVE_FAILED);
+	}
+	FindClose(h);
+	return set_timefilter_find_data(a, timetype,
+	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
+	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
+}
+
+static int
+set_timefilter_pathname_wcs(struct archive_match *a, int timetype,
+    const wchar_t *path)
+{
+	HANDLE h;
+	WIN32_FIND_DATAW d;
+
+	if (path == NULL || *path == L'\0') {
+		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+		return (ARCHIVE_FAILED);
+	}
+	h = FindFirstFileW(path, &d);
+	if (h == INVALID_HANDLE_VALUE) {
+		la_dosmaperr(GetLastError());
+		archive_set_error(&(a->archive), errno,
+		    "Failed to FindFirstFile");
+		return (ARCHIVE_FAILED);
+	}
+	FindClose(h);
+	return set_timefilter_find_data(a, timetype,
+	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
+	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
+}
+
+#else /* _WIN32 && !__CYGWIN__ */
+
+static int
+set_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)
+{
+	struct archive_entry *ae;
+	time_t ctime_sec, mtime_sec;
+	long ctime_ns, mtime_ns;
+
+	ae = archive_entry_new();
+	if (ae == NULL)
+		return (error_nomem(a));
+	archive_entry_copy_stat(ae, st);
+	ctime_sec = archive_entry_ctime(ae);
+	ctime_ns = archive_entry_ctime_nsec(ae);
+	mtime_sec = archive_entry_mtime(ae);
+	mtime_ns = archive_entry_mtime_nsec(ae);
+	archive_entry_free(ae);
+	return set_timefilter(a, timetype, mtime_sec, mtime_ns,
+			ctime_sec, ctime_ns);
+}
+
+static int
+set_timefilter_pathname_mbs(struct archive_match *a, int timetype,
+    const char *path)
+{
+	struct stat st;
+
+	if (path == NULL || *path == '\0') {
+		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+		return (ARCHIVE_FAILED);
+	}
+	if (la_stat(path, &st) != 0) {
+		archive_set_error(&(a->archive), errno, "Failed to stat()");
+		return (ARCHIVE_FAILED);
+	}
+	return (set_timefilter_stat(a, timetype, &st));
+}
+
+static int
+set_timefilter_pathname_wcs(struct archive_match *a, int timetype,
+    const wchar_t *path)
+{
+	struct archive_string as;
+	int r;
+
+	if (path == NULL || *path == L'\0') {
+		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Convert WCS filename to MBS filename. */
+	archive_string_init(&as);
+	if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {
+		archive_string_free(&as);
+		if (errno == ENOMEM)
+			return (error_nomem(a));
+		archive_set_error(&(a->archive), -1,
+		    "Failed to convert WCS to MBS");
+		return (ARCHIVE_FAILED);
+	}
+
+	r = set_timefilter_pathname_mbs(a, timetype, as.s);
+	archive_string_free(&as);
+
+	return (r);
+}
+#endif /* _WIN32 && !__CYGWIN__ */
+
+/*
+ * Call back functions for archive_rb.
+ */
+static int
+cmp_node_mbs(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
+	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
+	const char *p1, *p2;
+
+	archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);
+	archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);
+	if (p1 == NULL)
+		return (1);
+	if (p2 == NULL)
+		return (-1);
+	return (strcmp(p1, p2));
+}
+        
+static int
+cmp_key_mbs(const struct archive_rb_node *n, const void *key)
+{
+	struct match_file *f = (struct match_file *)(uintptr_t)n;
+	const char *p;
+
+	archive_mstring_get_mbs(NULL, &(f->pathname), &p);
+	if (p == NULL)
+		return (-1);
+	return (strcmp(p, (const char *)key));
+}
+
+static int
+cmp_node_wcs(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
+	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
+	const wchar_t *p1, *p2;
+
+	archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);
+	archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);
+	if (p1 == NULL)
+		return (1);
+	if (p2 == NULL)
+		return (-1);
+	return (wcscmp(p1, p2));
+}
+        
+static int
+cmp_key_wcs(const struct archive_rb_node *n, const void *key)
+{
+	struct match_file *f = (struct match_file *)(uintptr_t)n;
+	const wchar_t *p;
+
+	archive_mstring_get_wcs(NULL, &(f->pathname), &p);
+	if (p == NULL)
+		return (-1);
+	return (wcscmp(p, (const wchar_t *)key));
+}
+
+static void
+entry_list_init(struct entry_list *list)
+{
+	list->first = NULL;
+	list->last = &(list->first);
+	list->count = 0;
+}
+
+static void
+entry_list_free(struct entry_list *list)
+{
+	struct match_file *p, *q;
+
+	for (p = list->first; p != NULL; ) {
+		q = p;
+		p = p->next;
+		archive_mstring_clean(&(q->pathname));
+		free(q);
+	}
+}
+
+static void
+entry_list_add(struct entry_list *list, struct match_file *file)
+{
+	*list->last = file;
+	list->last = &(file->next);
+	list->count++;
+}
+
+static int
+add_entry(struct archive_match *a, int flag,
+    struct archive_entry *entry)
+{
+	struct match_file *f;
+	const void *pathname;
+	int r;
+
+	f = calloc(1, sizeof(*f));
+	if (f == NULL)
+		return (error_nomem(a));
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	pathname = archive_entry_pathname_w(entry);
+	if (pathname == NULL) {
+		free(f);
+		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
+		return (ARCHIVE_FAILED);
+	}
+	archive_mstring_copy_wcs(&(f->pathname), pathname);
+	a->exclusion_tree.rbt_ops = &rb_ops_wcs;
+#else
+	(void)rb_ops_wcs;
+	pathname = archive_entry_pathname(entry);
+	if (pathname == NULL) {
+		free(f);
+		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
+		return (ARCHIVE_FAILED);
+	}
+	archive_mstring_copy_mbs(&(f->pathname), pathname);
+	a->exclusion_tree.rbt_ops = &rb_ops_mbs;
+#endif
+	f->flag = flag;
+	f->mtime_sec = archive_entry_mtime(entry);
+	f->mtime_nsec = archive_entry_mtime_nsec(entry);
+	f->ctime_sec = archive_entry_ctime(entry);
+	f->ctime_nsec = archive_entry_ctime_nsec(entry);
+	r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));
+	if (!r) {
+		struct match_file *f2;
+
+		/* Get the duplicated file. */
+		f2 = (struct match_file *)__archive_rb_tree_find_node(
+			&(a->exclusion_tree), pathname);
+
+		/*
+		 * We always overwrite comparison condition.
+		 * If you do not want to overwrite it, you should not
+		 * call archive_match_exclude_entry(). We cannot know
+		 * what behavior you really expect since overwriting
+		 * condition might be different with the flag.
+		 */
+		if (f2 != NULL) {
+			f2->flag = f->flag;
+			f2->mtime_sec = f->mtime_sec;
+			f2->mtime_nsec = f->mtime_nsec;
+			f2->ctime_sec = f->ctime_sec;
+			f2->ctime_nsec = f->ctime_nsec;
+		}
+		/* Release the duplicated file. */
+		archive_mstring_clean(&(f->pathname));
+		free(f);
+		return (ARCHIVE_OK);
+	}
+	entry_list_add(&(a->exclusion_entry_list), f);
+	a->setflag |= TIME_IS_SET;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Test if entry is excluded by its timestamp.
+ */
+static int
+time_excluded(struct archive_match *a, struct archive_entry *entry)
+{
+	struct match_file *f;
+	const void *pathname;
+	time_t sec;
+	long nsec;
+
+	/*
+	 * If this file/dir is excluded by a time comparison, skip it.
+	 */
+	if (a->newer_ctime_filter) {
+		/* If ctime is not set, use mtime instead. */
+		if (archive_entry_ctime_is_set(entry))
+			sec = archive_entry_ctime(entry);
+		else
+			sec = archive_entry_mtime(entry);
+		if (sec < a->newer_ctime_sec)
+			return (1); /* Too old, skip it. */
+		if (sec == a->newer_ctime_sec) {
+			if (archive_entry_ctime_is_set(entry))
+				nsec = archive_entry_ctime_nsec(entry);
+			else
+				nsec = archive_entry_mtime_nsec(entry);
+			if (nsec < a->newer_ctime_nsec)
+				return (1); /* Too old, skip it. */
+			if (nsec == a->newer_ctime_nsec &&
+			    (a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)
+			      == 0)
+				return (1); /* Equal, skip it. */
+		}
+	}
+	if (a->older_ctime_filter) {
+		/* If ctime is not set, use mtime instead. */
+		if (archive_entry_ctime_is_set(entry))
+			sec = archive_entry_ctime(entry);
+		else
+			sec = archive_entry_mtime(entry);
+		if (sec > a->older_ctime_sec)
+			return (1); /* Too new, skip it. */
+		if (sec == a->older_ctime_sec) {
+			if (archive_entry_ctime_is_set(entry))
+				nsec = archive_entry_ctime_nsec(entry);
+			else
+				nsec = archive_entry_mtime_nsec(entry);
+			if (nsec > a->older_ctime_nsec)
+				return (1); /* Too new, skip it. */
+			if (nsec == a->older_ctime_nsec &&
+			    (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)
+			      == 0)
+				return (1); /* Equal, skip it. */
+		}
+	}
+	if (a->newer_mtime_filter) {
+		sec = archive_entry_mtime(entry);
+		if (sec < a->newer_mtime_sec)
+			return (1); /* Too old, skip it. */
+		if (sec == a->newer_mtime_sec) {
+			nsec = archive_entry_mtime_nsec(entry);
+			if (nsec < a->newer_mtime_nsec)
+				return (1); /* Too old, skip it. */
+			if (nsec == a->newer_mtime_nsec &&
+			    (a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)
+			       == 0)
+				return (1); /* Equal, skip it. */
+		}
+	}
+	if (a->older_mtime_filter) {
+		sec = archive_entry_mtime(entry);
+		if (sec > a->older_mtime_sec)
+			return (1); /* Too new, skip it. */
+		nsec = archive_entry_mtime_nsec(entry);
+		if (sec == a->older_mtime_sec) {
+			if (nsec > a->older_mtime_nsec)
+				return (1); /* Too new, skip it. */
+			if (nsec == a->older_mtime_nsec &&
+			    (a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)
+			       == 0)
+				return (1); /* Equal, skip it. */
+		}
+	}
+
+	/* If there is no exclusion list, include the file. */
+	if (a->exclusion_entry_list.count == 0)
+		return (0);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	pathname = archive_entry_pathname_w(entry);
+	a->exclusion_tree.rbt_ops = &rb_ops_wcs;
+#else
+	(void)rb_ops_wcs;
+	pathname = archive_entry_pathname(entry);
+	a->exclusion_tree.rbt_ops = &rb_ops_mbs;
+#endif
+	if (pathname == NULL)
+		return (0);
+
+	f = (struct match_file *)__archive_rb_tree_find_node(
+		&(a->exclusion_tree), pathname);
+	/* If the file wasn't rejected, include it. */
+	if (f == NULL)
+		return (0);
+
+	if (f->flag & ARCHIVE_MATCH_CTIME) {
+		sec = archive_entry_ctime(entry);
+		if (f->ctime_sec > sec) {
+			if (f->flag & ARCHIVE_MATCH_OLDER)
+				return (1);
+		} else if (f->ctime_sec < sec) {
+			if (f->flag & ARCHIVE_MATCH_NEWER)
+				return (1);
+		} else {
+			nsec = archive_entry_ctime_nsec(entry);
+			if (f->ctime_nsec > nsec) {
+				if (f->flag & ARCHIVE_MATCH_OLDER)
+					return (1);
+			} else if (f->ctime_nsec < nsec) {
+				if (f->flag & ARCHIVE_MATCH_NEWER)
+					return (1);
+			} else if (f->flag & ARCHIVE_MATCH_EQUAL)
+				return (1);
+		}
+	}
+	if (f->flag & ARCHIVE_MATCH_MTIME) {
+		sec = archive_entry_mtime(entry);
+		if (f->mtime_sec > sec) {
+			if (f->flag & ARCHIVE_MATCH_OLDER)
+				return (1);
+		} else if (f->mtime_sec < sec) {
+			if (f->flag & ARCHIVE_MATCH_NEWER)
+				return (1);
+		} else {
+			nsec = archive_entry_mtime_nsec(entry);
+			if (f->mtime_nsec > nsec) {
+				if (f->flag & ARCHIVE_MATCH_OLDER)
+					return (1);
+			} else if (f->mtime_nsec < nsec) {
+				if (f->flag & ARCHIVE_MATCH_NEWER)
+					return (1);
+			} else if (f->flag & ARCHIVE_MATCH_EQUAL)
+				return (1);
+		}
+	}
+	return (0);
+}
+
+/*
+ * Utility functions to manage inclusion owners
+ */
+
+int
+archive_match_include_uid(struct archive *_a, la_int64_t uid)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_include_uid");
+	a = (struct archive_match *)_a;
+	return (add_owner_id(a, &(a->inclusion_uids), uid));
+}
+
+int
+archive_match_include_gid(struct archive *_a, la_int64_t gid)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_include_gid");
+	a = (struct archive_match *)_a;
+	return (add_owner_id(a, &(a->inclusion_gids), gid));
+}
+
+int
+archive_match_include_uname(struct archive *_a, const char *uname)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_include_uname");
+	a = (struct archive_match *)_a;
+	return (add_owner_name(a, &(a->inclusion_unames), 1, uname));
+}
+
+int
+archive_match_include_uname_w(struct archive *_a, const wchar_t *uname)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_include_uname_w");
+	a = (struct archive_match *)_a;
+	return (add_owner_name(a, &(a->inclusion_unames), 0, uname));
+}
+
+int
+archive_match_include_gname(struct archive *_a, const char *gname)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_include_gname");
+	a = (struct archive_match *)_a;
+	return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));
+}
+
+int
+archive_match_include_gname_w(struct archive *_a, const wchar_t *gname)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_include_gname_w");
+	a = (struct archive_match *)_a;
+	return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));
+}
+
+/*
+ * Test function for owner(uid, gid, uname, gname).
+ *
+ * Returns 1 if archive entry is excluded.
+ * Returns 0 if archive entry is not excluded.
+ * Returns <0 if something error happened.
+ */
+int
+archive_match_owner_excluded(struct archive *_a,
+    struct archive_entry *entry)
+{
+	struct archive_match *a;
+
+	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");
+
+	a = (struct archive_match *)_a;
+	if (entry == NULL) {
+		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* If we don't have inclusion id set at all, the entry is always
+	 * not excluded. */
+	if ((a->setflag & ID_IS_SET) == 0)
+		return (0);
+	return (owner_excluded(a, entry));
+}
+
+static int
+add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)
+{
+	unsigned i;
+
+	if (ids->count + 1 >= ids->size) {
+		void *p;
+
+		if (ids->size == 0)
+			ids->size = 8;
+		else
+			ids->size *= 2;
+		p = realloc(ids->ids, sizeof(*ids->ids) * ids->size);
+		if (p == NULL)
+			return (error_nomem(a));
+		ids->ids = (int64_t *)p;
+	}
+
+	/* Find an insert point. */
+	for (i = 0; i < ids->count; i++) {
+		if (ids->ids[i] >= id)
+			break;
+	}
+
+	/* Add owner id. */
+	if (i == ids->count)
+		ids->ids[ids->count++] = id;
+	else if (ids->ids[i] != id) {
+		memmove(&(ids->ids[i+1]), &(ids->ids[i]),
+		    (ids->count - i) * sizeof(ids->ids[0]));
+		ids->ids[i] = id;
+		ids->count++;
+	}
+	a->setflag |= ID_IS_SET;
+	return (ARCHIVE_OK);
+}
+
+static int
+match_owner_id(struct id_array *ids, int64_t id)
+{
+	unsigned b, m, t;
+
+	t = 0;
+	b = (unsigned)ids->count;
+	while (t < b) {
+		m = (t + b)>>1;
+		if (ids->ids[m] == id)
+			return (1);
+		if (ids->ids[m] < id)
+			t = m + 1;
+		else
+			b = m;
+	}
+	return (0);
+}
+
+static int
+add_owner_name(struct archive_match *a, struct match_list *list,
+    int mbs, const void *name)
+{
+	struct match *match;
+
+	match = calloc(1, sizeof(*match));
+	if (match == NULL)
+		return (error_nomem(a));
+	if (mbs)
+		archive_mstring_copy_mbs(&(match->pattern), name);
+	else
+		archive_mstring_copy_wcs(&(match->pattern), name);
+	match_list_add(list, match);
+	a->setflag |= ID_IS_SET;
+	return (ARCHIVE_OK);
+}
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int
+match_owner_name_mbs(struct archive_match *a, struct match_list *list,
+    const char *name)
+{
+	struct match *m;
+	const char *p;
+
+	if (name == NULL || *name == '\0')
+		return (0);
+	for (m = list->first; m; m = m->next) {
+		if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)
+		    < 0 && errno == ENOMEM)
+			return (error_nomem(a));
+		if (p != NULL && strcmp(p, name) == 0) {
+			m->matches++;
+			return (1);
+		}
+	}
+	return (0);
+}
+#else
+static int
+match_owner_name_wcs(struct archive_match *a, struct match_list *list,
+    const wchar_t *name)
+{
+	struct match *m;
+	const wchar_t *p;
+
+	if (name == NULL || *name == L'\0')
+		return (0);
+	for (m = list->first; m; m = m->next) {
+		if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)
+		    < 0 && errno == ENOMEM)
+			return (error_nomem(a));
+		if (p != NULL && wcscmp(p, name) == 0) {
+			m->matches++;
+			return (1);
+		}
+	}
+	return (0);
+}
+#endif
+
+/*
+ * Test if entry is excluded by uid, gid, uname or gname.
+ */
+static int
+owner_excluded(struct archive_match *a, struct archive_entry *entry)
+{
+	int r;
+
+	if (a->inclusion_uids.count) {
+		if (!match_owner_id(&(a->inclusion_uids),
+		    archive_entry_uid(entry)))
+			return (1);
+	}
+
+	if (a->inclusion_gids.count) {
+		if (!match_owner_id(&(a->inclusion_gids),
+		    archive_entry_gid(entry)))
+			return (1);
+	}
+
+	if (a->inclusion_unames.count) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		r = match_owner_name_wcs(a, &(a->inclusion_unames),
+			archive_entry_uname_w(entry));
+#else
+		r = match_owner_name_mbs(a, &(a->inclusion_unames),
+			archive_entry_uname(entry));
+#endif
+		if (!r)
+			return (1);
+		else if (r < 0)
+			return (r);
+	}
+
+	if (a->inclusion_gnames.count) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		r = match_owner_name_wcs(a, &(a->inclusion_gnames),
+			archive_entry_gname_w(entry));
+#else
+		r = match_owner_name_mbs(a, &(a->inclusion_gnames),
+			archive_entry_gname(entry));
+#endif
+		if (!r)
+			return (1);
+		else if (r < 0)
+			return (r);
+	}
+	return (0);
+}
+
diff --git a/c/archive_openssl_evp_private.h b/c/archive_openssl_evp_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_openssl_evp_private.h
@@ -0,0 +1,53 @@
+/*-
+ * 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.
+ */
+
+#ifndef ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED
+#define ARCHIVE_OPENSSL_EVP_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <openssl/evp.h>
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memset */
+static inline EVP_MD_CTX *EVP_MD_CTX_new(void)
+{
+	EVP_MD_CTX *ctx = (EVP_MD_CTX *)calloc(1, sizeof(EVP_MD_CTX));
+	return ctx;
+}
+
+static inline void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
+{
+	EVP_MD_CTX_cleanup(ctx);
+	memset(ctx, 0, sizeof(*ctx));
+	free(ctx);
+}
+#endif
+
+#endif
diff --git a/c/archive_openssl_hmac_private.h b/c/archive_openssl_hmac_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_openssl_hmac_private.h
@@ -0,0 +1,54 @@
+/*-
+ * 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.
+ */
+
+#ifndef ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED
+#define ARCHIVE_OPENSSL_HMAC_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <openssl/hmac.h>
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memset */
+static inline HMAC_CTX *HMAC_CTX_new(void)
+{
+	HMAC_CTX *ctx = (HMAC_CTX *)calloc(1, sizeof(HMAC_CTX));
+	return ctx;
+}
+
+static inline void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+	HMAC_CTX_cleanup(ctx);
+	memset(ctx, 0, sizeof(*ctx));
+	free(ctx);
+}
+#endif
+
+#endif
diff --git a/c/archive_options.c b/c/archive_options.c
new file mode 100644
--- /dev/null
+++ b/c/archive_options.c
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 2011 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive_options_private.h"
+
+static const char *
+parse_option(const char **str,
+    const char **mod, const char **opt, const char **val);
+
+int
+_archive_set_option(struct archive *a,
+    const char *m, const char *o, const char *v,
+    int magic, const char *fn, option_handler use_option)
+{
+	const char *mp, *op, *vp;
+	int r;
+
+	archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
+
+	mp = (m != NULL && m[0] != '\0') ? m : NULL;
+	op = (o != NULL && o[0] != '\0') ? o : NULL;
+	vp = (v != NULL && v[0] != '\0') ? v : NULL;
+
+	if (op == NULL && vp == NULL)
+		return (ARCHIVE_OK);
+	if (op == NULL) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC, "Empty option");
+		return (ARCHIVE_FAILED);
+	}
+
+	r = use_option(a, mp, op, vp);
+	if (r == ARCHIVE_WARN - 1) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Unknown module name: `%s'", mp);
+		return (ARCHIVE_FAILED);
+	}
+	if (r == ARCHIVE_WARN) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Undefined option: `%s%s%s%s%s%s'",
+		    vp?"":"!", mp?mp:"", mp?":":"", op, vp?"=":"", vp?vp:"");
+		return (ARCHIVE_FAILED);
+	}
+	return (r);
+}
+
+int
+_archive_set_either_option(struct archive *a, const char *m, const char *o, const char *v,
+    option_handler use_format_option, option_handler use_filter_option)
+{
+	int r1, r2;
+
+	if (o == NULL && v == NULL)
+		return (ARCHIVE_OK);
+	if (o == NULL)
+		return (ARCHIVE_FAILED);
+
+	r1 = use_format_option(a, m, o, v);
+	if (r1 == ARCHIVE_FATAL)
+		return (ARCHIVE_FATAL);
+
+	r2 = use_filter_option(a, m, o, v);
+	if (r2 == ARCHIVE_FATAL)
+		return (ARCHIVE_FATAL);
+
+	if (r2 == ARCHIVE_WARN - 1)
+		return r1;
+	return r1 > r2 ? r1 : r2;
+}
+
+int
+_archive_set_options(struct archive *a, const char *options,
+    int magic, const char *fn, option_handler use_option)
+{
+	int allok = 1, anyok = 0, ignore_mod_err = 0, r;
+	char *data;
+	const char *s, *mod, *opt, *val;
+
+	archive_check_magic(a, magic, ARCHIVE_STATE_NEW, fn);
+
+	if (options == NULL || options[0] == '\0')
+		return ARCHIVE_OK;
+
+	if ((data = strdup(options)) == NULL) {
+		archive_set_error(a,
+		    ENOMEM, "Out of memory adding file to list");
+		return (ARCHIVE_FATAL);
+	}
+	s = (const char *)data;
+
+	do {
+		mod = opt = val = NULL;
+
+		parse_option(&s, &mod, &opt, &val);
+		if (mod == NULL && opt != NULL &&
+		    strcmp("__ignore_wrong_module_name__", opt) == 0) {
+			/* Ignore module name error */
+			if (val != NULL) {
+				ignore_mod_err = 1;
+				anyok = 1;
+			}
+			continue;
+		}
+
+		r = use_option(a, mod, opt, val);
+		if (r == ARCHIVE_FATAL) {
+			free(data);
+			return (ARCHIVE_FATAL);
+		}
+		if (r == ARCHIVE_FAILED && mod != NULL) {
+			free(data);
+			return (ARCHIVE_FAILED);
+		}
+		if (r == ARCHIVE_WARN - 1) {
+			if (ignore_mod_err)
+				continue;
+			/* The module name is wrong. */
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Unknown module name: `%s'", mod);
+			free(data);
+			return (ARCHIVE_FAILED);
+		}
+		if (r == ARCHIVE_WARN) {
+			/* The option name is wrong. No-one used this. */
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Undefined option: `%s%s%s'",
+			    mod?mod:"", mod?":":"", opt);
+			free(data);
+			return (ARCHIVE_FAILED);
+		}
+		if (r == ARCHIVE_OK)
+			anyok = 1;
+		else
+			allok = 0;
+	} while (s != NULL);
+
+	free(data);
+	return allok ? ARCHIVE_OK : anyok ? ARCHIVE_WARN : ARCHIVE_FAILED;
+}
+
+static const char *
+parse_option(const char **s, const char **m, const char **o, const char **v)
+{
+	const char *end, *mod, *opt, *val;
+	char *p;
+
+	end = NULL;
+	mod = NULL;
+	opt = *s;
+	val = "1";
+
+	p = strchr(opt, ',');
+
+	if (p != NULL) {
+		*p = '\0';
+		end = ((const char *)p) + 1;
+	}
+
+	if (0 == strlen(opt)) {
+		*s = end;
+		*m = NULL;
+		*o = NULL;
+		*v = NULL;
+		return end;
+	}
+
+	p = strchr(opt, ':');
+	if (p != NULL) {
+		*p = '\0';
+		mod = opt;
+		opt = ++p;
+	}
+
+	p = strchr(opt, '=');
+	if (p != NULL) {
+		*p = '\0';
+		val = ++p;
+	} else if (opt[0] == '!') {
+		++opt;
+		val = NULL;
+	}
+
+	*s = end;
+	*m = mod;
+	*o = opt;
+	*v = val;
+
+	return end;
+}
+
diff --git a/c/archive_options_private.h b/c/archive_options_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_options_private.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2011 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.
+ */
+
+#ifndef ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED
+#define ARCHIVE_OPTIONS_PRIVATE_H_INCLUDED
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include "archive_private.h"
+
+typedef int (*option_handler)(struct archive *a,
+    const char *mod, const char *opt, const char *val);
+
+int
+_archive_set_option(struct archive *a,
+    const char *mod, const char *opt, const char *val,
+    int magic, const char *fn, option_handler use_option);
+
+int
+_archive_set_options(struct archive *a, const char *options,
+    int magic, const char *fn, option_handler use_option);
+
+int
+_archive_set_either_option(struct archive *a,
+    const char *m, const char *o, const char *v,
+    option_handler use_format_option, option_handler use_filter_option);
+
+#endif
diff --git a/c/archive_pack_dev.c b/c/archive_pack_dev.c
new file mode 100644
--- /dev/null
+++ b/c/archive_pack_dev.c
@@ -0,0 +1,336 @@
+/*	$NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $	*/
+
+/*-
+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/* Originally from NetBSD's mknod(8) source. */
+
+#include "archive_platform.h"
+
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+#if !defined(lint)
+__RCSID("$NetBSD$");
+#endif /* not lint */
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#define HAVE_MAJOR
+#elif MAJOR_IN_SYSMACROS
+#include <sys/sysmacros.h>
+#define HAVE_MAJOR
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive_pack_dev.h"
+
+static	pack_t	pack_netbsd;
+static	pack_t	pack_freebsd;
+static	pack_t	pack_8_8;
+static	pack_t	pack_12_20;
+static	pack_t	pack_14_18;
+static	pack_t	pack_8_24;
+static	pack_t	pack_bsdos;
+static	int	compare_format(const void *, const void *);
+
+static const char iMajorError[] = "invalid major number";
+static const char iMinorError[] = "invalid minor number";
+static const char tooManyFields[] = "too many fields for format";
+
+/* This is blatantly stolen from libarchive/archive_entry.c,
+ * in an attempt to get this to play nice on MinGW... */
+#if !defined(HAVE_MAJOR) && !defined(major)
+/* Replacement for major/minor/makedev. */
+#define major(x) ((int)(0x00ff & ((x) >> 8)))
+#define minor(x) ((int)(0xffff00ff & (x)))
+#define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min)))
+#endif
+
+/* Play games to come up with a suitable makedev() definition. */
+#ifdef __QNXNTO__
+/* QNX.  <sigh> */
+#include <sys/netmgr.h>
+#define apd_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))
+#elif defined makedev
+/* There's a "makedev" macro. */
+#define apd_makedev(maj, min) makedev((maj), (min))
+#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__))
+/* Windows. <sigh> */
+#define apd_makedev(maj, min) mkdev((maj), (min))
+#else
+/* There's a "makedev" function. */
+#define apd_makedev(maj, min) makedev((maj), (min))
+#endif
+
+/* exported */
+dev_t
+pack_native(int n, unsigned long numbers[], const char **error)
+{
+	dev_t dev = 0;
+
+	if (n == 2) {
+		dev = apd_makedev(numbers[0], numbers[1]);
+		if ((unsigned long)major(dev) != numbers[0])
+			*error = iMajorError;
+		else if ((unsigned long)minor(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+static dev_t
+pack_netbsd(int n, unsigned long numbers[], const char **error)
+{
+	dev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_netbsd(numbers[0], numbers[1]);
+		if ((unsigned long)major_netbsd(dev) != numbers[0])
+			*error = iMajorError;
+		else if ((unsigned long)minor_netbsd(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_freebsd(x)	((int32_t)(((x) & 0x0000ff00) >> 8))
+#define	minor_freebsd(x)	((int32_t)(((x) & 0xffff00ff) >> 0))
+#define	makedev_freebsd(x,y)	((dev_t)((((x) << 8) & 0x0000ff00) | \
+					 (((y) << 0) & 0xffff00ff)))
+
+static dev_t
+pack_freebsd(int n, unsigned long numbers[], const char **error)
+{
+	dev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_freebsd(numbers[0], numbers[1]);
+		if ((unsigned long)major_freebsd(dev) != numbers[0])
+			*error = iMajorError;
+		if ((unsigned long)minor_freebsd(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_8_8(x)		((int32_t)(((x) & 0x0000ff00) >> 8))
+#define	minor_8_8(x)		((int32_t)(((x) & 0x000000ff) >> 0))
+#define	makedev_8_8(x,y)	((dev_t)((((x) << 8) & 0x0000ff00) | \
+					 (((y) << 0) & 0x000000ff)))
+
+static dev_t
+pack_8_8(int n, unsigned long numbers[], const char **error)
+{
+	dev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_8_8(numbers[0], numbers[1]);
+		if ((unsigned long)major_8_8(dev) != numbers[0])
+			*error = iMajorError;
+		if ((unsigned long)minor_8_8(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_12_20(x)		((int32_t)(((x) & 0xfff00000) >> 20))
+#define	minor_12_20(x)		((int32_t)(((x) & 0x000fffff) >>  0))
+#define	makedev_12_20(x,y)	((dev_t)((((x) << 20) & 0xfff00000) | \
+					 (((y) <<  0) & 0x000fffff)))
+
+static dev_t
+pack_12_20(int n, unsigned long numbers[], const char **error)
+{
+	dev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_12_20(numbers[0], numbers[1]);
+		if ((unsigned long)major_12_20(dev) != numbers[0])
+			*error = iMajorError;
+		if ((unsigned long)minor_12_20(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_14_18(x)		((int32_t)(((x) & 0xfffc0000) >> 18))
+#define	minor_14_18(x)		((int32_t)(((x) & 0x0003ffff) >>  0))
+#define	makedev_14_18(x,y)	((dev_t)((((x) << 18) & 0xfffc0000) | \
+					 (((y) <<  0) & 0x0003ffff)))
+
+static dev_t
+pack_14_18(int n, unsigned long numbers[], const char **error)
+{
+	dev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_14_18(numbers[0], numbers[1]);
+		if ((unsigned long)major_14_18(dev) != numbers[0])
+			*error = iMajorError;
+		if ((unsigned long)minor_14_18(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_8_24(x)		((int32_t)(((x) & 0xff000000) >> 24))
+#define	minor_8_24(x)		((int32_t)(((x) & 0x00ffffff) >>  0))
+#define	makedev_8_24(x,y)	((dev_t)((((x) << 24) & 0xff000000) | \
+					 (((y) <<  0) & 0x00ffffff)))
+
+static dev_t
+pack_8_24(int n, unsigned long numbers[], const char **error)
+{
+	dev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_8_24(numbers[0], numbers[1]);
+		if ((unsigned long)major_8_24(dev) != numbers[0])
+			*error = iMajorError;
+		if ((unsigned long)minor_8_24(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_12_12_8(x)	((int32_t)(((x) & 0xfff00000) >> 20))
+#define	unit_12_12_8(x)		((int32_t)(((x) & 0x000fff00) >>  8))
+#define	subunit_12_12_8(x)	((int32_t)(((x) & 0x000000ff) >>  0))
+#define	makedev_12_12_8(x,y,z)	((dev_t)((((x) << 20) & 0xfff00000) | \
+					 (((y) <<  8) & 0x000fff00) | \
+					 (((z) <<  0) & 0x000000ff)))
+
+static dev_t
+pack_bsdos(int n, unsigned long numbers[], const char **error)
+{
+	dev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_12_20(numbers[0], numbers[1]);
+		if ((unsigned long)major_12_20(dev) != numbers[0])
+			*error = iMajorError;
+		if ((unsigned long)minor_12_20(dev) != numbers[1])
+			*error = iMinorError;
+	} else if (n == 3) {
+		dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]);
+		if ((unsigned long)major_12_12_8(dev) != numbers[0])
+			*error = iMajorError;
+		if ((unsigned long)unit_12_12_8(dev) != numbers[1])
+			*error = "invalid unit number";
+		if ((unsigned long)subunit_12_12_8(dev) != numbers[2])
+			*error = "invalid subunit number";
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+		/* list of formats and pack functions */
+		/* this list must be sorted lexically */
+static const struct format {
+	const char	*name;
+	pack_t		*pack;
+} formats[] = {
+	{"386bsd",  pack_8_8},
+	{"4bsd",    pack_8_8},
+	{"bsdos",   pack_bsdos},
+	{"freebsd", pack_freebsd},
+	{"hpux",    pack_8_24},
+	{"isc",     pack_8_8},
+	{"linux",   pack_8_8},
+	{"native",  pack_native},
+	{"netbsd",  pack_netbsd},
+	{"osf1",    pack_12_20},
+	{"sco",     pack_8_8},
+	{"solaris", pack_14_18},
+	{"sunos",   pack_8_8},
+	{"svr3",    pack_8_8},
+	{"svr4",    pack_14_18},
+	{"ultrix",  pack_8_8},
+};
+
+static int
+compare_format(const void *key, const void *element)
+{
+	const char		*name;
+	const struct format	*format;
+
+	name = key;
+	format = element;
+
+	return (strcmp(name, format->name));
+}
+
+
+pack_t *
+pack_find(const char *name)
+{
+	struct format	*format;
+
+	format = bsearch(name, formats,
+	    sizeof(formats)/sizeof(formats[0]),
+	    sizeof(formats[0]), compare_format);
+	if (format == 0)
+		return (NULL);
+	return (format->pack);
+}
diff --git a/c/archive_pack_dev.h b/c/archive_pack_dev.h
new file mode 100644
--- /dev/null
+++ b/c/archive_pack_dev.h
@@ -0,0 +1,49 @@
+/*	$NetBSD: pack_dev.h,v 1.8 2013/06/14 16:28:20 tsutsui Exp $	*/
+
+/*-
+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/* Originally from NetBSD's mknod(8) source. */
+
+#ifndef	ARCHIVE_PACK_DEV_H
+#define	ARCHIVE_PACK_DEV_H
+
+typedef	dev_t pack_t(int, unsigned long [], const char **);
+
+pack_t	*pack_find(const char *);
+pack_t	 pack_native;
+
+#define	major_netbsd(x)		((int32_t)((((x) & 0x000fff00) >>  8)))
+#define	minor_netbsd(x)		((int32_t)((((x) & 0xfff00000) >> 12) | \
+					   (((x) & 0x000000ff) >>  0)))
+#define	makedev_netbsd(x,y)	((dev_t)((((x) <<  8) & 0x000fff00) | \
+					 (((y) << 12) & 0xfff00000) | \
+					 (((y) <<  0) & 0x000000ff)))
+
+#endif	/* ARCHIVE_PACK_DEV_H */
diff --git a/c/archive_pathmatch.c b/c/archive_pathmatch.c
new file mode 100644
--- /dev/null
+++ b/c/archive_pathmatch.c
@@ -0,0 +1,459 @@
+/*-
+ * 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
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive_pathmatch.h"
+
+/*
+ * Check whether a character 'c' is matched by a list specification [...]:
+ *    * Leading '!' or '^' negates the class.
+ *    * <char>-<char> is a range of characters
+ *    * \<char> removes any special meaning for <char>
+ *
+ * Some interesting boundary cases:
+ *   a-d-e is one range (a-d) followed by two single characters - and e.
+ *   \a-\d is same as a-d
+ *   a\-d is three single characters: a, d, -
+ *   Trailing - is not special (so [a-] is two characters a and -).
+ *   Initial - is not special ([a-] is same as [-a] is same as [\\-a])
+ *   This function never sees a trailing \.
+ *   [] always fails
+ *   [!] always succeeds
+ */
+static int
+pm_list(const char *start, const char *end, const char c, int flags)
+{
+	const char *p = start;
+	char rangeStart = '\0', nextRangeStart;
+	int match = 1, nomatch = 0;
+
+	/* This will be used soon... */
+	(void)flags; /* UNUSED */
+
+	/* If this is a negated class, return success for nomatch. */
+	if ((*p == '!' || *p == '^') && p < end) {
+		match = 0;
+		nomatch = 1;
+		++p;
+	}
+
+	while (p < end) {
+		nextRangeStart = '\0';
+		switch (*p) {
+		case '-':
+			/* Trailing or initial '-' is not special. */
+			if ((rangeStart == '\0') || (p == end - 1)) {
+				if (*p == c)
+					return (match);
+			} else {
+				char rangeEnd = *++p;
+				if (rangeEnd == '\\')
+					rangeEnd = *++p;
+				if ((rangeStart <= c) && (c <= rangeEnd))
+					return (match);
+			}
+			break;
+		case '\\':
+			++p;
+			/* Fall through */
+		default:
+			if (*p == c)
+				return (match);
+			nextRangeStart = *p; /* Possible start of range. */
+		}
+		rangeStart = nextRangeStart;
+		++p;
+	}
+	return (nomatch);
+}
+
+static int
+pm_list_w(const wchar_t *start, const wchar_t *end, const wchar_t c, int flags)
+{
+	const wchar_t *p = start;
+	wchar_t rangeStart = L'\0', nextRangeStart;
+	int match = 1, nomatch = 0;
+
+	/* This will be used soon... */
+	(void)flags; /* UNUSED */
+
+	/* If this is a negated class, return success for nomatch. */
+	if ((*p == L'!' || *p == L'^') && p < end) {
+		match = 0;
+		nomatch = 1;
+		++p;
+	}
+
+	while (p < end) {
+		nextRangeStart = L'\0';
+		switch (*p) {
+		case L'-':
+			/* Trailing or initial '-' is not special. */
+			if ((rangeStart == L'\0') || (p == end - 1)) {
+				if (*p == c)
+					return (match);
+			} else {
+				wchar_t rangeEnd = *++p;
+				if (rangeEnd == L'\\')
+					rangeEnd = *++p;
+				if ((rangeStart <= c) && (c <= rangeEnd))
+					return (match);
+			}
+			break;
+		case L'\\':
+			++p;
+			/* Fall through */
+		default:
+			if (*p == c)
+				return (match);
+			nextRangeStart = *p; /* Possible start of range. */
+		}
+		rangeStart = nextRangeStart;
+		++p;
+	}
+	return (nomatch);
+}
+
+/*
+ * If s is pointing to "./", ".//", "./././" or the like, skip it.
+ */
+static const char *
+pm_slashskip(const char *s) {
+	while ((*s == '/')
+	    || (s[0] == '.' && s[1] == '/')
+	    || (s[0] == '.' && s[1] == '\0'))
+		++s;
+	return (s);
+}
+
+static const wchar_t *
+pm_slashskip_w(const wchar_t *s) {
+	while ((*s == L'/')
+	    || (s[0] == L'.' && s[1] == L'/')
+	    || (s[0] == L'.' && s[1] == L'\0'))
+		++s;
+	return (s);
+}
+
+static int
+pm(const char *p, const char *s, int flags)
+{
+	const char *end;
+
+	/*
+	 * Ignore leading './', './/', '././', etc.
+	 */
+	if (s[0] == '.' && s[1] == '/')
+		s = pm_slashskip(s + 1);
+	if (p[0] == '.' && p[1] == '/')
+		p = pm_slashskip(p + 1);
+
+	for (;;) {
+		switch (*p) {
+		case '\0':
+			if (s[0] == '/') {
+				if (flags & PATHMATCH_NO_ANCHOR_END)
+					return (1);
+				/* "dir" == "dir/" == "dir/." */
+				s = pm_slashskip(s);
+			}
+			return (*s == '\0');
+		case '?':
+			/* ? always succeeds, unless we hit end of 's' */
+			if (*s == '\0')
+				return (0);
+			break;
+		case '*':
+			/* "*" == "**" == "***" ... */
+			while (*p == '*')
+				++p;
+			/* Trailing '*' always succeeds. */
+			if (*p == '\0')
+				return (1);
+			while (*s) {
+				if (archive_pathmatch(p, s, flags))
+					return (1);
+				++s;
+			}
+			return (0);
+		case '[':
+			/*
+			 * Find the end of the [...] character class,
+			 * ignoring \] that might occur within the class.
+			 */
+			end = p + 1;
+			while (*end != '\0' && *end != ']') {
+				if (*end == '\\' && end[1] != '\0')
+					++end;
+				++end;
+			}
+			if (*end == ']') {
+				/* We found [...], try to match it. */
+				if (!pm_list(p + 1, end, *s, flags))
+					return (0);
+				p = end; /* Jump to trailing ']' char. */
+				break;
+			} else
+				/* No final ']', so just match '['. */
+				if (*p != *s)
+					return (0);
+			break;
+		case '\\':
+			/* Trailing '\\' matches itself. */
+			if (p[1] == '\0') {
+				if (*s != '\\')
+					return (0);
+			} else {
+				++p;
+				if (*p != *s)
+					return (0);
+			}
+			break;
+		case '/':
+			if (*s != '/' && *s != '\0')
+				return (0);
+			/* Note: pattern "/\./" won't match "/";
+			 * pm_slashskip() correctly stops at backslash. */
+			p = pm_slashskip(p);
+			s = pm_slashskip(s);
+			if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END))
+				return (1);
+			--p; /* Counteract the increment below. */
+			--s;
+			break;
+		case '$':
+			/* '$' is special only at end of pattern and only
+			 * if PATHMATCH_NO_ANCHOR_END is specified. */
+			if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
+				/* "dir" == "dir/" == "dir/." */
+				return (*pm_slashskip(s) == '\0');
+			}
+			/* Otherwise, '$' is not special. */
+			/* FALL THROUGH */
+		default:
+			if (*p != *s)
+				return (0);
+			break;
+		}
+		++p;
+		++s;
+	}
+}
+
+static int
+pm_w(const wchar_t *p, const wchar_t *s, int flags)
+{
+	const wchar_t *end;
+
+	/*
+	 * Ignore leading './', './/', '././', etc.
+	 */
+	if (s[0] == L'.' && s[1] == L'/')
+		s = pm_slashskip_w(s + 1);
+	if (p[0] == L'.' && p[1] == L'/')
+		p = pm_slashskip_w(p + 1);
+
+	for (;;) {
+		switch (*p) {
+		case L'\0':
+			if (s[0] == L'/') {
+				if (flags & PATHMATCH_NO_ANCHOR_END)
+					return (1);
+				/* "dir" == "dir/" == "dir/." */
+				s = pm_slashskip_w(s);
+			}
+			return (*s == L'\0');
+		case L'?':
+			/* ? always succeeds, unless we hit end of 's' */
+			if (*s == L'\0')
+				return (0);
+			break;
+		case L'*':
+			/* "*" == "**" == "***" ... */
+			while (*p == L'*')
+				++p;
+			/* Trailing '*' always succeeds. */
+			if (*p == L'\0')
+				return (1);
+			while (*s) {
+				if (archive_pathmatch_w(p, s, flags))
+					return (1);
+				++s;
+			}
+			return (0);
+		case L'[':
+			/*
+			 * Find the end of the [...] character class,
+			 * ignoring \] that might occur within the class.
+			 */
+			end = p + 1;
+			while (*end != L'\0' && *end != L']') {
+				if (*end == L'\\' && end[1] != L'\0')
+					++end;
+				++end;
+			}
+			if (*end == L']') {
+				/* We found [...], try to match it. */
+				if (!pm_list_w(p + 1, end, *s, flags))
+					return (0);
+				p = end; /* Jump to trailing ']' char. */
+				break;
+			} else
+				/* No final ']', so just match '['. */
+				if (*p != *s)
+					return (0);
+			break;
+		case L'\\':
+			/* Trailing '\\' matches itself. */
+			if (p[1] == L'\0') {
+				if (*s != L'\\')
+					return (0);
+			} else {
+				++p;
+				if (*p != *s)
+					return (0);
+			}
+			break;
+		case L'/':
+			if (*s != L'/' && *s != L'\0')
+				return (0);
+			/* Note: pattern "/\./" won't match "/";
+			 * pm_slashskip() correctly stops at backslash. */
+			p = pm_slashskip_w(p);
+			s = pm_slashskip_w(s);
+			if (*p == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END))
+				return (1);
+			--p; /* Counteract the increment below. */
+			--s;
+			break;
+		case L'$':
+			/* '$' is special only at end of pattern and only
+			 * if PATHMATCH_NO_ANCHOR_END is specified. */
+			if (p[1] == L'\0' && (flags & PATHMATCH_NO_ANCHOR_END)){
+				/* "dir" == "dir/" == "dir/." */
+				return (*pm_slashskip_w(s) == L'\0');
+			}
+			/* Otherwise, '$' is not special. */
+			/* FALL THROUGH */
+		default:
+			if (*p != *s)
+				return (0);
+			break;
+		}
+		++p;
+		++s;
+	}
+}
+
+/* Main entry point. */
+int
+__archive_pathmatch(const char *p, const char *s, int flags)
+{
+	/* Empty pattern only matches the empty string. */
+	if (p == NULL || *p == '\0')
+		return (s == NULL || *s == '\0');
+
+	/* Leading '^' anchors the start of the pattern. */
+	if (*p == '^') {
+		++p;
+		flags &= ~PATHMATCH_NO_ANCHOR_START;
+	}
+
+	if (*p == '/' && *s != '/')
+		return (0);
+
+	/* Certain patterns anchor implicitly. */
+	if (*p == '*' || *p == '/') {
+		while (*p == '/')
+			++p;
+		while (*s == '/')
+			++s;
+		return (pm(p, s, flags));
+	}
+
+	/* If start is unanchored, try to match start of each path element. */
+	if (flags & PATHMATCH_NO_ANCHOR_START) {
+		for ( ; s != NULL; s = strchr(s, '/')) {
+			if (*s == '/')
+				s++;
+			if (pm(p, s, flags))
+				return (1);
+		}
+		return (0);
+	}
+
+	/* Default: Match from beginning. */
+	return (pm(p, s, flags));
+}
+
+int
+__archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags)
+{
+	/* Empty pattern only matches the empty string. */
+	if (p == NULL || *p == L'\0')
+		return (s == NULL || *s == L'\0');
+
+	/* Leading '^' anchors the start of the pattern. */
+	if (*p == L'^') {
+		++p;
+		flags &= ~PATHMATCH_NO_ANCHOR_START;
+	}
+
+	if (*p == L'/' && *s != L'/')
+		return (0);
+
+	/* Certain patterns anchor implicitly. */
+	if (*p == L'*' || *p == L'/') {
+		while (*p == L'/')
+			++p;
+		while (*s == L'/')
+			++s;
+		return (pm_w(p, s, flags));
+	}
+
+	/* If start is unanchored, try to match start of each path element. */
+	if (flags & PATHMATCH_NO_ANCHOR_START) {
+		for ( ; s != NULL; s = wcschr(s, L'/')) {
+			if (*s == L'/')
+				s++;
+			if (pm_w(p, s, flags))
+				return (1);
+		}
+		return (0);
+	}
+
+	/* Default: Match from beginning. */
+	return (pm_w(p, s, flags));
+}
diff --git a/c/archive_pathmatch.h b/c/archive_pathmatch.h
new file mode 100644
--- /dev/null
+++ b/c/archive_pathmatch.h
@@ -0,0 +1,52 @@
+/*-
+ * 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
+ *    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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_PATHMATCH_H
+#define ARCHIVE_PATHMATCH_H
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+/* Don't anchor at beginning unless the pattern starts with "^" */
+#define PATHMATCH_NO_ANCHOR_START	1
+/* Don't anchor at end unless the pattern ends with "$" */
+#define PATHMATCH_NO_ANCHOR_END 	2
+
+/* Note that "^" and "$" are not special unless you set the corresponding
+ * flag above. */
+
+int __archive_pathmatch(const char *p, const char *s, int flags);
+int __archive_pathmatch_w(const wchar_t *p, const wchar_t *s, int flags);
+
+#define archive_pathmatch(p, s, f)	__archive_pathmatch(p, s, f)
+#define archive_pathmatch_w(p, s, f)	__archive_pathmatch_w(p, s, f)
+
+#endif
diff --git a/c/archive_platform.h b/c/archive_platform.h
new file mode 100644
--- /dev/null
+++ b/c/archive_platform.h
@@ -0,0 +1,202 @@
+/*-
+ * 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: head/lib/libarchive/archive_platform.h 201090 2009-12-28 02:22:04Z kientzle $
+ */
+
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
+
+/*
+ * 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
+
+/* archive.h and archive_entry.h require this. */
+#define	__LIBARCHIVE_BUILD 1
+
+#if defined(PLATFORM_CONFIG_H)
+/* Use hand-built config.h in environments that need it. */
+#include PLATFORM_CONFIG_H
+#elif defined(HAVE_CONFIG_H)
+/* Most POSIX platforms use the 'configure' script to build config.h */
+#include "config.h"
+#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
+
+/* On macOS check for some symbols based on the deployment target version.  */
+#if defined(__APPLE__)
+# undef HAVE_FUTIMENS
+# undef HAVE_UTIMENSAT
+# include <AvailabilityMacros.h>
+# if MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
+#  define HAVE_FUTIMENS 1
+#  define HAVE_UTIMENSAT 1
+# endif
+#endif
+
+/* It should be possible to get rid of this by extending the feature-test
+ * macros to cover Windows API functions, probably along with non-trivial
+ * refactoring of code to find structures that sit more cleanly on top of
+ * either Windows or Posix APIs. */
+#if (defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)
+#include "archive_windows.h"
+#else
+#define la_stat(path,stref)		stat(path,stref)
+#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.
+ */
+
+/* Get a real definition for __FBSDID or __RCSID if we can */
+#if HAVE_SYS_CDEFS_H
+#include <sys/cdefs.h>
+#endif
+
+/* If not, define them so as to avoid dangling semicolons. */
+#ifndef __FBSDID
+#define	__FBSDID(a)     struct _undefined_hack
+#endif
+#ifndef __RCSID
+#define	__RCSID(a)     struct _undefined_hack
+#endif
+
+/* Try to get standard C99-style integer type definitions. */
+#if HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+/* Borland warns about its own constants!  */
+#if defined(__BORLANDC__)
+# if HAVE_DECL_UINT64_MAX
+#  undef	UINT64_MAX
+#  undef	HAVE_DECL_UINT64_MAX
+# endif
+# if HAVE_DECL_UINT64_MIN
+#  undef	UINT64_MIN
+#  undef	HAVE_DECL_UINT64_MIN
+# endif
+# if HAVE_DECL_INT64_MAX
+#  undef	INT64_MAX
+#  undef	HAVE_DECL_INT64_MAX
+# endif
+# if HAVE_DECL_INT64_MIN
+#  undef	INT64_MIN
+#  undef	HAVE_DECL_INT64_MIN
+# endif
+#endif
+
+/* Some platforms lack the standard *_MAX definitions. */
+#if !HAVE_DECL_SIZE_MAX
+#define	SIZE_MAX (~(size_t)0)
+#endif
+#if !HAVE_DECL_SSIZE_MAX
+#define	SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1))
+#endif
+#if !HAVE_DECL_UINT32_MAX
+#define	UINT32_MAX (~(uint32_t)0)
+#endif
+#if !HAVE_DECL_INT32_MAX
+#define	INT32_MAX ((int32_t)(UINT32_MAX >> 1))
+#endif
+#if !HAVE_DECL_INT32_MIN
+#define	INT32_MIN ((int32_t)(~INT32_MAX))
+#endif
+#if !HAVE_DECL_UINT64_MAX
+#define	UINT64_MAX (~(uint64_t)0)
+#endif
+#if !HAVE_DECL_INT64_MAX
+#define	INT64_MAX ((int64_t)(UINT64_MAX >> 1))
+#endif
+#if !HAVE_DECL_INT64_MIN
+#define	INT64_MIN ((int64_t)(~INT64_MAX))
+#endif
+#if !HAVE_DECL_UINTMAX_MAX
+#define	UINTMAX_MAX (~(uintmax_t)0)
+#endif
+#if !HAVE_DECL_INTMAX_MAX
+#define	INTMAX_MAX ((intmax_t)(UINTMAX_MAX >> 1))
+#endif
+#if !HAVE_DECL_INTMAX_MIN
+#define	INTMAX_MIN ((intmax_t)(~INTMAX_MAX))
+#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
+
+/*
+ * glibc 2.24 deprecates readdir_r
+ */
+#if defined(HAVE_READDIR_R) && (!defined(__GLIBC__) || !defined(__GLIBC_MINOR__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24))
+#define	USE_READDIR_R	1
+#else
+#undef	USE_READDIR_R
+#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
+
+#if defined(__GNUC__) && (__GNUC__ >= 7)
+#define	__LA_FALLTHROUGH	__attribute__((fallthrough))
+#else
+#define	__LA_FALLTHROUGH
+#endif
+
+#endif /* !ARCHIVE_PLATFORM_H_INCLUDED */
diff --git a/c/archive_platform_acl.h b/c/archive_platform_acl.h
new file mode 100644
--- /dev/null
+++ b/c/archive_platform_acl.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 2017 Martin Matuska
+ * 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$
+ */
+
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
+
+#ifndef ARCHIVE_PLATFORM_ACL_H_INCLUDED
+#define ARCHIVE_PLATFORM_ACL_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST_COMMON
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+/*
+ * Determine what ACL types are supported
+ */
+#if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_SUNOS || ARCHIVE_ACL_LIBACL
+#define ARCHIVE_ACL_POSIX1E     1
+#endif
+
+#if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_SUNOS_NFS4 || \
+    ARCHIVE_ACL_DARWIN  || ARCHIVE_ACL_LIBRICHACL
+#define ARCHIVE_ACL_NFS4        1
+#endif
+
+#if ARCHIVE_ACL_POSIX1E || ARCHIVE_ACL_NFS4
+#define ARCHIVE_ACL_SUPPORT     1
+#endif
+
+#endif	/* ARCHIVE_PLATFORM_ACL_H_INCLUDED */
diff --git a/c/archive_platform_xattr.h b/c/archive_platform_xattr.h
new file mode 100644
--- /dev/null
+++ b/c/archive_platform_xattr.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2017 Martin Matuska
+ * 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$
+ */
+
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
+
+#ifndef ARCHIVE_PLATFORM_XATTR_H_INCLUDED
+#define ARCHIVE_PLATFORM_XATTR_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST_COMMON
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+/*
+ * Determine if we support extended attributes
+ */
+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_FREEBSD || \
+    ARCHIVE_XATTR_AIX
+#define ARCHIVE_XATTR_SUPPORT     1
+#endif
+
+#endif	/* ARCHIVE_PLATFORM_XATTR_H_INCLUDED */
diff --git a/c/archive_ppmd7.c b/c/archive_ppmd7.c
new file mode 100644
--- /dev/null
+++ b/c/archive_ppmd7.c
@@ -0,0 +1,1168 @@
+/* Ppmd7.c -- PPMdH codec
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#include "archive_platform.h"
+
+#include <memory.h>
+
+#include "archive_ppmd7_private.h"
+
+#ifdef PPMD_32BIT
+  #define Ppmd7_GetPtr(p, ptr) (ptr)
+  #define Ppmd7_GetContext(p, ptr) (ptr)
+  #define Ppmd7_GetStats(p, ctx) ((ctx)->Stats)
+#else
+  #define Ppmd7_GetPtr(p, offs) ((void *)((p)->Base + (offs)))
+  #define Ppmd7_GetContext(p, offs) ((CPpmd7_Context *)Ppmd7_GetPtr((p), (offs)))
+  #define Ppmd7_GetStats(p, ctx) ((CPpmd_State *)Ppmd7_GetPtr((p), ((ctx)->Stats)))
+#endif
+
+#define Ppmd7_GetBinSumm(p) \
+    &p->BinSumm[Ppmd7Context_OneState(p->MinContext)->Freq - 1][p->PrevSuccess + \
+    p->NS2BSIndx[Ppmd7_GetContext(p, p->MinContext->Suffix)->NumStats - 1] + \
+    (p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol]) + \
+    2 * p->HB2Flag[Ppmd7Context_OneState(p->MinContext)->Symbol] + \
+    ((p->RunLength >> 26) & 0x20)]
+
+#define kTopValue (1 << 24)
+#define MAX_FREQ 124
+#define UNIT_SIZE 12
+
+#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
+#define U2I(nu) (p->Units2Indx[(nu) - 1])
+#define I2U(indx) (p->Indx2Units[indx])
+
+#ifdef PPMD_32BIT
+  #define REF(ptr) (ptr)
+#else
+  #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
+#endif
+
+#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
+
+#define CTX(ref) ((CPpmd7_Context *)Ppmd7_GetContext(p, ref))
+#define STATS(ctx) Ppmd7_GetStats(p, ctx)
+#define ONE_STATE(ctx) Ppmd7Context_OneState(ctx)
+#define SUFFIX(ctx) CTX((ctx)->Suffix)
+
+static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
+static const Byte PPMD7_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
+
+typedef CPpmd7_Context * CTX_PTR;
+
+struct CPpmd7_Node_;
+
+typedef
+  #ifdef PPMD_32BIT
+    struct CPpmd7_Node_ *
+  #else
+    UInt32
+  #endif
+  CPpmd7_Node_Ref;
+
+typedef struct CPpmd7_Node_
+{
+  UInt16 Stamp; /* must be at offset 0 as CPpmd7_Context::NumStats. Stamp=0 means free */
+  UInt16 NU;
+  CPpmd7_Node_Ref Next; /* must be at offset >= 4 */
+  CPpmd7_Node_Ref Prev;
+} CPpmd7_Node;
+
+#ifdef PPMD_32BIT
+  #define NODE(ptr) (ptr)
+#else
+  #define NODE(offs) ((CPpmd7_Node *)(p->Base + (offs)))
+#endif
+
+static void Ppmd7_Update1(CPpmd7 *p);
+static void Ppmd7_Update1_0(CPpmd7 *p);
+static void Ppmd7_Update2(CPpmd7 *p);
+static void Ppmd7_UpdateBin(CPpmd7 *p);
+static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked,
+                                    UInt32 *scale);
+
+/* ----------- Base ----------- */
+
+static void Ppmd7_Construct(CPpmd7 *p)
+{
+  unsigned i, k, m;
+
+  p->Base = 0;
+
+  for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
+  {
+    unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
+    do { p->Units2Indx[k++] = (Byte)i; } while(--step);
+    p->Indx2Units[i] = (Byte)k;
+  }
+
+  p->NS2BSIndx[0] = (0 << 1);
+  p->NS2BSIndx[1] = (1 << 1);
+  memset(p->NS2BSIndx + 2, (2 << 1), 9);
+  memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
+
+  for (i = 0; i < 3; i++)
+    p->NS2Indx[i] = (Byte)i;
+  for (m = i, k = 1; i < 256; i++)
+  {
+    p->NS2Indx[i] = (Byte)m;
+    if (--k == 0)
+      k = (++m) - 2;
+  }
+
+  memset(p->HB2Flag, 0, 0x40);
+  memset(p->HB2Flag + 0x40, 8, 0x100 - 0x40);
+}
+
+static void Ppmd7_Free(CPpmd7 *p)
+{
+  free(p->Base);
+  p->Size = 0;
+  p->Base = 0;
+}
+
+static Bool Ppmd7_Alloc(CPpmd7 *p, UInt32 size)
+{
+  if (p->Base == 0 || p->Size != size)
+  {
+    /* RestartModel() below assumes that p->Size >= UNIT_SIZE
+       (see the calculation of m->MinContext). */
+    if (size < UNIT_SIZE) {
+      return False;
+    }
+    Ppmd7_Free(p);
+    p->AlignOffset =
+      #ifdef PPMD_32BIT
+        (4 - size) & 3;
+      #else
+        4 - (size & 3);
+      #endif
+    if ((p->Base = (Byte *)malloc(p->AlignOffset + size
+        #ifndef PPMD_32BIT
+        + UNIT_SIZE
+        #endif
+        )) == 0)
+      return False;
+    p->Size = size;
+  }
+  return True;
+}
+
+static void InsertNode(CPpmd7 *p, void *node, unsigned indx)
+{
+  *((CPpmd_Void_Ref *)node) = p->FreeList[indx];
+  p->FreeList[indx] = REF(node);
+}
+
+static void *RemoveNode(CPpmd7 *p, unsigned indx)
+{
+  CPpmd_Void_Ref *node = (CPpmd_Void_Ref *)Ppmd7_GetPtr(p, p->FreeList[indx]);
+  p->FreeList[indx] = *node;
+  return node;
+}
+
+static void SplitBlock(CPpmd7 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
+{
+  unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
+  ptr = (Byte *)ptr + U2B(I2U(newIndx));
+  if (I2U(i = U2I(nu)) != nu)
+  {
+    unsigned k = I2U(--i);
+    InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
+  }
+  InsertNode(p, ptr, i);
+}
+
+static void GlueFreeBlocks(CPpmd7 *p)
+{
+  #ifdef PPMD_32BIT
+  CPpmd7_Node headItem;
+  CPpmd7_Node_Ref head = &headItem;
+  #else
+  CPpmd7_Node_Ref head = p->AlignOffset + p->Size;
+  #endif
+  
+  CPpmd7_Node_Ref n = head;
+  unsigned i;
+
+  p->GlueCount = 255;
+
+  /* create doubly-linked list of free blocks */
+  for (i = 0; i < PPMD_NUM_INDEXES; i++)
+  {
+    UInt16 nu = I2U(i);
+    CPpmd7_Node_Ref next = (CPpmd7_Node_Ref)p->FreeList[i];
+    p->FreeList[i] = 0;
+    while (next != 0)
+    {
+      CPpmd7_Node *node = NODE(next);
+      node->Next = n;
+      n = NODE(n)->Prev = next;
+      next = *(const CPpmd7_Node_Ref *)node;
+      node->Stamp = 0;
+      node->NU = (UInt16)nu;
+    }
+  }
+  NODE(head)->Stamp = 1;
+  NODE(head)->Next = n;
+  NODE(n)->Prev = head;
+  if (p->LoUnit != p->HiUnit)
+    ((CPpmd7_Node *)p->LoUnit)->Stamp = 1;
+  
+  /* Glue free blocks */
+  while (n != head)
+  {
+    CPpmd7_Node *node = NODE(n);
+    UInt32 nu = (UInt32)node->NU;
+    for (;;)
+    {
+      CPpmd7_Node *node2 = NODE(n) + nu;
+      nu += node2->NU;
+      if (node2->Stamp != 0 || nu >= 0x10000)
+        break;
+      NODE(node2->Prev)->Next = node2->Next;
+      NODE(node2->Next)->Prev = node2->Prev;
+      node->NU = (UInt16)nu;
+    }
+    n = node->Next;
+  }
+  
+  /* Fill lists of free blocks */
+  for (n = NODE(head)->Next; n != head;)
+  {
+    CPpmd7_Node *node = NODE(n);
+    unsigned nu;
+    CPpmd7_Node_Ref next = node->Next;
+    for (nu = node->NU; nu > 128; nu -= 128, node += 128)
+      InsertNode(p, node, PPMD_NUM_INDEXES - 1);
+    if (I2U(i = U2I(nu)) != nu)
+    {
+      unsigned k = I2U(--i);
+      InsertNode(p, node + k, nu - k - 1);
+    }
+    InsertNode(p, node, i);
+    n = next;
+  }
+}
+
+static void *AllocUnitsRare(CPpmd7 *p, unsigned indx)
+{
+  unsigned i;
+  void *retVal;
+  if (p->GlueCount == 0)
+  {
+    GlueFreeBlocks(p);
+    if (p->FreeList[indx] != 0)
+      return RemoveNode(p, indx);
+  }
+  i = indx;
+  do
+  {
+    if (++i == PPMD_NUM_INDEXES)
+    {
+      UInt32 numBytes = U2B(I2U(indx));
+      p->GlueCount--;
+      return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);
+    }
+  }
+  while (p->FreeList[i] == 0);
+  retVal = RemoveNode(p, i);
+  SplitBlock(p, retVal, i, indx);
+  return retVal;
+}
+
+static void *AllocUnits(CPpmd7 *p, unsigned indx)
+{
+  UInt32 numBytes;
+  if (p->FreeList[indx] != 0)
+    return RemoveNode(p, indx);
+  numBytes = U2B(I2U(indx));
+  if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))
+  {
+    void *retVal = p->LoUnit;
+    p->LoUnit += numBytes;
+    return retVal;
+  }
+  return AllocUnitsRare(p, indx);
+}
+
+#define MyMem12Cpy(dest, src, num) \
+  { UInt32 *d = (UInt32 *)dest; const UInt32 *s = (const UInt32 *)src; UInt32 n = num; \
+    do { d[0] = s[0]; d[1] = s[1]; d[2] = s[2]; s += 3; d += 3; } while(--n); }
+
+static void *ShrinkUnits(CPpmd7 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
+{
+  unsigned i0 = U2I(oldNU);
+  unsigned i1 = U2I(newNU);
+  if (i0 == i1)
+    return oldPtr;
+  if (p->FreeList[i1] != 0)
+  {
+    void *ptr = RemoveNode(p, i1);
+    MyMem12Cpy(ptr, oldPtr, newNU);
+    InsertNode(p, oldPtr, i0);
+    return ptr;
+  }
+  SplitBlock(p, oldPtr, i0, i1);
+  return oldPtr;
+}
+
+#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))
+
+static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
+{
+  (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);
+  (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);
+}
+
+static void RestartModel(CPpmd7 *p)
+{
+  unsigned i, k, m;
+
+  memset(p->FreeList, 0, sizeof(p->FreeList));
+  p->Text = p->Base + p->AlignOffset;
+  p->HiUnit = p->Text + p->Size;
+  p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
+  p->GlueCount = 0;
+
+  p->OrderFall = p->MaxOrder;
+  p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
+  p->PrevSuccess = 0;
+
+  p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
+  p->MinContext->Suffix = 0;
+  p->MinContext->NumStats = 256;
+  p->MinContext->SummFreq = 256 + 1;
+  p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
+  p->LoUnit += U2B(256 / 2);
+  p->MinContext->Stats = REF(p->FoundState);
+  for (i = 0; i < 256; i++)
+  {
+    CPpmd_State *s = &p->FoundState[i];
+    s->Symbol = (Byte)i;
+    s->Freq = 1;
+    SetSuccessor(s, 0);
+  }
+
+  for (i = 0; i < 128; i++)
+    for (k = 0; k < 8; k++)
+    {
+      UInt16 *dest = p->BinSumm[i] + k;
+      UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 2));
+      for (m = 0; m < 64; m += 8)
+        dest[m] = val;
+    }
+  
+  for (i = 0; i < 25; i++)
+    for (k = 0; k < 16; k++)
+    {
+      CPpmd_See *s = &p->See[i][k];
+      s->Summ = (UInt16)((5 * i + 10) << (s->Shift = PPMD_PERIOD_BITS - 4));
+      s->Count = 4;
+    }
+}
+
+static void Ppmd7_Init(CPpmd7 *p, unsigned maxOrder)
+{
+  p->MaxOrder = maxOrder;
+  RestartModel(p);
+  p->DummySee.Shift = PPMD_PERIOD_BITS;
+  p->DummySee.Summ = 0; /* unused */
+  p->DummySee.Count = 64; /* unused */
+}
+
+static CTX_PTR CreateSuccessors(CPpmd7 *p, Bool skip)
+{
+  CPpmd_State upState;
+  CTX_PTR c = p->MinContext;
+  CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
+  CPpmd_State *ps[PPMD7_MAX_ORDER];
+  unsigned numPs = 0;
+  
+  if (!skip)
+    ps[numPs++] = p->FoundState;
+  
+  while (c->Suffix)
+  {
+    CPpmd_Void_Ref successor;
+    CPpmd_State *s;
+    c = SUFFIX(c);
+    if (c->NumStats != 1)
+    {
+      for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);
+    }
+    else
+      s = ONE_STATE(c);
+    successor = SUCCESSOR(s);
+    if (successor != upBranch)
+    {
+      c = CTX(successor);
+      if (numPs == 0)
+        return c;
+      break;
+    }
+    ps[numPs++] = s;
+  }
+  
+  upState.Symbol = *(const Byte *)Ppmd7_GetPtr(p, upBranch);
+  SetSuccessor(&upState, upBranch + 1);
+  
+  if (c->NumStats == 1)
+    upState.Freq = ONE_STATE(c)->Freq;
+  else
+  {
+    UInt32 cf, s0;
+    CPpmd_State *s;
+    for (s = STATS(c); s->Symbol != upState.Symbol; s++);
+    cf = s->Freq - 1;
+    s0 = c->SummFreq - c->NumStats - cf;
+    upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((2 * cf + 3 * s0 - 1) / (2 * s0))));
+  }
+
+  while (numPs != 0)
+  {
+    /* Create Child */
+    CTX_PTR c1; /* = AllocContext(p); */
+    if (p->HiUnit != p->LoUnit)
+      c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);
+    else if (p->FreeList[0] != 0)
+      c1 = (CTX_PTR)RemoveNode(p, 0);
+    else
+    {
+      c1 = (CTX_PTR)AllocUnitsRare(p, 0);
+      if (!c1)
+        return NULL;
+    }
+    c1->NumStats = 1;
+    *ONE_STATE(c1) = upState;
+    c1->Suffix = REF(c);
+    SetSuccessor(ps[--numPs], REF(c1));
+    c = c1;
+  }
+  
+  return c;
+}
+
+static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)
+{
+  CPpmd_State tmp = *t1;
+  *t1 = *t2;
+  *t2 = tmp;
+}
+
+static void UpdateModel(CPpmd7 *p)
+{
+  CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);
+  CTX_PTR c;
+  unsigned s0, ns;
+  
+  if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
+  {
+    c = SUFFIX(p->MinContext);
+    
+    if (c->NumStats == 1)
+    {
+      CPpmd_State *s = ONE_STATE(c);
+      if (s->Freq < 32)
+        s->Freq++;
+    }
+    else
+    {
+      CPpmd_State *s = STATS(c);
+      if (s->Symbol != p->FoundState->Symbol)
+      {
+        do { s++; } while (s->Symbol != p->FoundState->Symbol);
+        if (s[0].Freq >= s[-1].Freq)
+        {
+          SwapStates(&s[0], &s[-1]);
+          s--;
+        }
+      }
+      if (s->Freq < MAX_FREQ - 9)
+      {
+        s->Freq += 2;
+        c->SummFreq += 2;
+      }
+    }
+  }
+
+  if (p->OrderFall == 0)
+  {
+    p->MinContext = p->MaxContext = CreateSuccessors(p, True);
+    if (p->MinContext == 0)
+    {
+      RestartModel(p);
+      return;
+    }
+    SetSuccessor(p->FoundState, REF(p->MinContext));
+    return;
+  }
+  
+  *p->Text++ = p->FoundState->Symbol;
+  successor = REF(p->Text);
+  if (p->Text >= p->UnitsStart)
+  {
+    RestartModel(p);
+    return;
+  }
+  
+  if (fSuccessor)
+  {
+    if (fSuccessor <= successor)
+    {
+      CTX_PTR cs = CreateSuccessors(p, False);
+      if (cs == NULL)
+      {
+        RestartModel(p);
+        return;
+      }
+      fSuccessor = REF(cs);
+    }
+    if (--p->OrderFall == 0)
+    {
+      successor = fSuccessor;
+      p->Text -= (p->MaxContext != p->MinContext);
+    }
+  }
+  else
+  {
+    SetSuccessor(p->FoundState, successor);
+    fSuccessor = REF(p->MinContext);
+  }
+  
+  s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - (p->FoundState->Freq - 1);
+  
+  for (c = p->MaxContext; c != p->MinContext; c = SUFFIX(c))
+  {
+    unsigned ns1;
+    UInt32 cf, sf;
+    if ((ns1 = c->NumStats) != 1)
+    {
+      if ((ns1 & 1) == 0)
+      {
+        /* Expand for one UNIT */
+        unsigned oldNU = ns1 >> 1;
+        unsigned i = U2I(oldNU);
+        if (i != U2I(oldNU + 1))
+        {
+          void *ptr = AllocUnits(p, i + 1);
+          void *oldPtr;
+          if (!ptr)
+          {
+            RestartModel(p);
+            return;
+          }
+          oldPtr = STATS(c);
+          MyMem12Cpy(ptr, oldPtr, oldNU);
+          InsertNode(p, oldPtr, i);
+          c->Stats = STATS_REF(ptr);
+        }
+      }
+      c->SummFreq = (UInt16)(c->SummFreq + (2 * ns1 < ns) + 2 * ((4 * ns1 <= ns) & (c->SummFreq <= 8 * ns1)));
+    }
+    else
+    {
+      CPpmd_State *s = (CPpmd_State*)AllocUnits(p, 0);
+      if (!s)
+      {
+        RestartModel(p);
+        return;
+      }
+      *s = *ONE_STATE(c);
+      c->Stats = REF(s);
+      if (s->Freq < MAX_FREQ / 4 - 1)
+        s->Freq <<= 1;
+      else
+        s->Freq = MAX_FREQ - 4;
+      c->SummFreq = (UInt16)(s->Freq + p->InitEsc + (ns > 3));
+    }
+    cf = 2 * (UInt32)p->FoundState->Freq * (c->SummFreq + 6);
+    sf = (UInt32)s0 + c->SummFreq;
+    if (cf < 6 * sf)
+    {
+      cf = 1 + (cf > sf) + (cf >= 4 * sf);
+      c->SummFreq += 3;
+    }
+    else
+    {
+      cf = 4 + (cf >= 9 * sf) + (cf >= 12 * sf) + (cf >= 15 * sf);
+      c->SummFreq = (UInt16)(c->SummFreq + cf);
+    }
+    {
+      CPpmd_State *s = STATS(c) + ns1;
+      SetSuccessor(s, successor);
+      s->Symbol = p->FoundState->Symbol;
+      s->Freq = (Byte)cf;
+      c->NumStats = (UInt16)(ns1 + 1);
+    }
+  }
+  p->MaxContext = p->MinContext = CTX(fSuccessor);
+}
+  
+static void Rescale(CPpmd7 *p)
+{
+  unsigned i, adder, sumFreq, escFreq;
+  CPpmd_State *stats = STATS(p->MinContext);
+  CPpmd_State *s = p->FoundState;
+  {
+    CPpmd_State tmp = *s;
+    for (; s != stats; s--)
+      s[0] = s[-1];
+    *s = tmp;
+  }
+  escFreq = p->MinContext->SummFreq - s->Freq;
+  s->Freq += 4;
+  adder = (p->OrderFall != 0);
+  s->Freq = (Byte)((s->Freq + adder) >> 1);
+  sumFreq = s->Freq;
+  
+  i = p->MinContext->NumStats - 1;
+  do
+  {
+    escFreq -= (++s)->Freq;
+    s->Freq = (Byte)((s->Freq + adder) >> 1);
+    sumFreq += s->Freq;
+    if (s[0].Freq > s[-1].Freq)
+    {
+      CPpmd_State *s1 = s;
+      CPpmd_State tmp = *s1;
+      do
+        s1[0] = s1[-1];
+      while (--s1 != stats && tmp.Freq > s1[-1].Freq);
+      *s1 = tmp;
+    }
+  }
+  while (--i);
+  
+  if (s->Freq == 0)
+  {
+    unsigned numStats = p->MinContext->NumStats;
+    unsigned n0, n1;
+    do { i++; } while ((--s)->Freq == 0);
+    escFreq += i;
+    p->MinContext->NumStats = (UInt16)(p->MinContext->NumStats - i);
+    if (p->MinContext->NumStats == 1)
+    {
+      CPpmd_State tmp = *stats;
+      do
+      {
+        tmp.Freq = (Byte)(tmp.Freq - (tmp.Freq >> 1));
+        escFreq >>= 1;
+      }
+      while (escFreq > 1);
+      InsertNode(p, stats, U2I(((numStats + 1) >> 1)));
+      *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;
+      return;
+    }
+    n0 = (numStats + 1) >> 1;
+    n1 = (p->MinContext->NumStats + 1) >> 1;
+    if (n0 != n1)
+      p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
+  }
+  p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
+  p->FoundState = STATS(p->MinContext);
+}
+
+static CPpmd_See *Ppmd7_MakeEscFreq(CPpmd7 *p, unsigned numMasked, UInt32 *escFreq)
+{
+  CPpmd_See *see;
+  unsigned nonMasked = p->MinContext->NumStats - numMasked;
+  if (p->MinContext->NumStats != 256)
+  {
+    see = p->See[p->NS2Indx[nonMasked - 1]] +
+        (nonMasked < (unsigned)SUFFIX(p->MinContext)->NumStats - p->MinContext->NumStats) +
+        2 * (p->MinContext->SummFreq < 11 * p->MinContext->NumStats) +
+        4 * (numMasked > nonMasked) +
+        p->HiBitsFlag;
+    {
+      unsigned r = (see->Summ >> see->Shift);
+      see->Summ = (UInt16)(see->Summ - r);
+      *escFreq = r + (r == 0);
+    }
+  }
+  else
+  {
+    see = &p->DummySee;
+    *escFreq = 1;
+  }
+  return see;
+}
+
+static void NextContext(CPpmd7 *p)
+{
+  CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
+  if (p->OrderFall == 0 && (Byte *)c > p->Text)
+    p->MinContext = p->MaxContext = c;
+  else
+    UpdateModel(p);
+}
+
+static void Ppmd7_Update1(CPpmd7 *p)
+{
+  CPpmd_State *s = p->FoundState;
+  s->Freq += 4;
+  p->MinContext->SummFreq += 4;
+  if (s[0].Freq > s[-1].Freq)
+  {
+    SwapStates(&s[0], &s[-1]);
+    p->FoundState = --s;
+    if (s->Freq > MAX_FREQ)
+      Rescale(p);
+  }
+  NextContext(p);
+}
+
+static void Ppmd7_Update1_0(CPpmd7 *p)
+{
+  p->PrevSuccess = (2 * p->FoundState->Freq > p->MinContext->SummFreq);
+  p->RunLength += p->PrevSuccess;
+  p->MinContext->SummFreq += 4;
+  if ((p->FoundState->Freq += 4) > MAX_FREQ)
+    Rescale(p);
+  NextContext(p);
+}
+
+static void Ppmd7_UpdateBin(CPpmd7 *p)
+{
+  p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 128 ? 1: 0));
+  p->PrevSuccess = 1;
+  p->RunLength++;
+  NextContext(p);
+}
+
+static void Ppmd7_Update2(CPpmd7 *p)
+{
+  p->MinContext->SummFreq += 4;
+  if ((p->FoundState->Freq += 4) > MAX_FREQ)
+    Rescale(p);
+  p->RunLength = p->InitRL;
+  UpdateModel(p);
+}
+
+/* ---------- Decode ---------- */
+
+static Bool Ppmd_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+  unsigned i;
+  p->Low = p->Bottom = 0;
+  p->Range = 0xFFFFFFFF;
+  for (i = 0; i < 4; i++)
+    p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
+  return (p->Code < 0xFFFFFFFF);
+}
+
+static Bool Ppmd7z_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+  if (p->Stream->Read((void *)p->Stream) != 0)
+    return False;
+  return Ppmd_RangeDec_Init(p);
+}
+
+static Bool PpmdRAR_RangeDec_Init(CPpmd7z_RangeDec *p)
+{
+  if (!Ppmd_RangeDec_Init(p))
+    return False;
+  p->Bottom = 0x8000;
+  return True;
+}
+
+static UInt32 Range_GetThreshold(void *pp, UInt32 total)
+{
+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+  return (p->Code - p->Low) / (p->Range /= total);
+}
+
+static void Range_Normalize(CPpmd7z_RangeDec *p)
+{
+  while (1)
+  {
+    if((p->Low ^ (p->Low + p->Range)) >= kTopValue)
+    {
+      if(p->Range >= p->Bottom)
+        break;
+      else
+        p->Range = ((uint32_t)(-(int32_t)p->Low)) & (p->Bottom - 1);
+    }
+    p->Code = (p->Code << 8) | p->Stream->Read((void *)p->Stream);
+    p->Range <<= 8;
+    p->Low <<= 8;
+  }
+}
+
+static void Range_Decode_7z(void *pp, UInt32 start, UInt32 size)
+{
+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+  p->Code -= start * p->Range;
+  p->Range *= size;
+  Range_Normalize(p);
+}
+
+static void Range_Decode_RAR(void *pp, UInt32 start, UInt32 size)
+{
+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+  p->Low += start * p->Range;
+  p->Range *= size;
+  Range_Normalize(p);
+}
+
+static UInt32 Range_DecodeBit_7z(void *pp, UInt32 size0)
+{
+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+  UInt32 newBound = (p->Range >> 14) * size0;
+  UInt32 symbol;
+  if (p->Code < newBound)
+  {
+    symbol = 0;
+    p->Range = newBound;
+  }
+  else
+  {
+    symbol = 1;
+    p->Code -= newBound;
+    p->Range -= newBound;
+  }
+  Range_Normalize(p);
+  return symbol;
+}
+
+static UInt32 Range_DecodeBit_RAR(void *pp, UInt32 size0)
+{
+  CPpmd7z_RangeDec *p = (CPpmd7z_RangeDec *)pp;
+  UInt32 bit, value = p->p.GetThreshold(p, PPMD_BIN_SCALE);
+  if(value < size0)
+  {
+    bit = 0;
+    p->p.Decode(p, 0, size0);
+  }
+  else
+  {
+    bit = 1;
+    p->p.Decode(p, size0, PPMD_BIN_SCALE - size0);
+  }
+  return bit;
+}
+
+static void Ppmd7z_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
+{
+  p->p.GetThreshold = Range_GetThreshold;
+  p->p.Decode = Range_Decode_7z;
+  p->p.DecodeBit = Range_DecodeBit_7z;
+}
+
+static void PpmdRAR_RangeDec_CreateVTable(CPpmd7z_RangeDec *p)
+{
+  p->p.GetThreshold = Range_GetThreshold;
+  p->p.Decode = Range_Decode_RAR;
+  p->p.DecodeBit = Range_DecodeBit_RAR;
+}
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+static int Ppmd7_DecodeSymbol(CPpmd7 *p, IPpmd7_RangeDec *rc)
+{
+  size_t charMask[256 / sizeof(size_t)];
+  if (p->MinContext->NumStats != 1)
+  {
+    CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);
+    unsigned i;
+    UInt32 count, hiCnt;
+    if ((count = rc->GetThreshold(rc, p->MinContext->SummFreq)) < (hiCnt = s->Freq))
+    {
+      Byte symbol;
+      rc->Decode(rc, 0, s->Freq);
+      p->FoundState = s;
+      symbol = s->Symbol;
+      Ppmd7_Update1_0(p);
+      return symbol;
+    }
+    p->PrevSuccess = 0;
+    i = p->MinContext->NumStats - 1;
+    do
+    {
+      if ((hiCnt += (++s)->Freq) > count)
+      {
+        Byte symbol;
+        rc->Decode(rc, hiCnt - s->Freq, s->Freq);
+        p->FoundState = s;
+        symbol = s->Symbol;
+        Ppmd7_Update1(p);
+        return symbol;
+      }
+    }
+    while (--i);
+    if (count >= p->MinContext->SummFreq)
+      return -2;
+    p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];
+    rc->Decode(rc, hiCnt, p->MinContext->SummFreq - hiCnt);
+    PPMD_SetAllBitsIn256Bytes(charMask);
+    MASK(s->Symbol) = 0;
+    i = p->MinContext->NumStats - 1;
+    do { MASK((--s)->Symbol) = 0; } while (--i);
+  }
+  else
+  {
+    UInt16 *prob = Ppmd7_GetBinSumm(p);
+    if (rc->DecodeBit(rc, *prob) == 0)
+    {
+      Byte symbol;
+      *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+      symbol = (p->FoundState = Ppmd7Context_OneState(p->MinContext))->Symbol;
+      Ppmd7_UpdateBin(p);
+      return symbol;
+    }
+    *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+    p->InitEsc = PPMD7_kExpEscape[*prob >> 10];
+    PPMD_SetAllBitsIn256Bytes(charMask);
+    MASK(Ppmd7Context_OneState(p->MinContext)->Symbol) = 0;
+    p->PrevSuccess = 0;
+  }
+  for (;;)
+  {
+    CPpmd_State *ps[256], *s;
+    UInt32 freqSum, count, hiCnt;
+    CPpmd_See *see;
+    unsigned i, num, numMasked = p->MinContext->NumStats;
+    do
+    {
+      p->OrderFall++;
+      if (!p->MinContext->Suffix)
+        return -1;
+      p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);
+    }
+    while (p->MinContext->NumStats == numMasked);
+    hiCnt = 0;
+    s = Ppmd7_GetStats(p, p->MinContext);
+    i = 0;
+    num = p->MinContext->NumStats - numMasked;
+    do
+    {
+      int k = (int)(MASK(s->Symbol));
+      hiCnt += (s->Freq & k);
+      ps[i] = s++;
+      i -= k;
+    }
+    while (i != num);
+
+    see = Ppmd7_MakeEscFreq(p, numMasked, &freqSum);
+    freqSum += hiCnt;
+    count = rc->GetThreshold(rc, freqSum);
+
+    if (count < hiCnt)
+    {
+      Byte symbol;
+      CPpmd_State **pps = ps;
+      for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);
+      s = *pps;
+      rc->Decode(rc, hiCnt - s->Freq, s->Freq);
+      Ppmd_See_Update(see);
+      p->FoundState = s;
+      symbol = s->Symbol;
+      Ppmd7_Update2(p);
+      return symbol;
+    }
+    if (count >= freqSum)
+      return -2;
+    rc->Decode(rc, hiCnt, freqSum - hiCnt);
+    see->Summ = (UInt16)(see->Summ + freqSum);
+    do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);
+  }
+}
+
+/* ---------- Encode ---------- Ppmd7Enc.c */
+
+#define kTopValue (1 << 24)
+
+static void Ppmd7z_RangeEnc_Init(CPpmd7z_RangeEnc *p)
+{
+  p->Low = 0;
+  p->Range = 0xFFFFFFFF;
+  p->Cache = 0;
+  p->CacheSize = 1;
+}
+
+static void RangeEnc_ShiftLow(CPpmd7z_RangeEnc *p)
+{
+  if ((UInt32)p->Low < (UInt32)0xFF000000 || (unsigned)(p->Low >> 32) != 0)
+  {
+    Byte temp = p->Cache;
+    do
+    {
+      p->Stream->Write(p->Stream, (Byte)(temp + (Byte)(p->Low >> 32)));
+      temp = 0xFF;
+    }
+    while(--p->CacheSize != 0);
+    p->Cache = (Byte)((UInt32)p->Low >> 24);
+  }
+  p->CacheSize++;
+  p->Low = ((UInt32)p->Low << 8) & 0xFFFFFFFF;
+}
+
+static void RangeEnc_Encode(CPpmd7z_RangeEnc *p, UInt32 start, UInt32 size, UInt32 total)
+{
+  p->Low += (UInt64)start * (UInt64)(p->Range /= total);
+  p->Range *= size;
+  while (p->Range < kTopValue)
+  {
+    p->Range <<= 8;
+    RangeEnc_ShiftLow(p);
+  }
+}
+
+static void RangeEnc_EncodeBit_0(CPpmd7z_RangeEnc *p, UInt32 size0)
+{
+  p->Range = (p->Range >> 14) * size0;
+  while (p->Range < kTopValue)
+  {
+    p->Range <<= 8;
+    RangeEnc_ShiftLow(p);
+  }
+}
+
+static void RangeEnc_EncodeBit_1(CPpmd7z_RangeEnc *p, UInt32 size0)
+{
+  UInt32 newBound = (p->Range >> 14) * size0;
+  p->Low += newBound;
+  p->Range -= newBound;
+  while (p->Range < kTopValue)
+  {
+    p->Range <<= 8;
+    RangeEnc_ShiftLow(p);
+  }
+}
+
+static void Ppmd7z_RangeEnc_FlushData(CPpmd7z_RangeEnc *p)
+{
+  unsigned i;
+  for (i = 0; i < 5; i++)
+    RangeEnc_ShiftLow(p);
+}
+
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+static void Ppmd7_EncodeSymbol(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol)
+{
+  size_t charMask[256 / sizeof(size_t)];
+  if (p->MinContext->NumStats != 1)
+  {
+    CPpmd_State *s = Ppmd7_GetStats(p, p->MinContext);
+    UInt32 sum;
+    unsigned i;
+    if (s->Symbol == symbol)
+    {
+      RangeEnc_Encode(rc, 0, s->Freq, p->MinContext->SummFreq);
+      p->FoundState = s;
+      Ppmd7_Update1_0(p);
+      return;
+    }
+    p->PrevSuccess = 0;
+    sum = s->Freq;
+    i = p->MinContext->NumStats - 1;
+    do
+    {
+      if ((++s)->Symbol == symbol)
+      {
+        RangeEnc_Encode(rc, sum, s->Freq, p->MinContext->SummFreq);
+        p->FoundState = s;
+        Ppmd7_Update1(p);
+        return;
+      }
+      sum += s->Freq;
+    }
+    while (--i);
+    
+    p->HiBitsFlag = p->HB2Flag[p->FoundState->Symbol];
+    PPMD_SetAllBitsIn256Bytes(charMask);
+    MASK(s->Symbol) = 0;
+    i = p->MinContext->NumStats - 1;
+    do { MASK((--s)->Symbol) = 0; } while (--i);
+    RangeEnc_Encode(rc, sum, p->MinContext->SummFreq - sum, p->MinContext->SummFreq);
+  }
+  else
+  {
+    UInt16 *prob = Ppmd7_GetBinSumm(p);
+    CPpmd_State *s = Ppmd7Context_OneState(p->MinContext);
+    if (s->Symbol == symbol)
+    {
+      RangeEnc_EncodeBit_0(rc, *prob);
+      *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+      p->FoundState = s;
+      Ppmd7_UpdateBin(p);
+      return;
+    }
+    else
+    {
+      RangeEnc_EncodeBit_1(rc, *prob);
+      *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+      p->InitEsc = PPMD7_kExpEscape[*prob >> 10];
+      PPMD_SetAllBitsIn256Bytes(charMask);
+      MASK(s->Symbol) = 0;
+      p->PrevSuccess = 0;
+    }
+  }
+  for (;;)
+  {
+    UInt32 escFreq;
+    CPpmd_See *see;
+    CPpmd_State *s;
+    UInt32 sum;
+    unsigned i, numMasked = p->MinContext->NumStats;
+    do
+    {
+      p->OrderFall++;
+      if (!p->MinContext->Suffix)
+        return; /* EndMarker (symbol = -1) */
+      p->MinContext = Ppmd7_GetContext(p, p->MinContext->Suffix);
+    }
+    while (p->MinContext->NumStats == numMasked);
+    
+    see = Ppmd7_MakeEscFreq(p, numMasked, &escFreq);
+    s = Ppmd7_GetStats(p, p->MinContext);
+    sum = 0;
+    i = p->MinContext->NumStats;
+    do
+    {
+      int cur = s->Symbol;
+      if (cur == symbol)
+      {
+        UInt32 low = sum;
+        CPpmd_State *s1 = s;
+        do
+        {
+          sum += (s->Freq & (int)(MASK(s->Symbol)));
+          s++;
+        }
+        while (--i);
+        RangeEnc_Encode(rc, low, s1->Freq, sum + escFreq);
+        Ppmd_See_Update(see);
+        p->FoundState = s1;
+        Ppmd7_Update2(p);
+        return;
+      }
+      sum += (s->Freq & (int)(MASK(cur)));
+      MASK(cur) = 0;
+      s++;
+    }
+    while (--i);
+    
+    RangeEnc_Encode(rc, sum, escFreq, sum + escFreq);
+    see->Summ = (UInt16)(see->Summ + sum + escFreq);
+  }
+}
+
+const IPpmd7 __archive_ppmd7_functions =
+{
+  &Ppmd7_Construct,
+  &Ppmd7_Alloc,
+  &Ppmd7_Free,
+  &Ppmd7_Init,
+  &Ppmd7z_RangeDec_CreateVTable,
+  &PpmdRAR_RangeDec_CreateVTable,
+  &Ppmd7z_RangeDec_Init,
+  &PpmdRAR_RangeDec_Init,
+  &Ppmd7_DecodeSymbol,
+  &Ppmd7z_RangeEnc_Init,
+  &Ppmd7z_RangeEnc_FlushData,
+  &Ppmd7_EncodeSymbol
+};
diff --git a/c/archive_ppmd7_private.h b/c/archive_ppmd7_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_ppmd7_private.h
@@ -0,0 +1,119 @@
+/* Ppmd7.h -- PPMdH compression codec
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+/* This code supports virtual RangeDecoder and includes the implementation
+of RangeCoder from 7z, instead of RangeCoder from original PPMd var.H.
+If you need the compatibility with original PPMd var.H, you can use external RangeDecoder */
+
+#ifndef ARCHIVE_PPMD7_PRIVATE_H_INCLUDED
+#define ARCHIVE_PPMD7_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_ppmd_private.h"
+
+#define PPMD7_MIN_ORDER 2
+#define PPMD7_MAX_ORDER 64
+
+#define PPMD7_MIN_MEM_SIZE (1 << 11)
+#define PPMD7_MAX_MEM_SIZE (0xFFFFFFFFu - 12 * 3)
+
+struct CPpmd7_Context_;
+
+typedef
+  #ifdef PPMD_32BIT
+    struct CPpmd7_Context_ *
+  #else
+    UInt32
+  #endif
+  CPpmd7_Context_Ref;
+
+typedef struct CPpmd7_Context_
+{
+  UInt16 NumStats;
+  UInt16 SummFreq;
+  CPpmd_State_Ref Stats;
+  CPpmd7_Context_Ref Suffix;
+} CPpmd7_Context;
+
+#define Ppmd7Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)
+
+typedef struct
+{
+  CPpmd7_Context *MinContext, *MaxContext;
+  CPpmd_State *FoundState;
+  unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder, HiBitsFlag;
+  Int32 RunLength, InitRL; /* must be 32-bit at least */
+
+  UInt32 Size;
+  UInt32 GlueCount;
+  Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;
+  UInt32 AlignOffset;
+
+  Byte Indx2Units[PPMD_NUM_INDEXES];
+  Byte Units2Indx[128];
+  CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];
+  Byte NS2Indx[256], NS2BSIndx[256], HB2Flag[256];
+  CPpmd_See DummySee, See[25][16];
+  UInt16 BinSumm[128][64];
+} CPpmd7;
+
+/* ---------- Decode ---------- */
+
+typedef struct
+{
+  UInt32 (*GetThreshold)(void *p, UInt32 total);
+  void (*Decode)(void *p, UInt32 start, UInt32 size);
+  UInt32 (*DecodeBit)(void *p, UInt32 size0);
+} IPpmd7_RangeDec;
+
+typedef struct
+{
+  IPpmd7_RangeDec p;
+  UInt32 Range;
+  UInt32 Code;
+  UInt32 Low;
+  UInt32 Bottom;
+  IByteIn *Stream;
+} CPpmd7z_RangeDec;
+
+/* ---------- Encode ---------- */
+
+typedef struct
+{
+  UInt64 Low;
+  UInt32 Range;
+  Byte Cache;
+  UInt64 CacheSize;
+  IByteOut *Stream;
+} CPpmd7z_RangeEnc;
+
+typedef struct
+{
+  /* Base Functions */
+  void (*Ppmd7_Construct)(CPpmd7 *p);
+  Bool (*Ppmd7_Alloc)(CPpmd7 *p, UInt32 size);
+  void (*Ppmd7_Free)(CPpmd7 *p);
+  void (*Ppmd7_Init)(CPpmd7 *p, unsigned maxOrder);
+  #define Ppmd7_WasAllocated(p) ((p)->Base != NULL)
+
+  /* Decode Functions */
+  void (*Ppmd7z_RangeDec_CreateVTable)(CPpmd7z_RangeDec *p);
+  void (*PpmdRAR_RangeDec_CreateVTable)(CPpmd7z_RangeDec *p);
+  Bool (*Ppmd7z_RangeDec_Init)(CPpmd7z_RangeDec *p);
+  Bool (*PpmdRAR_RangeDec_Init)(CPpmd7z_RangeDec *p);
+  #define Ppmd7z_RangeDec_IsFinishedOK(p) ((p)->Code == 0)
+  int (*Ppmd7_DecodeSymbol)(CPpmd7 *p, IPpmd7_RangeDec *rc);
+
+  /* Encode Functions */
+  void (*Ppmd7z_RangeEnc_Init)(CPpmd7z_RangeEnc *p);
+  void (*Ppmd7z_RangeEnc_FlushData)(CPpmd7z_RangeEnc *p);
+
+  void (*Ppmd7_EncodeSymbol)(CPpmd7 *p, CPpmd7z_RangeEnc *rc, int symbol);
+} IPpmd7;
+
+extern const IPpmd7 __archive_ppmd7_functions;
+#endif
diff --git a/c/archive_ppmd8.c b/c/archive_ppmd8.c
new file mode 100644
--- /dev/null
+++ b/c/archive_ppmd8.c
@@ -0,0 +1,1287 @@
+/* Ppmd8.c -- PPMdI codec
+2016-05-21 : Igor Pavlov : Public domain
+This code is based on PPMd var.I (2002): Dmitry Shkarin : Public domain */
+
+#include "archive_platform.h"
+
+#include <string.h>
+
+#include "archive_ppmd8_private.h"
+
+const Byte PPMD8_kExpEscape[16] = { 25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2 };
+static const UInt16 kInitBinEsc[] = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051};
+
+#define MAX_FREQ 124
+#define UNIT_SIZE 12
+
+#define U2B(nu) ((UInt32)(nu) * UNIT_SIZE)
+#define U2I(nu) (p->Units2Indx[(nu) - 1])
+#define I2U(indx) (p->Indx2Units[indx])
+
+#ifdef PPMD_32BIT
+  #define REF(ptr) (ptr)
+#else
+  #define REF(ptr) ((UInt32)((Byte *)(ptr) - (p)->Base))
+#endif
+
+#define STATS_REF(ptr) ((CPpmd_State_Ref)REF(ptr))
+
+#define CTX(ref) ((CPpmd8_Context *)Ppmd8_GetContext(p, ref))
+#define STATS(ctx) Ppmd8_GetStats(p, ctx)
+#define ONE_STATE(ctx) Ppmd8Context_OneState(ctx)
+#define SUFFIX(ctx) CTX((ctx)->Suffix)
+
+#define kTop (1 << 24)
+#define kBot (1 << 15)
+
+typedef CPpmd8_Context * CTX_PTR;
+
+struct CPpmd8_Node_;
+
+typedef
+  #ifdef PPMD_32BIT
+    struct CPpmd8_Node_ *
+  #else
+    UInt32
+  #endif
+  CPpmd8_Node_Ref;
+
+typedef struct CPpmd8_Node_
+{
+  UInt32 Stamp;
+  CPpmd8_Node_Ref Next;
+  UInt32 NU;
+} CPpmd8_Node;
+
+#ifdef PPMD_32BIT
+  #define NODE(ptr) (ptr)
+#else
+  #define NODE(offs) ((CPpmd8_Node *)(p->Base + (offs)))
+#endif
+
+#define EMPTY_NODE 0xFFFFFFFF
+
+void Ppmd8_Construct(CPpmd8 *p)
+{
+  unsigned i, k, m;
+
+  p->Base = 0;
+
+  for (i = 0, k = 0; i < PPMD_NUM_INDEXES; i++)
+  {
+    unsigned step = (i >= 12 ? 4 : (i >> 2) + 1);
+    do { p->Units2Indx[k++] = (Byte)i; } while (--step);
+    p->Indx2Units[i] = (Byte)k;
+  }
+
+  p->NS2BSIndx[0] = (0 << 1);
+  p->NS2BSIndx[1] = (1 << 1);
+  memset(p->NS2BSIndx + 2, (2 << 1), 9);
+  memset(p->NS2BSIndx + 11, (3 << 1), 256 - 11);
+
+  for (i = 0; i < 5; i++)
+    p->NS2Indx[i] = (Byte)i;
+  for (m = i, k = 1; i < 260; i++)
+  {
+    p->NS2Indx[i] = (Byte)m;
+    if (--k == 0)
+      k = (++m) - 4;
+  }
+}
+
+void Ppmd8_Free(CPpmd8 *p)
+{
+  free(p->Base);
+  p->Size = 0;
+  p->Base = 0;
+}
+
+Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size)
+{
+  if (p->Base == 0 || p->Size != size)
+  {
+    Ppmd8_Free(p);
+    p->AlignOffset =
+      #ifdef PPMD_32BIT
+        (4 - size) & 3;
+      #else
+        4 - (size & 3);
+      #endif
+    if ((p->Base = (Byte *)malloc(p->AlignOffset + size)) == 0)
+      return False;
+    p->Size = size;
+  }
+  return True;
+}
+
+static void InsertNode(CPpmd8 *p, void *node, unsigned indx)
+{
+  ((CPpmd8_Node *)node)->Stamp = EMPTY_NODE;
+  ((CPpmd8_Node *)node)->Next = (CPpmd8_Node_Ref)p->FreeList[indx];
+  ((CPpmd8_Node *)node)->NU = I2U(indx);
+  p->FreeList[indx] = REF(node);
+  p->Stamps[indx]++;
+}
+
+static void *RemoveNode(CPpmd8 *p, unsigned indx)
+{
+  CPpmd8_Node *node = NODE((CPpmd8_Node_Ref)p->FreeList[indx]);
+  p->FreeList[indx] = node->Next;
+  p->Stamps[indx]--;
+  return node;
+}
+
+static void SplitBlock(CPpmd8 *p, void *ptr, unsigned oldIndx, unsigned newIndx)
+{
+  unsigned i, nu = I2U(oldIndx) - I2U(newIndx);
+  ptr = (Byte *)ptr + U2B(I2U(newIndx));
+  if (I2U(i = U2I(nu)) != nu)
+  {
+    unsigned k = I2U(--i);
+    InsertNode(p, ((Byte *)ptr) + U2B(k), nu - k - 1);
+  }
+  InsertNode(p, ptr, i);
+}
+
+static void GlueFreeBlocks(CPpmd8 *p)
+{
+  CPpmd8_Node_Ref head = 0;
+  CPpmd8_Node_Ref *prev = &head;
+  unsigned i;
+
+  p->GlueCount = 1 << 13;
+  memset(p->Stamps, 0, sizeof(p->Stamps));
+  
+  /* Order-0 context is always at top UNIT, so we don't need guard NODE at the end.
+     All blocks up to p->LoUnit can be free, so we need guard NODE at LoUnit. */
+  if (p->LoUnit != p->HiUnit)
+    ((CPpmd8_Node *)p->LoUnit)->Stamp = 0;
+
+  /* Glue free blocks */
+  for (i = 0; i < PPMD_NUM_INDEXES; i++)
+  {
+    CPpmd8_Node_Ref next = (CPpmd8_Node_Ref)p->FreeList[i];
+    p->FreeList[i] = 0;
+    while (next != 0)
+    {
+      CPpmd8_Node *node = NODE(next);
+      if (node->NU != 0)
+      {
+        CPpmd8_Node *node2;
+        *prev = next;
+        prev = &(node->Next);
+        while ((node2 = node + node->NU)->Stamp == EMPTY_NODE)
+        {
+          node->NU += node2->NU;
+          node2->NU = 0;
+        }
+      }
+      next = node->Next;
+    }
+  }
+  *prev = 0;
+  
+  /* Fill lists of free blocks */
+  while (head != 0)
+  {
+    CPpmd8_Node *node = NODE(head);
+    unsigned nu;
+    head = node->Next;
+    nu = node->NU;
+    if (nu == 0)
+      continue;
+    for (; nu > 128; nu -= 128, node += 128)
+      InsertNode(p, node, PPMD_NUM_INDEXES - 1);
+    if (I2U(i = U2I(nu)) != nu)
+    {
+      unsigned k = I2U(--i);
+      InsertNode(p, node + k, nu - k - 1);
+    }
+    InsertNode(p, node, i);
+  }
+}
+
+static void *AllocUnitsRare(CPpmd8 *p, unsigned indx)
+{
+  unsigned i;
+  void *retVal;
+  if (p->GlueCount == 0)
+  {
+    GlueFreeBlocks(p);
+    if (p->FreeList[indx] != 0)
+      return RemoveNode(p, indx);
+  }
+  i = indx;
+  do
+  {
+    if (++i == PPMD_NUM_INDEXES)
+    {
+      UInt32 numBytes = U2B(I2U(indx));
+      p->GlueCount--;
+      return ((UInt32)(p->UnitsStart - p->Text) > numBytes) ? (p->UnitsStart -= numBytes) : (NULL);
+    }
+  }
+  while (p->FreeList[i] == 0);
+  retVal = RemoveNode(p, i);
+  SplitBlock(p, retVal, i, indx);
+  return retVal;
+}
+
+static void *AllocUnits(CPpmd8 *p, unsigned indx)
+{
+  UInt32 numBytes;
+  if (p->FreeList[indx] != 0)
+    return RemoveNode(p, indx);
+  numBytes = U2B(I2U(indx));
+  if (numBytes <= (UInt32)(p->HiUnit - p->LoUnit))
+  {
+    void *retVal = p->LoUnit;
+    p->LoUnit += numBytes;
+    return retVal;
+  }
+  return AllocUnitsRare(p, indx);
+}
+
+#define MyMem12Cpy(dest, src, num) \
+  { UInt32 *d = (UInt32 *)dest; const UInt32 *z = (const UInt32 *)src; UInt32 n = num; \
+    do { d[0] = z[0]; d[1] = z[1]; d[2] = z[2]; z += 3; d += 3; } while (--n); }
+
+static void *ShrinkUnits(CPpmd8 *p, void *oldPtr, unsigned oldNU, unsigned newNU)
+{
+  unsigned i0 = U2I(oldNU);
+  unsigned i1 = U2I(newNU);
+  if (i0 == i1)
+    return oldPtr;
+  if (p->FreeList[i1] != 0)
+  {
+    void *ptr = RemoveNode(p, i1);
+    MyMem12Cpy(ptr, oldPtr, newNU);
+    InsertNode(p, oldPtr, i0);
+    return ptr;
+  }
+  SplitBlock(p, oldPtr, i0, i1);
+  return oldPtr;
+}
+
+static void FreeUnits(CPpmd8 *p, void *ptr, unsigned nu)
+{
+  InsertNode(p, ptr, U2I(nu));
+}
+
+static void SpecialFreeUnit(CPpmd8 *p, void *ptr)
+{
+  if ((Byte *)ptr != p->UnitsStart)
+    InsertNode(p, ptr, 0);
+  else
+  {
+    #ifdef PPMD8_FREEZE_SUPPORT
+    *(UInt32 *)ptr = EMPTY_NODE; /* it's used for (Flags == 0xFF) check in RemoveBinContexts */
+    #endif
+    p->UnitsStart += UNIT_SIZE;
+  }
+}
+
+static void *MoveUnitsUp(CPpmd8 *p, void *oldPtr, unsigned nu)
+{
+  unsigned indx = U2I(nu);
+  void *ptr;
+  if ((Byte *)oldPtr > p->UnitsStart + 16 * 1024 || REF(oldPtr) > p->FreeList[indx])
+    return oldPtr;
+  ptr = RemoveNode(p, indx);
+  MyMem12Cpy(ptr, oldPtr, nu);
+  if ((Byte*)oldPtr != p->UnitsStart)
+    InsertNode(p, oldPtr, indx);
+  else
+    p->UnitsStart += U2B(I2U(indx));
+  return ptr;
+}
+
+static void ExpandTextArea(CPpmd8 *p)
+{
+  UInt32 count[PPMD_NUM_INDEXES];
+  unsigned i;
+  memset(count, 0, sizeof(count));
+  if (p->LoUnit != p->HiUnit)
+    ((CPpmd8_Node *)p->LoUnit)->Stamp = 0;
+  
+  {
+    CPpmd8_Node *node = (CPpmd8_Node *)p->UnitsStart;
+    for (; node->Stamp == EMPTY_NODE; node += node->NU)
+    {
+      node->Stamp = 0;
+      count[U2I(node->NU)]++;
+    }
+    p->UnitsStart = (Byte *)node;
+  }
+  
+  for (i = 0; i < PPMD_NUM_INDEXES; i++)
+  {
+    CPpmd8_Node_Ref *next = (CPpmd8_Node_Ref *)&p->FreeList[i];
+    while (count[i] != 0)
+    {
+      CPpmd8_Node *node = NODE(*next);
+      while (node->Stamp == 0)
+      {
+        *next = node->Next;
+        node = NODE(*next);
+        p->Stamps[i]--;
+        if (--count[i] == 0)
+          break;
+      }
+      next = &node->Next;
+    }
+  }
+}
+
+#define SUCCESSOR(p) ((CPpmd_Void_Ref)((p)->SuccessorLow | ((UInt32)(p)->SuccessorHigh << 16)))
+
+static void SetSuccessor(CPpmd_State *p, CPpmd_Void_Ref v)
+{
+  (p)->SuccessorLow = (UInt16)((UInt32)(v) & 0xFFFF);
+  (p)->SuccessorHigh = (UInt16)(((UInt32)(v) >> 16) & 0xFFFF);
+}
+
+#define RESET_TEXT(offs) { p->Text = p->Base + p->AlignOffset + (offs); }
+
+static void RestartModel(CPpmd8 *p)
+{
+  unsigned i, k, m, r;
+
+  memset(p->FreeList, 0, sizeof(p->FreeList));
+  memset(p->Stamps, 0, sizeof(p->Stamps));
+  RESET_TEXT(0);
+  p->HiUnit = p->Text + p->Size;
+  p->LoUnit = p->UnitsStart = p->HiUnit - p->Size / 8 / UNIT_SIZE * 7 * UNIT_SIZE;
+  p->GlueCount = 0;
+
+  p->OrderFall = p->MaxOrder;
+  p->RunLength = p->InitRL = -(Int32)((p->MaxOrder < 12) ? p->MaxOrder : 12) - 1;
+  p->PrevSuccess = 0;
+
+  p->MinContext = p->MaxContext = (CTX_PTR)(p->HiUnit -= UNIT_SIZE); /* AllocContext(p); */
+  p->MinContext->Suffix = 0;
+  p->MinContext->NumStats = 255;
+  p->MinContext->Flags = 0;
+  p->MinContext->SummFreq = 256 + 1;
+  p->FoundState = (CPpmd_State *)p->LoUnit; /* AllocUnits(p, PPMD_NUM_INDEXES - 1); */
+  p->LoUnit += U2B(256 / 2);
+  p->MinContext->Stats = REF(p->FoundState);
+  for (i = 0; i < 256; i++)
+  {
+    CPpmd_State *s = &p->FoundState[i];
+    s->Symbol = (Byte)i;
+    s->Freq = 1;
+    SetSuccessor(s, 0);
+  }
+
+  for (i = m = 0; m < 25; m++)
+  {
+    while (p->NS2Indx[i] == m)
+      i++;
+    for (k = 0; k < 8; k++)
+    {
+      UInt16 val = (UInt16)(PPMD_BIN_SCALE - kInitBinEsc[k] / (i + 1));
+      UInt16 *dest = p->BinSumm[m] + k;
+      for (r = 0; r < 64; r += 8)
+        dest[r] = val;
+    }
+  }
+
+  for (i = m = 0; m < 24; m++)
+  {
+    while (p->NS2Indx[i + 3] == m + 3)
+      i++;
+    for (k = 0; k < 32; k++)
+    {
+      CPpmd_See *s = &p->See[m][k];
+      s->Summ = (UInt16)((2 * i + 5) << (s->Shift = PPMD_PERIOD_BITS - 4));
+      s->Count = 7;
+    }
+  }
+}
+
+void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod)
+{
+  p->MaxOrder = maxOrder;
+  p->RestoreMethod = restoreMethod;
+  RestartModel(p);
+  p->DummySee.Shift = PPMD_PERIOD_BITS;
+  p->DummySee.Summ = 0; /* unused */
+  p->DummySee.Count = 64; /* unused */
+}
+
+static void Refresh(CPpmd8 *p, CTX_PTR ctx, unsigned oldNU, unsigned scale)
+{
+  unsigned i = ctx->NumStats, escFreq, sumFreq, flags;
+  CPpmd_State *s = (CPpmd_State *)ShrinkUnits(p, STATS(ctx), oldNU, (i + 2) >> 1);
+  ctx->Stats = REF(s);
+  #ifdef PPMD8_FREEZE_SUPPORT
+  /* fixed over Shkarin's code. Fixed code is not compatible with original code for some files in FREEZE mode. */
+  scale |= (ctx->SummFreq >= ((UInt32)1 << 15));
+  #endif
+  flags = (ctx->Flags & (0x10 + 0x04 * scale)) + 0x08 * (s->Symbol >= 0x40);
+  escFreq = ctx->SummFreq - s->Freq;
+  sumFreq = (s->Freq = (Byte)((s->Freq + scale) >> scale));
+  do
+  {
+    escFreq -= (++s)->Freq;
+    sumFreq += (s->Freq = (Byte)((s->Freq + scale) >> scale));
+    flags |= 0x08 * (s->Symbol >= 0x40);
+  }
+  while (--i);
+  ctx->SummFreq = (UInt16)(sumFreq + ((escFreq + scale) >> scale));
+  ctx->Flags = (Byte)flags;
+}
+
+static void SwapStates(CPpmd_State *t1, CPpmd_State *t2)
+{
+  CPpmd_State tmp = *t1;
+  *t1 = *t2;
+  *t2 = tmp;
+}
+
+static CPpmd_Void_Ref CutOff(CPpmd8 *p, CTX_PTR ctx, unsigned order)
+{
+  int i;
+  unsigned tmp;
+  CPpmd_State *s;
+  
+  if (!ctx->NumStats)
+  {
+    s = ONE_STATE(ctx);
+    if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart)
+    {
+      if (order < p->MaxOrder)
+        SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));
+      else
+        SetSuccessor(s, 0);
+      if (SUCCESSOR(s) || order <= 9) /* O_BOUND */
+        return REF(ctx);
+    }
+    SpecialFreeUnit(p, ctx);
+    return 0;
+  }
+
+  ctx->Stats = STATS_REF(MoveUnitsUp(p, STATS(ctx), tmp = ((unsigned)ctx->NumStats + 2) >> 1));
+
+  for (s = STATS(ctx) + (i = ctx->NumStats); s >= STATS(ctx); s--)
+    if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) < p->UnitsStart)
+    {
+      CPpmd_State *s2 = STATS(ctx) + (i--);
+      SetSuccessor(s, 0);
+      SwapStates(s, s2);
+    }
+    else if (order < p->MaxOrder)
+      SetSuccessor(s, CutOff(p, CTX(SUCCESSOR(s)), order + 1));
+    else
+      SetSuccessor(s, 0);
+    
+  if (i != ctx->NumStats && order)
+  {
+    ctx->NumStats = (Byte)i;
+    s = STATS(ctx);
+    if (i < 0)
+    {
+      FreeUnits(p, s, tmp);
+      SpecialFreeUnit(p, ctx);
+      return 0;
+    }
+    if (i == 0)
+    {
+      ctx->Flags = (Byte)((ctx->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40));
+      *ONE_STATE(ctx) = *s;
+      FreeUnits(p, s, tmp);
+      /* 9.31: the code was fixed. It's was not BUG, if Freq <= MAX_FREQ = 124 */
+      ONE_STATE(ctx)->Freq = (Byte)(((unsigned)ONE_STATE(ctx)->Freq + 11) >> 3);
+    }
+    else
+      Refresh(p, ctx, tmp, ctx->SummFreq > 16 * i);
+  }
+  return REF(ctx);
+}
+
+#ifdef PPMD8_FREEZE_SUPPORT
+static CPpmd_Void_Ref RemoveBinContexts(CPpmd8 *p, CTX_PTR ctx, unsigned order)
+{
+  CPpmd_State *s;
+  if (!ctx->NumStats)
+  {
+    s = ONE_STATE(ctx);
+    if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)
+      SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));
+    else
+      SetSuccessor(s, 0);
+    /* Suffix context can be removed already, since different (high-order)
+       Successors may refer to same context. So we check Flags == 0xFF (Stamp == EMPTY_NODE) */
+    if (!SUCCESSOR(s) && (!SUFFIX(ctx)->NumStats || SUFFIX(ctx)->Flags == 0xFF))
+    {
+      FreeUnits(p, ctx, 1);
+      return 0;
+    }
+    else
+      return REF(ctx);
+  }
+
+  for (s = STATS(ctx) + ctx->NumStats; s >= STATS(ctx); s--)
+    if ((Byte *)Ppmd8_GetPtr(p, SUCCESSOR(s)) >= p->UnitsStart && order < p->MaxOrder)
+      SetSuccessor(s, RemoveBinContexts(p, CTX(SUCCESSOR(s)), order + 1));
+    else
+      SetSuccessor(s, 0);
+  
+  return REF(ctx);
+}
+#endif
+
+static UInt32 GetUsedMemory(const CPpmd8 *p)
+{
+  UInt32 v = 0;
+  unsigned i;
+  for (i = 0; i < PPMD_NUM_INDEXES; i++)
+    v += p->Stamps[i] * I2U(i);
+  return p->Size - (UInt32)(p->HiUnit - p->LoUnit) - (UInt32)(p->UnitsStart - p->Text) - U2B(v);
+}
+
+#ifdef PPMD8_FREEZE_SUPPORT
+  #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1, fSuccessor)
+#else
+  #define RESTORE_MODEL(c1, fSuccessor) RestoreModel(p, c1)
+#endif
+
+static void RestoreModel(CPpmd8 *p, CTX_PTR c1
+    #ifdef PPMD8_FREEZE_SUPPORT
+    , CTX_PTR fSuccessor
+    #endif
+    )
+{
+  CTX_PTR c;
+  CPpmd_State *s;
+  RESET_TEXT(0);
+  for (c = p->MaxContext; c != c1; c = SUFFIX(c))
+    if (--(c->NumStats) == 0)
+    {
+      s = STATS(c);
+      c->Flags = (Byte)((c->Flags & 0x10) + 0x08 * (s->Symbol >= 0x40));
+      *ONE_STATE(c) = *s;
+      SpecialFreeUnit(p, s);
+      ONE_STATE(c)->Freq = (Byte)(((unsigned)ONE_STATE(c)->Freq + 11) >> 3);
+    }
+    else
+      Refresh(p, c, (c->NumStats+3) >> 1, 0);
+ 
+  for (; c != p->MinContext; c = SUFFIX(c))
+    if (!c->NumStats)
+      ONE_STATE(c)->Freq = (Byte)(ONE_STATE(c)->Freq - (ONE_STATE(c)->Freq >> 1));
+    else if ((c->SummFreq += 4) > 128 + 4 * c->NumStats)
+      Refresh(p, c, (c->NumStats + 2) >> 1, 1);
+
+  #ifdef PPMD8_FREEZE_SUPPORT
+  if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+  {
+    p->MaxContext = fSuccessor;
+    p->GlueCount += !(p->Stamps[1] & 1);
+  }
+  else if (p->RestoreMethod == PPMD8_RESTORE_METHOD_FREEZE)
+  {
+    while (p->MaxContext->Suffix)
+      p->MaxContext = SUFFIX(p->MaxContext);
+    RemoveBinContexts(p, p->MaxContext, 0);
+    p->RestoreMethod++;
+    p->GlueCount = 0;
+    p->OrderFall = p->MaxOrder;
+  }
+  else
+  #endif
+  if (p->RestoreMethod == PPMD8_RESTORE_METHOD_RESTART || GetUsedMemory(p) < (p->Size >> 1))
+    RestartModel(p);
+  else
+  {
+    while (p->MaxContext->Suffix)
+      p->MaxContext = SUFFIX(p->MaxContext);
+    do
+    {
+      CutOff(p, p->MaxContext, 0);
+      ExpandTextArea(p);
+    }
+    while (GetUsedMemory(p) > 3 * (p->Size >> 2));
+    p->GlueCount = 0;
+    p->OrderFall = p->MaxOrder;
+  }
+}
+
+static CTX_PTR CreateSuccessors(CPpmd8 *p, Bool skip, CPpmd_State *s1, CTX_PTR c)
+{
+  CPpmd_State upState;
+  Byte flags;
+  CPpmd_Byte_Ref upBranch = (CPpmd_Byte_Ref)SUCCESSOR(p->FoundState);
+  /* fixed over Shkarin's code. Maybe it could work without + 1 too. */
+  CPpmd_State *ps[PPMD8_MAX_ORDER + 1];
+  unsigned numPs = 0;
+  
+  if (!skip)
+    ps[numPs++] = p->FoundState;
+  
+  while (c->Suffix)
+  {
+    CPpmd_Void_Ref successor;
+    CPpmd_State *s;
+    c = SUFFIX(c);
+    if (s1)
+    {
+      s = s1;
+      s1 = NULL;
+    }
+    else if (c->NumStats != 0)
+    {
+      for (s = STATS(c); s->Symbol != p->FoundState->Symbol; s++);
+      if (s->Freq < MAX_FREQ - 9)
+      {
+        s->Freq++;
+        c->SummFreq++;
+      }
+    }
+    else
+    {
+      s = ONE_STATE(c);
+      s->Freq = (Byte)(s->Freq + (!SUFFIX(c)->NumStats & (s->Freq < 24)));
+    }
+    successor = SUCCESSOR(s);
+    if (successor != upBranch)
+    {
+      c = CTX(successor);
+      if (numPs == 0)
+        return c;
+      break;
+    }
+    ps[numPs++] = s;
+  }
+  
+  upState.Symbol = *(const Byte *)Ppmd8_GetPtr(p, upBranch);
+  SetSuccessor(&upState, upBranch + 1);
+  flags = (Byte)(0x10 * (p->FoundState->Symbol >= 0x40) + 0x08 * (upState.Symbol >= 0x40));
+
+  if (c->NumStats == 0)
+    upState.Freq = ONE_STATE(c)->Freq;
+  else
+  {
+    UInt32 cf, s0;
+    CPpmd_State *s;
+    for (s = STATS(c); s->Symbol != upState.Symbol; s++);
+    cf = s->Freq - 1;
+    s0 = c->SummFreq - c->NumStats - cf;
+    upState.Freq = (Byte)(1 + ((2 * cf <= s0) ? (5 * cf > s0) : ((cf + 2 * s0 - 3) / s0)));
+  }
+
+  do
+  {
+    /* Create Child */
+    CTX_PTR c1; /* = AllocContext(p); */
+    if (p->HiUnit != p->LoUnit)
+      c1 = (CTX_PTR)(p->HiUnit -= UNIT_SIZE);
+    else if (p->FreeList[0] != 0)
+      c1 = (CTX_PTR)RemoveNode(p, 0);
+    else
+    {
+      c1 = (CTX_PTR)AllocUnitsRare(p, 0);
+      if (!c1)
+        return NULL;
+    }
+    c1->NumStats = 0;
+    c1->Flags = flags;
+    *ONE_STATE(c1) = upState;
+    c1->Suffix = REF(c);
+    SetSuccessor(ps[--numPs], REF(c1));
+    c = c1;
+  }
+  while (numPs != 0);
+  
+  return c;
+}
+
+static CTX_PTR ReduceOrder(CPpmd8 *p, CPpmd_State *s1, CTX_PTR c)
+{
+  CPpmd_State *s = NULL;
+  CTX_PTR c1 = c;
+  CPpmd_Void_Ref upBranch = REF(p->Text);
+  
+  #ifdef PPMD8_FREEZE_SUPPORT
+  /* The BUG in Shkarin's code was fixed: ps could overflow in CUT_OFF mode. */
+  CPpmd_State *ps[PPMD8_MAX_ORDER + 1];
+  unsigned numPs = 0;
+  ps[numPs++] = p->FoundState;
+  #endif
+
+  SetSuccessor(p->FoundState, upBranch);
+  p->OrderFall++;
+
+  for (;;)
+  {
+    if (s1)
+    {
+      c = SUFFIX(c);
+      s = s1;
+      s1 = NULL;
+    }
+    else
+    {
+      if (!c->Suffix)
+      {
+        #ifdef PPMD8_FREEZE_SUPPORT
+        if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+        {
+          do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);
+          RESET_TEXT(1);
+          p->OrderFall = 1;
+        }
+        #endif
+        return c;
+      }
+      c = SUFFIX(c);
+      if (c->NumStats)
+      {
+        if ((s = STATS(c))->Symbol != p->FoundState->Symbol)
+          do { s++; } while (s->Symbol != p->FoundState->Symbol);
+        if (s->Freq < MAX_FREQ - 9)
+        {
+          s->Freq += 2;
+          c->SummFreq += 2;
+        }
+      }
+      else
+      {
+        s = ONE_STATE(c);
+        s->Freq = (Byte)(s->Freq + (s->Freq < 32));
+      }
+    }
+    if (SUCCESSOR(s))
+      break;
+    #ifdef PPMD8_FREEZE_SUPPORT
+    ps[numPs++] = s;
+    #endif
+    SetSuccessor(s, upBranch);
+    p->OrderFall++;
+  }
+  
+  #ifdef PPMD8_FREEZE_SUPPORT
+  if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+  {
+    c = CTX(SUCCESSOR(s));
+    do { SetSuccessor(ps[--numPs], REF(c)); } while (numPs);
+    RESET_TEXT(1);
+    p->OrderFall = 1;
+    return c;
+  }
+  else
+  #endif
+  if (SUCCESSOR(s) <= upBranch)
+  {
+    CTX_PTR successor;
+    CPpmd_State *s2 = p->FoundState;
+    p->FoundState = s;
+
+    successor = CreateSuccessors(p, False, NULL, c);
+    if (successor == NULL)
+      SetSuccessor(s, 0);
+    else
+      SetSuccessor(s, REF(successor));
+    p->FoundState = s2;
+  }
+  
+  if (p->OrderFall == 1 && c1 == p->MaxContext)
+  {
+    SetSuccessor(p->FoundState, SUCCESSOR(s));
+    p->Text--;
+  }
+  if (SUCCESSOR(s) == 0)
+    return NULL;
+  return CTX(SUCCESSOR(s));
+}
+
+static void UpdateModel(CPpmd8 *p)
+{
+  CPpmd_Void_Ref successor, fSuccessor = SUCCESSOR(p->FoundState);
+  CTX_PTR c;
+  unsigned s0, ns, fFreq = p->FoundState->Freq;
+  Byte flag, fSymbol = p->FoundState->Symbol;
+  CPpmd_State *s = NULL;
+  
+  if (p->FoundState->Freq < MAX_FREQ / 4 && p->MinContext->Suffix != 0)
+  {
+    c = SUFFIX(p->MinContext);
+    
+    if (c->NumStats == 0)
+    {
+      s = ONE_STATE(c);
+      if (s->Freq < 32)
+        s->Freq++;
+    }
+    else
+    {
+      s = STATS(c);
+      if (s->Symbol != p->FoundState->Symbol)
+      {
+        do { s++; } while (s->Symbol != p->FoundState->Symbol);
+        if (s[0].Freq >= s[-1].Freq)
+        {
+          SwapStates(&s[0], &s[-1]);
+          s--;
+        }
+      }
+      if (s->Freq < MAX_FREQ - 9)
+      {
+        s->Freq += 2;
+        c->SummFreq += 2;
+      }
+    }
+  }
+  
+  c = p->MaxContext;
+  if (p->OrderFall == 0 && fSuccessor)
+  {
+    CTX_PTR cs = CreateSuccessors(p, True, s, p->MinContext);
+    if (cs == 0)
+    {
+      SetSuccessor(p->FoundState, 0);
+      RESTORE_MODEL(c, CTX(fSuccessor));
+    }
+    else
+    {
+      SetSuccessor(p->FoundState, REF(cs));
+      p->MaxContext = cs;
+    }
+    return;
+  }
+  
+  *p->Text++ = p->FoundState->Symbol;
+  successor = REF(p->Text);
+  if (p->Text >= p->UnitsStart)
+  {
+    RESTORE_MODEL(c, CTX(fSuccessor)); /* check it */
+    return;
+  }
+  
+  if (!fSuccessor)
+  {
+    CTX_PTR cs = ReduceOrder(p, s, p->MinContext);
+    if (cs == NULL)
+    {
+      RESTORE_MODEL(c, 0);
+      return;
+    }
+    fSuccessor = REF(cs);
+  }
+  else if ((Byte *)Ppmd8_GetPtr(p, fSuccessor) < p->UnitsStart)
+  {
+    CTX_PTR cs = CreateSuccessors(p, False, s, p->MinContext);
+    if (cs == NULL)
+    {
+      RESTORE_MODEL(c, 0);
+      return;
+    }
+    fSuccessor = REF(cs);
+  }
+  
+  if (--p->OrderFall == 0)
+  {
+    successor = fSuccessor;
+    p->Text -= (p->MaxContext != p->MinContext);
+  }
+  #ifdef PPMD8_FREEZE_SUPPORT
+  else if (p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE)
+  {
+    successor = fSuccessor;
+    RESET_TEXT(0);
+    p->OrderFall = 0;
+  }
+  #endif
+  
+  s0 = p->MinContext->SummFreq - (ns = p->MinContext->NumStats) - fFreq;
+  flag = (Byte)(0x08 * (fSymbol >= 0x40));
+  
+  for (; c != p->MinContext; c = SUFFIX(c))
+  {
+    unsigned ns1;
+    UInt32 cf, sf;
+    if ((ns1 = c->NumStats) != 0)
+    {
+      if ((ns1 & 1) != 0)
+      {
+        /* Expand for one UNIT */
+        unsigned oldNU = (ns1 + 1) >> 1;
+        unsigned i = U2I(oldNU);
+        if (i != U2I(oldNU + 1))
+        {
+          void *ptr = AllocUnits(p, i + 1);
+          void *oldPtr;
+          if (!ptr)
+          {
+            RESTORE_MODEL(c, CTX(fSuccessor));
+            return;
+          }
+          oldPtr = STATS(c);
+          MyMem12Cpy(ptr, oldPtr, oldNU);
+          InsertNode(p, oldPtr, i);
+          c->Stats = STATS_REF(ptr);
+        }
+      }
+      c->SummFreq = (UInt16)(c->SummFreq + (3 * ns1 + 1 < ns));
+    }
+    else
+    {
+      CPpmd_State *s2 = (CPpmd_State*)AllocUnits(p, 0);
+      if (!s2)
+      {
+        RESTORE_MODEL(c, CTX(fSuccessor));
+        return;
+      }
+      *s2 = *ONE_STATE(c);
+      c->Stats = REF(s2);
+      if (s2->Freq < MAX_FREQ / 4 - 1)
+        s2->Freq <<= 1;
+      else
+        s2->Freq = MAX_FREQ - 4;
+      c->SummFreq = (UInt16)(s2->Freq + p->InitEsc + (ns > 2));
+    }
+    cf = 2 * fFreq * (c->SummFreq + 6);
+    sf = (UInt32)s0 + c->SummFreq;
+    if (cf < 6 * sf)
+    {
+      cf = 1 + (cf > sf) + (cf >= 4 * sf);
+      c->SummFreq += 4;
+    }
+    else
+    {
+      cf = 4 + (cf > 9 * sf) + (cf > 12 * sf) + (cf > 15 * sf);
+      c->SummFreq = (UInt16)(c->SummFreq + cf);
+    }
+    {
+      CPpmd_State *s2 = STATS(c) + ns1 + 1;
+      SetSuccessor(s2, successor);
+      s2->Symbol = fSymbol;
+      s2->Freq = (Byte)cf;
+      c->Flags |= flag;
+      c->NumStats = (Byte)(ns1 + 1);
+    }
+  }
+  p->MaxContext = p->MinContext = CTX(fSuccessor);
+}
+  
+static void Rescale(CPpmd8 *p)
+{
+  unsigned i, adder, sumFreq, escFreq;
+  CPpmd_State *stats = STATS(p->MinContext);
+  CPpmd_State *s = p->FoundState;
+  {
+    CPpmd_State tmp = *s;
+    for (; s != stats; s--)
+      s[0] = s[-1];
+    *s = tmp;
+  }
+  escFreq = p->MinContext->SummFreq - s->Freq;
+  s->Freq += 4;
+  adder = (p->OrderFall != 0
+      #ifdef PPMD8_FREEZE_SUPPORT
+      || p->RestoreMethod > PPMD8_RESTORE_METHOD_FREEZE
+      #endif
+      );
+  s->Freq = (Byte)((s->Freq + adder) >> 1);
+  sumFreq = s->Freq;
+  
+  i = p->MinContext->NumStats;
+  do
+  {
+    escFreq -= (++s)->Freq;
+    s->Freq = (Byte)((s->Freq + adder) >> 1);
+    sumFreq += s->Freq;
+    if (s[0].Freq > s[-1].Freq)
+    {
+      CPpmd_State *s1 = s;
+      CPpmd_State tmp = *s1;
+      do
+        s1[0] = s1[-1];
+      while (--s1 != stats && tmp.Freq > s1[-1].Freq);
+      *s1 = tmp;
+    }
+  }
+  while (--i);
+  
+  if (s->Freq == 0)
+  {
+    unsigned numStats = p->MinContext->NumStats;
+    unsigned n0, n1;
+    do { i++; } while ((--s)->Freq == 0);
+    escFreq += i;
+    p->MinContext->NumStats = (Byte)(p->MinContext->NumStats - i);
+    if (p->MinContext->NumStats == 0)
+    {
+      CPpmd_State tmp = *stats;
+      tmp.Freq = (Byte)((2 * tmp.Freq + escFreq - 1) / escFreq);
+      if (tmp.Freq > MAX_FREQ / 3)
+        tmp.Freq = MAX_FREQ / 3;
+      InsertNode(p, stats, U2I((numStats + 2) >> 1));
+      p->MinContext->Flags = (Byte)((p->MinContext->Flags & 0x10) + 0x08 * (tmp.Symbol >= 0x40));
+      *(p->FoundState = ONE_STATE(p->MinContext)) = tmp;
+      return;
+    }
+    n0 = (numStats + 2) >> 1;
+    n1 = (p->MinContext->NumStats + 2) >> 1;
+    if (n0 != n1)
+      p->MinContext->Stats = STATS_REF(ShrinkUnits(p, stats, n0, n1));
+    p->MinContext->Flags &= ~0x08;
+    p->MinContext->Flags |= 0x08 * ((s = STATS(p->MinContext))->Symbol >= 0x40);
+    i = p->MinContext->NumStats;
+    do { p->MinContext->Flags |= 0x08*((++s)->Symbol >= 0x40); } while (--i);
+  }
+  p->MinContext->SummFreq = (UInt16)(sumFreq + escFreq - (escFreq >> 1));
+  p->MinContext->Flags |= 0x4;
+  p->FoundState = STATS(p->MinContext);
+}
+
+CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked1, UInt32 *escFreq)
+{
+  CPpmd_See *see;
+  if (p->MinContext->NumStats != 0xFF)
+  {
+    see = p->See[(unsigned)p->NS2Indx[(unsigned)p->MinContext->NumStats + 2] - 3] +
+        (p->MinContext->SummFreq > 11 * ((unsigned)p->MinContext->NumStats + 1)) +
+        2 * (unsigned)(2 * (unsigned)p->MinContext->NumStats <
+        ((unsigned)SUFFIX(p->MinContext)->NumStats + numMasked1)) +
+        p->MinContext->Flags;
+    {
+      unsigned r = (see->Summ >> see->Shift);
+      see->Summ = (UInt16)(see->Summ - r);
+      *escFreq = r + (r == 0);
+    }
+  }
+  else
+  {
+    see = &p->DummySee;
+    *escFreq = 1;
+  }
+  return see;
+}
+
+static void NextContext(CPpmd8 *p)
+{
+  CTX_PTR c = CTX(SUCCESSOR(p->FoundState));
+  if (p->OrderFall == 0 && (Byte *)c >= p->UnitsStart)
+    p->MinContext = p->MaxContext = c;
+  else
+  {
+    UpdateModel(p);
+    p->MinContext = p->MaxContext;
+  }
+}
+
+void Ppmd8_Update1(CPpmd8 *p)
+{
+  CPpmd_State *s = p->FoundState;
+  s->Freq += 4;
+  p->MinContext->SummFreq += 4;
+  if (s[0].Freq > s[-1].Freq)
+  {
+    SwapStates(&s[0], &s[-1]);
+    p->FoundState = --s;
+    if (s->Freq > MAX_FREQ)
+      Rescale(p);
+  }
+  NextContext(p);
+}
+
+void Ppmd8_Update1_0(CPpmd8 *p)
+{
+  p->PrevSuccess = (2 * p->FoundState->Freq >= p->MinContext->SummFreq);
+  p->RunLength += p->PrevSuccess;
+  p->MinContext->SummFreq += 4;
+  if ((p->FoundState->Freq += 4) > MAX_FREQ)
+    Rescale(p);
+  NextContext(p);
+}
+
+void Ppmd8_UpdateBin(CPpmd8 *p)
+{
+  p->FoundState->Freq = (Byte)(p->FoundState->Freq + (p->FoundState->Freq < 196));
+  p->PrevSuccess = 1;
+  p->RunLength++;
+  NextContext(p);
+}
+
+void Ppmd8_Update2(CPpmd8 *p)
+{
+  p->MinContext->SummFreq += 4;
+  if ((p->FoundState->Freq += 4) > MAX_FREQ)
+    Rescale(p);
+  p->RunLength = p->InitRL;
+  UpdateModel(p);
+  p->MinContext = p->MaxContext;
+}
+
+/* Ppmd8Dec.c -- PPMdI Decoder
+2010-04-16 : Igor Pavlov : Public domain
+This code is based on:
+  PPMd var.I (2002): Dmitry Shkarin : Public domain
+  Carryless rangecoder (1999): Dmitry Subbotin : Public domain */
+
+Bool Ppmd8_RangeDec_Init(CPpmd8 *p)
+{
+  unsigned i;
+  p->Low = 0;
+  p->Range = 0xFFFFFFFF;
+  p->Code = 0;
+  for (i = 0; i < 4; i++)
+    p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In);
+  return (p->Code < 0xFFFFFFFF);
+}
+
+static UInt32 RangeDec_GetThreshold(CPpmd8 *p, UInt32 total)
+{
+  return p->Code / (p->Range /= total);
+}
+
+static void RangeDec_Decode(CPpmd8 *p, UInt32 start, UInt32 size)
+{
+  start *= p->Range;
+  p->Low += start;
+  p->Code -= start;
+  p->Range *= size;
+
+  while ((p->Low ^ (p->Low + p->Range)) < kTop ||
+      (p->Range < kBot && ((p->Range = (0 - p->Low) & (kBot - 1)), 1)))
+  {
+    p->Code = (p->Code << 8) | p->Stream.In->Read(p->Stream.In);
+    p->Range <<= 8;
+    p->Low <<= 8;
+  }
+}
+
+#define MASK(sym) ((signed char *)charMask)[sym]
+
+int Ppmd8_DecodeSymbol(CPpmd8 *p)
+{
+  size_t charMask[256 / sizeof(size_t)];
+  if (p->MinContext->NumStats != 0)
+  {
+    CPpmd_State *s = Ppmd8_GetStats(p, p->MinContext);
+    unsigned i;
+    UInt32 count, hiCnt;
+    if ((count = RangeDec_GetThreshold(p, p->MinContext->SummFreq)) < (hiCnt = s->Freq))
+    {
+      Byte symbol;
+      RangeDec_Decode(p, 0, s->Freq);
+      p->FoundState = s;
+      symbol = s->Symbol;
+      Ppmd8_Update1_0(p);
+      return symbol;
+    }
+    p->PrevSuccess = 0;
+    i = p->MinContext->NumStats;
+    do
+    {
+      if ((hiCnt += (++s)->Freq) > count)
+      {
+        Byte symbol;
+        RangeDec_Decode(p, hiCnt - s->Freq, s->Freq);
+        p->FoundState = s;
+        symbol = s->Symbol;
+        Ppmd8_Update1(p);
+        return symbol;
+      }
+    }
+    while (--i);
+    if (count >= p->MinContext->SummFreq)
+      return -2;
+    RangeDec_Decode(p, hiCnt, p->MinContext->SummFreq - hiCnt);
+    PPMD_SetAllBitsIn256Bytes(charMask);
+    MASK(s->Symbol) = 0;
+    i = p->MinContext->NumStats;
+    do { MASK((--s)->Symbol) = 0; } while (--i);
+  }
+  else
+  {
+    UInt16 *prob = Ppmd8_GetBinSumm(p);
+    if (((p->Code / (p->Range >>= 14)) < *prob))
+    {
+      Byte symbol;
+      RangeDec_Decode(p, 0, *prob);
+      *prob = (UInt16)PPMD_UPDATE_PROB_0(*prob);
+      symbol = (p->FoundState = Ppmd8Context_OneState(p->MinContext))->Symbol;
+      Ppmd8_UpdateBin(p);
+      return symbol;
+    }
+    RangeDec_Decode(p, *prob, (1 << 14) - *prob);
+    *prob = (UInt16)PPMD_UPDATE_PROB_1(*prob);
+    p->InitEsc = PPMD8_kExpEscape[*prob >> 10];
+    PPMD_SetAllBitsIn256Bytes(charMask);
+    MASK(Ppmd8Context_OneState(p->MinContext)->Symbol) = 0;
+    p->PrevSuccess = 0;
+  }
+  for (;;)
+  {
+    CPpmd_State *ps[256], *s;
+    UInt32 freqSum, count, hiCnt;
+    CPpmd_See *see;
+    unsigned i, num, numMasked = p->MinContext->NumStats;
+    do
+    {
+      p->OrderFall++;
+      if (!p->MinContext->Suffix)
+        return -1;
+      p->MinContext = Ppmd8_GetContext(p, p->MinContext->Suffix);
+    }
+    while (p->MinContext->NumStats == numMasked);
+    hiCnt = 0;
+    s = Ppmd8_GetStats(p, p->MinContext);
+    i = 0;
+    num = p->MinContext->NumStats - numMasked;
+    do
+    {
+      int k = (int)(MASK(s->Symbol));
+      hiCnt += (s->Freq & k);
+      ps[i] = s++;
+      i -= k;
+    }
+    while (i != num);
+    
+    see = Ppmd8_MakeEscFreq(p, numMasked, &freqSum);
+    freqSum += hiCnt;
+    count = RangeDec_GetThreshold(p, freqSum);
+    
+    if (count < hiCnt)
+    {
+      Byte symbol;
+      CPpmd_State **pps = ps;
+      for (hiCnt = 0; (hiCnt += (*pps)->Freq) <= count; pps++);
+      s = *pps;
+      RangeDec_Decode(p, hiCnt - s->Freq, s->Freq);
+      Ppmd_See_Update(see);
+      p->FoundState = s;
+      symbol = s->Symbol;
+      Ppmd8_Update2(p);
+      return symbol;
+    }
+    if (count >= freqSum)
+      return -2;
+    RangeDec_Decode(p, hiCnt, freqSum - hiCnt);
+    see->Summ = (UInt16)(see->Summ + freqSum);
+    do { MASK(ps[--i]->Symbol) = 0; } while (i != 0);
+  }
+}
+
+/* H->I changes:
+  NS2Indx
+  GlewCount, and Glue method
+  BinSum
+  See / EscFreq
+  CreateSuccessors updates more suffix contexts
+  UpdateModel consts.
+  PrevSuccess Update
+*/
+
+const IPpmd8 __archive_ppmd8_functions =
+{
+  &Ppmd8_Construct,
+  &Ppmd8_Alloc,
+  &Ppmd8_Free,
+  &Ppmd8_Init,
+  &Ppmd8_RangeDec_Init,
+  &Ppmd8_DecodeSymbol,
+};
diff --git a/c/archive_ppmd8_private.h b/c/archive_ppmd8_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_ppmd8_private.h
@@ -0,0 +1,148 @@
+/* Ppmd8.h -- PPMdI codec
+2011-01-27 : Igor Pavlov : Public domain
+This code is based on:
+  PPMd var.I (2002): Dmitry Shkarin : Public domain
+  Carryless rangecoder (1999): Dmitry Subbotin : Public domain */
+
+#ifndef ARCHIVE_PPMD8_PRIVATE_H_INCLUDED
+#define ARCHIVE_PPMD8_PRIVATE_H_INCLUDED
+
+#include "archive_ppmd_private.h"
+
+#define PPMD8_MIN_ORDER 2
+#define PPMD8_MAX_ORDER 16
+
+struct CPpmd8_Context_;
+
+typedef
+  #ifdef PPMD_32BIT
+    struct CPpmd8_Context_ *
+  #else
+    UInt32
+  #endif
+  CPpmd8_Context_Ref;
+
+#pragma pack(push, 1)
+
+typedef struct CPpmd8_Context_
+{
+  Byte NumStats;
+  Byte Flags;
+  UInt16 SummFreq;
+  CPpmd_State_Ref Stats;
+  CPpmd8_Context_Ref Suffix;
+} CPpmd8_Context;
+
+#pragma pack(pop)
+
+#define Ppmd8Context_OneState(p) ((CPpmd_State *)&(p)->SummFreq)
+
+/* The BUG in Shkarin's code for FREEZE mode was fixed, but that fixed
+   code is not compatible with original code for some files compressed
+   in FREEZE mode. So we disable FREEZE mode support. */
+
+enum
+{
+  PPMD8_RESTORE_METHOD_RESTART,
+  PPMD8_RESTORE_METHOD_CUT_OFF
+  #ifdef PPMD8_FREEZE_SUPPORT
+  , PPMD8_RESTORE_METHOD_FREEZE
+  #endif
+};
+
+typedef struct
+{
+  CPpmd8_Context *MinContext, *MaxContext;
+  CPpmd_State *FoundState;
+  unsigned OrderFall, InitEsc, PrevSuccess, MaxOrder;
+  Int32 RunLength, InitRL; /* must be 32-bit at least */
+
+  UInt32 Size;
+  UInt32 GlueCount;
+  Byte *Base, *LoUnit, *HiUnit, *Text, *UnitsStart;
+  UInt32 AlignOffset;
+  unsigned RestoreMethod;
+
+  /* Range Coder */
+  UInt32 Range;
+  UInt32 Code;
+  UInt32 Low;
+  union
+  {
+    IByteIn *In;
+    IByteOut *Out;
+  } Stream;
+
+  Byte Indx2Units[PPMD_NUM_INDEXES];
+  Byte Units2Indx[128];
+  CPpmd_Void_Ref FreeList[PPMD_NUM_INDEXES];
+  UInt32 Stamps[PPMD_NUM_INDEXES];
+
+  Byte NS2BSIndx[256], NS2Indx[260];
+  CPpmd_See DummySee, See[24][32];
+  UInt16 BinSumm[25][64];
+} CPpmd8;
+
+void Ppmd8_Construct(CPpmd8 *p);
+Bool Ppmd8_Alloc(CPpmd8 *p, UInt32 size);
+void Ppmd8_Free(CPpmd8 *p);
+void Ppmd8_Init(CPpmd8 *p, unsigned maxOrder, unsigned restoreMethod);
+#define Ppmd8_WasAllocated(p) ((p)->Base != NULL)
+
+
+/* ---------- Internal Functions ---------- */
+
+extern const Byte PPMD8_kExpEscape[16];
+
+#ifdef PPMD_32BIT
+  #define Ppmd8_GetPtr(p, ptr) (ptr)
+  #define Ppmd8_GetContext(p, ptr) (ptr)
+  #define Ppmd8_GetStats(p, ctx) ((ctx)->Stats)
+#else
+  #define Ppmd8_GetPtr(p, offs) ((void *)((p)->Base + (offs)))
+  #define Ppmd8_GetContext(p, offs) ((CPpmd8_Context *)Ppmd8_GetPtr((p), (offs)))
+  #define Ppmd8_GetStats(p, ctx) ((CPpmd_State *)Ppmd8_GetPtr((p), ((ctx)->Stats)))
+#endif
+
+void Ppmd8_Update1(CPpmd8 *p);
+void Ppmd8_Update1_0(CPpmd8 *p);
+void Ppmd8_Update2(CPpmd8 *p);
+void Ppmd8_UpdateBin(CPpmd8 *p);
+
+#define Ppmd8_GetBinSumm(p) \
+    &p->BinSumm[p->NS2Indx[Ppmd8Context_OneState(p->MinContext)->Freq - 1]][ \
+    p->NS2BSIndx[Ppmd8_GetContext(p, p->MinContext->Suffix)->NumStats] + \
+    p->PrevSuccess + p->MinContext->Flags + ((p->RunLength >> 26) & 0x20)]
+
+CPpmd_See *Ppmd8_MakeEscFreq(CPpmd8 *p, unsigned numMasked, UInt32 *scale);
+
+
+/* ---------- Decode ---------- */
+
+Bool Ppmd8_RangeDec_Init(CPpmd8 *p);
+#define Ppmd8_RangeDec_IsFinishedOK(p) ((p)->Code == 0)
+int Ppmd8_DecodeSymbol(CPpmd8 *p); /* returns: -1 as EndMarker, -2 as DataError */
+
+/* ---------- Encode ---------- */
+
+#define Ppmd8_RangeEnc_Init(p) { (p)->Low = 0; (p)->Range = 0xFFFFFFFF; }
+void Ppmd8_RangeEnc_FlushData(CPpmd8 *p);
+void Ppmd8_EncodeSymbol(CPpmd8 *p, int symbol); /* symbol = -1 means EndMarker */
+
+typedef struct
+{
+  /* Base Functions */
+  void (*Ppmd8_Construct)(CPpmd8 *p);
+  Bool (*Ppmd8_Alloc)(CPpmd8 *p, UInt32 size);
+  void (*Ppmd8_Free)(CPpmd8 *p);
+  void (*Ppmd8_Init)(CPpmd8 *p, unsigned max_order, unsigned restore_method);
+  #define Ppmd7_WasAllocated(p) ((p)->Base != NULL)
+
+  /* Decode Functions */
+  int (*Ppmd8_RangeDec_Init)(CPpmd8 *p);
+  int (*Ppmd8_DecodeSymbol)(CPpmd8 *p);
+} IPpmd8;
+
+extern const IPpmd8 __archive_ppmd8_functions;
+
+#endif
diff --git a/c/archive_ppmd_private.h b/c/archive_ppmd_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_ppmd_private.h
@@ -0,0 +1,151 @@
+/* Ppmd.h -- PPMD codec common code
+2010-03-12 : Igor Pavlov : Public domain
+This code is based on PPMd var.H (2001): Dmitry Shkarin : Public domain */
+
+#ifndef ARCHIVE_PPMD_PRIVATE_H_INCLUDED
+#define ARCHIVE_PPMD_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <stddef.h>
+
+#include "archive_read_private.h"
+
+/*** Begin defined in Types.h ***/
+
+#if !defined(ZCONF_H)
+typedef unsigned char Byte;
+#endif
+typedef short Int16;
+typedef unsigned short UInt16;
+
+#ifdef _LZMA_UINT32_IS_ULONG
+typedef long Int32;
+typedef unsigned long UInt32;
+#else
+typedef int Int32;
+typedef unsigned int UInt32;
+#endif
+
+#ifdef _SZ_NO_INT_64
+
+/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers.
+   NOTES: Some code will work incorrectly in that case! */
+
+typedef long Int64;
+typedef unsigned long UInt64;
+
+#else
+
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+typedef __int64 Int64;
+typedef unsigned __int64 UInt64;
+#define UINT64_CONST(n) n
+#else
+typedef long long int Int64;
+typedef unsigned long long int UInt64;
+#define UINT64_CONST(n) n ## ULL
+#endif
+
+#endif
+
+typedef int Bool;
+#define True 1
+#define False 0
+
+/* The following interfaces use first parameter as pointer to structure */
+
+typedef struct
+{
+  struct archive_read *a;
+  Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */
+} IByteIn;
+
+typedef struct
+{
+  struct archive_write *a;
+  void (*Write)(void *p, Byte b);
+} IByteOut;
+
+/*** End defined in Types.h ***/
+/*** Begin defined in CpuArch.h ***/
+
+#if defined(_M_IX86) || defined(__i386__)
+#define MY_CPU_X86
+#endif
+
+#if defined(MY_CPU_X86) || defined(_M_ARM)
+#define MY_CPU_32BIT
+#endif
+
+#ifdef MY_CPU_32BIT
+#define PPMD_32BIT
+#endif
+
+/*** End defined in CpuArch.h ***/
+
+#define PPMD_INT_BITS 7
+#define PPMD_PERIOD_BITS 7
+#define PPMD_BIN_SCALE (1 << (PPMD_INT_BITS + PPMD_PERIOD_BITS))
+
+#define PPMD_GET_MEAN_SPEC(summ, shift, round) (((summ) + (1 << ((shift) - (round)))) >> (shift))
+#define PPMD_GET_MEAN(summ) PPMD_GET_MEAN_SPEC((summ), PPMD_PERIOD_BITS, 2)
+#define PPMD_UPDATE_PROB_0(prob) ((prob) + (1 << PPMD_INT_BITS) - PPMD_GET_MEAN(prob))
+#define PPMD_UPDATE_PROB_1(prob) ((prob) - PPMD_GET_MEAN(prob))
+
+#define PPMD_N1 4
+#define PPMD_N2 4
+#define PPMD_N3 4
+#define PPMD_N4 ((128 + 3 - 1 * PPMD_N1 - 2 * PPMD_N2 - 3 * PPMD_N3) / 4)
+#define PPMD_NUM_INDEXES (PPMD_N1 + PPMD_N2 + PPMD_N3 + PPMD_N4)
+
+/* SEE-contexts for PPM-contexts with masked symbols */
+typedef struct
+{
+  UInt16 Summ; /* Freq */
+  Byte Shift;  /* Speed of Freq change; low Shift is for fast change */
+  Byte Count;  /* Count to next change of Shift */
+} CPpmd_See;
+
+#define Ppmd_See_Update(p)  if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) \
+    { (p)->Summ <<= 1; (p)->Count = (Byte)(3 << (p)->Shift++); }
+
+typedef struct
+{
+  Byte Symbol;
+  Byte Freq;
+  UInt16 SuccessorLow;
+  UInt16 SuccessorHigh;
+} CPpmd_State;
+
+typedef
+  #ifdef PPMD_32BIT
+    CPpmd_State *
+  #else
+    UInt32
+  #endif
+  CPpmd_State_Ref;
+
+typedef
+  #ifdef PPMD_32BIT
+    void *
+  #else
+    UInt32
+  #endif
+  CPpmd_Void_Ref;
+
+typedef
+  #ifdef PPMD_32BIT
+    Byte *
+  #else
+    UInt32
+  #endif
+  CPpmd_Byte_Ref;
+
+#define PPMD_SetAllBitsIn256Bytes(p) \
+  { unsigned j; for (j = 0; j < 256 / sizeof(p[0]); j += 8) { \
+  p[j+7] = p[j+6] = p[j+5] = p[j+4] = p[j+3] = p[j+2] = p[j+1] = p[j+0] = ~(size_t)0; }}
+
+#endif
diff --git a/c/archive_private.h b/c/archive_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_private.h
@@ -0,0 +1,176 @@
+/*-
+ * 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: head/lib/libarchive/archive_private.h 201098 2009-12-28 02:58:14Z kientzle $
+ */
+
+#ifndef ARCHIVE_PRIVATE_H_INCLUDED
+#define ARCHIVE_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#if HAVE_ICONV_H
+#include <iconv.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+
+#if defined(__GNUC__) && (__GNUC__ > 2 || \
+			  (__GNUC__ == 2 && __GNUC_MINOR__ >= 5))
+#define	__LA_DEAD	__attribute__((__noreturn__))
+#else
+#define	__LA_DEAD
+#endif
+
+#define	ARCHIVE_WRITE_MAGIC	(0xb0c5c0deU)
+#define	ARCHIVE_READ_MAGIC	(0xdeb0c5U)
+#define	ARCHIVE_WRITE_DISK_MAGIC (0xc001b0c5U)
+#define	ARCHIVE_READ_DISK_MAGIC (0xbadb0c5U)
+#define	ARCHIVE_MATCH_MAGIC	(0xcad11c9U)
+
+#define	ARCHIVE_STATE_NEW	1U
+#define	ARCHIVE_STATE_HEADER	2U
+#define	ARCHIVE_STATE_DATA	4U
+#define	ARCHIVE_STATE_EOF	0x10U
+#define	ARCHIVE_STATE_CLOSED	0x20U
+#define	ARCHIVE_STATE_FATAL	0x8000U
+#define	ARCHIVE_STATE_ANY	(0xFFFFU & ~ARCHIVE_STATE_FATAL)
+
+struct archive_vtable {
+	int	(*archive_close)(struct archive *);
+	int	(*archive_free)(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, int64_t);
+
+	int	(*archive_read_next_header)(struct archive *,
+	    struct archive_entry **);
+	int	(*archive_read_next_header2)(struct archive *,
+	    struct archive_entry *);
+	int	(*archive_read_data_block)(struct archive *,
+	    const void **, size_t *, int64_t *);
+
+	int	(*archive_filter_count)(struct archive *);
+	int64_t (*archive_filter_bytes)(struct archive *, int);
+	int	(*archive_filter_code)(struct archive *, int);
+	const char * (*archive_filter_name)(struct archive *, int);
+};
+
+struct archive_string_conv;
+
+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 int	magic;
+	unsigned int	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;
+
+	/* Number of file entries processed. */
+	int		  file_count;
+
+	int		  archive_error_number;
+	const char	 *error;
+	struct archive_string	error_string;
+
+	char *current_code;
+	unsigned current_codepage; /* Current ACP(ANSI CodePage). */
+	unsigned current_oemcp; /* Current OEMCP(OEM CodePage). */
+	struct archive_string_conv *sconv;
+
+	/*
+	 * Used by archive_read_data() to track blocks and copy
+	 * data to client buffers, filling gaps with zero bytes.
+	 */
+	const char	 *read_data_block;
+	int64_t		  read_data_offset;
+	int64_t		  read_data_output_offset;
+	size_t		  read_data_remaining;
+
+	/*
+	 * Used by formats/filters to determine the amount of data
+	 * requested from a call to archive_read_data(). This is only
+	 * useful when the format/filter has seek support.
+	 */
+	char		  read_data_is_posix_read;
+	size_t		  read_data_requested;
+};
+
+/* Check magic value and state; return(ARCHIVE_FATAL) if it isn't valid. */
+int	__archive_check_magic(struct archive *, unsigned int magic,
+	    unsigned int state, const char *func);
+#define	archive_check_magic(a, expected_magic, allowed_states, function_name) \
+	do { \
+		int magic_test = __archive_check_magic((a), (expected_magic), \
+			(allowed_states), (function_name)); \
+		if (magic_test == ARCHIVE_FATAL) \
+			return ARCHIVE_FATAL; \
+	} while (0)
+
+void	__archive_errx(int retvalue, const char *msg) __LA_DEAD;
+
+void	__archive_ensure_cloexec_flag(int fd);
+int	__archive_mktemp(const char *tmpdir);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+int	__archive_mkstemp(wchar_t *template);
+#else
+int	__archive_mkstemp(char *template);
+#endif
+
+int	__archive_clean(struct archive *);
+
+void __archive_reset_read_data(struct archive *);
+
+#define	err_combine(a,b)	((a) < (b) ? (a) : (b))
+
+#if defined(__BORLANDC__) || (defined(_MSC_VER) &&  _MSC_VER <= 1300)
+# define	ARCHIVE_LITERAL_LL(x)	x##i64
+# define	ARCHIVE_LITERAL_ULL(x)	x##ui64
+#else
+# define	ARCHIVE_LITERAL_LL(x)	x##ll
+# define	ARCHIVE_LITERAL_ULL(x)	x##ull
+#endif
+
+#endif
diff --git a/c/archive_random.c b/c/archive_random.c
new file mode 100644
--- /dev/null
+++ b/c/archive_random.c
@@ -0,0 +1,272 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * 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$");
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+
+#ifdef HAVE_FCNTL
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+static void arc4random_buf(void *, size_t);
+
+#endif /* HAVE_ARC4RANDOM_BUF */
+
+#include "archive.h"
+#include "archive_random_private.h"
+
+#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
+#include <wincrypt.h>
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC	0
+#endif
+
+/*
+ * Random number generator function.
+ * This simply calls arc4random_buf function if the platform provides it.
+ */
+
+int
+archive_random(void *buf, size_t nbytes)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	HCRYPTPROV hProv;
+	BOOL success;
+
+	success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
+	    CRYPT_VERIFYCONTEXT);
+	if (!success && GetLastError() == (DWORD)NTE_BAD_KEYSET) {
+		success = CryptAcquireContext(&hProv, NULL, NULL,
+		    PROV_RSA_FULL, CRYPT_NEWKEYSET);
+	}
+	if (success) {
+		success = CryptGenRandom(hProv, (DWORD)nbytes, (BYTE*)buf);
+		CryptReleaseContext(hProv, 0);
+		if (success)
+			return ARCHIVE_OK;
+	}
+	/* TODO: Does this case really happen? */
+	return ARCHIVE_FAILED;
+#else
+	arc4random_buf(buf, nbytes);
+	return ARCHIVE_OK;
+#endif
+}
+
+#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+
+/*	$OpenBSD: arc4random.c,v 1.24 2013/06/11 16:59:50 deraadt Exp $	*/
+/*
+ * Copyright (c) 1996, David Mazieres <dm@uun.org>
+ * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Arc4 random number generator for OpenBSD.
+ *
+ * This code is derived from section 17.1 of Applied Cryptography,
+ * second edition, which describes a stream cipher allegedly
+ * compatible with RSA Labs "RC4" cipher (the actual description of
+ * which is a trade secret).  The same algorithm is used as a stream
+ * cipher called "arcfour" in Tatu Ylonen's ssh package.
+ *
+ * RC4 is a registered trademark of RSA Laboratories.
+ */
+
+#ifdef __GNUC__
+#define inline __inline
+#else				/* !__GNUC__ */
+#define inline
+#endif				/* !__GNUC__ */
+
+struct arc4_stream {
+	uint8_t i;
+	uint8_t j;
+	uint8_t s[256];
+};
+
+#define	RANDOMDEV	"/dev/urandom"
+#define	KEYSIZE		128
+#ifdef HAVE_PTHREAD_H
+static pthread_mutex_t	arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
+#define	_ARC4_LOCK()	pthread_mutex_lock(&arc4random_mtx);
+#define	_ARC4_UNLOCK()  pthread_mutex_unlock(&arc4random_mtx);
+#else
+#define	_ARC4_LOCK()
+#define	_ARC4_UNLOCK()
+#endif
+
+static int rs_initialized;
+static struct arc4_stream rs;
+static pid_t arc4_stir_pid;
+static int arc4_count;
+
+static inline uint8_t arc4_getbyte(void);
+static void arc4_stir(void);
+
+static inline void
+arc4_init(void)
+{
+	int     n;
+
+	for (n = 0; n < 256; n++)
+		rs.s[n] = n;
+	rs.i = 0;
+	rs.j = 0;
+}
+
+static inline void
+arc4_addrandom(u_char *dat, int datlen)
+{
+	int     n;
+	uint8_t si;
+
+	rs.i--;
+	for (n = 0; n < 256; n++) {
+		rs.i = (rs.i + 1);
+		si = rs.s[rs.i];
+		rs.j = (rs.j + si + dat[n % datlen]);
+		rs.s[rs.i] = rs.s[rs.j];
+		rs.s[rs.j] = si;
+	}
+	rs.j = rs.i;
+}
+
+static void
+arc4_stir(void)
+{
+	int done, fd, i;
+	struct {
+		struct timeval	tv;
+		pid_t		pid;
+		u_char	 	rnd[KEYSIZE];
+	} rdat;
+
+	if (!rs_initialized) {
+		arc4_init();
+		rs_initialized = 1;
+	}
+	done = 0;
+	fd = open(RANDOMDEV, O_RDONLY | O_CLOEXEC, 0);
+	if (fd >= 0) {
+		if (read(fd, &rdat, KEYSIZE) == KEYSIZE)
+			done = 1;
+		(void)close(fd);
+	}
+	if (!done) {
+		(void)gettimeofday(&rdat.tv, NULL);
+		rdat.pid = getpid();
+		/* We'll just take whatever was on the stack too... */
+	}
+
+	arc4_addrandom((u_char *)&rdat, KEYSIZE);
+
+	/*
+	 * Discard early keystream, as per recommendations in:
+	 * "(Not So) Random Shuffles of RC4" by Ilya Mironov.
+	 * As per the Network Operations Division, cryptographic requirements
+	 * published on wikileaks on March 2017.
+	 */
+
+	for (i = 0; i < 3072; i++)
+		(void)arc4_getbyte();
+	arc4_count = 1600000;
+}
+
+static void
+arc4_stir_if_needed(void)
+{
+	pid_t pid = getpid();
+
+	if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid) {
+		arc4_stir_pid = pid;
+		arc4_stir();
+	}
+}
+
+static inline uint8_t
+arc4_getbyte(void)
+{
+	uint8_t si, sj;
+
+	rs.i = (rs.i + 1);
+	si = rs.s[rs.i];
+	rs.j = (rs.j + si);
+	sj = rs.s[rs.j];
+	rs.s[rs.i] = sj;
+	rs.s[rs.j] = si;
+	return (rs.s[(si + sj) & 0xff]);
+}
+
+static void
+arc4random_buf(void *_buf, size_t n)
+{
+	u_char *buf = (u_char *)_buf;
+	_ARC4_LOCK();
+	arc4_stir_if_needed();
+	while (n--) {
+		if (--arc4_count <= 0)
+			arc4_stir();
+		buf[n] = arc4_getbyte();
+	}
+	_ARC4_UNLOCK();
+}
+
+#endif /* !HAVE_ARC4RANDOM_BUF */
diff --git a/c/archive_random_private.h b/c/archive_random_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_random_private.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * 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.
+ */
+
+#ifndef ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
+#define ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/* Random number generator. */
+int archive_random(void *buf, size_t nbytes);
+
+#endif /* ARCHIVE_RANDOM_PRIVATE_H_INCLUDED */
diff --git a/c/archive_rb.c b/c/archive_rb.c
new file mode 100644
--- /dev/null
+++ b/c/archive_rb.c
@@ -0,0 +1,709 @@
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas <matt@3am-software.com>.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ *
+ * Based on: NetBSD: rb.c,v 1.6 2010/04/30 13:58:09 joerg Exp
+ */
+
+#include "archive_platform.h"
+
+#include <stddef.h>
+
+#include "archive_rb.h"
+
+/* Keep in sync with archive_rb.h */
+#define	RB_DIR_LEFT		0
+#define	RB_DIR_RIGHT		1
+#define	RB_DIR_OTHER		1
+#define	rb_left			rb_nodes[RB_DIR_LEFT]
+#define	rb_right		rb_nodes[RB_DIR_RIGHT]
+
+#define	RB_FLAG_POSITION	0x2
+#define	RB_FLAG_RED		0x1
+#define	RB_FLAG_MASK		(RB_FLAG_POSITION|RB_FLAG_RED)
+#define	RB_FATHER(rb) \
+    ((struct archive_rb_node *)((rb)->rb_info & ~RB_FLAG_MASK))
+#define	RB_SET_FATHER(rb, father) \
+    ((void)((rb)->rb_info = (uintptr_t)(father)|((rb)->rb_info & RB_FLAG_MASK)))
+
+#define	RB_SENTINEL_P(rb)	((rb) == NULL)
+#define	RB_LEFT_SENTINEL_P(rb)	RB_SENTINEL_P((rb)->rb_left)
+#define	RB_RIGHT_SENTINEL_P(rb)	RB_SENTINEL_P((rb)->rb_right)
+#define	RB_FATHER_SENTINEL_P(rb) RB_SENTINEL_P(RB_FATHER((rb)))
+#define	RB_CHILDLESS_P(rb) \
+    (RB_SENTINEL_P(rb) || (RB_LEFT_SENTINEL_P(rb) && RB_RIGHT_SENTINEL_P(rb)))
+#define	RB_TWOCHILDREN_P(rb) \
+    (!RB_SENTINEL_P(rb) && !RB_LEFT_SENTINEL_P(rb) && !RB_RIGHT_SENTINEL_P(rb))
+
+#define	RB_POSITION(rb)	\
+    (((rb)->rb_info & RB_FLAG_POSITION) ? RB_DIR_RIGHT : RB_DIR_LEFT)
+#define	RB_RIGHT_P(rb)		(RB_POSITION(rb) == RB_DIR_RIGHT)
+#define	RB_LEFT_P(rb)		(RB_POSITION(rb) == RB_DIR_LEFT)
+#define	RB_RED_P(rb) 		(!RB_SENTINEL_P(rb) && ((rb)->rb_info & RB_FLAG_RED) != 0)
+#define	RB_BLACK_P(rb) 		(RB_SENTINEL_P(rb) || ((rb)->rb_info & RB_FLAG_RED) == 0)
+#define	RB_MARK_RED(rb) 	((void)((rb)->rb_info |= RB_FLAG_RED))
+#define	RB_MARK_BLACK(rb) 	((void)((rb)->rb_info &= ~RB_FLAG_RED))
+#define	RB_INVERT_COLOR(rb) 	((void)((rb)->rb_info ^= RB_FLAG_RED))
+#define	RB_ROOT_P(rbt, rb)	((rbt)->rbt_root == (rb))
+#define	RB_SET_POSITION(rb, position) \
+    ((void)((position) ? ((rb)->rb_info |= RB_FLAG_POSITION) : \
+    ((rb)->rb_info &= ~RB_FLAG_POSITION)))
+#define	RB_ZERO_PROPERTIES(rb)	((void)((rb)->rb_info &= ~RB_FLAG_MASK))
+#define	RB_COPY_PROPERTIES(dst, src) \
+    ((void)((dst)->rb_info ^= ((dst)->rb_info ^ (src)->rb_info) & RB_FLAG_MASK))
+#define RB_SWAP_PROPERTIES(a, b) do { \
+    uintptr_t xorinfo = ((a)->rb_info ^ (b)->rb_info) & RB_FLAG_MASK; \
+    (a)->rb_info ^= xorinfo; \
+    (b)->rb_info ^= xorinfo; \
+  } while (/*CONSTCOND*/ 0)
+
+static void __archive_rb_tree_insert_rebalance(struct archive_rb_tree *,
+    struct archive_rb_node *);
+static void __archive_rb_tree_removal_rebalance(struct archive_rb_tree *,
+    struct archive_rb_node *, unsigned int);
+
+#define	RB_SENTINEL_NODE	NULL
+
+#define T	1
+#define	F	0
+
+void
+__archive_rb_tree_init(struct archive_rb_tree *rbt,
+    const struct archive_rb_tree_ops *ops)
+{
+	rbt->rbt_ops = ops;
+	*((struct archive_rb_node **)&rbt->rbt_root) = RB_SENTINEL_NODE;
+}
+
+struct archive_rb_node *
+__archive_rb_tree_find_node(struct archive_rb_tree *rbt, const void *key)
+{
+	archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;
+	struct archive_rb_node *parent = rbt->rbt_root;
+
+	while (!RB_SENTINEL_P(parent)) {
+		const signed int diff = (*compare_key)(parent, key);
+		if (diff == 0)
+			return parent;
+		parent = parent->rb_nodes[diff > 0];
+	}
+
+	return NULL;
+}
+ 
+struct archive_rb_node *
+__archive_rb_tree_find_node_geq(struct archive_rb_tree *rbt, const void *key)
+{
+	archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;
+	struct archive_rb_node *parent = rbt->rbt_root;
+	struct archive_rb_node *last = NULL;
+
+	while (!RB_SENTINEL_P(parent)) {
+		const signed int diff = (*compare_key)(parent, key);
+		if (diff == 0)
+			return parent;
+		if (diff < 0)
+			last = parent;
+		parent = parent->rb_nodes[diff > 0];
+	}
+
+	return last;
+}
+ 
+struct archive_rb_node *
+__archive_rb_tree_find_node_leq(struct archive_rb_tree *rbt, const void *key)
+{
+	archive_rbto_compare_key_fn compare_key = rbt->rbt_ops->rbto_compare_key;
+	struct archive_rb_node *parent = rbt->rbt_root;
+	struct archive_rb_node *last = NULL;
+
+	while (!RB_SENTINEL_P(parent)) {
+		const signed int diff = (*compare_key)(parent, key);
+		if (diff == 0)
+			return parent;
+		if (diff > 0)
+			last = parent;
+		parent = parent->rb_nodes[diff > 0];
+	}
+
+	return last;
+}
+
+int
+__archive_rb_tree_insert_node(struct archive_rb_tree *rbt,
+    struct archive_rb_node *self)
+{
+	archive_rbto_compare_nodes_fn compare_nodes = rbt->rbt_ops->rbto_compare_nodes;
+	struct archive_rb_node *parent, *tmp;
+	unsigned int position;
+	int rebalance;
+
+	tmp = rbt->rbt_root;
+	/*
+	 * This is a hack.  Because rbt->rbt_root is just a
+	 * struct archive_rb_node *, just like rb_node->rb_nodes[RB_DIR_LEFT],
+	 * we can use this fact to avoid a lot of tests for root and know
+	 * that even at root, updating
+	 * RB_FATHER(rb_node)->rb_nodes[RB_POSITION(rb_node)] will
+	 * update rbt->rbt_root.
+	 */
+	parent = (struct archive_rb_node *)(void *)&rbt->rbt_root;
+	position = RB_DIR_LEFT;
+
+	/*
+	 * Find out where to place this new leaf.
+	 */
+	while (!RB_SENTINEL_P(tmp)) {
+		const signed int diff = (*compare_nodes)(tmp, self);
+		if (diff == 0) {
+			/*
+			 * Node already exists; don't insert.
+			 */
+			return F;
+		}
+		parent = tmp;
+		position = (diff > 0);
+		tmp = parent->rb_nodes[position];
+	}
+
+	/*
+	 * Initialize the node and insert as a leaf into the tree.
+	 */
+	RB_SET_FATHER(self, parent);
+	RB_SET_POSITION(self, position);
+	if (parent == (struct archive_rb_node *)(void *)&rbt->rbt_root) {
+		RB_MARK_BLACK(self);		/* root is always black */
+		rebalance = F;
+	} else {
+		/*
+		 * All new nodes are colored red.  We only need to rebalance
+		 * if our parent is also red.
+		 */
+		RB_MARK_RED(self);
+		rebalance = RB_RED_P(parent);
+	}
+	self->rb_left = parent->rb_nodes[position];
+	self->rb_right = parent->rb_nodes[position];
+	parent->rb_nodes[position] = self;
+
+	/*
+	 * Rebalance tree after insertion
+	 */
+	if (rebalance)
+		__archive_rb_tree_insert_rebalance(rbt, self);
+
+	return T;
+}
+
+/*
+ * Swap the location and colors of 'self' and its child @ which.  The child
+ * can not be a sentinel node.  This is our rotation function.  However,
+ * since it preserves coloring, it great simplifies both insertion and
+ * removal since rotation almost always involves the exchanging of colors
+ * as a separate step.
+ */
+/*ARGSUSED*/
+static void
+__archive_rb_tree_reparent_nodes(
+    struct archive_rb_node *old_father, const unsigned int which)
+{
+	const unsigned int other = which ^ RB_DIR_OTHER;
+	struct archive_rb_node * const grandpa = RB_FATHER(old_father);
+	struct archive_rb_node * const old_child = old_father->rb_nodes[which];
+	struct archive_rb_node * const new_father = old_child;
+	struct archive_rb_node * const new_child = old_father;
+
+	if (new_father == NULL)
+		return;
+	/*
+	 * Exchange descendant linkages.
+	 */
+	grandpa->rb_nodes[RB_POSITION(old_father)] = new_father;
+	new_child->rb_nodes[which] = old_child->rb_nodes[other];
+	new_father->rb_nodes[other] = new_child;
+
+	/*
+	 * Update ancestor linkages
+	 */
+	RB_SET_FATHER(new_father, grandpa);
+	RB_SET_FATHER(new_child, new_father);
+
+	/*
+	 * Exchange properties between new_father and new_child.  The only
+	 * change is that new_child's position is now on the other side.
+	 */
+	RB_SWAP_PROPERTIES(new_father, new_child);
+	RB_SET_POSITION(new_child, other);
+
+	/*
+	 * Make sure to reparent the new child to ourself.
+	 */
+	if (!RB_SENTINEL_P(new_child->rb_nodes[which])) {
+		RB_SET_FATHER(new_child->rb_nodes[which], new_child);
+		RB_SET_POSITION(new_child->rb_nodes[which], which);
+	}
+
+}
+
+static void
+__archive_rb_tree_insert_rebalance(struct archive_rb_tree *rbt,
+    struct archive_rb_node *self)
+{
+	struct archive_rb_node * father = RB_FATHER(self);
+	struct archive_rb_node * grandpa;
+	struct archive_rb_node * uncle;
+	unsigned int which;
+	unsigned int other;
+
+	for (;;) {
+		/*
+		 * We are red and our parent is red, therefore we must have a
+		 * grandfather and he must be black.
+		 */
+		grandpa = RB_FATHER(father);
+		which = (father == grandpa->rb_right);
+		other = which ^ RB_DIR_OTHER;
+		uncle = grandpa->rb_nodes[other];
+
+		if (RB_BLACK_P(uncle))
+			break;
+
+		/*
+		 * Case 1: our uncle is red
+		 *   Simply invert the colors of our parent and
+		 *   uncle and make our grandparent red.  And
+		 *   then solve the problem up at his level.
+		 */
+		RB_MARK_BLACK(uncle);
+		RB_MARK_BLACK(father);
+		if (RB_ROOT_P(rbt, grandpa)) {
+			/*
+			 * If our grandpa is root, don't bother
+			 * setting him to red, just return.
+			 */
+			return;
+		}
+		RB_MARK_RED(grandpa);
+		self = grandpa;
+		father = RB_FATHER(self);
+		if (RB_BLACK_P(father)) {
+			/*
+			 * If our great-grandpa is black, we're done.
+			 */
+			return;
+		}
+	}
+
+	/*
+	 * Case 2&3: our uncle is black.
+	 */
+	if (self == father->rb_nodes[other]) {
+		/*
+		 * Case 2: we are on the same side as our uncle
+		 *   Swap ourselves with our parent so this case
+		 *   becomes case 3.  Basically our parent becomes our
+		 *   child.
+		 */
+		__archive_rb_tree_reparent_nodes(father, other);
+	}
+	/*
+	 * Case 3: we are opposite a child of a black uncle.
+	 *   Swap our parent and grandparent.  Since our grandfather
+	 *   is black, our father will become black and our new sibling
+	 *   (former grandparent) will become red.
+	 */
+	__archive_rb_tree_reparent_nodes(grandpa, which);
+
+	/*
+	 * Final step: Set the root to black.
+	 */
+	RB_MARK_BLACK(rbt->rbt_root);
+}
+
+static void
+__archive_rb_tree_prune_node(struct archive_rb_tree *rbt,
+    struct archive_rb_node *self, int rebalance)
+{
+	const unsigned int which = RB_POSITION(self);
+	struct archive_rb_node *father = RB_FATHER(self);
+
+	/*
+	 * Since we are childless, we know that self->rb_left is pointing
+	 * to the sentinel node.
+	 */
+	father->rb_nodes[which] = self->rb_left;
+
+	/*
+	 * Rebalance if requested.
+	 */
+	if (rebalance)
+		__archive_rb_tree_removal_rebalance(rbt, father, which);
+}
+
+/*
+ * When deleting an interior node
+ */
+static void
+__archive_rb_tree_swap_prune_and_rebalance(struct archive_rb_tree *rbt,
+    struct archive_rb_node *self, struct archive_rb_node *standin)
+{
+	const unsigned int standin_which = RB_POSITION(standin);
+	unsigned int standin_other = standin_which ^ RB_DIR_OTHER;
+	struct archive_rb_node *standin_son;
+	struct archive_rb_node *standin_father = RB_FATHER(standin);
+	int rebalance = RB_BLACK_P(standin);
+
+	if (standin_father == self) {
+		/*
+		 * As a child of self, any children would be opposite of
+		 * our parent.
+		 */
+		standin_son = standin->rb_nodes[standin_which];
+	} else {
+		/*
+		 * Since we aren't a child of self, any children would be
+		 * on the same side as our parent.
+		 */
+		standin_son = standin->rb_nodes[standin_other];
+	}
+
+	if (RB_RED_P(standin_son)) {
+		/*
+		 * We know we have a red child so if we flip it to black
+		 * we don't have to rebalance.
+		 */
+		RB_MARK_BLACK(standin_son);
+		rebalance = F;
+
+		if (standin_father != self) {
+			/*
+			 * Change the son's parentage to point to his grandpa.
+			 */
+			RB_SET_FATHER(standin_son, standin_father);
+			RB_SET_POSITION(standin_son, standin_which);
+		}
+	}
+
+	if (standin_father == self) {
+		/*
+		 * If we are about to delete the standin's father, then when
+		 * we call rebalance, we need to use ourselves as our father.
+		 * Otherwise remember our original father.  Also, since we are
+		 * our standin's father we only need to reparent the standin's
+		 * brother.
+		 *
+		 * |    R      -->     S    |
+		 * |  Q   S    -->   Q   T  |
+		 * |        t  -->          |
+		 *
+		 * Have our son/standin adopt his brother as his new son.
+		 */
+		standin_father = standin;
+	} else {
+		/*
+		 * |    R          -->    S       .  |
+		 * |   / \  |   T  -->   / \  |  /   |
+		 * |  ..... | S    -->  ..... | T    |
+		 *
+		 * Sever standin's connection to his father.
+		 */
+		standin_father->rb_nodes[standin_which] = standin_son;
+		/*
+		 * Adopt the far son.
+		 */
+		standin->rb_nodes[standin_other] = self->rb_nodes[standin_other];
+		RB_SET_FATHER(standin->rb_nodes[standin_other], standin);
+		/*
+		 * Use standin_other because we need to preserve standin_which
+		 * for the removal_rebalance.
+		 */
+		standin_other = standin_which;
+	}
+
+	/*
+	 * Move the only remaining son to our standin.  If our standin is our
+	 * son, this will be the only son needed to be moved.
+	 */
+	standin->rb_nodes[standin_other] = self->rb_nodes[standin_other];
+	RB_SET_FATHER(standin->rb_nodes[standin_other], standin);
+
+	/*
+	 * Now copy the result of self to standin and then replace
+	 * self with standin in the tree.
+	 */
+	RB_COPY_PROPERTIES(standin, self);
+	RB_SET_FATHER(standin, RB_FATHER(self));
+	RB_FATHER(standin)->rb_nodes[RB_POSITION(standin)] = standin;
+
+	if (rebalance)
+		__archive_rb_tree_removal_rebalance(rbt, standin_father, standin_which);
+}
+
+/*
+ * We could do this by doing
+ *	__archive_rb_tree_node_swap(rbt, self, which);
+ *	__archive_rb_tree_prune_node(rbt, self, F);
+ *
+ * But it's more efficient to just evaluate and recolor the child.
+ */
+static void
+__archive_rb_tree_prune_blackred_branch(
+    struct archive_rb_node *self, unsigned int which)
+{
+	struct archive_rb_node *father = RB_FATHER(self);
+	struct archive_rb_node *son = self->rb_nodes[which];
+
+	/*
+	 * Remove ourselves from the tree and give our former child our
+	 * properties (position, color, root).
+	 */
+	RB_COPY_PROPERTIES(son, self);
+	father->rb_nodes[RB_POSITION(son)] = son;
+	RB_SET_FATHER(son, father);
+}
+/*
+ *
+ */
+void
+__archive_rb_tree_remove_node(struct archive_rb_tree *rbt,
+    struct archive_rb_node *self)
+{
+	struct archive_rb_node *standin;
+	unsigned int which;
+
+	/*
+	 * In the following diagrams, we (the node to be removed) are S.  Red
+	 * nodes are lowercase.  T could be either red or black.
+	 *
+	 * Remember the major axiom of the red-black tree: the number of
+	 * black nodes from the root to each leaf is constant across all
+	 * leaves, only the number of red nodes varies.
+	 *
+	 * Thus removing a red leaf doesn't require any other changes to a
+	 * red-black tree.  So if we must remove a node, attempt to rearrange
+	 * the tree so we can remove a red node.
+	 *
+	 * The simplest case is a childless red node or a childless root node:
+	 *
+	 * |    T  -->    T  |    or    |  R  -->  *  |
+	 * |  s    -->  *    |
+	 */
+	if (RB_CHILDLESS_P(self)) {
+		const int rebalance = RB_BLACK_P(self) && !RB_ROOT_P(rbt, self);
+		__archive_rb_tree_prune_node(rbt, self, rebalance);
+		return;
+	}
+	if (!RB_TWOCHILDREN_P(self)) {
+		/*
+		 * The next simplest case is the node we are deleting is
+		 * black and has one red child.
+		 *
+		 * |      T  -->      T  -->      T  |
+		 * |    S    -->  R      -->  R      |
+		 * |  r      -->    s    -->    *    |
+		 */
+		which = RB_LEFT_SENTINEL_P(self) ? RB_DIR_RIGHT : RB_DIR_LEFT;
+		__archive_rb_tree_prune_blackred_branch(self, which);
+		return;
+	}
+
+	/*
+	 * We invert these because we prefer to remove from the inside of
+	 * the tree.
+	 */
+	which = RB_POSITION(self) ^ RB_DIR_OTHER;
+
+	/*
+	 * Let's find the node closes to us opposite of our parent
+	 * Now swap it with ourself, "prune" it, and rebalance, if needed.
+	 */
+	standin = __archive_rb_tree_iterate(rbt, self, which);
+	__archive_rb_tree_swap_prune_and_rebalance(rbt, self, standin);
+}
+
+static void
+__archive_rb_tree_removal_rebalance(struct archive_rb_tree *rbt,
+    struct archive_rb_node *parent, unsigned int which)
+{
+
+	while (RB_BLACK_P(parent->rb_nodes[which])) {
+		unsigned int other = which ^ RB_DIR_OTHER;
+		struct archive_rb_node *brother = parent->rb_nodes[other];
+
+		if (brother == NULL)
+			return;/* The tree may be broken. */
+		/*
+		 * For cases 1, 2a, and 2b, our brother's children must
+		 * be black and our father must be black
+		 */
+		if (RB_BLACK_P(parent)
+		    && RB_BLACK_P(brother->rb_left)
+		    && RB_BLACK_P(brother->rb_right)) {
+			if (RB_RED_P(brother)) {
+				/*
+				 * Case 1: Our brother is red, swap its
+				 * position (and colors) with our parent. 
+				 * This should now be case 2b (unless C or E
+				 * has a red child which is case 3; thus no
+				 * explicit branch to case 2b).
+				 *
+				 *    B         ->        D
+				 *  A     d     ->    b     E
+				 *      C   E   ->  A   C
+				 */
+				__archive_rb_tree_reparent_nodes(parent, other);
+				brother = parent->rb_nodes[other];
+				if (brother == NULL)
+					return;/* The tree may be broken. */
+			} else {
+				/*
+				 * Both our parent and brother are black.
+				 * Change our brother to red, advance up rank
+				 * and go through the loop again.
+				 *
+				 *    B         ->   *B
+				 * *A     D     ->  A     d
+				 *      C   E   ->      C   E
+				 */
+				RB_MARK_RED(brother);
+				if (RB_ROOT_P(rbt, parent))
+					return;	/* root == parent == black */
+				which = RB_POSITION(parent);
+				parent = RB_FATHER(parent);
+				continue;
+			}
+		}
+		/*
+		 * Avoid an else here so that case 2a above can hit either
+		 * case 2b, 3, or 4.
+		 */
+		if (RB_RED_P(parent)
+		    && RB_BLACK_P(brother)
+		    && RB_BLACK_P(brother->rb_left)
+		    && RB_BLACK_P(brother->rb_right)) {
+			/*
+			 * We are black, our father is red, our brother and
+			 * both nephews are black.  Simply invert/exchange the
+			 * colors of our father and brother (to black and red
+			 * respectively).
+			 *
+			 *	|    f        -->    F        |
+			 *	|  *     B    -->  *     b    |
+			 *	|      N   N  -->      N   N  |
+			 */
+			RB_MARK_BLACK(parent);
+			RB_MARK_RED(brother);
+			break;		/* We're done! */
+		} else {
+			/*
+			 * Our brother must be black and have at least one
+			 * red child (it may have two).
+			 */
+			if (RB_BLACK_P(brother->rb_nodes[other])) {
+				/*
+				 * Case 3: our brother is black, our near
+				 * nephew is red, and our far nephew is black.
+				 * Swap our brother with our near nephew.  
+				 * This result in a tree that matches case 4.
+				 * (Our father could be red or black).
+				 *
+				 *	|    F      -->    F      |
+				 *	|  x     B  -->  x   B    |
+				 *	|      n    -->        n  |
+				 */
+				__archive_rb_tree_reparent_nodes(brother, which);
+				brother = parent->rb_nodes[other];
+			}
+			/*
+			 * Case 4: our brother is black and our far nephew
+			 * is red.  Swap our father and brother locations and
+			 * change our far nephew to black.  (these can be
+			 * done in either order so we change the color first).
+			 * The result is a valid red-black tree and is a
+			 * terminal case.  (again we don't care about the
+			 * father's color)
+			 *
+			 * If the father is red, we will get a red-black-black
+			 * tree:
+			 *	|  f      ->  f      -->    b    |
+			 *	|    B    ->    B    -->  F   N  |
+			 *	|      n  ->      N  -->         |
+			 *
+			 * If the father is black, we will get an all black
+			 * tree:
+			 *	|  F      ->  F      -->    B    |
+			 *	|    B    ->    B    -->  F   N  |
+			 *	|      n  ->      N  -->         |
+			 *
+			 * If we had two red nephews, then after the swap,
+			 * our former father would have a red grandson. 
+			 */
+			if (brother->rb_nodes[other] == NULL)
+				return;/* The tree may be broken. */
+			RB_MARK_BLACK(brother->rb_nodes[other]);
+			__archive_rb_tree_reparent_nodes(parent, other);
+			break;		/* We're done! */
+		}
+	}
+}
+
+struct archive_rb_node *
+__archive_rb_tree_iterate(struct archive_rb_tree *rbt,
+    struct archive_rb_node *self, const unsigned int direction)
+{
+	const unsigned int other = direction ^ RB_DIR_OTHER;
+
+	if (self == NULL) {
+		self = rbt->rbt_root;
+		if (RB_SENTINEL_P(self))
+			return NULL;
+		while (!RB_SENTINEL_P(self->rb_nodes[direction]))
+			self = self->rb_nodes[direction];
+		return self;
+	}
+	/*
+	 * We can't go any further in this direction.  We proceed up in the
+	 * opposite direction until our parent is in direction we want to go.
+	 */
+	if (RB_SENTINEL_P(self->rb_nodes[direction])) {
+		while (!RB_ROOT_P(rbt, self)) {
+			if (other == (unsigned int)RB_POSITION(self))
+				return RB_FATHER(self);
+			self = RB_FATHER(self);
+		}
+		return NULL;
+	}
+
+	/*
+	 * Advance down one in current direction and go down as far as possible
+	 * in the opposite direction.
+	 */
+	self = self->rb_nodes[direction];
+	while (!RB_SENTINEL_P(self->rb_nodes[other]))
+		self = self->rb_nodes[other];
+	return self;
+}
diff --git a/c/archive_rb.h b/c/archive_rb.h
new file mode 100644
--- /dev/null
+++ b/c/archive_rb.h
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas <matt@3am-software.com>.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ *
+ * Based on NetBSD: rb.h,v 1.13 2009/08/16 10:57:01 yamt Exp
+ */
+
+#ifndef ARCHIVE_RB_H_INCLUDED
+#define	ARCHIVE_RB_H_INCLUDED
+
+struct archive_rb_node {
+	struct archive_rb_node *rb_nodes[2];
+	/*
+	 * rb_info contains the two flags and the parent back pointer.
+	 * We put the two flags in the low two bits since we know that
+	 * rb_node will have an alignment of 4 or 8 bytes.
+	 */
+	uintptr_t rb_info;
+};
+
+#define	ARCHIVE_RB_DIR_LEFT		0
+#define	ARCHIVE_RB_DIR_RIGHT		1
+
+#define ARCHIVE_RB_TREE_MIN(T) \
+    __archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_LEFT)
+#define ARCHIVE_RB_TREE_MAX(T) \
+    __archive_rb_tree_iterate((T), NULL, ARCHIVE_RB_DIR_RIGHT)
+#define ARCHIVE_RB_TREE_NEXT(T, N) \
+    __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_RIGHT)
+#define ARCHIVE_RB_TREE_PREV(T, N) \
+    __archive_rb_tree_iterate((T), (N), ARCHIVE_RB_DIR_LEFT)
+#define ARCHIVE_RB_TREE_FOREACH(N, T) \
+    for ((N) = ARCHIVE_RB_TREE_MIN(T); (N); \
+	(N) = ARCHIVE_RB_TREE_NEXT((T), (N)))
+#define ARCHIVE_RB_TREE_FOREACH_REVERSE(N, T) \
+    for ((N) = ARCHIVE_RB_TREE_MAX(T); (N); \
+	(N) = ARCHIVE_RB_TREE_PREV((T), (N)))
+#define ARCHIVE_RB_TREE_FOREACH_SAFE(N, T, S) \
+    for ((N) = ARCHIVE_RB_TREE_MIN(T); \
+	(N) && ((S) = ARCHIVE_RB_TREE_NEXT((T), (N)), 1); \
+	(N) = (S))
+#define ARCHIVE_RB_TREE_FOREACH_REVERSE_SAFE(N, T, S) \
+    for ((N) = ARCHIVE_RB_TREE_MAX(T); \
+        (N) && ((S) = ARCHIVE_RB_TREE_PREV((T), (N)), 1); \
+        (N) = (S))
+
+/*
+ * archive_rbto_compare_nodes_fn:
+ *	return a positive value if the first node < the second node.
+ *	return a negative value if the first node > the second node.
+ *	return 0 if they are considered same.
+ *
+ * archive_rbto_compare_key_fn:
+ *	return a positive value if the node < the key.
+ *	return a negative value if the node > the key.
+ *	return 0 if they are considered same.
+ */
+
+typedef signed int (*const archive_rbto_compare_nodes_fn)(const struct archive_rb_node *,
+    const struct archive_rb_node *);
+typedef signed int (*const archive_rbto_compare_key_fn)(const struct archive_rb_node *,
+    const void *);
+
+struct archive_rb_tree_ops {
+	archive_rbto_compare_nodes_fn rbto_compare_nodes;
+	archive_rbto_compare_key_fn rbto_compare_key;
+};
+
+struct archive_rb_tree {
+	struct archive_rb_node *rbt_root;
+	const struct archive_rb_tree_ops *rbt_ops;
+};
+
+void	__archive_rb_tree_init(struct archive_rb_tree *,
+    const struct archive_rb_tree_ops *);
+int	__archive_rb_tree_insert_node(struct archive_rb_tree *,
+    struct archive_rb_node *);
+struct archive_rb_node	*
+	__archive_rb_tree_find_node(struct archive_rb_tree *, const void *);
+struct archive_rb_node	*
+	__archive_rb_tree_find_node_geq(struct archive_rb_tree *, const void *);
+struct archive_rb_node	*
+	__archive_rb_tree_find_node_leq(struct archive_rb_tree *, const void *);
+void	__archive_rb_tree_remove_node(struct archive_rb_tree *, struct archive_rb_node *);
+struct archive_rb_node *
+	__archive_rb_tree_iterate(struct archive_rb_tree *,
+	struct archive_rb_node *, const unsigned int);
+
+#endif	/* ARCHIVE_RB_H_*/
diff --git a/c/archive_read.c b/c/archive_read.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read.c
@@ -0,0 +1,1752 @@
+/*-
+ * Copyright (c) 2003-2011 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: head/lib/libarchive/archive_read.c 201157 2009-12-29 05:30:23Z kientzle $");
+
+#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"
+
+#define minimum(a, b) (a < b ? a : b)
+
+static int	choose_filters(struct archive_read *);
+static int	choose_format(struct archive_read *);
+static int	close_filters(struct archive_read *);
+static struct archive_vtable *archive_read_vtable(void);
+static int64_t	_archive_filter_bytes(struct archive *, int);
+static int	_archive_filter_code(struct archive *, int);
+static const char *_archive_filter_name(struct archive *, int);
+static int  _archive_filter_count(struct archive *);
+static int	_archive_read_close(struct archive *);
+static int	_archive_read_data_block(struct archive *,
+		    const void **, size_t *, int64_t *);
+static int	_archive_read_free(struct archive *);
+static int	_archive_read_next_header(struct archive *,
+		    struct archive_entry **);
+static int	_archive_read_next_header2(struct archive *,
+		    struct archive_entry *);
+static int64_t  advance_file_pointer(struct archive_read_filter *, int64_t);
+
+static struct archive_vtable *
+archive_read_vtable(void)
+{
+	static struct archive_vtable av;
+	static int inited = 0;
+
+	if (!inited) {
+		av.archive_filter_bytes = _archive_filter_bytes;
+		av.archive_filter_code = _archive_filter_code;
+		av.archive_filter_name = _archive_filter_name;
+		av.archive_filter_count = _archive_filter_count;
+		av.archive_read_data_block = _archive_read_data_block;
+		av.archive_read_next_header = _archive_read_next_header;
+		av.archive_read_next_header2 = _archive_read_next_header2;
+		av.archive_free = _archive_read_free;
+		av.archive_close = _archive_read_close;
+		inited = 1;
+	}
+	return (&av);
+}
+
+/*
+ * Allocate, initialize and return a struct archive object.
+ */
+struct archive *
+archive_read_new(void)
+{
+	struct archive_read *a;
+
+	a = (struct archive_read *)calloc(1, sizeof(*a));
+	if (a == NULL)
+		return (NULL);
+	a->archive.magic = ARCHIVE_READ_MAGIC;
+
+	a->archive.state = ARCHIVE_STATE_NEW;
+	a->entry = archive_entry_new2(&a->archive);
+	a->archive.vtable = archive_read_vtable();
+
+	a->passphrases.last = &a->passphrases.first;
+
+	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, la_int64_t d,
+    la_int64_t i)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+		ARCHIVE_STATE_ANY, "archive_read_extract_set_skip_file"))
+		return;
+	a->skip_file_set = 1;
+	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_open1. */
+	archive_read_set_open_callback(a, client_opener);
+	archive_read_set_read_callback(a, client_reader);
+	archive_read_set_close_callback(a, client_closer);
+	archive_read_set_callback_data(a, client_data);
+	return archive_read_open1(a);
+}
+
+
+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)
+{
+	/* Old archive_read_open2() is just a thin shell around
+	 * archive_read_open1. */
+	archive_read_set_callback_data(a, client_data);
+	archive_read_set_open_callback(a, client_opener);
+	archive_read_set_read_callback(a, client_reader);
+	archive_read_set_skip_callback(a, client_skipper);
+	archive_read_set_close_callback(a, client_closer);
+	return archive_read_open1(a);
+}
+
+static ssize_t
+client_read_proxy(struct archive_read_filter *self, const void **buff)
+{
+	ssize_t r;
+	r = (self->archive->client.reader)(&self->archive->archive,
+	    self->data, buff);
+	return (r);
+}
+
+static int64_t
+client_skip_proxy(struct archive_read_filter *self, int64_t request)
+{
+	if (request < 0)
+		__archive_errx(1, "Negative skip requested.");
+	if (request == 0)
+		return 0;
+
+	if (self->archive->client.skipper != NULL) {
+		/* Seek requests over 1GiB are broken down into
+		 * multiple seeks.  This avoids overflows when the
+		 * requests get passed through 32-bit arguments. */
+		int64_t skip_limit = (int64_t)1 << 30;
+		int64_t total = 0;
+		for (;;) {
+			int64_t get, ask = request;
+			if (ask > skip_limit)
+				ask = skip_limit;
+			get = (self->archive->client.skipper)
+				(&self->archive->archive, self->data, ask);
+			total += get;
+			if (get == 0 || get == request)
+				return (total);
+			if (get > request)
+				return ARCHIVE_FATAL;
+			request -= get;
+		}
+	} else if (self->archive->client.seeker != NULL
+		&& request > 64 * 1024) {
+		/* If the client provided a seeker but not a skipper,
+		 * we can use the seeker to skip forward.
+		 *
+		 * Note: This isn't always a good idea.  The client
+		 * skipper is allowed to skip by less than requested
+		 * if it needs to maintain block alignment.  The
+		 * seeker is not allowed to play such games, so using
+		 * the seeker here may be a performance loss compared
+		 * to just reading and discarding.  That's why we
+		 * only do this for skips of over 64k.
+		 */
+		int64_t before = self->position;
+		int64_t after = (self->archive->client.seeker)
+		    (&self->archive->archive, self->data, request, SEEK_CUR);
+		if (after != before + request)
+			return ARCHIVE_FATAL;
+		return after - before;
+	}
+	return 0;
+}
+
+static int64_t
+client_seek_proxy(struct archive_read_filter *self, int64_t offset, int whence)
+{
+	/* DO NOT use the skipper here!  If we transparently handled
+	 * forward seek here by using the skipper, that will break
+	 * other libarchive code that assumes a successful forward
+	 * seek means it can also seek backwards.
+	 */
+	if (self->archive->client.seeker == NULL) {
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "Current client reader does not support seeking a device");
+		return (ARCHIVE_FAILED);
+	}
+	return (self->archive->client.seeker)(&self->archive->archive,
+	    self->data, offset, whence);
+}
+
+static int
+client_close_proxy(struct archive_read_filter *self)
+{
+	int r = ARCHIVE_OK, r2;
+	unsigned int i;
+
+	if (self->archive->client.closer == NULL)
+		return (r);
+	for (i = 0; i < self->archive->client.nodes; i++)
+	{
+		r2 = (self->archive->client.closer)
+			((struct archive *)self->archive,
+				self->archive->client.dataset[i].data);
+		if (r > r2)
+			r = r2;
+	}
+	return (r);
+}
+
+static int
+client_open_proxy(struct archive_read_filter *self)
+{
+  int r = ARCHIVE_OK;
+	if (self->archive->client.opener != NULL)
+		r = (self->archive->client.opener)(
+		    (struct archive *)self->archive, self->data);
+	return (r);
+}
+
+static int
+client_switch_proxy(struct archive_read_filter *self, unsigned int iindex)
+{
+  int r1 = ARCHIVE_OK, r2 = ARCHIVE_OK;
+	void *data2 = NULL;
+
+	/* Don't do anything if already in the specified data node */
+	if (self->archive->client.cursor == iindex)
+		return (ARCHIVE_OK);
+
+	self->archive->client.cursor = iindex;
+	data2 = self->archive->client.dataset[self->archive->client.cursor].data;
+	if (self->archive->client.switcher != NULL)
+	{
+		r1 = r2 = (self->archive->client.switcher)
+			((struct archive *)self->archive, self->data, data2);
+		self->data = data2;
+	}
+	else
+	{
+		/* Attempt to call close and open instead */
+		if (self->archive->client.closer != NULL)
+			r1 = (self->archive->client.closer)
+				((struct archive *)self->archive, self->data);
+		self->data = data2;
+		if (self->archive->client.opener != NULL)
+			r2 = (self->archive->client.opener)
+				((struct archive *)self->archive, self->data);
+	}
+	return (r1 < r2) ? r1 : r2;
+}
+
+int
+archive_read_set_open_callback(struct archive *_a,
+    archive_open_callback *client_opener)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_open_callback");
+	a->client.opener = client_opener;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_set_read_callback(struct archive *_a,
+    archive_read_callback *client_reader)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_read_callback");
+	a->client.reader = client_reader;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_set_skip_callback(struct archive *_a,
+    archive_skip_callback *client_skipper)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_skip_callback");
+	a->client.skipper = client_skipper;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_set_seek_callback(struct archive *_a,
+    archive_seek_callback *client_seeker)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_seek_callback");
+	a->client.seeker = client_seeker;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_set_close_callback(struct archive *_a,
+    archive_close_callback *client_closer)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_close_callback");
+	a->client.closer = client_closer;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_set_switch_callback(struct archive *_a,
+    archive_switch_callback *client_switcher)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_switch_callback");
+	a->client.switcher = client_switcher;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_set_callback_data(struct archive *_a, void *client_data)
+{
+	return archive_read_set_callback_data2(_a, client_data, 0);
+}
+
+int
+archive_read_set_callback_data2(struct archive *_a, void *client_data,
+    unsigned int iindex)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_set_callback_data2");
+
+	if (a->client.nodes == 0)
+	{
+		a->client.dataset = (struct archive_read_data_node *)
+		    calloc(1, sizeof(*a->client.dataset));
+		if (a->client.dataset == NULL)
+		{
+			archive_set_error(&a->archive, ENOMEM,
+				"No memory.");
+			return ARCHIVE_FATAL;
+		}
+		a->client.nodes = 1;
+	}
+
+	if (iindex > a->client.nodes - 1)
+	{
+		archive_set_error(&a->archive, EINVAL,
+			"Invalid index specified.");
+		return ARCHIVE_FATAL;
+	}
+	a->client.dataset[iindex].data = client_data;
+	a->client.dataset[iindex].begin_position = -1;
+	a->client.dataset[iindex].total_size = -1;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_add_callback_data(struct archive *_a, void *client_data,
+    unsigned int iindex)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	void *p;
+	unsigned int i;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_add_callback_data");
+	if (iindex > a->client.nodes) {
+		archive_set_error(&a->archive, EINVAL,
+			"Invalid index specified.");
+		return ARCHIVE_FATAL;
+	}
+	p = realloc(a->client.dataset, sizeof(*a->client.dataset)
+		* (++(a->client.nodes)));
+	if (p == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+			"No memory.");
+		return ARCHIVE_FATAL;
+	}
+	a->client.dataset = (struct archive_read_data_node *)p;
+	for (i = a->client.nodes - 1; i > iindex; i--) {
+		a->client.dataset[i].data = a->client.dataset[i-1].data;
+		a->client.dataset[i].begin_position = -1;
+		a->client.dataset[i].total_size = -1;
+	}
+	a->client.dataset[iindex].data = client_data;
+	a->client.dataset[iindex].begin_position = -1;
+	a->client.dataset[iindex].total_size = -1;
+	return ARCHIVE_OK;
+}
+
+int
+archive_read_append_callback_data(struct archive *_a, void *client_data)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	return archive_read_add_callback_data(_a, client_data, a->client.nodes);
+}
+
+int
+archive_read_prepend_callback_data(struct archive *_a, void *client_data)
+{
+	return archive_read_add_callback_data(_a, client_data, 0);
+}
+
+int
+archive_read_open1(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter *filter, *tmp;
+	int slot, e = ARCHIVE_OK;
+	unsigned int i;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_open");
+	archive_clear_error(&a->archive);
+
+	if (a->client.reader == NULL) {
+		archive_set_error(&a->archive, EINVAL,
+		    "No reader function provided to archive_read_open");
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Open data source. */
+	if (a->client.opener != NULL) {
+		e = (a->client.opener)(&a->archive, a->client.dataset[0].data);
+		if (e != 0) {
+			/* If the open failed, call the closer to clean up. */
+			if (a->client.closer) {
+				for (i = 0; i < a->client.nodes; i++)
+					(a->client.closer)(&a->archive,
+					    a->client.dataset[i].data);
+			}
+			return (e);
+		}
+	}
+
+	filter = calloc(1, sizeof(*filter));
+	if (filter == NULL)
+		return (ARCHIVE_FATAL);
+	filter->bidder = NULL;
+	filter->upstream = NULL;
+	filter->archive = a;
+	filter->data = a->client.dataset[0].data;
+	filter->open = client_open_proxy;
+	filter->read = client_read_proxy;
+	filter->skip = client_skip_proxy;
+	filter->seek = client_seek_proxy;
+	filter->close = client_close_proxy;
+	filter->sswitch = client_switch_proxy;
+	filter->name = "none";
+	filter->code = ARCHIVE_FILTER_NONE;
+
+	a->client.dataset[0].begin_position = 0;
+	if (!a->filter || !a->bypass_filter_bidding)
+	{
+		a->filter = filter;
+		/* Build out the input pipeline. */
+		e = choose_filters(a);
+		if (e < ARCHIVE_WARN) {
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		}
+	}
+	else
+	{
+		/* Need to add "NONE" type filter at the end of the filter chain */
+		tmp = a->filter;
+		while (tmp->upstream)
+			tmp = tmp->upstream;
+		tmp->upstream = filter;
+	}
+
+	if (!a->format)
+	{
+		slot = choose_format(a);
+		if (slot < 0) {
+			close_filters(a);
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		}
+		a->format = &(a->formats[slot]);
+	}
+
+	a->archive.state = ARCHIVE_STATE_HEADER;
+
+	/* Ensure libarchive starts from the first node in a multivolume set */
+	client_switch_proxy(a->filter, 0);
+	return (e);
+}
+
+/*
+ * Allow each registered stream transform to bid on whether
+ * it wants to handle this stream.  Repeat until we've finished
+ * building the pipeline.
+ */
+
+/* We won't build a filter pipeline with more stages than this. */
+#define MAX_NUMBER_FILTERS 25
+
+static int
+choose_filters(struct archive_read *a)
+{
+	int number_bidders, i, bid, best_bid, number_filters;
+	struct archive_read_filter_bidder *bidder, *best_bidder;
+	struct archive_read_filter *filter;
+	ssize_t avail;
+	int r;
+
+	for (number_filters = 0; number_filters < MAX_NUMBER_FILTERS; ++number_filters) {
+		number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+		best_bid = 0;
+		best_bidder = NULL;
+
+		bidder = a->bidders;
+		for (i = 0; i < number_bidders; i++, bidder++) {
+			if (bidder->bid != NULL) {
+				bid = (bidder->bid)(bidder, a->filter);
+				if (bid > best_bid) {
+					best_bid = bid;
+					best_bidder = bidder;
+				}
+			}
+		}
+
+		/* If no bidder, we're done. */
+		if (best_bidder == NULL) {
+			/* Verify the filter by asking it for some data. */
+			__archive_read_filter_ahead(a->filter, 1, &avail);
+			if (avail < 0) {
+				__archive_read_free_filters(a);
+				return (ARCHIVE_FATAL);
+			}
+			a->archive.compression_name = a->filter->name;
+			a->archive.compression_code = a->filter->code;
+			return (ARCHIVE_OK);
+		}
+
+		filter
+		    = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+		if (filter == NULL)
+			return (ARCHIVE_FATAL);
+		filter->bidder = best_bidder;
+		filter->archive = a;
+		filter->upstream = a->filter;
+		a->filter = filter;
+		r = (best_bidder->init)(a->filter);
+		if (r != ARCHIVE_OK) {
+			__archive_read_free_filters(a);
+			return (ARCHIVE_FATAL);
+		}
+	}
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Input requires too many filters for decoding");
+	return (ARCHIVE_FATAL);
+}
+
+int
+__archive_read_header(struct archive_read *a, struct archive_entry *entry)
+{
+	if (a->filter->read_header)
+		return a->filter->read_header(a->filter, entry);
+	else
+		return (ARCHIVE_OK);
+}
+
+/*
+ * Read header of next entry.
+ */
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	int r1 = ARCHIVE_OK, r2;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_read_next_header");
+
+	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) {
+		r1 = archive_read_data_skip(&a->archive);
+		if (r1 == ARCHIVE_EOF)
+			archive_set_error(&a->archive, EIO,
+			    "Premature end-of-file.");
+		if (r1 == ARCHIVE_EOF || r1 == ARCHIVE_FATAL) {
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	/* Record start-of-header offset in uncompressed stream. */
+	a->header_position = a->filter->position;
+
+	++_a->file_count;
+	r2 = (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 (r2) {
+	case ARCHIVE_EOF:
+		a->archive.state = ARCHIVE_STATE_EOF;
+		--_a->file_count;/* Revert a file counter. */
+		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;
+	}
+
+	__archive_reset_read_data(&a->archive);
+
+	a->data_start_node = a->client.cursor;
+	/* EOF always wins; otherwise return the worst error. */
+	return (r2 < r1 || r2 == ARCHIVE_EOF) ? r2 : r1;
+}
+
+static int
+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+{
+	int ret;
+	struct archive_read *a = (struct archive_read *)_a;
+	*entryp = NULL;
+	ret = _archive_read_next_header2(_a, a->entry);
+	*entryp = a->entry;
+	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 for convenience of bidders. */
+	a->format = &(a->formats[0]);
+	for (i = 0; i < slots; i++, a->format++) {
+		if (a->format->bid) {
+			bid = (a->format->bid)(a, best_bid);
+			if (bid == ARCHIVE_FATAL)
+				return (ARCHIVE_FATAL);
+			if (a->filter->position != 0)
+				__archive_read_seek(a, 0, SEEK_SET);
+			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_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "No formats registered");
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * 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.
+ */
+la_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);
+}
+
+/*
+ * Returns 1 if the archive contains at least one encrypted entry.
+ * If the archive format not support encryption at all
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED is returned.
+ * If for any other reason (e.g. not enough data read so far)
+ * we cannot say whether there are encrypted entries, then
+ * ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW is returned.
+ * In general, this function will return values below zero when the
+ * reader is uncertain or totally incapable of encryption support.
+ * When this function returns 0 you can be sure that the reader
+ * supports encryption detection but no encrypted entries have
+ * been found yet.
+ *
+ * NOTE: If the metadata/header of an archive is also encrypted, you
+ * cannot rely on the number of encrypted entries. That is why this
+ * function does not return the number of encrypted entries but#
+ * just shows that there are some.
+ */
+int
+archive_read_has_encrypted_entries(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	int format_supports_encryption = archive_read_format_capabilities(_a)
+			& (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA | ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+
+	if (!_a || !format_supports_encryption) {
+		/* Format in general doesn't support encryption */
+		return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;
+	}
+
+	/* A reader potentially has read enough data now. */
+	if (a->format && a->format->has_encrypted_entries) {
+		return (a->format->has_encrypted_entries)(a);
+	}
+
+	/* For any other reason we cannot say how many entries are there. */
+	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+/*
+ * Returns a bitmask of capabilities that are supported by the archive format reader.
+ * If the reader has no special capabilities, ARCHIVE_READ_FORMAT_CAPS_NONE is returned.
+ */
+int
+archive_read_format_capabilities(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	if (a && a->format && a->format->format_capabilties) {
+		return (a->format->format_capabilties)(a);
+	}
+	return ARCHIVE_READ_FORMAT_CAPS_NONE;
+}
+
+/*
+ * 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.
+ */
+la_ssize_t
+archive_read_data(struct archive *_a, void *buff, size_t s)
+{
+	struct archive *a = (struct archive *)_a;
+	char	*dest;
+	const void *read_buf;
+	size_t	 bytes_read;
+	size_t	 len;
+	int	 r;
+
+	bytes_read = 0;
+	dest = (char *)buff;
+
+	while (s > 0) {
+		if (a->read_data_offset == a->read_data_output_offset &&
+		    a->read_data_remaining == 0) {
+			read_buf = a->read_data_block;
+			a->read_data_is_posix_read = 1;
+			a->read_data_requested = s;
+			r = archive_read_data_block(a, &read_buf,
+			    &a->read_data_remaining, &a->read_data_offset);
+			a->read_data_block = read_buf;
+			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_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 + (int64_t)s <
+		    a->read_data_offset) {
+			len = s;
+		} else if (a->read_data_output_offset <
+		    a->read_data_offset) {
+			len = (size_t)(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;
+			if (len) {
+				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;
+			}
+		}
+	}
+	a->read_data_is_posix_read = 0;
+	a->read_data_requested = 0;
+	return (bytes_read);
+}
+
+/*
+ * Reset the read_data_* variables, used for starting a new entry.
+ */
+void __archive_reset_read_data(struct archive * a)
+{
+	a->read_data_output_offset = 0;
+	a->read_data_remaining = 0;
+	a->read_data_is_posix_read = 0;
+	a->read_data_requested = 0;
+
+   /* extra resets, from rar.c */
+   a->read_data_block = NULL;
+   a->read_data_offset = 0;
+}
+
+/*
+ * 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;
+	int64_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);
+}
+
+la_int64_t
+archive_seek_data(struct archive *_a, int64_t offset, int whence)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_seek_data_block");
+
+	if (a->format->seek_data == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Internal error: "
+		    "No format_seek_data_block function registered");
+		return (ARCHIVE_FATAL);
+	}
+
+	return (a->format->seek_data)(a, offset, whence);
+}
+
+/*
+ * 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.
+ */
+static int
+_archive_read_data_block(struct archive *_a,
+    const void **buff, size_t *size, int64_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 function registered");
+		return (ARCHIVE_FATAL);
+	}
+
+	return (a->format->read_data)(a, buff, size, offset);
+}
+
+static int
+close_filters(struct archive_read *a)
+{
+	struct archive_read_filter *f = a->filter;
+	int r = ARCHIVE_OK;
+	/* Close each filter in the pipeline. */
+	while (f != NULL) {
+		struct archive_read_filter *t = f->upstream;
+		if (!f->closed && f->close != NULL) {
+			int r1 = (f->close)(f);
+			f->closed = 1;
+			if (r1 < r)
+				r = r1;
+		}
+		free(f->buffer);
+		f->buffer = NULL;
+		f = t;
+	}
+	return r;
+}
+
+void
+__archive_read_free_filters(struct archive_read *a)
+{
+	/* Make sure filters are closed and their buffers are freed */
+	close_filters(a);
+
+	while (a->filter != NULL) {
+		struct archive_read_filter *t = a->filter->upstream;
+		free(a->filter);
+		a->filter = t;
+	}
+}
+
+/*
+ * return the count of # of filters in use
+ */
+static int
+_archive_filter_count(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter *p = a->filter;
+	int count = 0;
+	while(p) {
+		count++;
+		p = p->upstream;
+	}
+	return count;
+}
+
+/*
+ * Close the file and all I/O.
+ */
+static 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_STATE_FATAL, "archive_read_close");
+	if (a->archive.state == ARCHIVE_STATE_CLOSED)
+		return (ARCHIVE_OK);
+	archive_clear_error(&a->archive);
+	a->archive.state = ARCHIVE_STATE_CLOSED;
+
+	/* TODO: Clean up the formatters. */
+
+	/* Release the filter objects. */
+	r1 = close_filters(a);
+	if (r1 < r)
+		r = r1;
+
+	return (r);
+}
+
+/*
+ * Release memory and other resources.
+ */
+static int
+_archive_read_free(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_passphrase *p;
+	int i, n;
+	int slots;
+	int r = ARCHIVE_OK;
+
+	if (_a == NULL)
+		return (ARCHIVE_OK);
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");
+	if (a->archive.state != ARCHIVE_STATE_CLOSED
+	    && a->archive.state != ARCHIVE_STATE_FATAL)
+		r = archive_read_close(&a->archive);
+
+	/* Call cleanup functions registered by optional components. */
+	if (a->cleanup_archive_extract != NULL)
+		r = (a->cleanup_archive_extract)(a);
+
+	/* Cleanup format-specific data. */
+	slots = sizeof(a->formats) / sizeof(a->formats[0]);
+	for (i = 0; i < slots; i++) {
+		a->format = &(a->formats[i]);
+		if (a->formats[i].cleanup)
+			(a->formats[i].cleanup)(a);
+	}
+
+	/* Free the filters */
+	__archive_read_free_filters(a);
+
+	/* Release the bidder objects. */
+	n = sizeof(a->bidders)/sizeof(a->bidders[0]);
+	for (i = 0; i < n; i++) {
+		if (a->bidders[i].free != NULL) {
+			int r1 = (a->bidders[i].free)(&a->bidders[i]);
+			if (r1 < r)
+				r = r1;
+		}
+	}
+
+	/* Release passphrase list. */
+	p = a->passphrases.first;
+	while (p != NULL) {
+		struct archive_read_passphrase *np = p->next;
+
+		/* A passphrase should be cleaned. */
+		memset(p->passphrase, 0, strlen(p->passphrase));
+		free(p->passphrase);
+		free(p);
+		p = np;
+	}
+
+	archive_string_free(&a->archive.error_string);
+	archive_entry_free(a->entry);
+	a->archive.magic = 0;
+	__archive_clean(&a->archive);
+	free(a->client.dataset);
+	free(a);
+	return (r);
+}
+
+static struct archive_read_filter *
+get_filter(struct archive *_a, int n)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter *f = a->filter;
+	/* We use n == -1 for 'the last filter', which is always the
+	 * client proxy. */
+	if (n == -1 && f != NULL) {
+		struct archive_read_filter *last = f;
+		f = f->upstream;
+		while (f != NULL) {
+			last = f;
+			f = f->upstream;
+		}
+		return (last);
+	}
+	if (n < 0)
+		return NULL;
+	while (n > 0 && f != NULL) {
+		f = f->upstream;
+		--n;
+	}
+	return (f);
+}
+
+static int
+_archive_filter_code(struct archive *_a, int n)
+{
+	struct archive_read_filter *f = get_filter(_a, n);
+	return f == NULL ? -1 : f->code;
+}
+
+static const char *
+_archive_filter_name(struct archive *_a, int n)
+{
+	struct archive_read_filter *f = get_filter(_a, n);
+	return f != NULL ? f->name : NULL;
+}
+
+static int64_t
+_archive_filter_bytes(struct archive *_a, int n)
+{
+	struct archive_read_filter *f = get_filter(_a, n);
+	return f == NULL ? -1 : f->position;
+}
+
+/*
+ * 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,
+    const char *name,
+    int (*bid)(struct archive_read *, int),
+    int (*options)(struct archive_read *, const char *, const char *),
+    int (*read_header)(struct archive_read *, struct archive_entry *),
+    int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *),
+    int (*read_data_skip)(struct archive_read *),
+    int64_t (*seek_data)(struct archive_read *, int64_t, int),
+    int (*cleanup)(struct archive_read *),
+    int (*format_capabilities)(struct archive_read *),
+    int (*has_encrypted_entries)(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].options = options;
+			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].seek_data = seek_data;
+			a->formats[i].cleanup = cleanup;
+			a->formats[i].data = format_data;
+			a->formats[i].name = name;
+			a->formats[i].format_capabilties = format_capabilities;
+			a->formats[i].has_encrypted_entries = has_encrypted_entries;
+			return (ARCHIVE_OK);
+		}
+	}
+
+	archive_set_error(&a->archive, ENOMEM,
+	    "Not enough slots for format registration");
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Used internally by decompression routines to register their bid and
+ * initialization functions.
+ */
+int
+__archive_read_get_bidder(struct archive_read *a,
+    struct archive_read_filter_bidder **bidder)
+{
+	int i, number_slots;
+
+	number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+	for (i = 0; i < number_slots; i++) {
+		if (a->bidders[i].bid == NULL) {
+			memset(a->bidders + i, 0, sizeof(a->bidders[0]));
+			*bidder = (a->bidders + i);
+			return (ARCHIVE_OK);
+		}
+	}
+
+	archive_set_error(&a->archive, ENOMEM,
+	    "Not enough slots for filter registration");
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * The next section implements the peek/consume internal I/O
+ * system used by archive readers.  This system allows simple
+ * read-ahead for consumers while preserving zero-copy operation
+ * most of the time.
+ *
+ * The two key operations:
+ *  * The read-ahead function returns a pointer to a block of data
+ *    that satisfies a minimum request.
+ *  * The consume function advances the file pointer.
+ *
+ * In the ideal case, filters generate blocks of data
+ * and __archive_read_ahead() just returns pointers directly into
+ * those blocks.  Then __archive_read_consume() just bumps those
+ * pointers.  Only if your request would span blocks does the I/O
+ * layer use a copy buffer to provide you with a contiguous block of
+ * data.
+ *
+ * A couple of useful idioms:
+ *  * "I just want some data."  Ask for 1 byte and pay attention to
+ *    the "number of bytes available" from __archive_read_ahead().
+ *    Consume whatever you actually use.
+ *  * "I want to output a large block of data."  As above, ask for 1 byte,
+ *    emit all that's available (up to whatever limit you have), consume
+ *    it all, then repeat until you're done.  This effectively means that
+ *    you're passing along the blocks that came from your provider.
+ *  * "I want to peek ahead by a large amount."  Ask for 4k or so, then
+ *    double and repeat until you get an error or have enough.  Note
+ *    that the I/O layer will likely end up expanding its copy buffer
+ *    to fit your request, so use this technique cautiously.  This
+ *    technique is used, for example, by some of the format tasting
+ *    code that has uncertain look-ahead needs.
+ */
+
+/*
+ * Looks ahead in the input stream:
+ *  * If 'avail' pointer is provided, that returns number of bytes available
+ *    in the current buffer, which may be much larger than requested.
+ *  * If end-of-file, *avail gets set to zero.
+ *  * If error, *avail gets error code.
+ *  * If request can be met, returns pointer to data.
+ *  * If minimum request cannot be met, returns NULL.
+ *
+ * Note: If you just want "some data", ask for 1 byte and pay attention
+ * to *avail, which will have the actual amount available.  If you
+ * know exactly how many bytes you need, just ask for that and treat
+ * a NULL return as an error.
+ *
+ * Important:  This does NOT move the file pointer.  See
+ * __archive_read_consume() below.
+ */
+const void *
+__archive_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
+{
+	return (__archive_read_filter_ahead(a->filter, min, avail));
+}
+
+const void *
+__archive_read_filter_ahead(struct archive_read_filter *filter,
+    size_t min, ssize_t *avail)
+{
+	ssize_t bytes_read;
+	size_t tocopy;
+
+	if (filter->fatal) {
+		if (avail)
+			*avail = ARCHIVE_FATAL;
+		return (NULL);
+	}
+
+	/*
+	 * Keep pulling more data until we can satisfy the request.
+	 */
+	for (;;) {
+
+		/*
+		 * If we can satisfy from the copy buffer (and the
+		 * copy buffer isn't empty), we're done.  In particular,
+		 * note that min == 0 is a perfectly well-defined
+		 * request.
+		 */
+		if (filter->avail >= min && filter->avail > 0) {
+			if (avail != NULL)
+				*avail = filter->avail;
+			return (filter->next);
+		}
+
+		/*
+		 * We can satisfy directly from client buffer if everything
+		 * currently in the copy buffer is still in the client buffer.
+		 */
+		if (filter->client_total >= filter->client_avail + filter->avail
+		    && filter->client_avail + filter->avail >= min) {
+			/* "Roll back" to client buffer. */
+			filter->client_avail += filter->avail;
+			filter->client_next -= filter->avail;
+			/* Copy buffer is now empty. */
+			filter->avail = 0;
+			filter->next = filter->buffer;
+			/* Return data from client buffer. */
+			if (avail != NULL)
+				*avail = filter->client_avail;
+			return (filter->client_next);
+		}
+
+		/* Move data forward in copy buffer if necessary. */
+		if (filter->next > filter->buffer &&
+		    filter->next + min > filter->buffer + filter->buffer_size) {
+			if (filter->avail > 0)
+				memmove(filter->buffer, filter->next,
+				    filter->avail);
+			filter->next = filter->buffer;
+		}
+
+		/* If we've used up the client data, get more. */
+		if (filter->client_avail <= 0) {
+			if (filter->end_of_file) {
+				if (avail != NULL)
+					*avail = 0;
+				return (NULL);
+			}
+			bytes_read = (filter->read)(filter,
+			    &filter->client_buff);
+			if (bytes_read < 0) {		/* Read error. */
+				filter->client_total = filter->client_avail = 0;
+				filter->client_next =
+				    filter->client_buff = NULL;
+				filter->fatal = 1;
+				if (avail != NULL)
+					*avail = ARCHIVE_FATAL;
+				return (NULL);
+			}
+			if (bytes_read == 0) {
+				/* Check for another client object first */
+				if (filter->archive->client.cursor !=
+				      filter->archive->client.nodes - 1) {
+					if (client_switch_proxy(filter,
+					    filter->archive->client.cursor + 1)
+					    == ARCHIVE_OK)
+						continue;
+				}
+				/* Premature end-of-file. */
+				filter->client_total = filter->client_avail = 0;
+				filter->client_next =
+				    filter->client_buff = NULL;
+				filter->end_of_file = 1;
+				/* Return whatever we do have. */
+				if (avail != NULL)
+					*avail = filter->avail;
+				return (NULL);
+			}
+			filter->client_total = bytes_read;
+			filter->client_avail = filter->client_total;
+			filter->client_next = filter->client_buff;
+		} else {
+			/*
+			 * We can't satisfy the request from the copy
+			 * buffer or the existing client data, so we
+			 * need to copy more client data over to the
+			 * copy buffer.
+			 */
+
+			/* Ensure the buffer is big enough. */
+			if (min > filter->buffer_size) {
+				size_t s, t;
+				char *p;
+
+				/* Double the buffer; watch for overflow. */
+				s = t = filter->buffer_size;
+				if (s == 0)
+					s = min;
+				while (s < min) {
+					t *= 2;
+					if (t <= s) { /* Integer overflow! */
+						archive_set_error(
+						    &filter->archive->archive,
+						    ENOMEM,
+						    "Unable to allocate copy"
+						    " buffer");
+						filter->fatal = 1;
+						if (avail != NULL)
+							*avail = ARCHIVE_FATAL;
+						return (NULL);
+					}
+					s = t;
+				}
+				/* Now s >= min, so allocate a new buffer. */
+				p = (char *)malloc(s);
+				if (p == NULL) {
+					archive_set_error(
+						&filter->archive->archive,
+						ENOMEM,
+					    "Unable to allocate copy buffer");
+					filter->fatal = 1;
+					if (avail != NULL)
+						*avail = ARCHIVE_FATAL;
+					return (NULL);
+				}
+				/* Move data into newly-enlarged buffer. */
+				if (filter->avail > 0)
+					memmove(p, filter->next, filter->avail);
+				free(filter->buffer);
+				filter->next = filter->buffer = p;
+				filter->buffer_size = s;
+			}
+
+			/* We can add client data to copy buffer. */
+			/* First estimate: copy to fill rest of buffer. */
+			tocopy = (filter->buffer + filter->buffer_size)
+			    - (filter->next + filter->avail);
+			/* Don't waste time buffering more than we need to. */
+			if (tocopy + filter->avail > min)
+				tocopy = min - filter->avail;
+			/* Don't copy more than is available. */
+			if (tocopy > filter->client_avail)
+				tocopy = filter->client_avail;
+
+			memcpy(filter->next + filter->avail,
+			    filter->client_next, tocopy);
+			/* Remove this data from client buffer. */
+			filter->client_next += tocopy;
+			filter->client_avail -= tocopy;
+			/* add it to copy buffer. */
+			filter->avail += tocopy;
+		}
+	}
+}
+
+/*
+ * Move the file pointer forward.
+ */
+int64_t
+__archive_read_consume(struct archive_read *a, int64_t request)
+{
+	return (__archive_read_filter_consume(a->filter, request));
+}
+
+int64_t
+__archive_read_filter_consume(struct archive_read_filter * filter,
+    int64_t request)
+{
+	int64_t skipped;
+
+	if (request < 0)
+		return ARCHIVE_FATAL;
+	if (request == 0)
+		return 0;
+
+	skipped = advance_file_pointer(filter, request);
+	if (skipped == request)
+		return (skipped);
+	/* We hit EOF before we satisfied the skip request. */
+	if (skipped < 0)  /* Map error code to 0 for error message below. */
+		skipped = 0;
+	archive_set_error(&filter->archive->archive,
+	    ARCHIVE_ERRNO_MISC,
+	    "Truncated input file (needed %jd bytes, only %jd available)",
+	    (intmax_t)request, (intmax_t)skipped);
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Advance the file pointer by the amount requested.
+ * Returns the amount actually advanced, which may be less than the
+ * request if EOF is encountered first.
+ * Returns a negative value if there's an I/O error.
+ */
+static int64_t
+advance_file_pointer(struct archive_read_filter *filter, int64_t request)
+{
+	int64_t bytes_skipped, total_bytes_skipped = 0;
+	ssize_t bytes_read;
+	size_t min;
+
+	if (filter->fatal)
+		return (-1);
+
+	/* Use up the copy buffer first. */
+	if (filter->avail > 0) {
+		min = (size_t)minimum(request, (int64_t)filter->avail);
+		filter->next += min;
+		filter->avail -= min;
+		request -= min;
+		filter->position += min;
+		total_bytes_skipped += min;
+	}
+
+	/* Then use up the client buffer. */
+	if (filter->client_avail > 0) {
+		min = (size_t)minimum(request, (int64_t)filter->client_avail);
+		filter->client_next += min;
+		filter->client_avail -= min;
+		request -= min;
+		filter->position += min;
+		total_bytes_skipped += min;
+	}
+	if (request == 0)
+		return (total_bytes_skipped);
+
+	/* If there's an optimized skip function, use it. */
+	if (filter->skip != NULL) {
+		bytes_skipped = (filter->skip)(filter, request);
+		if (bytes_skipped < 0) {	/* error */
+			filter->fatal = 1;
+			return (bytes_skipped);
+		}
+		filter->position += bytes_skipped;
+		total_bytes_skipped += bytes_skipped;
+		request -= bytes_skipped;
+		if (request == 0)
+			return (total_bytes_skipped);
+	}
+
+	/* Use ordinary reads as necessary to complete the request. */
+	for (;;) {
+		bytes_read = (filter->read)(filter, &filter->client_buff);
+		if (bytes_read < 0) {
+			filter->client_buff = NULL;
+			filter->fatal = 1;
+			return (bytes_read);
+		}
+
+		if (bytes_read == 0) {
+			if (filter->archive->client.cursor !=
+			      filter->archive->client.nodes - 1) {
+				if (client_switch_proxy(filter,
+				    filter->archive->client.cursor + 1)
+				    == ARCHIVE_OK)
+					continue;
+			}
+			filter->client_buff = NULL;
+			filter->end_of_file = 1;
+			return (total_bytes_skipped);
+		}
+
+		if (bytes_read >= request) {
+			filter->client_next =
+			    ((const char *)filter->client_buff) + request;
+			filter->client_avail = (size_t)(bytes_read - request);
+			filter->client_total = bytes_read;
+			total_bytes_skipped += request;
+			filter->position += request;
+			return (total_bytes_skipped);
+		}
+
+		filter->position += bytes_read;
+		total_bytes_skipped += bytes_read;
+		request -= bytes_read;
+	}
+}
+
+/**
+ * Returns ARCHIVE_FAILED if seeking isn't supported.
+ */
+int64_t
+__archive_read_seek(struct archive_read *a, int64_t offset, int whence)
+{
+	return __archive_read_filter_seek(a->filter, offset, whence);
+}
+
+int64_t
+__archive_read_filter_seek(struct archive_read_filter *filter, int64_t offset,
+    int whence)
+{
+	struct archive_read_client *client;
+	int64_t r;
+	unsigned int cursor;
+
+	if (filter->closed || filter->fatal)
+		return (ARCHIVE_FATAL);
+	if (filter->seek == NULL)
+		return (ARCHIVE_FAILED);
+
+	client = &(filter->archive->client);
+	switch (whence) {
+	case SEEK_CUR:
+		/* Adjust the offset and use SEEK_SET instead */
+		offset += filter->position;
+		__LA_FALLTHROUGH;
+	case SEEK_SET:
+		cursor = 0;
+		while (1)
+		{
+			if (client->dataset[cursor].begin_position < 0 ||
+			    client->dataset[cursor].total_size < 0 ||
+			    client->dataset[cursor].begin_position +
+			      client->dataset[cursor].total_size - 1 > offset ||
+			    cursor + 1 >= client->nodes)
+				break;
+			r = client->dataset[cursor].begin_position +
+				client->dataset[cursor].total_size;
+			client->dataset[++cursor].begin_position = r;
+		}
+		while (1) {
+			r = client_switch_proxy(filter, cursor);
+			if (r != ARCHIVE_OK)
+				return r;
+			if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)
+				return r;
+			client->dataset[cursor].total_size = r;
+			if (client->dataset[cursor].begin_position +
+			    client->dataset[cursor].total_size - 1 > offset ||
+			    cursor + 1 >= client->nodes)
+				break;
+			r = client->dataset[cursor].begin_position +
+				client->dataset[cursor].total_size;
+			client->dataset[++cursor].begin_position = r;
+		}
+		offset -= client->dataset[cursor].begin_position;
+		if (offset < 0
+		    || offset > client->dataset[cursor].total_size)
+			return ARCHIVE_FATAL;
+		if ((r = client_seek_proxy(filter, offset, SEEK_SET)) < 0)
+			return r;
+		break;
+
+	case SEEK_END:
+		cursor = 0;
+		while (1) {
+			if (client->dataset[cursor].begin_position < 0 ||
+			    client->dataset[cursor].total_size < 0 ||
+			    cursor + 1 >= client->nodes)
+				break;
+			r = client->dataset[cursor].begin_position +
+				client->dataset[cursor].total_size;
+			client->dataset[++cursor].begin_position = r;
+		}
+		while (1) {
+			r = client_switch_proxy(filter, cursor);
+			if (r != ARCHIVE_OK)
+				return r;
+			if ((r = client_seek_proxy(filter, 0, SEEK_END)) < 0)
+				return r;
+			client->dataset[cursor].total_size = r;
+			r = client->dataset[cursor].begin_position +
+				client->dataset[cursor].total_size;
+			if (cursor + 1 >= client->nodes)
+				break;
+			client->dataset[++cursor].begin_position = r;
+		}
+		while (1) {
+			if (r + offset >=
+			    client->dataset[cursor].begin_position)
+				break;
+			offset += client->dataset[cursor].total_size;
+			if (cursor == 0)
+				break;
+			cursor--;
+			r = client->dataset[cursor].begin_position +
+				client->dataset[cursor].total_size;
+		}
+		offset = (r + offset) - client->dataset[cursor].begin_position;
+		if ((r = client_switch_proxy(filter, cursor)) != ARCHIVE_OK)
+			return r;
+		r = client_seek_proxy(filter, offset, SEEK_SET);
+		if (r < ARCHIVE_OK)
+			return r;
+		break;
+
+	default:
+		return (ARCHIVE_FATAL);
+	}
+	r += client->dataset[cursor].begin_position;
+
+	if (r >= 0) {
+		/*
+		 * Ouch.  Clearing the buffer like this hurts, especially
+		 * at bid time.  A lot of our efficiency at bid time comes
+		 * from having bidders reuse the data we've already read.
+		 *
+		 * TODO: If the seek request is in data we already
+		 * have, then don't call the seek callback.
+		 *
+		 * TODO: Zip seeks to end-of-file at bid time.  If
+		 * other formats also start doing this, we may need to
+		 * find a way for clients to fudge the seek offset to
+		 * a block boundary.
+		 *
+		 * Hmmm... If whence was SEEK_END, we know the file
+		 * size is (r - offset).  Can we use that to simplify
+		 * the TODO items above?
+		 */
+		filter->avail = filter->client_avail = 0;
+		filter->next = filter->buffer;
+		filter->position = r;
+		filter->end_of_file = 0;
+	}
+	return r;
+}
diff --git a/c/archive_read_add_passphrase.c b/c/archive_read_add_passphrase.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_add_passphrase.c
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "archive_read_private.h"
+
+static void
+add_passphrase_to_tail(struct archive_read *a,
+    struct archive_read_passphrase *p)
+{
+	*a->passphrases.last = p;
+	a->passphrases.last = &p->next;
+	p->next = NULL;
+}
+
+static struct archive_read_passphrase *
+remove_passphrases_from_head(struct archive_read *a)
+{
+	struct archive_read_passphrase *p;
+
+	p = a->passphrases.first;
+	if (p != NULL)
+		a->passphrases.first = p->next;
+	return (p);
+}
+
+static void
+insert_passphrase_to_head(struct archive_read *a,
+    struct archive_read_passphrase *p)
+{
+	p->next = a->passphrases.first;
+	a->passphrases.first = p;
+	if (&a->passphrases.first == a->passphrases.last) {
+		a->passphrases.last = &p->next;
+		p->next = NULL;
+	}
+}
+
+static struct archive_read_passphrase *
+new_read_passphrase(struct archive_read *a, const char *passphrase)
+{
+	struct archive_read_passphrase *p;
+
+	p = malloc(sizeof(*p));
+	if (p == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (NULL);
+	}
+	p->passphrase = strdup(passphrase);
+	if (p->passphrase == NULL) {
+		free(p);
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (NULL);
+	}
+	return (p);
+}
+
+int
+archive_read_add_passphrase(struct archive *_a, const char *passphrase)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_passphrase *p;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+		"archive_read_add_passphrase");
+
+	if (passphrase == NULL || passphrase[0] == '\0') {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Empty passphrase is unacceptable");
+		return (ARCHIVE_FAILED);
+	}
+
+	p = new_read_passphrase(a, passphrase);
+	if (p == NULL)
+		return (ARCHIVE_FATAL);
+	add_passphrase_to_tail(a, p);
+
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_set_passphrase_callback(struct archive *_a, void *client_data,
+    archive_passphrase_callback *cb)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+		"archive_read_set_passphrase_callback");
+
+	a->passphrases.callback = cb;
+	a->passphrases.client_data = client_data;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Call this in advance when you start to get a passphrase for decryption
+ * for a entry.
+ */
+void
+__archive_read_reset_passphrase(struct archive_read *a)
+{
+
+	a->passphrases.candidate = -1;
+}
+
+/*
+ * Get a passphrase for decryption.
+ */
+const char *
+__archive_read_next_passphrase(struct archive_read *a)
+{
+	struct archive_read_passphrase *p;
+	const char *passphrase;
+
+	if (a->passphrases.candidate < 0) {
+		/* Count out how many passphrases we have. */
+		int cnt = 0;
+
+		for (p = a->passphrases.first; p != NULL; p = p->next)
+			cnt++;
+		a->passphrases.candidate = cnt;
+		p = a->passphrases.first;
+	} else if (a->passphrases.candidate > 1) {
+		/* Rotate a passphrase list. */
+		a->passphrases.candidate--;
+		p = remove_passphrases_from_head(a);
+		add_passphrase_to_tail(a, p);
+		/* Pick a new passphrase candidate up. */
+		p = a->passphrases.first;
+	} else if (a->passphrases.candidate == 1) {
+		/* This case is that all candidates failed to decrypt. */
+		a->passphrases.candidate = 0;
+		if (a->passphrases.first->next != NULL) {
+			/* Rotate a passphrase list. */
+			p = remove_passphrases_from_head(a);
+			add_passphrase_to_tail(a, p);
+		}
+		p = NULL;
+	} else  /* There is no passphrase candidate. */
+		p = NULL;
+
+	if (p != NULL)
+		passphrase = p->passphrase;
+	else if (a->passphrases.callback != NULL) {
+		/* Get a passphrase through a call-back function
+		 * since we tried all passphrases out or we don't
+		 * have it. */
+		passphrase = a->passphrases.callback(&a->archive,
+		    a->passphrases.client_data);
+		if (passphrase != NULL) {
+			p = new_read_passphrase(a, passphrase);
+			if (p == NULL)
+				return (NULL);
+			insert_passphrase_to_head(a, p);
+			a->passphrases.candidate = 1;
+		}
+	} else
+		passphrase = NULL;
+
+	return (passphrase);
+}
diff --git a/c/archive_read_append_filter.c b/c/archive_read_append_filter.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_append_filter.c
@@ -0,0 +1,204 @@
+/*-
+ * Copyright (c) 2003-2012 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+int
+archive_read_append_filter(struct archive *_a, int code)
+{
+  int r1, r2, number_bidders, i;
+  char str[20];
+  struct archive_read_filter_bidder *bidder;
+  struct archive_read_filter *filter;
+  struct archive_read *a = (struct archive_read *)_a;
+
+  r2 = (ARCHIVE_OK);
+  switch (code)
+  {
+    case ARCHIVE_FILTER_NONE:
+      /* No filter to add, so do nothing.
+       * NOTE: An initial "NONE" type filter is always set at the end of the
+       * filter chain.
+       */
+      r1 = (ARCHIVE_OK);
+      break;
+    case ARCHIVE_FILTER_GZIP:
+      strcpy(str, "gzip");
+      r1 = archive_read_support_filter_gzip(_a);
+      break;
+    case ARCHIVE_FILTER_BZIP2:
+      strcpy(str, "bzip2");
+      r1 = archive_read_support_filter_bzip2(_a);
+      break;
+    case ARCHIVE_FILTER_COMPRESS:
+      strcpy(str, "compress (.Z)");
+      r1 = archive_read_support_filter_compress(_a);
+      break;
+    case ARCHIVE_FILTER_PROGRAM:
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+          "Cannot append program filter using archive_read_append_filter");
+      return (ARCHIVE_FATAL);
+    case ARCHIVE_FILTER_LZMA:
+      strcpy(str, "lzma");
+      r1 = archive_read_support_filter_lzma(_a);
+      break;
+    case ARCHIVE_FILTER_XZ:
+      strcpy(str, "xz");
+      r1 = archive_read_support_filter_xz(_a);
+      break;
+    case ARCHIVE_FILTER_UU:
+      strcpy(str, "uu");
+      r1 = archive_read_support_filter_uu(_a);
+      break;
+    case ARCHIVE_FILTER_RPM:
+      strcpy(str, "rpm");
+      r1 = archive_read_support_filter_rpm(_a);
+      break;
+    case ARCHIVE_FILTER_LZ4:
+      strcpy(str, "lz4");
+      r1 = archive_read_support_filter_lz4(_a);
+      break;
+    case ARCHIVE_FILTER_ZSTD:
+      strcpy(str, "zstd");
+      r1 = archive_read_support_filter_zstd(_a);
+      break;
+    case ARCHIVE_FILTER_LZIP:
+      strcpy(str, "lzip");
+      r1 = archive_read_support_filter_lzip(_a);
+      break;
+    case ARCHIVE_FILTER_LRZIP:
+      strcpy(str, "lrzip");
+      r1 = archive_read_support_filter_lrzip(_a);
+      break;
+    default:
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+          "Invalid filter code specified");
+      return (ARCHIVE_FATAL);
+  }
+
+  if (code != ARCHIVE_FILTER_NONE)
+  {
+    number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+    bidder = a->bidders;
+    for (i = 0; i < number_bidders; i++, bidder++)
+    {
+      if (!bidder->name || !strcmp(bidder->name, str))
+        break;
+    }
+    if (!bidder->name || strcmp(bidder->name, str))
+    {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+          "Internal error: Unable to append filter");
+      return (ARCHIVE_FATAL);
+    }
+
+    filter
+        = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+    if (filter == NULL)
+    {
+      archive_set_error(&a->archive, ENOMEM, "Out of memory");
+      return (ARCHIVE_FATAL);
+    }
+    filter->bidder = bidder;
+    filter->archive = a;
+    filter->upstream = a->filter;
+    a->filter = filter;
+    r2 = (bidder->init)(a->filter);
+    if (r2 != ARCHIVE_OK) {
+      __archive_read_free_filters(a);
+      return (ARCHIVE_FATAL);
+    }
+  }
+
+  a->bypass_filter_bidding = 1;
+  return (r1 < r2) ? r1 : r2;
+}
+
+int
+archive_read_append_filter_program(struct archive *_a, const char *cmd)
+{
+  return (archive_read_append_filter_program_signature(_a, cmd, NULL, 0));
+}
+
+int
+archive_read_append_filter_program_signature(struct archive *_a,
+  const char *cmd, const void *signature, size_t signature_len)
+{
+  int r, number_bidders, i;
+  struct archive_read_filter_bidder *bidder;
+  struct archive_read_filter *filter;
+  struct archive_read *a = (struct archive_read *)_a;
+
+  if (archive_read_support_filter_program_signature(_a, cmd, signature,
+    signature_len) != (ARCHIVE_OK))
+    return (ARCHIVE_FATAL);
+
+  number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+  bidder = a->bidders;
+  for (i = 0; i < number_bidders; i++, bidder++)
+  {
+    /* Program bidder name set to filter name after initialization */
+    if (bidder->data && !bidder->name)
+      break;
+  }
+  if (!bidder->data)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+        "Internal error: Unable to append program filter");
+    return (ARCHIVE_FATAL);
+  }
+
+  filter
+      = (struct archive_read_filter *)calloc(1, sizeof(*filter));
+  if (filter == NULL)
+  {
+    archive_set_error(&a->archive, ENOMEM, "Out of memory");
+    return (ARCHIVE_FATAL);
+  }
+  filter->bidder = bidder;
+  filter->archive = a;
+  filter->upstream = a->filter;
+  a->filter = filter;
+  r = (bidder->init)(a->filter);
+  if (r != ARCHIVE_OK) {
+    __archive_read_free_filters(a);
+    return (ARCHIVE_FATAL);
+  }
+  bidder->name = a->filter->name;
+
+  a->bypass_filter_bidding = 1;
+  return r;
+}
diff --git a/c/archive_read_data_into_fd.c b/c/archive_read_data_into_fd.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_data_into_fd.c
@@ -0,0 +1,139 @@
+/*-
+ * 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.16 2008/05/23 05:01:29 cperciva 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.
+ */
+static int
+pad_to(struct archive *a, int fd, int can_lseek,
+    size_t nulls_size, const char *nulls,
+    int64_t target_offset, int64_t actual_offset)
+{
+	size_t to_write;
+	ssize_t bytes_written;
+
+	if (can_lseek) {
+		actual_offset = lseek(fd,
+		    target_offset - actual_offset, SEEK_CUR);
+		if (actual_offset != target_offset) {
+			archive_set_error(a, errno, "Seek error");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_OK);
+	}
+	while (target_offset > actual_offset) {
+		to_write = nulls_size;
+		if (target_offset < actual_offset + (int64_t)nulls_size)
+			to_write = (size_t)(target_offset - actual_offset);
+		bytes_written = write(fd, nulls, to_write);
+		if (bytes_written < 0) {
+			archive_set_error(a, errno, "Write error");
+			return (ARCHIVE_FATAL);
+		}
+		actual_offset += bytes_written;
+	}
+	return (ARCHIVE_OK);
+}
+
+
+int
+archive_read_data_into_fd(struct archive *a, int fd)
+{
+	struct stat st;
+	int r, r2;
+	const void *buff;
+	size_t size, bytes_to_write;
+	ssize_t bytes_written;
+	int64_t target_offset;
+	int64_t actual_offset = 0;
+	int can_lseek;
+	char *nulls = NULL;
+	size_t nulls_size = 16384;
+
+	archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_data_into_fd");
+
+	can_lseek = (fstat(fd, &st) == 0) && S_ISREG(st.st_mode);
+	if (!can_lseek)
+		nulls = calloc(1, nulls_size);
+
+	while ((r = archive_read_data_block(a, &buff, &size, &target_offset)) ==
+	    ARCHIVE_OK) {
+		const char *p = buff;
+		if (target_offset > actual_offset) {
+			r = pad_to(a, fd, can_lseek, nulls_size, nulls,
+			    target_offset, actual_offset);
+			if (r != ARCHIVE_OK)
+				break;
+			actual_offset = target_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");
+				r = ARCHIVE_FATAL;
+				goto cleanup;
+			}
+			actual_offset += bytes_written;
+			p += bytes_written;
+			size -= bytes_written;
+		}
+	}
+
+	if (r == ARCHIVE_EOF && target_offset > actual_offset) {
+		r2 = pad_to(a, fd, can_lseek, nulls_size, nulls,
+		    target_offset, actual_offset);
+		if (r2 != ARCHIVE_OK)
+			r = r2;
+	}
+
+cleanup:
+	free(nulls);
+	if (r != ARCHIVE_EOF)
+		return (r);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_read_disk_entry_from_file.c b/c/archive_read_disk_entry_from_file.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_disk_entry_from_file.c
@@ -0,0 +1,1044 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
+ * 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");
+
+/* This is the tree-walking code for POSIX systems. */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if defined(HAVE_SYS_XATTR_H)
+#include <sys/xattr.h>
+#elif defined(HAVE_ATTR_XATTR_H)
+#include <attr/xattr.h>
+#endif
+#ifdef HAVE_SYS_EA_H
+#include <sys/ea.h>
+#endif
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+#ifdef HAVE_LINUX_FIEMAP_H
+#include <linux/fiemap.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>      /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC	0
+#endif
+
+static int setup_mac_metadata(struct archive_read_disk *,
+    struct archive_entry *, int *fd);
+static int setup_xattrs(struct archive_read_disk *,
+    struct archive_entry *, int *fd);
+static int setup_sparse(struct archive_read_disk *,
+    struct archive_entry *, int *fd);
+#if defined(HAVE_LINUX_FIEMAP_H)
+static int setup_sparse_fiemap(struct archive_read_disk *,
+    struct archive_entry *, int *fd);
+#endif
+
+#if !ARCHIVE_ACL_SUPPORT
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	(void)a;      /* UNUSED */
+	(void)entry;  /* UNUSED */
+	(void)fd;     /* UNUSED */
+	return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * Enter working directory and return working pathname of archive_entry.
+ * If a pointer to an integer is provided and its value is below zero
+ * open a file descriptor on this pathname.
+ */
+const char *
+archive_read_disk_entry_setup_path(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	const char *path;
+
+	path = archive_entry_sourcepath(entry);
+
+	if (path == NULL || (a->tree != NULL &&
+	    a->tree_enter_working_dir(a->tree) != 0))
+		path = archive_entry_pathname(entry);
+	if (path == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		   "Couldn't determine path");
+	} else if (fd != NULL && *fd < 0 && a->tree != NULL &&
+	    (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK)) {
+		*fd = a->open_on_current_dir(a->tree, path,
+		    O_RDONLY | O_NONBLOCK);
+	}
+	return (path);
+}
+
+int
+archive_read_disk_entry_from_file(struct archive *_a,
+    struct archive_entry *entry,
+    int fd,
+    const struct stat *st)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	const char *path, *name;
+	struct stat s;
+	int initial_fd = fd;
+	int r, r1;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
+		"archive_read_disk_entry_from_file");
+
+	archive_clear_error(_a);
+	path = archive_entry_sourcepath(entry);
+	if (path == NULL)
+		path = archive_entry_pathname(entry);
+
+	if (a->tree == NULL) {
+		if (st == NULL) {
+#if HAVE_FSTAT
+			if (fd >= 0) {
+				if (fstat(fd, &s) != 0) {
+					archive_set_error(&a->archive, errno,
+					    "Can't fstat");
+					return (ARCHIVE_FAILED);
+				}
+			} else
+#endif
+#if HAVE_LSTAT
+			if (!a->follow_symlinks) {
+				if (lstat(path, &s) != 0) {
+					archive_set_error(&a->archive, errno,
+					    "Can't lstat %s", path);
+					return (ARCHIVE_FAILED);
+				}
+			} else
+#endif
+			if (la_stat(path, &s) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Can't stat %s", path);
+				return (ARCHIVE_FAILED);
+			}
+			st = &s;
+		}
+		archive_entry_copy_stat(entry, st);
+	}
+
+	/* Lookup uname/gname */
+	name = archive_read_disk_uname(_a, archive_entry_uid(entry));
+	if (name != NULL)
+		archive_entry_copy_uname(entry, name);
+	name = archive_read_disk_gname(_a, archive_entry_gid(entry));
+	if (name != NULL)
+		archive_entry_copy_gname(entry, name);
+
+#ifdef HAVE_STRUCT_STAT_ST_FLAGS
+	/* On FreeBSD, we get flags for free with the stat. */
+	/* TODO: Does this belong in copy_stat()? */
+	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 && st->st_flags != 0)
+		archive_entry_set_fflags(entry, st->st_flags, 0);
+#endif
+
+#if (defined(FS_IOC_GETFLAGS) && defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+    (defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
+	/* Linux requires an extra ioctl to pull the flags.  Although
+	 * this is an extra step, it has a nice side-effect: We get an
+	 * open file descriptor which we can use in the subsequent lookups. */
+	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0 &&
+	    (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) {
+		if (fd < 0) {
+			if (a->tree != NULL)
+				fd = a->open_on_current_dir(a->tree, path,
+					O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+			else
+				fd = open(path, O_RDONLY | O_NONBLOCK |
+						O_CLOEXEC);
+			__archive_ensure_cloexec_flag(fd);
+		}
+		if (fd >= 0) {
+			int stflags;
+			r = ioctl(fd,
+#if defined(FS_IOC_GETFLAGS)
+			    FS_IOC_GETFLAGS,
+#else
+			    EXT2_IOC_GETFLAGS,
+#endif
+			    &stflags);
+			if (r == 0 && stflags != 0)
+				archive_entry_set_fflags(entry, stflags, 0);
+		}
+	}
+#endif
+
+#if defined(HAVE_READLINK) || defined(HAVE_READLINKAT)
+	if (S_ISLNK(st->st_mode)) {
+		size_t linkbuffer_len = st->st_size;
+		char *linkbuffer;
+		int lnklen;
+
+		linkbuffer = malloc(linkbuffer_len + 1);
+		if (linkbuffer == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Couldn't read link data");
+			return (ARCHIVE_FAILED);
+		}
+		if (a->tree != NULL) {
+#ifdef HAVE_READLINKAT
+			lnklen = readlinkat(a->tree_current_dir_fd(a->tree),
+			    path, linkbuffer, linkbuffer_len);
+#else
+			if (a->tree_enter_working_dir(a->tree) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Couldn't read link data");
+				free(linkbuffer);
+				return (ARCHIVE_FAILED);
+			}
+			lnklen = readlink(path, linkbuffer, linkbuffer_len);
+#endif /* HAVE_READLINKAT */
+		} else
+			lnklen = readlink(path, linkbuffer, linkbuffer_len);
+		if (lnklen < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't read link data");
+			free(linkbuffer);
+			return (ARCHIVE_FAILED);
+		}
+		linkbuffer[lnklen] = '\0';
+		archive_entry_set_symlink(entry, linkbuffer);
+		free(linkbuffer);
+	}
+#endif /* HAVE_READLINK || HAVE_READLINKAT */
+
+	r = 0;
+	if ((a->flags & ARCHIVE_READDISK_NO_ACL) == 0)
+		r = archive_read_disk_entry_setup_acls(a, entry, &fd);
+	if ((a->flags & ARCHIVE_READDISK_NO_XATTR) == 0) {
+		r1 = setup_xattrs(a, entry, &fd);
+		if (r1 < r)
+			r = r1;
+	}
+	if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {
+		r1 = setup_mac_metadata(a, entry, &fd);
+		if (r1 < r)
+			r = r1;
+	}
+	r1 = setup_sparse(a, entry, &fd);
+	if (r1 < r)
+		r = r1;
+
+	/* If we opened the file earlier in this function, close it. */
+	if (initial_fd != fd)
+		close(fd);
+	return (r);
+}
+
+#if defined(__APPLE__) && defined(HAVE_COPYFILE_H)
+/*
+ * The Mac OS "copyfile()" API copies the extended metadata for a
+ * file into a separate file in AppleDouble format (see RFC 1740).
+ *
+ * Mac OS tar and cpio implementations store this extended
+ * metadata as a separate entry just before the regular entry
+ * with a "._" prefix added to the filename.
+ *
+ * Note that this is currently done unconditionally; the tar program has
+ * an option to discard this information before the archive is written.
+ *
+ * TODO: If there's a failure, report it and return ARCHIVE_WARN.
+ */
+static int
+setup_mac_metadata(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	int tempfd = -1;
+	int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR;
+	struct stat copyfile_stat;
+	int ret = ARCHIVE_OK;
+	void *buff = NULL;
+	int have_attrs;
+	const char *name, *tempdir;
+	struct archive_string tempfile;
+
+	(void)fd; /* UNUSED */
+
+	name = archive_read_disk_entry_setup_path(a, entry, NULL);
+	if (name == NULL)
+		return (ARCHIVE_WARN);
+
+	/* Short-circuit if there's nothing to do. */
+	have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK);
+	if (have_attrs == -1) {
+		archive_set_error(&a->archive, errno,
+			"Could not check extended attributes");
+		return (ARCHIVE_WARN);
+	}
+	if (have_attrs == 0)
+		return (ARCHIVE_OK);
+
+	tempdir = NULL;
+	if (issetugid() == 0)
+		tempdir = getenv("TMPDIR");
+	if (tempdir == NULL)
+		tempdir = _PATH_TMP;
+	archive_string_init(&tempfile);
+	archive_strcpy(&tempfile, tempdir);
+	archive_strcat(&tempfile, "tar.md.XXXXXX");
+	tempfd = mkstemp(tempfile.s);
+	if (tempfd < 0) {
+		archive_set_error(&a->archive, errno,
+		    "Could not open extended attribute file");
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	__archive_ensure_cloexec_flag(tempfd);
+
+	/* XXX I wish copyfile() could pack directly to a memory
+	 * buffer; that would avoid the temp file here.  For that
+	 * matter, it would be nice if fcopyfile() actually worked,
+	 * that would reduce the many open/close races here. */
+	if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) {
+		archive_set_error(&a->archive, errno,
+		    "Could not pack extended attributes");
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	if (fstat(tempfd, &copyfile_stat)) {
+		archive_set_error(&a->archive, errno,
+		    "Could not check size of extended attributes");
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	buff = malloc(copyfile_stat.st_size);
+	if (buff == NULL) {
+		archive_set_error(&a->archive, errno,
+		    "Could not allocate memory for extended attributes");
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) {
+		archive_set_error(&a->archive, errno,
+		    "Could not read extended attributes into memory");
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size);
+
+cleanup:
+	if (tempfd >= 0) {
+		close(tempfd);
+		unlink(tempfile.s);
+	}
+	archive_string_free(&tempfile);
+	free(buff);
+	return (ret);
+}
+
+#else
+
+/*
+ * Stub implementation for non-Mac systems.
+ */
+static int
+setup_mac_metadata(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	(void)a; /* UNUSED */
+	(void)entry; /* UNUSED */
+	(void)fd; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+#endif
+
+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
+
+/*
+ * Linux, Darwin and AIX extended attribute support.
+ *
+ * TODO:  By using a stack-allocated buffer for the first
+ * call to getxattr(), we might be able to avoid the second
+ * call entirely.  We only need the second call if the
+ * stack-allocated buffer is too small.  But a modest buffer
+ * of 1024 bytes or so will often be big enough.  Same applies
+ * to listxattr().
+ */
+
+
+static int
+setup_xattr(struct archive_read_disk *a,
+    struct archive_entry *entry, const char *name, int fd, const char *accpath)
+{
+	ssize_t size;
+	void *value = NULL;
+
+
+	if (fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+		size = fgetxattr(fd, name, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+		size = fgetxattr(fd, name, NULL, 0, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+		size = fgetea(fd, name, NULL, 0);
+#endif
+	} else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+		size = lgetxattr(accpath, name, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+		size = getxattr(accpath, name, NULL, 0, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+		size = lgetea(accpath, name, NULL, 0);
+#endif
+	} else {
+#if ARCHIVE_XATTR_LINUX
+		size = getxattr(accpath, name, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+		size = getxattr(accpath, name, NULL, 0, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+		size = getea(accpath, name, NULL, 0);
+#endif
+	}
+
+	if (size == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Couldn't query extended attribute");
+		return (ARCHIVE_WARN);
+	}
+
+	if (size > 0 && (value = malloc(size)) == NULL) {
+		archive_set_error(&a->archive, errno, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+
+
+	if (fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+		size = fgetxattr(fd, name, value, size);
+#elif ARCHIVE_XATTR_DARWIN
+		size = fgetxattr(fd, name, value, size, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+		size = fgetea(fd, name, value, size);
+#endif
+	} else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+		size = lgetxattr(accpath, name, value, size);
+#elif ARCHIVE_XATTR_DARWIN
+		size = getxattr(accpath, name, value, size, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+		size = lgetea(accpath, name, value, size);
+#endif
+	} else {
+#if ARCHIVE_XATTR_LINUX
+		size = getxattr(accpath, name, value, size);
+#elif ARCHIVE_XATTR_DARWIN
+		size = getxattr(accpath, name, value, size, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+		size = getea(accpath, name, value, size);
+#endif
+	}
+
+	if (size == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Couldn't read extended attribute");
+		return (ARCHIVE_WARN);
+	}
+
+	archive_entry_xattr_add_entry(entry, name, value, size);
+
+	free(value);
+	return (ARCHIVE_OK);
+}
+
+static int
+setup_xattrs(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	char *list, *p;
+	const char *path;
+	ssize_t list_size;
+
+	path = NULL;
+
+	if (*fd < 0) {
+		path = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (path == NULL)
+			return (ARCHIVE_WARN);
+	}
+
+	if (*fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+		list_size = flistxattr(*fd, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+		list_size = flistxattr(*fd, NULL, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+		list_size = flistea(*fd, NULL, 0);
+#endif
+	} else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+		list_size = llistxattr(path, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+		list_size = listxattr(path, NULL, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+		list_size = llistea(path, NULL, 0);
+#endif
+	} else {
+#if ARCHIVE_XATTR_LINUX
+		list_size = listxattr(path, NULL, 0);
+#elif ARCHIVE_XATTR_DARWIN
+		list_size = listxattr(path, NULL, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+		list_size = listea(path, NULL, 0);
+#endif
+	}
+
+	if (list_size == -1) {
+		if (errno == ENOTSUP || errno == ENOSYS)
+			return (ARCHIVE_OK);
+		archive_set_error(&a->archive, errno,
+			"Couldn't list extended attributes");
+		return (ARCHIVE_WARN);
+	}
+
+	if (list_size == 0)
+		return (ARCHIVE_OK);
+
+	if ((list = malloc(list_size)) == NULL) {
+		archive_set_error(&a->archive, errno, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (*fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+		list_size = flistxattr(*fd, list, list_size);
+#elif ARCHIVE_XATTR_DARWIN
+		list_size = flistxattr(*fd, list, list_size, 0);
+#elif ARCHIVE_XATTR_AIX
+		list_size = flistea(*fd, list, list_size);
+#endif
+	} else if (!a->follow_symlinks) {
+#if ARCHIVE_XATTR_LINUX
+		list_size = llistxattr(path, list, list_size);
+#elif ARCHIVE_XATTR_DARWIN
+		list_size = listxattr(path, list, list_size, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+		list_size = llistea(path, list, list_size);
+#endif
+	} else {
+#if ARCHIVE_XATTR_LINUX
+		list_size = listxattr(path, list, list_size);
+#elif ARCHIVE_XATTR_DARWIN
+		list_size = listxattr(path, list, list_size, 0);
+#elif ARCHIVE_XATTR_AIX
+		list_size = listea(path, list, list_size);
+#endif
+	}
+
+	if (list_size == -1) {
+		archive_set_error(&a->archive, errno,
+			"Couldn't retrieve extended attributes");
+		free(list);
+		return (ARCHIVE_WARN);
+	}
+
+	for (p = list; (p - list) < list_size; p += strlen(p) + 1) {
+#if ARCHIVE_XATTR_LINUX
+		/* Linux: skip POSIX.1e ACL extended attributes */
+		if (strncmp(p, "system.", 7) == 0 &&
+		   (strcmp(p + 7, "posix_acl_access") == 0 ||
+		    strcmp(p + 7, "posix_acl_default") == 0))
+			continue;
+		if (strncmp(p, "trusted.SGI_", 12) == 0 &&
+		   (strcmp(p + 12, "ACL_DEFAULT") == 0 ||
+		    strcmp(p + 12, "ACL_FILE") == 0))
+			continue;
+
+		/* Linux: xfsroot namespace is obsolete and unsupported */
+		if (strncmp(p, "xfsroot.", 8) == 0)
+			continue;
+#endif
+		setup_xattr(a, entry, p, *fd, path);
+	}
+
+	free(list);
+	return (ARCHIVE_OK);
+}
+
+#elif ARCHIVE_XATTR_FREEBSD
+
+/*
+ * FreeBSD extattr interface.
+ */
+
+/* TODO: Implement this.  Follow the Linux model above, but
+ * with FreeBSD-specific system calls, of course.  Be careful
+ * to not include the system extattrs that hold ACLs; we handle
+ * those separately.
+ */
+static int
+setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
+    int namespace, const char *name, const char *fullname, int fd,
+    const char *path);
+
+static int
+setup_xattr(struct archive_read_disk *a, struct archive_entry *entry,
+    int namespace, const char *name, const char *fullname, int fd,
+    const char *accpath)
+{
+	ssize_t size;
+	void *value = NULL;
+
+	if (fd >= 0)
+		size = extattr_get_fd(fd, namespace, name, NULL, 0);
+	else if (!a->follow_symlinks)
+		size = extattr_get_link(accpath, namespace, name, NULL, 0);
+	else
+		size = extattr_get_file(accpath, namespace, name, NULL, 0);
+
+	if (size == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Couldn't query extended attribute");
+		return (ARCHIVE_WARN);
+	}
+
+	if (size > 0 && (value = malloc(size)) == NULL) {
+		archive_set_error(&a->archive, errno, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (fd >= 0)
+		size = extattr_get_fd(fd, namespace, name, value, size);
+	else if (!a->follow_symlinks)
+		size = extattr_get_link(accpath, namespace, name, value, size);
+	else
+		size = extattr_get_file(accpath, namespace, name, value, size);
+
+	if (size == -1) {
+		free(value);
+		archive_set_error(&a->archive, errno,
+		    "Couldn't read extended attribute");
+		return (ARCHIVE_WARN);
+	}
+
+	archive_entry_xattr_add_entry(entry, fullname, value, size);
+
+	free(value);
+	return (ARCHIVE_OK);
+}
+
+static int
+setup_xattrs(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	char buff[512];
+	char *list, *p;
+	ssize_t list_size;
+	const char *path;
+	int namespace = EXTATTR_NAMESPACE_USER;
+
+	path = NULL;
+
+	if (*fd < 0) {
+		path = archive_read_disk_entry_setup_path(a, entry, fd);
+		if (path == NULL)
+			return (ARCHIVE_WARN);
+	}
+
+	if (*fd >= 0)
+		list_size = extattr_list_fd(*fd, namespace, NULL, 0);
+	else if (!a->follow_symlinks)
+		list_size = extattr_list_link(path, namespace, NULL, 0);
+	else
+		list_size = extattr_list_file(path, namespace, NULL, 0);
+
+	if (list_size == -1 && errno == EOPNOTSUPP)
+		return (ARCHIVE_OK);
+	if (list_size == -1) {
+		archive_set_error(&a->archive, errno,
+			"Couldn't list extended attributes");
+		return (ARCHIVE_WARN);
+	}
+
+	if (list_size == 0)
+		return (ARCHIVE_OK);
+
+	if ((list = malloc(list_size)) == NULL) {
+		archive_set_error(&a->archive, errno, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (*fd >= 0)
+		list_size = extattr_list_fd(*fd, namespace, list, list_size);
+	else if (!a->follow_symlinks)
+		list_size = extattr_list_link(path, namespace, list, list_size);
+	else
+		list_size = extattr_list_file(path, namespace, list, list_size);
+
+	if (list_size == -1) {
+		archive_set_error(&a->archive, errno,
+			"Couldn't retrieve extended attributes");
+		free(list);
+		return (ARCHIVE_WARN);
+	}
+
+	p = list;
+	while ((p - list) < list_size) {
+		size_t len = 255 & (int)*p;
+		char *name;
+
+		strcpy(buff, "user.");
+		name = buff + strlen(buff);
+		memcpy(name, p + 1, len);
+		name[len] = '\0';
+		setup_xattr(a, entry, namespace, name, buff, *fd, path);
+		p += 1 + len;
+	}
+
+	free(list);
+	return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ * Generic (stub) extended attribute support.
+ */
+static int
+setup_xattrs(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	(void)a;     /* UNUSED */
+	(void)entry; /* UNUSED */
+	(void)fd;    /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+#endif
+
+#if defined(HAVE_LINUX_FIEMAP_H)
+
+/*
+ * Linux FIEMAP sparse interface.
+ *
+ * The FIEMAP ioctl returns an "extent" for each physical allocation
+ * on disk.  We need to process those to generate a more compact list
+ * of logical file blocks.  We also need to be very careful to use
+ * FIEMAP_FLAG_SYNC here, since there are reports that Linux sometimes
+ * does not report allocations for newly-written data that hasn't
+ * been synced to disk.
+ *
+ * It's important to return a minimal sparse file list because we want
+ * to not trigger sparse file extensions if we don't have to, since
+ * not all readers support them.
+ */
+
+static int
+setup_sparse_fiemap(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	char buff[4096];
+	struct fiemap *fm;
+	struct fiemap_extent *fe;
+	int64_t size;
+	int count, do_fiemap, iters;
+	int exit_sts = ARCHIVE_OK;
+	const char *path;
+
+	if (archive_entry_filetype(entry) != AE_IFREG
+	    || archive_entry_size(entry) <= 0
+	    || archive_entry_hardlink(entry) != NULL)
+		return (ARCHIVE_OK);
+
+	if (*fd < 0) {
+		path = archive_read_disk_entry_setup_path(a, entry, NULL);
+		if (path == NULL)
+			return (ARCHIVE_FAILED);
+
+		if (a->tree != NULL)
+			*fd = a->open_on_current_dir(a->tree, path,
+				O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+		else
+			*fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+		if (*fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't open `%s'", path);
+			return (ARCHIVE_FAILED);
+		}
+		__archive_ensure_cloexec_flag(*fd);
+	}
+
+	/* Initialize buffer to avoid the error valgrind complains about. */
+	memset(buff, 0, sizeof(buff));
+	count = (sizeof(buff) - sizeof(*fm))/sizeof(*fe);
+	fm = (struct fiemap *)buff;
+	fm->fm_start = 0;
+	fm->fm_length = ~0ULL;;
+	fm->fm_flags = FIEMAP_FLAG_SYNC;
+	fm->fm_extent_count = count;
+	do_fiemap = 1;
+	size = archive_entry_size(entry);
+	for (iters = 0; ; ++iters) {
+		int i, r;
+
+		r = ioctl(*fd, FS_IOC_FIEMAP, fm); 
+		if (r < 0) {
+			/* When something error happens, it is better we
+			 * should return ARCHIVE_OK because an earlier
+			 * version(<2.6.28) cannot perform FS_IOC_FIEMAP. */
+			goto exit_setup_sparse_fiemap;
+		}
+		if (fm->fm_mapped_extents == 0) {
+			if (iters == 0) {
+				/* Fully sparse file; insert a zero-length "data" entry */
+				archive_entry_sparse_add_entry(entry, 0, 0);
+			}
+			break;
+		}
+		fe = fm->fm_extents;
+		for (i = 0; i < (int)fm->fm_mapped_extents; i++, fe++) {
+			if (!(fe->fe_flags & FIEMAP_EXTENT_UNWRITTEN)) {
+				/* The fe_length of the last block does not
+				 * adjust itself to its size files. */
+				int64_t length = fe->fe_length;
+				if (fe->fe_logical + length > (uint64_t)size)
+					length -= fe->fe_logical + length - size;
+				if (fe->fe_logical == 0 && length == size) {
+					/* This is not sparse. */
+					do_fiemap = 0;
+					break;
+				}
+				if (length > 0)
+					archive_entry_sparse_add_entry(entry,
+					    fe->fe_logical, length);
+			}
+			if (fe->fe_flags & FIEMAP_EXTENT_LAST)
+				do_fiemap = 0;
+		}
+		if (do_fiemap) {
+			fe = fm->fm_extents + fm->fm_mapped_extents -1;
+			fm->fm_start = fe->fe_logical + fe->fe_length;
+		} else
+			break;
+	}
+exit_setup_sparse_fiemap:
+	return (exit_sts);
+}
+
+#if !defined(SEEK_HOLE) || !defined(SEEK_DATA)
+static int
+setup_sparse(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	return setup_sparse_fiemap(a, entry, fd);
+}
+#endif
+#endif	/* defined(HAVE_LINUX_FIEMAP_H) */
+
+#if defined(SEEK_HOLE) && defined(SEEK_DATA)
+
+/*
+ * SEEK_HOLE sparse interface (FreeBSD, Linux, Solaris)
+ */
+
+static int
+setup_sparse(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	int64_t size;
+	off_t initial_off;
+	off_t off_s, off_e;
+	int exit_sts = ARCHIVE_OK;
+	int check_fully_sparse = 0;
+	const char *path;
+
+	if (archive_entry_filetype(entry) != AE_IFREG
+	    || archive_entry_size(entry) <= 0
+	    || archive_entry_hardlink(entry) != NULL)
+		return (ARCHIVE_OK);
+
+	/* Does filesystem support the reporting of hole ? */
+	if (*fd < 0)
+		path = archive_read_disk_entry_setup_path(a, entry, fd);
+	else
+		path = NULL;
+
+	if (*fd >= 0) {
+#ifdef _PC_MIN_HOLE_SIZE
+		if (fpathconf(*fd, _PC_MIN_HOLE_SIZE) <= 0)
+			return (ARCHIVE_OK);
+#endif
+		initial_off = lseek(*fd, 0, SEEK_CUR);
+		if (initial_off != 0)
+			lseek(*fd, 0, SEEK_SET);
+	} else {
+		if (path == NULL)
+			return (ARCHIVE_FAILED);
+#ifdef _PC_MIN_HOLE_SIZE
+		if (pathconf(path, _PC_MIN_HOLE_SIZE) <= 0)
+			return (ARCHIVE_OK);
+#endif
+		*fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+		if (*fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't open `%s'", path);
+			return (ARCHIVE_FAILED);
+		}
+		__archive_ensure_cloexec_flag(*fd);
+		initial_off = 0;
+	}
+
+#ifndef _PC_MIN_HOLE_SIZE
+	/* Check if the underlying filesystem supports seek hole */
+	off_s = lseek(*fd, 0, SEEK_HOLE);
+	if (off_s < 0)
+#if defined(HAVE_LINUX_FIEMAP_H)
+		return setup_sparse_fiemap(a, entry, fd);
+#else
+		goto exit_setup_sparse;
+#endif
+	else if (off_s > 0)
+		lseek(*fd, 0, SEEK_SET);
+#endif
+
+	off_s = 0;
+	size = archive_entry_size(entry);
+	while (off_s < size) {
+		off_s = lseek(*fd, off_s, SEEK_DATA);
+		if (off_s == (off_t)-1) {
+			if (errno == ENXIO) {
+				/* no more hole */
+				if (archive_entry_sparse_count(entry) == 0) {
+					/* Potentially a fully-sparse file. */
+					check_fully_sparse = 1;
+				}
+				break;
+			}
+			archive_set_error(&a->archive, errno,
+			    "lseek(SEEK_HOLE) failed");
+			exit_sts = ARCHIVE_FAILED;
+			goto exit_setup_sparse;
+		}
+		off_e = lseek(*fd, off_s, SEEK_HOLE);
+		if (off_e == (off_t)-1) {
+			if (errno == ENXIO) {
+				off_e = lseek(*fd, 0, SEEK_END);
+				if (off_e != (off_t)-1)
+					break;/* no more data */
+			}
+			archive_set_error(&a->archive, errno,
+			    "lseek(SEEK_DATA) failed");
+			exit_sts = ARCHIVE_FAILED;
+			goto exit_setup_sparse;
+		}
+		if (off_s == 0 && off_e == size)
+			break;/* This is not sparse. */
+		archive_entry_sparse_add_entry(entry, off_s,
+			off_e - off_s);
+		off_s = off_e;
+	}
+
+	if (check_fully_sparse) {
+		if (lseek(*fd, 0, SEEK_HOLE) == 0 &&
+			lseek(*fd, 0, SEEK_END) == size) {
+			/* Fully sparse file; insert a zero-length "data" entry */
+			archive_entry_sparse_add_entry(entry, 0, 0);
+		}
+	}
+exit_setup_sparse:
+	lseek(*fd, initial_off, SEEK_SET);
+	return (exit_sts);
+}
+
+#elif !defined(HAVE_LINUX_FIEMAP_H)
+
+/*
+ * Generic (stub) sparse support.
+ */
+static int
+setup_sparse(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	(void)a;     /* UNUSED */
+	(void)entry; /* UNUSED */
+	(void)fd;    /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+#endif
+
+#endif /* !defined(_WIN32) || defined(__CYGWIN__) */
+
diff --git a/c/archive_read_disk_posix.c b/c/archive_read_disk_posix.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_disk_posix.c
@@ -0,0 +1,2722 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* This is the tree-walking code for POSIX systems. */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
+#ifdef HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_LINUX_MAGIC_H
+#include <linux/magic.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>      /* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#include <ext2fs/ext2_fs.h>     /* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_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
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+
+#ifndef HAVE_FCHDIR
+#error fchdir function required.
+#endif
+#ifndef O_BINARY
+#define O_BINARY	0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC	0
+#endif
+
+/*-
+ * This is a new directory-walking system that addresses a number
+ * of problems I've had with fts(3).  In particular, it has no
+ * pathname-length limits (other than the size of 'int'), handles
+ * deep logical traversals, uses considerably less memory, and has
+ * an opaque interface (easier to modify in the future).
+ *
+ * Internally, it keeps a single list of "tree_entry" items that
+ * represent filesystem objects that require further attention.
+ * Non-directories are not kept in memory: they are pulled from
+ * readdir(), returned to the client, then freed as soon as possible.
+ * Any directory entry to be traversed gets pushed onto the stack.
+ *
+ * There is surprisingly little information that needs to be kept for
+ * each item on the stack.  Just the name, depth (represented here as the
+ * string length of the parent directory's pathname), and some markers
+ * indicating how to get back to the parent (via chdir("..") for a
+ * regular dir or via fchdir(2) for a symlink).
+ */
+/*
+ * TODO:
+ *    1) Loop checking.
+ *    3) Arbitrary logical traversals by closing/reopening intermediate fds.
+ */
+
+struct restore_time {
+	const char		*name;
+	time_t			 mtime;
+	long			 mtime_nsec;
+	time_t			 atime;
+	long			 atime_nsec;
+	mode_t			 filetype;
+	int			 noatime;
+};
+
+struct tree_entry {
+	int			 depth;
+	struct tree_entry	*next;
+	struct tree_entry	*parent;
+	struct archive_string	 name;
+	size_t			 dirname_length;
+	int64_t			 dev;
+	int64_t			 ino;
+	int			 flags;
+	int			 filesystem_id;
+	/* How to return back to the parent of a symlink. */
+	int			 symlink_parent_fd;
+	/* How to restore time of a directory. */
+	struct restore_time	 restore_time;
+};
+
+struct filesystem {
+	int64_t		dev;
+	int		synthetic;
+	int		remote;
+	int		noatime;
+#if defined(USE_READDIR_R)
+	size_t		name_max;
+#endif
+	long		incr_xfer_size;
+	long		max_xfer_size;
+	long		min_xfer_size;
+	long		xfer_align;
+
+	/*
+	 * Buffer used for reading file contents.
+	 */
+	/* Exactly allocated memory pointer. */
+	unsigned char	*allocation_ptr;
+	/* Pointer adjusted to the filesystem alignment . */
+	unsigned char	*buff;
+	size_t		 buff_size;
+};
+
+/* Definitions for tree_entry.flags bitmap. */
+#define	isDir		1  /* This entry is a regular directory. */
+#define	isDirLink	2  /* This entry is a symbolic link to a directory. */
+#define	needsFirstVisit	4  /* This is an initial entry. */
+#define	needsDescent	8  /* This entry needs to be previsited. */
+#define	needsOpen	16 /* This is a directory that needs to be opened. */
+#define	needsAscent	32 /* This entry needs to be postvisited. */
+
+/*
+ * Local data for this package.
+ */
+struct tree {
+	struct tree_entry	*stack;
+	struct tree_entry	*current;
+	DIR			*d;
+#define	INVALID_DIR_HANDLE NULL
+	struct dirent		*de;
+#if defined(USE_READDIR_R)
+	struct dirent		*dirent;
+	size_t			 dirent_allocated;
+#endif
+	int			 flags;
+	int			 visit_type;
+	/* Error code from last failed operation. */
+	int			 tree_errno;
+
+	/* Dynamically-sized buffer for holding path */
+	struct archive_string	 path;
+
+	/* Last path element */
+	const char		*basename;
+	/* Leading dir length */
+	size_t			 dirname_length;
+
+	int			 depth;
+	int			 openCount;
+	int			 maxOpenCount;
+	int			 initial_dir_fd;
+	int			 working_dir_fd;
+
+	struct stat		 lst;
+	struct stat		 st;
+	int			 descend;
+	int			 nlink;
+	/* How to restore time of a file. */
+	struct restore_time	 restore_time;
+
+	struct entry_sparse {
+		int64_t		 length;
+		int64_t		 offset;
+	}			*sparse_list, *current_sparse;
+	int			 sparse_count;
+	int			 sparse_list_size;
+
+	char			 initial_symlink_mode;
+	char			 symlink_mode;
+	struct filesystem	*current_filesystem;
+	struct filesystem	*filesystem_table;
+	int			 initial_filesystem_id;
+	int			 current_filesystem_id;
+	int			 max_filesystem_id;
+	int			 allocated_filesystem;
+
+	int			 entry_fd;
+	int			 entry_eof;
+	int64_t			 entry_remaining_bytes;
+	int64_t			 entry_total;
+	unsigned char		*entry_buff;
+	size_t			 entry_buff_size;
+};
+
+/* Definitions for tree.flags bitmap. */
+#define	hasStat		16 /* The st entry is valid. */
+#define	hasLstat	32 /* The lst entry is valid. */
+#define	onWorkingDir	64 /* We are on the working dir where we are
+			    * reading directory entry at this time. */
+#define	needsRestoreTimes 128
+#define	onInitialDir	256 /* We are on the initial dir. */
+
+static int
+tree_dir_next_posix(struct tree *t);
+
+#ifdef HAVE_DIRENT_D_NAMLEN
+/* BSD extension; avoids need for a strlen() call. */
+#define	D_NAMELEN(dp)	(dp)->d_namlen
+#else
+#define	D_NAMELEN(dp)	(strlen((dp)->d_name))
+#endif
+
+/* Initiate/terminate a tree traversal. */
+static struct tree *tree_open(const char *, int, int);
+static struct tree *tree_reopen(struct tree *, const char *, int);
+static void tree_close(struct tree *);
+static void tree_free(struct tree *);
+static void tree_push(struct tree *, const char *, int, int64_t, int64_t,
+		struct restore_time *);
+static int tree_enter_initial_dir(struct tree *);
+static int tree_enter_working_dir(struct tree *);
+static int tree_current_dir_fd(struct tree *);
+
+/*
+ * tree_next() returns Zero if there is no next entry, non-zero if
+ * there is.  Note that directories are visited three times.
+ * Directories are always visited first as part of enumerating their
+ * parent; that is a "regular" visit.  If tree_descend() is invoked at
+ * that time, the directory is added to a work list and will
+ * subsequently be visited two more times: once just after descending
+ * into the directory ("postdescent") and again just after ascending
+ * back to the parent ("postascent").
+ *
+ * TREE_ERROR_DIR is returned if the descent failed (because the
+ * directory couldn't be opened, for instance).  This is returned
+ * instead of TREE_POSTDESCENT/TREE_POSTASCENT.  TREE_ERROR_DIR is not a
+ * fatal error, but it does imply that the relevant subtree won't be
+ * visited.  TREE_ERROR_FATAL is returned for an error that left the
+ * traversal completely hosed.  Right now, this is only returned for
+ * chdir() failures during ascent.
+ */
+#define	TREE_REGULAR		1
+#define	TREE_POSTDESCENT	2
+#define	TREE_POSTASCENT		3
+#define	TREE_ERROR_DIR		-1
+#define	TREE_ERROR_FATAL	-2
+
+static int tree_next(struct tree *);
+
+/*
+ * Return information about the current entry.
+ */
+
+/*
+ * The current full pathname, length of the full pathname, and a name
+ * that can be used to access the file.  Because tree does use chdir
+ * extensively, the access path is almost never the same as the full
+ * current path.
+ *
+ * TODO: On platforms that support it, use openat()-style operations
+ * to eliminate the chdir() operations entirely while still supporting
+ * arbitrarily deep traversals.  This makes access_path troublesome to
+ * support, of course, which means we'll need a rich enough interface
+ * that clients can function without it.  (In particular, we'll need
+ * tree_current_open() that returns an open file descriptor.)
+ *
+ */
+static const char *tree_current_path(struct tree *);
+static const char *tree_current_access_path(struct tree *);
+
+/*
+ * Request the lstat() or stat() data for the current path.  Since the
+ * tree package needs to do some of this anyway, and caches the
+ * results, you should take advantage of it here if you need it rather
+ * than make a redundant stat() or lstat() call of your own.
+ */
+static const struct stat *tree_current_stat(struct tree *);
+static const struct stat *tree_current_lstat(struct tree *);
+static int	tree_current_is_symblic_link_target(struct tree *);
+
+/* The following functions use tricks to avoid a certain number of
+ * stat()/lstat() calls. */
+/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */
+static int tree_current_is_physical_dir(struct tree *);
+/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */
+static int tree_current_is_dir(struct tree *);
+static int update_current_filesystem(struct archive_read_disk *a,
+		    int64_t dev);
+static int setup_current_filesystem(struct archive_read_disk *);
+static int tree_target_is_same_as_parent(struct tree *, const struct stat *);
+
+static int	_archive_read_disk_open(struct archive *, const char *);
+static int	_archive_read_free(struct archive *);
+static int	_archive_read_close(struct archive *);
+static int	_archive_read_data_block(struct archive *,
+		    const void **, size_t *, int64_t *);
+static int	_archive_read_next_header(struct archive *,
+		    struct archive_entry **);
+static int	_archive_read_next_header2(struct archive *,
+		    struct archive_entry *);
+static const char *trivial_lookup_gname(void *, int64_t gid);
+static const char *trivial_lookup_uname(void *, int64_t uid);
+static int	setup_sparse(struct archive_read_disk *, struct archive_entry *);
+static int	close_and_restore_time(int fd, struct tree *,
+		    struct restore_time *);
+static int	open_on_current_dir(struct tree *, const char *, int);
+static int	tree_dup(int);
+
+
+static struct archive_vtable *
+archive_read_disk_vtable(void)
+{
+	static struct archive_vtable av;
+	static int inited = 0;
+
+	if (!inited) {
+		av.archive_free = _archive_read_free;
+		av.archive_close = _archive_read_close;
+		av.archive_read_data_block = _archive_read_data_block;
+		av.archive_read_next_header = _archive_read_next_header;
+		av.archive_read_next_header2 = _archive_read_next_header2;
+		inited = 1;
+	}
+	return (&av);
+}
+
+const char *
+archive_read_disk_gname(struct archive *_a, la_int64_t gid)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+		ARCHIVE_STATE_ANY, "archive_read_disk_gname"))
+		return (NULL);
+	if (a->lookup_gname == NULL)
+		return (NULL);
+	return ((*a->lookup_gname)(a->lookup_gname_data, gid));
+}
+
+const char *
+archive_read_disk_uname(struct archive *_a, la_int64_t uid)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+		ARCHIVE_STATE_ANY, "archive_read_disk_uname"))
+		return (NULL);
+	if (a->lookup_uname == NULL)
+		return (NULL);
+	return ((*a->lookup_uname)(a->lookup_uname_data, uid));
+}
+
+int
+archive_read_disk_set_gname_lookup(struct archive *_a,
+    void *private_data,
+    const char * (*lookup_gname)(void *private, la_int64_t gid),
+    void (*cleanup_gname)(void *private))
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");
+
+	if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+		(a->cleanup_gname)(a->lookup_gname_data);
+
+	a->lookup_gname = lookup_gname;
+	a->cleanup_gname = cleanup_gname;
+	a->lookup_gname_data = private_data;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_uname_lookup(struct archive *_a,
+    void *private_data,
+    const char * (*lookup_uname)(void *private, la_int64_t uid),
+    void (*cleanup_uname)(void *private))
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");
+
+	if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+		(a->cleanup_uname)(a->lookup_uname_data);
+
+	a->lookup_uname = lookup_uname;
+	a->cleanup_uname = cleanup_uname;
+	a->lookup_uname_data = private_data;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Create a new archive_read_disk object and initialize it with global state.
+ */
+struct archive *
+archive_read_disk_new(void)
+{
+	struct archive_read_disk *a;
+
+	a = (struct archive_read_disk *)calloc(1, sizeof(*a));
+	if (a == NULL)
+		return (NULL);
+	a->archive.magic = ARCHIVE_READ_DISK_MAGIC;
+	a->archive.state = ARCHIVE_STATE_NEW;
+	a->archive.vtable = archive_read_disk_vtable();
+	a->entry = archive_entry_new2(&a->archive);
+	a->lookup_uname = trivial_lookup_uname;
+	a->lookup_gname = trivial_lookup_gname;
+	a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
+	a->open_on_current_dir = open_on_current_dir;
+	a->tree_current_dir_fd = tree_current_dir_fd;
+	a->tree_enter_working_dir = tree_enter_working_dir;
+	return (&a->archive);
+}
+
+static int
+_archive_read_free(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	int r;
+
+	if (_a == NULL)
+		return (ARCHIVE_OK);
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");
+
+	if (a->archive.state != ARCHIVE_STATE_CLOSED)
+		r = _archive_read_close(&a->archive);
+	else
+		r = ARCHIVE_OK;
+
+	tree_free(a->tree);
+	if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+		(a->cleanup_gname)(a->lookup_gname_data);
+	if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+		(a->cleanup_uname)(a->lookup_uname_data);
+	archive_string_free(&a->archive.error_string);
+	archive_entry_free(a->entry);
+	a->archive.magic = 0;
+	__archive_clean(&a->archive);
+	free(a);
+	return (r);
+}
+
+static int
+_archive_read_close(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");
+
+	if (a->archive.state != ARCHIVE_STATE_FATAL)
+		a->archive.state = ARCHIVE_STATE_CLOSED;
+
+	tree_close(a->tree);
+
+	return (ARCHIVE_OK);
+}
+
+static void
+setup_symlink_mode(struct archive_read_disk *a, char symlink_mode,
+    int follow_symlinks)
+{
+	a->symlink_mode = symlink_mode;
+	a->follow_symlinks = follow_symlinks;
+	if (a->tree != NULL) {
+		a->tree->initial_symlink_mode = a->symlink_mode;
+		a->tree->symlink_mode = a->symlink_mode;
+	}
+}
+
+int
+archive_read_disk_set_symlink_logical(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical");
+	setup_symlink_mode(a, 'L', 1);
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_physical(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical");
+	setup_symlink_mode(a, 'P', 0);
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_hybrid(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid");
+	setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_atime_restored(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime");
+#ifdef HAVE_UTIMES
+	a->flags |= ARCHIVE_READDISK_RESTORE_ATIME;
+	if (a->tree != NULL)
+		a->tree->flags |= needsRestoreTimes;
+	return (ARCHIVE_OK);
+#else
+	/* Display warning and unset flag */
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Cannot restore access time on this system");
+	a->flags &= ~ARCHIVE_READDISK_RESTORE_ATIME;
+	return (ARCHIVE_WARN);
+#endif
+}
+
+int
+archive_read_disk_set_behavior(struct archive *_a, int flags)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	int r = ARCHIVE_OK;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
+
+	a->flags = flags;
+
+	if (flags & ARCHIVE_READDISK_RESTORE_ATIME)
+		r = archive_read_disk_set_atime_restored(_a);
+	else {
+		if (a->tree != NULL)
+			a->tree->flags &= ~needsRestoreTimes;
+	}
+	return (r);
+}
+
+/*
+ * Trivial implementations of gname/uname lookup functions.
+ * These are normally overridden by the client, but these stub
+ * versions ensure that we always have something that works.
+ */
+static const char *
+trivial_lookup_gname(void *private_data, int64_t gid)
+{
+	(void)private_data; /* UNUSED */
+	(void)gid; /* UNUSED */
+	return (NULL);
+}
+
+static const char *
+trivial_lookup_uname(void *private_data, int64_t uid)
+{
+	(void)private_data; /* UNUSED */
+	(void)uid; /* UNUSED */
+	return (NULL);
+}
+
+/*
+ * Allocate memory for the reading buffer adjusted to the filesystem
+ * alignment.
+ */
+static int
+setup_suitable_read_buffer(struct archive_read_disk *a)
+{
+	struct tree *t = a->tree;
+	struct filesystem *cf = t->current_filesystem;
+	size_t asize;
+	size_t s;
+
+	if (cf->allocation_ptr == NULL) {
+		/* If we couldn't get a filesystem alignment,
+		 * we use 4096 as default value but we won't use
+		 * O_DIRECT to open() and openat() operations. */
+		long xfer_align = (cf->xfer_align == -1)?4096:cf->xfer_align;
+
+		if (cf->max_xfer_size != -1)
+			asize = cf->max_xfer_size + xfer_align;
+		else {
+			long incr = cf->incr_xfer_size;
+			/* Some platform does not set a proper value to
+			 * incr_xfer_size.*/
+			if (incr < 0)
+				incr = cf->min_xfer_size;
+			if (cf->min_xfer_size < 0) {
+				incr = xfer_align;
+				asize = xfer_align;
+			} else
+				asize = cf->min_xfer_size;
+
+			/* Increase a buffer size up to 64K bytes in
+			 * a proper increment size. */
+			while (asize < 1024*64)
+				asize += incr;
+			/* Take a margin to adjust to the filesystem
+			 * alignment. */
+			asize += xfer_align;
+		}
+		cf->allocation_ptr = malloc(asize);
+		if (cf->allocation_ptr == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Couldn't allocate memory");
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		}
+
+		/*
+		 * Calculate proper address for the filesystem.
+		 */
+		s = (uintptr_t)cf->allocation_ptr;
+		s %= xfer_align;
+		if (s > 0)
+			s = xfer_align - s;
+
+		/*
+		 * Set a read buffer pointer in the proper alignment of
+		 * the current filesystem.
+		 */
+		cf->buff = cf->allocation_ptr + s;
+		cf->buff_size = asize - xfer_align;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+_archive_read_data_block(struct archive *_a, const void **buff,
+    size_t *size, int64_t *offset)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	struct tree *t = a->tree;
+	int r;
+	ssize_t bytes;
+	int64_t sparse_bytes;
+	size_t buffbytes;
+	int empty_sparse_region = 0;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_data_block");
+
+	if (t->entry_eof || t->entry_remaining_bytes <= 0) {
+		r = ARCHIVE_EOF;
+		goto abort_read_data;
+	}
+
+	/*
+	 * Open the current file.
+	 */
+	if (t->entry_fd < 0) {
+		int flags = O_RDONLY | O_BINARY | O_CLOEXEC;
+
+		/*
+		 * Eliminate or reduce cache effects if we can.
+		 *
+		 * Carefully consider this to be enabled.
+		 */
+#if defined(O_DIRECT) && 0/* Disabled for now */
+		if (t->current_filesystem->xfer_align != -1 &&
+		    t->nlink == 1)
+			flags |= O_DIRECT;
+#endif
+#if defined(O_NOATIME)
+		/*
+		 * Linux has O_NOATIME flag; use it if we need.
+		 */
+		if ((t->flags & needsRestoreTimes) != 0 &&
+		    t->restore_time.noatime == 0)
+			flags |= O_NOATIME;
+#endif
+		t->entry_fd = open_on_current_dir(t,
+		    tree_current_access_path(t), flags);
+		__archive_ensure_cloexec_flag(t->entry_fd);
+#if defined(O_NOATIME)
+		/*
+		 * When we did open the file with O_NOATIME flag,
+		 * if successful, set 1 to t->restore_time.noatime
+		 * not to restore an atime of the file later.
+		 * if failed by EPERM, retry it without O_NOATIME flag.
+		 */
+		if (flags & O_NOATIME) {
+			if (t->entry_fd >= 0)
+				t->restore_time.noatime = 1;
+			else if (errno == EPERM)
+				flags &= ~O_NOATIME;
+		}
+#endif
+		if (t->entry_fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't open %s", tree_current_path(t));
+			r = ARCHIVE_FAILED;
+			tree_enter_initial_dir(t);
+			goto abort_read_data;
+		}
+		tree_enter_initial_dir(t);
+	}
+
+	/*
+	 * Allocate read buffer if not allocated.
+	 */
+	if (t->current_filesystem->allocation_ptr == NULL) {
+		r = setup_suitable_read_buffer(a);
+		if (r != ARCHIVE_OK) {
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			goto abort_read_data;
+		}
+	}
+	t->entry_buff = t->current_filesystem->buff;
+	t->entry_buff_size = t->current_filesystem->buff_size;
+
+	buffbytes = t->entry_buff_size;
+	if ((int64_t)buffbytes > t->current_sparse->length)
+		buffbytes = t->current_sparse->length;
+
+	if (t->current_sparse->length == 0)
+		empty_sparse_region = 1;
+
+	/*
+	 * Skip hole.
+	 * TODO: Should we consider t->current_filesystem->xfer_align?
+	 */
+	if (t->current_sparse->offset > t->entry_total) {
+		if (lseek(t->entry_fd,
+		    (off_t)t->current_sparse->offset, SEEK_SET) < 0) {
+			archive_set_error(&a->archive, errno, "Seek error");
+			r = ARCHIVE_FATAL;
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			goto abort_read_data;
+		}
+		sparse_bytes = t->current_sparse->offset - t->entry_total;
+		t->entry_remaining_bytes -= sparse_bytes;
+		t->entry_total += sparse_bytes;
+	}
+
+	/*
+	 * Read file contents.
+	 */
+	if (buffbytes > 0) {
+		bytes = read(t->entry_fd, t->entry_buff, buffbytes);
+		if (bytes < 0) {
+			archive_set_error(&a->archive, errno, "Read error");
+			r = ARCHIVE_FATAL;
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			goto abort_read_data;
+		}
+	} else
+		bytes = 0;
+	/*
+	 * Return an EOF unless we've read a leading empty sparse region, which
+	 * is used to represent fully-sparse files.
+	*/
+	if (bytes == 0 && !empty_sparse_region) {
+		/* Get EOF */
+		t->entry_eof = 1;
+		r = ARCHIVE_EOF;
+		goto abort_read_data;
+	}
+	*buff = t->entry_buff;
+	*size = bytes;
+	*offset = t->entry_total;
+	t->entry_total += bytes;
+	t->entry_remaining_bytes -= bytes;
+	if (t->entry_remaining_bytes == 0) {
+		/* Close the current file descriptor */
+		close_and_restore_time(t->entry_fd, t, &t->restore_time);
+		t->entry_fd = -1;
+		t->entry_eof = 1;
+	}
+	t->current_sparse->offset += bytes;
+	t->current_sparse->length -= bytes;
+	if (t->current_sparse->length == 0 && !t->entry_eof)
+		t->current_sparse++;
+	return (ARCHIVE_OK);
+
+abort_read_data:
+	*buff = NULL;
+	*size = 0;
+	*offset = t->entry_total;
+	if (t->entry_fd >= 0) {
+		/* Close the current file descriptor */
+		close_and_restore_time(t->entry_fd, t, &t->restore_time);
+		t->entry_fd = -1;
+	}
+	return (r);
+}
+
+static int
+next_entry(struct archive_read_disk *a, struct tree *t,
+    struct archive_entry *entry)
+{
+	const struct stat *st; /* info to use for this entry */
+	const struct stat *lst;/* lstat() information */
+	const char *name;
+	int delayed, delayed_errno, descend, r;
+	struct archive_string delayed_str;
+
+	delayed = ARCHIVE_OK;
+	delayed_errno = 0;
+	archive_string_init(&delayed_str);
+
+	st = NULL;
+	lst = NULL;
+	t->descend = 0;
+	do {
+		switch (tree_next(t)) {
+		case TREE_ERROR_FATAL:
+			archive_set_error(&a->archive, t->tree_errno,
+			    "%s: Unable to continue traversing directory tree",
+			    tree_current_path(t));
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			tree_enter_initial_dir(t);
+			return (ARCHIVE_FATAL);
+		case TREE_ERROR_DIR:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: Couldn't visit directory",
+			    tree_current_path(t));
+			tree_enter_initial_dir(t);
+			return (ARCHIVE_FAILED);
+		case 0:
+			tree_enter_initial_dir(t);
+			return (ARCHIVE_EOF);
+		case TREE_POSTDESCENT:
+		case TREE_POSTASCENT:
+			break;
+		case TREE_REGULAR:
+			lst = tree_current_lstat(t);
+			if (lst == NULL) {
+			    if (errno == ENOENT && t->depth > 0) {
+				delayed = ARCHIVE_WARN;
+				delayed_errno = errno;
+				if (delayed_str.length == 0) {
+					archive_string_sprintf(&delayed_str,
+					    "%s", tree_current_path(t));
+				} else {
+					archive_string_sprintf(&delayed_str,
+					    " %s", tree_current_path(t));
+				}
+			    } else {
+				archive_set_error(&a->archive, errno,
+				    "%s: Cannot stat",
+				    tree_current_path(t));
+				tree_enter_initial_dir(t);
+				return (ARCHIVE_FAILED);
+			    }
+			}
+			break;
+		}
+	} while (lst == NULL);
+
+#ifdef __APPLE__
+	if (a->flags & ARCHIVE_READDISK_MAC_COPYFILE) {
+		/* If we're using copyfile(), ignore "._XXX" files. */
+		const char *bname = strrchr(tree_current_path(t), '/');
+		if (bname == NULL)
+			bname = tree_current_path(t);
+		else
+			++bname;
+		if (bname[0] == '.' && bname[1] == '_')
+			return (ARCHIVE_RETRY);
+	}
+#endif
+
+	archive_entry_copy_pathname(entry, tree_current_path(t));
+	/*
+	 * Perform path matching.
+	 */
+	if (a->matching) {
+		r = archive_match_path_excluded(a->matching, entry);
+		if (r < 0) {
+			archive_set_error(&(a->archive), errno,
+			    "Failed : %s", archive_error_string(a->matching));
+			return (r);
+		}
+		if (r) {
+			if (a->excluded_cb_func)
+				a->excluded_cb_func(&(a->archive),
+				    a->excluded_cb_data, entry);
+			return (ARCHIVE_RETRY);
+		}
+	}
+
+	/*
+	 * Distinguish 'L'/'P'/'H' symlink following.
+	 */
+	switch(t->symlink_mode) {
+	case 'H':
+		/* 'H': After the first item, rest like 'P'. */
+		t->symlink_mode = 'P';
+		/* 'H': First item (from command line) like 'L'. */
+		/* FALLTHROUGH */
+	case 'L':
+		/* 'L': Do descend through a symlink to dir. */
+		descend = tree_current_is_dir(t);
+		/* 'L': Follow symlinks to files. */
+		a->symlink_mode = 'L';
+		a->follow_symlinks = 1;
+		/* 'L': Archive symlinks as targets, if we can. */
+		st = tree_current_stat(t);
+		if (st != NULL && !tree_target_is_same_as_parent(t, st))
+			break;
+		/* If stat fails, we have a broken symlink;
+		 * in that case, don't follow the link. */
+		/* FALLTHROUGH */
+	default:
+		/* 'P': Don't descend through a symlink to dir. */
+		descend = tree_current_is_physical_dir(t);
+		/* 'P': Don't follow symlinks to files. */
+		a->symlink_mode = 'P';
+		a->follow_symlinks = 0;
+		/* 'P': Archive symlinks as symlinks. */
+		st = lst;
+		break;
+	}
+
+	if (update_current_filesystem(a, st->st_dev) != ARCHIVE_OK) {
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		tree_enter_initial_dir(t);
+		return (ARCHIVE_FATAL);
+	}
+	if (t->initial_filesystem_id == -1)
+		t->initial_filesystem_id = t->current_filesystem_id;
+	if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) {
+		if (t->initial_filesystem_id != t->current_filesystem_id)
+			descend = 0;
+	}
+	t->descend = descend;
+
+	/*
+	 * Honor nodump flag.
+	 * If the file is marked with nodump flag, do not return this entry.
+	 */
+	if (a->flags & ARCHIVE_READDISK_HONOR_NODUMP) {
+#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP)
+		if (st->st_flags & UF_NODUMP)
+			return (ARCHIVE_RETRY);
+#elif (defined(FS_IOC_GETFLAGS) && defined(FS_NODUMP_FL) && \
+       defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+      (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_NODUMP_FL) && \
+       defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
+		if (S_ISREG(st->st_mode) || S_ISDIR(st->st_mode)) {
+			int stflags;
+
+			t->entry_fd = open_on_current_dir(t,
+			    tree_current_access_path(t),
+			    O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+			__archive_ensure_cloexec_flag(t->entry_fd);
+			if (t->entry_fd >= 0) {
+				r = ioctl(t->entry_fd,
+#ifdef FS_IOC_GETFLAGS
+				FS_IOC_GETFLAGS,
+#else
+				EXT2_IOC_GETFLAGS,
+#endif
+					&stflags);
+#ifdef FS_NODUMP_FL
+				if (r == 0 && (stflags & FS_NODUMP_FL) != 0)
+#else
+				if (r == 0 && (stflags & EXT2_NODUMP_FL) != 0)
+#endif
+					return (ARCHIVE_RETRY);
+			}
+		}
+#endif
+	}
+
+	archive_entry_copy_stat(entry, st);
+
+	/* Save the times to be restored. This must be in before
+	 * calling archive_read_disk_descend() or any chance of it,
+	 * especially, invoking a callback. */
+	t->restore_time.mtime = archive_entry_mtime(entry);
+	t->restore_time.mtime_nsec = archive_entry_mtime_nsec(entry);
+	t->restore_time.atime = archive_entry_atime(entry);
+	t->restore_time.atime_nsec = archive_entry_atime_nsec(entry);
+	t->restore_time.filetype = archive_entry_filetype(entry);
+	t->restore_time.noatime = t->current_filesystem->noatime;
+
+	/*
+	 * Perform time matching.
+	 */
+	if (a->matching) {
+		r = archive_match_time_excluded(a->matching, entry);
+		if (r < 0) {
+			archive_set_error(&(a->archive), errno,
+			    "Failed : %s", archive_error_string(a->matching));
+			return (r);
+		}
+		if (r) {
+			if (a->excluded_cb_func)
+				a->excluded_cb_func(&(a->archive),
+				    a->excluded_cb_data, entry);
+			return (ARCHIVE_RETRY);
+		}
+	}
+
+	/* Lookup uname/gname */
+	name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry));
+	if (name != NULL)
+		archive_entry_copy_uname(entry, name);
+	name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry));
+	if (name != NULL)
+		archive_entry_copy_gname(entry, name);
+
+	/*
+	 * Perform owner matching.
+	 */
+	if (a->matching) {
+		r = archive_match_owner_excluded(a->matching, entry);
+		if (r < 0) {
+			archive_set_error(&(a->archive), errno,
+			    "Failed : %s", archive_error_string(a->matching));
+			return (r);
+		}
+		if (r) {
+			if (a->excluded_cb_func)
+				a->excluded_cb_func(&(a->archive),
+				    a->excluded_cb_data, entry);
+			return (ARCHIVE_RETRY);
+		}
+	}
+
+	/*
+	 * Invoke a meta data filter callback.
+	 */
+	if (a->metadata_filter_func) {
+		if (!a->metadata_filter_func(&(a->archive),
+		    a->metadata_filter_data, entry))
+			return (ARCHIVE_RETRY);
+	}
+
+	/*
+	 * Populate the archive_entry with metadata from the disk.
+	 */
+	archive_entry_copy_sourcepath(entry, tree_current_access_path(t));
+	r = archive_read_disk_entry_from_file(&(a->archive), entry,
+		t->entry_fd, st);
+
+	if (r == ARCHIVE_OK) {
+		r = delayed;
+		if (r != ARCHIVE_OK) {
+			archive_string_sprintf(&delayed_str, ": %s",
+			    "File removed before we read it");
+			archive_set_error(&(a->archive), delayed_errno,
+			    "%s", delayed_str.s);
+		}
+	}
+	archive_string_free(&delayed_str);
+
+	return (r);
+}
+
+static int
+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+{
+	int ret;
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	*entryp = NULL;
+	ret = _archive_read_next_header2(_a, a->entry);
+	*entryp = a->entry;
+	return ret;
+}
+
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	struct tree *t;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_read_next_header2");
+
+	t = a->tree;
+	if (t->entry_fd >= 0) {
+		close_and_restore_time(t->entry_fd, t, &t->restore_time);
+		t->entry_fd = -1;
+	}
+
+	archive_entry_clear(entry);
+
+	for (;;) {
+		r = next_entry(a, t, entry);
+		if (t->entry_fd >= 0) {
+			close(t->entry_fd);
+			t->entry_fd = -1;
+		}
+
+		if (r == ARCHIVE_RETRY) {
+			archive_entry_clear(entry);
+			continue;
+		}
+		break;
+	}
+
+	/* Return to the initial directory. */
+	tree_enter_initial_dir(t);
+
+	/*
+	 * 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 (r) {
+	case ARCHIVE_EOF:
+		a->archive.state = ARCHIVE_STATE_EOF;
+		break;
+	case ARCHIVE_OK:
+	case ARCHIVE_WARN:
+		/* Overwrite the sourcepath based on the initial directory. */
+		archive_entry_copy_sourcepath(entry, tree_current_path(t));
+		t->entry_total = 0;
+		if (archive_entry_filetype(entry) == AE_IFREG) {
+			t->nlink = archive_entry_nlink(entry);
+			t->entry_remaining_bytes = archive_entry_size(entry);
+			t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0;
+			if (!t->entry_eof &&
+			    setup_sparse(a, entry) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		} else {
+			t->entry_remaining_bytes = 0;
+			t->entry_eof = 1;
+		}
+		a->archive.state = ARCHIVE_STATE_DATA;
+		break;
+	case ARCHIVE_RETRY:
+		break;
+	case ARCHIVE_FATAL:
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		break;
+	}
+
+	__archive_reset_read_data(&a->archive);
+	return (r);
+}
+
+static int
+setup_sparse(struct archive_read_disk *a, struct archive_entry *entry)
+{
+	struct tree *t = a->tree;
+	int64_t length, offset;
+	int i;
+
+	t->sparse_count = archive_entry_sparse_reset(entry);
+	if (t->sparse_count+1 > t->sparse_list_size) {
+		free(t->sparse_list);
+		t->sparse_list_size = t->sparse_count + 1;
+		t->sparse_list = malloc(sizeof(t->sparse_list[0]) *
+		    t->sparse_list_size);
+		if (t->sparse_list == NULL) {
+			t->sparse_list_size = 0;
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate data");
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		}
+	}
+	for (i = 0; i < t->sparse_count; i++) {
+		archive_entry_sparse_next(entry, &offset, &length);
+		t->sparse_list[i].offset = offset;
+		t->sparse_list[i].length = length;
+	}
+	if (i == 0) {
+		t->sparse_list[i].offset = 0;
+		t->sparse_list[i].length = archive_entry_size(entry);
+	} else {
+		t->sparse_list[i].offset = archive_entry_size(entry);
+		t->sparse_list[i].length = 0;
+	}
+	t->current_sparse = t->sparse_list;
+
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_matching(struct archive *_a, struct archive *_ma,
+    void (*_excluded_func)(struct archive *, void *, struct archive_entry *),
+    void *_client_data)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_matching");
+	a->matching = _ma;
+	a->excluded_cb_func = _excluded_func;
+	a->excluded_cb_data = _client_data;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_metadata_filter_callback(struct archive *_a,
+    int (*_metadata_filter_func)(struct archive *, void *,
+    struct archive_entry *), void *_client_data)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
+	    "archive_read_disk_set_metadata_filter_callback");
+
+	a->metadata_filter_func = _metadata_filter_func;
+	a->metadata_filter_data = _client_data;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_can_descend(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	struct tree *t = a->tree;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_read_disk_can_descend");
+
+	return (t->visit_type == TREE_REGULAR && t->descend);
+}
+
+/*
+ * Called by the client to mark the directory just returned from
+ * tree_next() as needing to be visited.
+ */
+int
+archive_read_disk_descend(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	struct tree *t = a->tree;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_read_disk_descend");
+
+	if (t->visit_type != TREE_REGULAR || !t->descend)
+		return (ARCHIVE_OK);
+
+	/*
+	 * We must not treat the initial specified path as a physical dir,
+	 * because if we do then we will try and ascend out of it by opening
+	 * ".." which is (a) wrong and (b) causes spurious permissions errors
+	 * if ".." is not readable by us. Instead, treat it as if it were a
+	 * symlink. (This uses an extra fd, but it can only happen once at the
+	 * top level of a traverse.) But we can't necessarily assume t->st is
+	 * valid here (though t->lst is), which complicates the logic a
+	 * little.
+	 */
+	if (tree_current_is_physical_dir(t)) {
+		tree_push(t, t->basename, t->current_filesystem_id,
+		    t->lst.st_dev, t->lst.st_ino, &t->restore_time);
+		if (t->stack->parent->parent != NULL)
+			t->stack->flags |= isDir;
+		else
+			t->stack->flags |= isDirLink;
+	} else if (tree_current_is_dir(t)) {
+		tree_push(t, t->basename, t->current_filesystem_id,
+		    t->st.st_dev, t->st.st_ino, &t->restore_time);
+		t->stack->flags |= isDirLink;
+	}
+	t->descend = 0;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_open(struct archive *_a, const char *pathname)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+	    "archive_read_disk_open");
+	archive_clear_error(&a->archive);
+
+	return (_archive_read_disk_open(_a, pathname));
+}
+
+int
+archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	struct archive_string path;
+	int ret;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+	    "archive_read_disk_open_w");
+	archive_clear_error(&a->archive);
+
+	/* Make a char string from a wchar_t string. */
+	archive_string_init(&path);
+	if (archive_string_append_from_wcs(&path, pathname,
+	    wcslen(pathname)) != 0) {
+		if (errno == ENOMEM)
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+		else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Can't convert a path to a char string");
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		ret = ARCHIVE_FATAL;
+	} else
+		ret = _archive_read_disk_open(_a, path.s);
+
+	archive_string_free(&path);
+	return (ret);
+}
+
+static int
+_archive_read_disk_open(struct archive *_a, const char *pathname)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	if (a->tree != NULL)
+		a->tree = tree_reopen(a->tree, pathname,
+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+	else
+		a->tree = tree_open(pathname, a->symlink_mode,
+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+	if (a->tree == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate tar data");
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+	a->archive.state = ARCHIVE_STATE_HEADER;
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Return a current filesystem ID which is index of the filesystem entry
+ * you've visited through archive_read_disk.
+ */
+int
+archive_read_disk_current_filesystem(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_disk_current_filesystem");
+
+	return (a->tree->current_filesystem_id);
+}
+
+static int
+update_current_filesystem(struct archive_read_disk *a, int64_t dev)
+{
+	struct tree *t = a->tree;
+	int i, fid;
+
+	if (t->current_filesystem != NULL &&
+	    t->current_filesystem->dev == dev)
+		return (ARCHIVE_OK);
+
+	for (i = 0; i < t->max_filesystem_id; i++) {
+		if (t->filesystem_table[i].dev == dev) {
+			/* There is the filesystem ID we've already generated. */
+			t->current_filesystem_id = i;
+			t->current_filesystem = &(t->filesystem_table[i]);
+			return (ARCHIVE_OK);
+		}
+	}
+
+	/*
+	 * This is the new filesystem which we have to generate a new ID for.
+	 */
+	fid = t->max_filesystem_id++;
+	if (t->max_filesystem_id > t->allocated_filesystem) {
+		size_t s;
+		void *p;
+
+		s = t->max_filesystem_id * 2;
+		p = realloc(t->filesystem_table,
+		        s * sizeof(*t->filesystem_table));
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate tar data");
+			return (ARCHIVE_FATAL);
+		}
+		t->filesystem_table = (struct filesystem *)p;
+		t->allocated_filesystem = s;
+	}
+	t->current_filesystem_id = fid;
+	t->current_filesystem = &(t->filesystem_table[fid]);
+	t->current_filesystem->dev = dev;
+	t->current_filesystem->allocation_ptr = NULL;
+	t->current_filesystem->buff = NULL;
+
+	/* Setup the current filesystem properties which depend on
+	 * platform specific. */
+	return (setup_current_filesystem(a));
+}
+
+/*
+ * Returns 1 if current filesystem is generated filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_synthetic(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_disk_current_filesystem");
+
+	return (a->tree->current_filesystem->synthetic);
+}
+
+/*
+ * Returns 1 if current filesystem is remote filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_remote(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_disk_current_filesystem");
+
+	return (a->tree->current_filesystem->remote);
+}
+
+#if defined(_PC_REC_INCR_XFER_SIZE) && defined(_PC_REC_MAX_XFER_SIZE) &&\
+	defined(_PC_REC_MIN_XFER_SIZE) && defined(_PC_REC_XFER_ALIGN)
+static int
+get_xfer_size(struct tree *t, int fd, const char *path)
+{
+	t->current_filesystem->xfer_align = -1;
+	errno = 0;
+	if (fd >= 0) {
+		t->current_filesystem->incr_xfer_size =
+		    fpathconf(fd, _PC_REC_INCR_XFER_SIZE);
+		t->current_filesystem->max_xfer_size =
+		    fpathconf(fd, _PC_REC_MAX_XFER_SIZE);
+		t->current_filesystem->min_xfer_size =
+		    fpathconf(fd, _PC_REC_MIN_XFER_SIZE);
+		t->current_filesystem->xfer_align =
+		    fpathconf(fd, _PC_REC_XFER_ALIGN);
+	} else if (path != NULL) {
+		t->current_filesystem->incr_xfer_size =
+		    pathconf(path, _PC_REC_INCR_XFER_SIZE);
+		t->current_filesystem->max_xfer_size =
+		    pathconf(path, _PC_REC_MAX_XFER_SIZE);
+		t->current_filesystem->min_xfer_size =
+		    pathconf(path, _PC_REC_MIN_XFER_SIZE);
+		t->current_filesystem->xfer_align =
+		    pathconf(path, _PC_REC_XFER_ALIGN);
+	}
+	/* At least we need an alignment size. */
+	if (t->current_filesystem->xfer_align == -1)
+		return ((errno == EINVAL)?1:-1);
+	else
+		return (0);
+}
+#else
+static int
+get_xfer_size(struct tree *t, int fd, const char *path)
+{
+	(void)t; /* UNUSED */
+	(void)fd; /* UNUSED */
+	(void)path; /* UNUSED */
+	return (1);/* Not supported */
+}
+#endif
+
+#if defined(HAVE_STATFS) && defined(HAVE_FSTATFS) && defined(MNT_LOCAL) \
+	&& !defined(ST_LOCAL)
+
+/*
+ * Gather current filesystem properties on FreeBSD, OpenBSD and Mac OS X.
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+	struct tree *t = a->tree;
+	struct statfs sfs;
+#if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC)
+/* TODO: configure should set GETVFSBYNAME_ARG_TYPE to make
+ * this accurate; some platforms have both and we need the one that's
+ * used by getvfsbyname()
+ *
+ * Then the following would become:
+ *  #if defined(GETVFSBYNAME_ARG_TYPE)
+ *   GETVFSBYNAME_ARG_TYPE vfc;
+ *  #endif
+ */
+#  if defined(HAVE_STRUCT_XVFSCONF)
+	struct xvfsconf vfc;
+#  else
+	struct vfsconf vfc;
+#  endif
+#endif
+	int r, xr = 0;
+#if !defined(HAVE_STRUCT_STATFS_F_NAMEMAX)
+	long nm;
+#endif
+
+	t->current_filesystem->synthetic = -1;
+	t->current_filesystem->remote = -1;
+	if (tree_current_is_symblic_link_target(t)) {
+#if defined(HAVE_OPENAT)
+		/*
+		 * Get file system statistics on any directory
+		 * where current is.
+		 */
+		int fd = openat(tree_current_dir_fd(t),
+		    tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+		__archive_ensure_cloexec_flag(fd);
+		if (fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "openat failed");
+			return (ARCHIVE_FAILED);
+		}
+		r = fstatfs(fd, &sfs);
+		if (r == 0)
+			xr = get_xfer_size(t, fd, NULL);
+		close(fd);
+#else
+		if (tree_enter_working_dir(t) != 0) {
+			archive_set_error(&a->archive, errno, "fchdir failed");
+			return (ARCHIVE_FAILED);
+		}
+		r = statfs(tree_current_access_path(t), &sfs);
+		if (r == 0)
+			xr = get_xfer_size(t, -1, tree_current_access_path(t));
+#endif
+	} else {
+		r = fstatfs(tree_current_dir_fd(t), &sfs);
+		if (r == 0)
+			xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+	}
+	if (r == -1 || xr == -1) {
+		archive_set_error(&a->archive, errno, "statfs failed");
+		return (ARCHIVE_FAILED);
+	} else if (xr == 1) {
+		/* pathconf(_PC_REX_*) operations are not supported. */
+		t->current_filesystem->xfer_align = sfs.f_bsize;
+		t->current_filesystem->max_xfer_size = -1;
+		t->current_filesystem->min_xfer_size = sfs.f_iosize;
+		t->current_filesystem->incr_xfer_size = sfs.f_iosize;
+	}
+	if (sfs.f_flags & MNT_LOCAL)
+		t->current_filesystem->remote = 0;
+	else
+		t->current_filesystem->remote = 1;
+
+#if defined(HAVE_GETVFSBYNAME) && defined(VFCF_SYNTHETIC)
+	r = getvfsbyname(sfs.f_fstypename, &vfc);
+	if (r == -1) {
+		archive_set_error(&a->archive, errno, "getvfsbyname failed");
+		return (ARCHIVE_FAILED);
+	}
+	if (vfc.vfc_flags & VFCF_SYNTHETIC)
+		t->current_filesystem->synthetic = 1;
+	else
+		t->current_filesystem->synthetic = 0;
+#endif
+
+#if defined(MNT_NOATIME)
+	if (sfs.f_flags & MNT_NOATIME)
+		t->current_filesystem->noatime = 1;
+	else
+#endif
+		t->current_filesystem->noatime = 0;
+
+#if defined(USE_READDIR_R)
+	/* Set maximum filename length. */
+#if defined(HAVE_STRUCT_STATFS_F_NAMEMAX)
+	t->current_filesystem->name_max = sfs.f_namemax;
+#else
+# if defined(_PC_NAME_MAX)
+	/* Mac OS X does not have f_namemax in struct statfs. */
+	if (tree_current_is_symblic_link_target(t)) {
+		if (tree_enter_working_dir(t) != 0) {
+			archive_set_error(&a->archive, errno, "fchdir failed");
+			return (ARCHIVE_FAILED);
+		}
+		nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX);
+	} else
+		nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX);
+# else
+	nm = -1;
+# endif
+	if (nm == -1)
+		t->current_filesystem->name_max = NAME_MAX;
+	else
+		t->current_filesystem->name_max = nm;
+#endif
+#endif /* USE_READDIR_R */
+	return (ARCHIVE_OK);
+}
+
+#elif (defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS)) && defined(ST_LOCAL)
+
+/*
+ * Gather current filesystem properties on NetBSD
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+	struct tree *t = a->tree;
+	struct statvfs svfs;
+	int r, xr = 0;
+
+	t->current_filesystem->synthetic = -1;
+	if (tree_enter_working_dir(t) != 0) {
+		archive_set_error(&a->archive, errno, "fchdir failed");
+		return (ARCHIVE_FAILED);
+	}
+	if (tree_current_is_symblic_link_target(t)) {
+		r = statvfs(tree_current_access_path(t), &svfs);
+		if (r == 0)
+			xr = get_xfer_size(t, -1, tree_current_access_path(t));
+	} else {
+#ifdef HAVE_FSTATVFS
+		r = fstatvfs(tree_current_dir_fd(t), &svfs);
+		if (r == 0)
+			xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+#else
+		r = statvfs(".", &svfs);
+		if (r == 0)
+			xr = get_xfer_size(t, -1, ".");
+#endif
+	}
+	if (r == -1 || xr == -1) {
+		t->current_filesystem->remote = -1;
+		archive_set_error(&a->archive, errno, "statvfs failed");
+		return (ARCHIVE_FAILED);
+	} else if (xr == 1) {
+		/* Usually come here unless NetBSD supports _PC_REC_XFER_ALIGN
+		 * for pathconf() function. */
+		t->current_filesystem->xfer_align = svfs.f_frsize;
+		t->current_filesystem->max_xfer_size = -1;
+#if defined(HAVE_STRUCT_STATVFS_F_IOSIZE)
+		t->current_filesystem->min_xfer_size = svfs.f_iosize;
+		t->current_filesystem->incr_xfer_size = svfs.f_iosize;
+#else
+		t->current_filesystem->min_xfer_size = svfs.f_bsize;
+		t->current_filesystem->incr_xfer_size = svfs.f_bsize;
+#endif
+	}
+	if (svfs.f_flag & ST_LOCAL)
+		t->current_filesystem->remote = 0;
+	else
+		t->current_filesystem->remote = 1;
+
+#if defined(ST_NOATIME)
+	if (svfs.f_flag & ST_NOATIME)
+		t->current_filesystem->noatime = 1;
+	else
+#endif
+		t->current_filesystem->noatime = 0;
+
+	/* Set maximum filename length. */
+	t->current_filesystem->name_max = svfs.f_namemax;
+	return (ARCHIVE_OK);
+}
+
+#elif defined(HAVE_SYS_STATFS_H) && defined(HAVE_LINUX_MAGIC_H) &&\
+	defined(HAVE_STATFS) && defined(HAVE_FSTATFS)
+/*
+ * Note: statfs is deprecated since LSB 3.2
+ */
+
+#ifndef CIFS_SUPER_MAGIC
+#define CIFS_SUPER_MAGIC 0xFF534D42
+#endif
+#ifndef DEVFS_SUPER_MAGIC
+#define DEVFS_SUPER_MAGIC 0x1373
+#endif
+
+/*
+ * Gather current filesystem properties on Linux
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+	struct tree *t = a->tree;
+	struct statfs sfs;
+#if defined(HAVE_STATVFS)
+	struct statvfs svfs;
+#endif
+	int r, vr = 0, xr = 0;
+
+	if (tree_current_is_symblic_link_target(t)) {
+#if defined(HAVE_OPENAT)
+		/*
+		 * Get file system statistics on any directory
+		 * where current is.
+		 */
+		int fd = openat(tree_current_dir_fd(t),
+		    tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+		__archive_ensure_cloexec_flag(fd);
+		if (fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "openat failed");
+			return (ARCHIVE_FAILED);
+		}
+#if defined(HAVE_FSTATVFS)
+		vr = fstatvfs(fd, &svfs);/* for f_flag, mount flags */
+#endif
+		r = fstatfs(fd, &sfs);
+		if (r == 0)
+			xr = get_xfer_size(t, fd, NULL);
+		close(fd);
+#else
+		if (tree_enter_working_dir(t) != 0) {
+			archive_set_error(&a->archive, errno, "fchdir failed");
+			return (ARCHIVE_FAILED);
+		}
+#if defined(HAVE_STATVFS)
+		vr = statvfs(tree_current_access_path(t), &svfs);
+#endif
+		r = statfs(tree_current_access_path(t), &sfs);
+		if (r == 0)
+			xr = get_xfer_size(t, -1, tree_current_access_path(t));
+#endif
+	} else {
+#ifdef HAVE_FSTATFS
+#if defined(HAVE_FSTATVFS)
+		vr = fstatvfs(tree_current_dir_fd(t), &svfs);
+#endif
+		r = fstatfs(tree_current_dir_fd(t), &sfs);
+		if (r == 0)
+			xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+#else
+		if (tree_enter_working_dir(t) != 0) {
+			archive_set_error(&a->archive, errno, "fchdir failed");
+			return (ARCHIVE_FAILED);
+		}
+#if defined(HAVE_STATVFS)
+		vr = statvfs(".", &svfs);
+#endif
+		r = statfs(".", &sfs);
+		if (r == 0)
+			xr = get_xfer_size(t, -1, ".");
+#endif
+	}
+	if (r == -1 || xr == -1 || vr == -1) {
+		t->current_filesystem->synthetic = -1;
+		t->current_filesystem->remote = -1;
+		archive_set_error(&a->archive, errno, "statfs failed");
+		return (ARCHIVE_FAILED);
+	} else if (xr == 1) {
+		/* pathconf(_PC_REX_*) operations are not supported. */
+#if defined(HAVE_STATVFS)
+		t->current_filesystem->xfer_align = svfs.f_frsize;
+		t->current_filesystem->max_xfer_size = -1;
+		t->current_filesystem->min_xfer_size = svfs.f_bsize;
+		t->current_filesystem->incr_xfer_size = svfs.f_bsize;
+#else
+		t->current_filesystem->xfer_align = sfs.f_frsize;
+		t->current_filesystem->max_xfer_size = -1;
+		t->current_filesystem->min_xfer_size = sfs.f_bsize;
+		t->current_filesystem->incr_xfer_size = sfs.f_bsize;
+#endif
+	}
+	switch (sfs.f_type) {
+	case AFS_SUPER_MAGIC:
+	case CIFS_SUPER_MAGIC:
+	case CODA_SUPER_MAGIC:
+	case NCP_SUPER_MAGIC:/* NetWare */
+	case NFS_SUPER_MAGIC:
+	case SMB_SUPER_MAGIC:
+		t->current_filesystem->remote = 1;
+		t->current_filesystem->synthetic = 0;
+		break;
+	case DEVFS_SUPER_MAGIC:
+	case PROC_SUPER_MAGIC:
+	case USBDEVICE_SUPER_MAGIC:
+		t->current_filesystem->remote = 0;
+		t->current_filesystem->synthetic = 1;
+		break;
+	default:
+		t->current_filesystem->remote = 0;
+		t->current_filesystem->synthetic = 0;
+		break;
+	}
+
+#if defined(ST_NOATIME)
+#if defined(HAVE_STATVFS)
+	if (svfs.f_flag & ST_NOATIME)
+#else
+	if (sfs.f_flags & ST_NOATIME)
+#endif
+		t->current_filesystem->noatime = 1;
+	else
+#endif
+		t->current_filesystem->noatime = 0;
+
+#if defined(USE_READDIR_R)
+	/* Set maximum filename length. */
+	t->current_filesystem->name_max = sfs.f_namelen;
+#endif
+	return (ARCHIVE_OK);
+}
+
+#elif defined(HAVE_SYS_STATVFS_H) &&\
+	(defined(HAVE_STATVFS) || defined(HAVE_FSTATVFS))
+
+/*
+ * Gather current filesystem properties on other posix platform.
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+	struct tree *t = a->tree;
+	struct statvfs svfs;
+	int r, xr = 0;
+
+	t->current_filesystem->synthetic = -1;/* Not supported */
+	t->current_filesystem->remote = -1;/* Not supported */
+	if (tree_current_is_symblic_link_target(t)) {
+#if defined(HAVE_OPENAT)
+		/*
+		 * Get file system statistics on any directory
+		 * where current is.
+		 */
+		int fd = openat(tree_current_dir_fd(t),
+		    tree_current_access_path(t), O_RDONLY | O_CLOEXEC);
+		__archive_ensure_cloexec_flag(fd);
+		if (fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "openat failed");
+			return (ARCHIVE_FAILED);
+		}
+		r = fstatvfs(fd, &svfs);
+		if (r == 0)
+			xr = get_xfer_size(t, fd, NULL);
+		close(fd);
+#else
+		if (tree_enter_working_dir(t) != 0) {
+			archive_set_error(&a->archive, errno, "fchdir failed");
+			return (ARCHIVE_FAILED);
+		}
+		r = statvfs(tree_current_access_path(t), &svfs);
+		if (r == 0)
+			xr = get_xfer_size(t, -1, tree_current_access_path(t));
+#endif
+	} else {
+#ifdef HAVE_FSTATVFS
+		r = fstatvfs(tree_current_dir_fd(t), &svfs);
+		if (r == 0)
+			xr = get_xfer_size(t, tree_current_dir_fd(t), NULL);
+#else
+		if (tree_enter_working_dir(t) != 0) {
+			archive_set_error(&a->archive, errno, "fchdir failed");
+			return (ARCHIVE_FAILED);
+		}
+		r = statvfs(".", &svfs);
+		if (r == 0)
+			xr = get_xfer_size(t, -1, ".");
+#endif
+	}
+	if (r == -1 || xr == -1) {
+		t->current_filesystem->synthetic = -1;
+		t->current_filesystem->remote = -1;
+		archive_set_error(&a->archive, errno, "statvfs failed");
+		return (ARCHIVE_FAILED);
+	} else if (xr == 1) {
+		/* pathconf(_PC_REX_*) operations are not supported. */
+		t->current_filesystem->xfer_align = svfs.f_frsize;
+		t->current_filesystem->max_xfer_size = -1;
+		t->current_filesystem->min_xfer_size = svfs.f_bsize;
+		t->current_filesystem->incr_xfer_size = svfs.f_bsize;
+	}
+
+#if defined(ST_NOATIME)
+	if (svfs.f_flag & ST_NOATIME)
+		t->current_filesystem->noatime = 1;
+	else
+#endif
+		t->current_filesystem->noatime = 0;
+
+#if defined(USE_READDIR_R)
+	/* Set maximum filename length. */
+	t->current_filesystem->name_max = svfs.f_namemax;
+#endif
+	return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ * Generic: Gather current filesystem properties.
+ * TODO: Is this generic function really needed?
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+	struct tree *t = a->tree;
+#if defined(_PC_NAME_MAX) && defined(USE_READDIR_R)
+	long nm;
+#endif
+	t->current_filesystem->synthetic = -1;/* Not supported */
+	t->current_filesystem->remote = -1;/* Not supported */
+	t->current_filesystem->noatime = 0;
+	(void)get_xfer_size(t, -1, ".");/* Dummy call to avoid build error. */
+	t->current_filesystem->xfer_align = -1;/* Unknown */
+	t->current_filesystem->max_xfer_size = -1;
+	t->current_filesystem->min_xfer_size = -1;
+	t->current_filesystem->incr_xfer_size = -1;
+
+#if defined(USE_READDIR_R)
+	/* Set maximum filename length. */
+#  if defined(_PC_NAME_MAX)
+	if (tree_current_is_symblic_link_target(t)) {
+		if (tree_enter_working_dir(t) != 0) {
+			archive_set_error(&a->archive, errno, "fchdir failed");
+			return (ARCHIVE_FAILED);
+		}
+		nm = pathconf(tree_current_access_path(t), _PC_NAME_MAX);
+	} else
+		nm = fpathconf(tree_current_dir_fd(t), _PC_NAME_MAX);
+	if (nm == -1)
+#  endif /* _PC_NAME_MAX */
+		/*
+		 * Some systems (HP-UX or others?) incorrectly defined
+		 * NAME_MAX macro to be a smaller value.
+		 */
+#  if defined(NAME_MAX) && NAME_MAX >= 255
+		t->current_filesystem->name_max = NAME_MAX;
+#  else
+		/* No way to get a trusted value of maximum filename
+		 * length. */
+		t->current_filesystem->name_max = PATH_MAX;
+#  endif /* NAME_MAX */
+#  if defined(_PC_NAME_MAX)
+	else
+		t->current_filesystem->name_max = nm;
+#  endif /* _PC_NAME_MAX */
+#endif /* USE_READDIR_R */
+	return (ARCHIVE_OK);
+}
+
+#endif
+
+static int
+close_and_restore_time(int fd, struct tree *t, struct restore_time *rt)
+{
+#ifndef HAVE_UTIMES
+	(void)t; /* UNUSED */
+	(void)rt; /* UNUSED */
+	return (close(fd));
+#else
+#if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__)
+	struct timespec timespecs[2];
+#endif
+	struct timeval times[2];
+
+	if ((t->flags & needsRestoreTimes) == 0 || rt->noatime) {
+		if (fd >= 0)
+			return (close(fd));
+		else
+			return (0);
+	}
+
+#if defined(HAVE_FUTIMENS) && !defined(__CYGWIN__)
+	timespecs[1].tv_sec = rt->mtime;
+	timespecs[1].tv_nsec = rt->mtime_nsec;
+
+	timespecs[0].tv_sec = rt->atime;
+	timespecs[0].tv_nsec = rt->atime_nsec;
+	/* futimens() is defined in POSIX.1-2008. */
+	if (futimens(fd, timespecs) == 0)
+		return (close(fd));
+#endif
+
+	times[1].tv_sec = rt->mtime;
+	times[1].tv_usec = rt->mtime_nsec / 1000;
+
+	times[0].tv_sec = rt->atime;
+	times[0].tv_usec = rt->atime_nsec / 1000;
+
+#if !defined(HAVE_FUTIMENS) && defined(HAVE_FUTIMES) && !defined(__CYGWIN__)
+	if (futimes(fd, times) == 0)
+		return (close(fd));
+#endif
+	close(fd);
+#if defined(HAVE_FUTIMESAT)
+	if (futimesat(tree_current_dir_fd(t), rt->name, times) == 0)
+		return (0);
+#endif
+#ifdef HAVE_LUTIMES
+	if (lutimes(rt->name, times) != 0)
+#else
+	if (AE_IFLNK != rt->filetype && utimes(rt->name, times) != 0)
+#endif
+		return (-1);
+#endif
+	return (0);
+}
+
+static int
+open_on_current_dir(struct tree *t, const char *path, int flags)
+{
+#ifdef HAVE_OPENAT
+	return (openat(tree_current_dir_fd(t), path, flags));
+#else
+	if (tree_enter_working_dir(t) != 0)
+		return (-1);
+	return (open(path, flags));
+#endif
+}
+
+static int
+tree_dup(int fd)
+{
+	int new_fd;
+#ifdef F_DUPFD_CLOEXEC
+	static volatile int can_dupfd_cloexec = 1;
+
+	if (can_dupfd_cloexec) {
+		new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+		if (new_fd != -1)
+			return (new_fd);
+		/* Linux 2.6.18 - 2.6.23 declare F_DUPFD_CLOEXEC,
+		 * but it cannot be used. So we have to try dup(). */
+		/* We won't try F_DUPFD_CLOEXEC. */
+		can_dupfd_cloexec = 0;
+	}
+#endif /* F_DUPFD_CLOEXEC */
+	new_fd = dup(fd);
+	__archive_ensure_cloexec_flag(new_fd);
+	return (new_fd);
+}
+
+/*
+ * Add a directory path to the current stack.
+ */
+static void
+tree_push(struct tree *t, const char *path, int filesystem_id,
+    int64_t dev, int64_t ino, struct restore_time *rt)
+{
+	struct tree_entry *te;
+
+	te = calloc(1, sizeof(*te));
+	te->next = t->stack;
+	te->parent = t->current;
+	if (te->parent)
+		te->depth = te->parent->depth + 1;
+	t->stack = te;
+	archive_string_init(&te->name);
+	te->symlink_parent_fd = -1;
+	archive_strcpy(&te->name, path);
+	te->flags = needsDescent | needsOpen | needsAscent;
+	te->filesystem_id = filesystem_id;
+	te->dev = dev;
+	te->ino = ino;
+	te->dirname_length = t->dirname_length;
+	te->restore_time.name = te->name.s;
+	if (rt != NULL) {
+		te->restore_time.mtime = rt->mtime;
+		te->restore_time.mtime_nsec = rt->mtime_nsec;
+		te->restore_time.atime = rt->atime;
+		te->restore_time.atime_nsec = rt->atime_nsec;
+		te->restore_time.filetype = rt->filetype;
+		te->restore_time.noatime = rt->noatime;
+	}
+}
+
+/*
+ * Append a name to the current dir path.
+ */
+static void
+tree_append(struct tree *t, const char *name, size_t name_length)
+{
+	size_t size_needed;
+
+	t->path.s[t->dirname_length] = '\0';
+	t->path.length = t->dirname_length;
+	/* Strip trailing '/' from name, unless entire name is "/". */
+	while (name_length > 1 && name[name_length - 1] == '/')
+		name_length--;
+
+	/* Resize pathname buffer as needed. */
+	size_needed = name_length + t->dirname_length + 2;
+	archive_string_ensure(&t->path, size_needed);
+	/* Add a separating '/' if it's needed. */
+	if (t->dirname_length > 0 && t->path.s[archive_strlen(&t->path)-1] != '/')
+		archive_strappend_char(&t->path, '/');
+	t->basename = t->path.s + archive_strlen(&t->path);
+	archive_strncat(&t->path, name, name_length);
+	t->restore_time.name = t->basename;
+}
+
+/*
+ * Open a directory tree for traversal.
+ */
+static struct tree *
+tree_open(const char *path, int symlink_mode, int restore_time)
+{
+	struct tree *t;
+
+	if ((t = calloc(1, sizeof(*t))) == NULL)
+		return (NULL);
+	archive_string_init(&t->path);
+	archive_string_ensure(&t->path, 31);
+	t->initial_symlink_mode = symlink_mode;
+	return (tree_reopen(t, path, restore_time));
+}
+
+static struct tree *
+tree_reopen(struct tree *t, const char *path, int restore_time)
+{
+#if defined(O_PATH)
+	/* Linux */
+	const int o_flag = O_PATH;
+#elif defined(O_SEARCH)
+	/* SunOS */
+	const int o_flag = O_SEARCH;
+#elif defined(__FreeBSD__) && defined(O_EXEC)
+	/* FreeBSD */
+	const int o_flag = O_EXEC;
+#endif
+
+	t->flags = (restore_time != 0)?needsRestoreTimes:0;
+	t->flags |= onInitialDir;
+	t->visit_type = 0;
+	t->tree_errno = 0;
+	t->dirname_length = 0;
+	t->depth = 0;
+	t->descend = 0;
+	t->current = NULL;
+	t->d = INVALID_DIR_HANDLE;
+	t->symlink_mode = t->initial_symlink_mode;
+	archive_string_empty(&t->path);
+	t->entry_fd = -1;
+	t->entry_eof = 0;
+	t->entry_remaining_bytes = 0;
+	t->initial_filesystem_id = -1;
+
+	/* First item is set up a lot like a symlink traversal. */
+	tree_push(t, path, 0, 0, 0, NULL);
+	t->stack->flags = needsFirstVisit;
+	t->maxOpenCount = t->openCount = 1;
+	t->initial_dir_fd = open(".", O_RDONLY | O_CLOEXEC);
+#if defined(O_PATH) || defined(O_SEARCH) || \
+ (defined(__FreeBSD__) && defined(O_EXEC))
+	/*
+	 * Most likely reason to fail opening "." is that it's not readable,
+	 * so try again for execute. The consequences of not opening this are
+	 * unhelpful and unnecessary errors later.
+	 */
+	if (t->initial_dir_fd < 0)
+		t->initial_dir_fd = open(".", o_flag | O_CLOEXEC);
+#endif
+	__archive_ensure_cloexec_flag(t->initial_dir_fd);
+	t->working_dir_fd = tree_dup(t->initial_dir_fd);
+	return (t);
+}
+
+static int
+tree_descent(struct tree *t)
+{
+	int flag, new_fd, r = 0;
+
+	t->dirname_length = archive_strlen(&t->path);
+	flag = O_RDONLY | O_CLOEXEC;
+#if defined(O_DIRECTORY)
+	flag |= O_DIRECTORY;
+#endif
+	new_fd = open_on_current_dir(t, t->stack->name.s, flag);
+	__archive_ensure_cloexec_flag(new_fd);
+	if (new_fd < 0) {
+		t->tree_errno = errno;
+		r = TREE_ERROR_DIR;
+	} else {
+		t->depth++;
+		/* If it is a link, set up fd for the ascent. */
+		if (t->stack->flags & isDirLink) {
+			t->stack->symlink_parent_fd = t->working_dir_fd;
+			t->openCount++;
+			if (t->openCount > t->maxOpenCount)
+				t->maxOpenCount = t->openCount;
+		} else
+			close(t->working_dir_fd);
+		/* Renew the current working directory. */
+		t->working_dir_fd = new_fd;
+		t->flags &= ~onWorkingDir;
+	}
+	return (r);
+}
+
+/*
+ * We've finished a directory; ascend back to the parent.
+ */
+static int
+tree_ascend(struct tree *t)
+{
+	struct tree_entry *te;
+	int new_fd, r = 0, prev_dir_fd;
+
+	te = t->stack;
+	prev_dir_fd = t->working_dir_fd;
+	if (te->flags & isDirLink)
+		new_fd = te->symlink_parent_fd;
+	else {
+		new_fd = open_on_current_dir(t, "..", O_RDONLY | O_CLOEXEC);
+		__archive_ensure_cloexec_flag(new_fd);
+	}
+	if (new_fd < 0) {
+		t->tree_errno = errno;
+		r = TREE_ERROR_FATAL;
+	} else {
+		/* Renew the current working directory. */
+		t->working_dir_fd = new_fd;
+		t->flags &= ~onWorkingDir;
+		/* Current directory has been changed, we should
+		 * close an fd of previous working directory. */
+		close_and_restore_time(prev_dir_fd, t, &te->restore_time);
+		if (te->flags & isDirLink) {
+			t->openCount--;
+			te->symlink_parent_fd = -1;
+		}
+		t->depth--;
+	}
+	return (r);
+}
+
+/*
+ * Return to the initial directory where tree_open() was performed.
+ */
+static int
+tree_enter_initial_dir(struct tree *t)
+{
+	int r = 0;
+
+	if ((t->flags & onInitialDir) == 0) {
+		r = fchdir(t->initial_dir_fd);
+		if (r == 0) {
+			t->flags &= ~onWorkingDir;
+			t->flags |= onInitialDir;
+		}
+	}
+	return (r);
+}
+
+/*
+ * Restore working directory of directory traversals.
+ */
+static int
+tree_enter_working_dir(struct tree *t)
+{
+	int r = 0;
+
+	/*
+	 * Change the current directory if really needed.
+	 * Sometimes this is unneeded when we did not do
+	 * descent.
+	 */
+	if (t->depth > 0 && (t->flags & onWorkingDir) == 0) {
+		r = fchdir(t->working_dir_fd);
+		if (r == 0) {
+			t->flags &= ~onInitialDir;
+			t->flags |= onWorkingDir;
+		}
+	}
+	return (r);
+}
+
+static int
+tree_current_dir_fd(struct tree *t)
+{
+	return (t->working_dir_fd);
+}
+
+/*
+ * Pop the working stack.
+ */
+static void
+tree_pop(struct tree *t)
+{
+	struct tree_entry *te;
+
+	t->path.s[t->dirname_length] = '\0';
+	t->path.length = t->dirname_length;
+	if (t->stack == t->current && t->current != NULL)
+		t->current = t->current->parent;
+	te = t->stack;
+	t->stack = te->next;
+	t->dirname_length = te->dirname_length;
+	t->basename = t->path.s + t->dirname_length;
+	while (t->basename[0] == '/')
+		t->basename++;
+	archive_string_free(&te->name);
+	free(te);
+}
+
+/*
+ * Get the next item in the tree traversal.
+ */
+static int
+tree_next(struct tree *t)
+{
+	int r;
+
+	while (t->stack != NULL) {
+		/* If there's an open dir, get the next entry from there. */
+		if (t->d != INVALID_DIR_HANDLE) {
+			r = tree_dir_next_posix(t);
+			if (r == 0)
+				continue;
+			return (r);
+		}
+
+		if (t->stack->flags & needsFirstVisit) {
+			/* Top stack item needs a regular visit. */
+			t->current = t->stack;
+			tree_append(t, t->stack->name.s,
+			    archive_strlen(&(t->stack->name)));
+			/* t->dirname_length = t->path_length; */
+			/* tree_pop(t); */
+			t->stack->flags &= ~needsFirstVisit;
+			return (t->visit_type = TREE_REGULAR);
+		} else if (t->stack->flags & needsDescent) {
+			/* Top stack item is dir to descend into. */
+			t->current = t->stack;
+			tree_append(t, t->stack->name.s,
+			    archive_strlen(&(t->stack->name)));
+			t->stack->flags &= ~needsDescent;
+			r = tree_descent(t);
+			if (r != 0) {
+				tree_pop(t);
+				t->visit_type = r;
+			} else
+				t->visit_type = TREE_POSTDESCENT;
+			return (t->visit_type);
+		} else if (t->stack->flags & needsOpen) {
+			t->stack->flags &= ~needsOpen;
+			r = tree_dir_next_posix(t);
+			if (r == 0)
+				continue;
+			return (r);
+		} else if (t->stack->flags & needsAscent) {
+		        /* Top stack item is dir and we're done with it. */
+			r = tree_ascend(t);
+			tree_pop(t);
+			t->visit_type = r != 0 ? r : TREE_POSTASCENT;
+			return (t->visit_type);
+		} else {
+			/* Top item on stack is dead. */
+			tree_pop(t);
+			t->flags &= ~hasLstat;
+			t->flags &= ~hasStat;
+		}
+	}
+	return (t->visit_type = 0);
+}
+
+static int
+tree_dir_next_posix(struct tree *t)
+{
+	int r;
+	const char *name;
+	size_t namelen;
+
+	if (t->d == NULL) {
+#if defined(USE_READDIR_R)
+		size_t dirent_size;
+#endif
+
+#if defined(HAVE_FDOPENDIR)
+		t->d = fdopendir(tree_dup(t->working_dir_fd));
+#else /* HAVE_FDOPENDIR */
+		if (tree_enter_working_dir(t) == 0) {
+			t->d = opendir(".");
+#if HAVE_DIRFD || defined(dirfd)
+			__archive_ensure_cloexec_flag(dirfd(t->d));
+#endif
+		}
+#endif /* HAVE_FDOPENDIR */
+		if (t->d == NULL) {
+			r = tree_ascend(t); /* Undo "chdir" */
+			tree_pop(t);
+			t->tree_errno = errno;
+			t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
+			return (t->visit_type);
+		}
+#if defined(USE_READDIR_R)
+		dirent_size = offsetof(struct dirent, d_name) +
+		  t->filesystem_table[t->current->filesystem_id].name_max + 1;
+		if (t->dirent == NULL || t->dirent_allocated < dirent_size) {
+			free(t->dirent);
+			t->dirent = malloc(dirent_size);
+			if (t->dirent == NULL) {
+				closedir(t->d);
+				t->d = INVALID_DIR_HANDLE;
+				(void)tree_ascend(t);
+				tree_pop(t);
+				t->tree_errno = ENOMEM;
+				t->visit_type = TREE_ERROR_DIR;
+				return (t->visit_type);
+			}
+			t->dirent_allocated = dirent_size;
+		}
+#endif /* USE_READDIR_R */
+	}
+	for (;;) {
+		errno = 0;
+#if defined(USE_READDIR_R)
+		r = readdir_r(t->d, t->dirent, &t->de);
+#ifdef _AIX
+		/* Note: According to the man page, return value 9 indicates
+		 * that the readdir_r was not successful and the error code
+		 * is set to the global errno variable. And then if the end
+		 * of directory entries was reached, the return value is 9
+		 * and the third parameter is set to NULL and errno is
+		 * unchanged. */
+		if (r == 9)
+			r = errno;
+#endif /* _AIX */
+		if (r != 0 || t->de == NULL) {
+#else
+		t->de = readdir(t->d);
+		if (t->de == NULL) {
+			r = errno;
+#endif
+			closedir(t->d);
+			t->d = INVALID_DIR_HANDLE;
+			if (r != 0) {
+				t->tree_errno = r;
+				t->visit_type = TREE_ERROR_DIR;
+				return (t->visit_type);
+			} else
+				return (0);
+		}
+		name = t->de->d_name;
+		namelen = D_NAMELEN(t->de);
+		t->flags &= ~hasLstat;
+		t->flags &= ~hasStat;
+		if (name[0] == '.' && name[1] == '\0')
+			continue;
+		if (name[0] == '.' && name[1] == '.' && name[2] == '\0')
+			continue;
+		tree_append(t, name, namelen);
+		return (t->visit_type = TREE_REGULAR);
+	}
+}
+
+
+/*
+ * Get the stat() data for the entry just returned from tree_next().
+ */
+static const struct stat *
+tree_current_stat(struct tree *t)
+{
+	if (!(t->flags & hasStat)) {
+#ifdef HAVE_FSTATAT
+		if (fstatat(tree_current_dir_fd(t),
+		    tree_current_access_path(t), &t->st, 0) != 0)
+#else
+		if (tree_enter_working_dir(t) != 0)
+			return NULL;
+		if (la_stat(tree_current_access_path(t), &t->st) != 0)
+#endif
+			return NULL;
+		t->flags |= hasStat;
+	}
+	return (&t->st);
+}
+
+/*
+ * Get the lstat() data for the entry just returned from tree_next().
+ */
+static const struct stat *
+tree_current_lstat(struct tree *t)
+{
+	if (!(t->flags & hasLstat)) {
+#ifdef HAVE_FSTATAT
+		if (fstatat(tree_current_dir_fd(t),
+		    tree_current_access_path(t), &t->lst,
+		    AT_SYMLINK_NOFOLLOW) != 0)
+#else
+		if (tree_enter_working_dir(t) != 0)
+			return NULL;
+		if (lstat(tree_current_access_path(t), &t->lst) != 0)
+#endif
+			return NULL;
+		t->flags |= hasLstat;
+	}
+	return (&t->lst);
+}
+
+/*
+ * Test whether current entry is a dir or link to a dir.
+ */
+static int
+tree_current_is_dir(struct tree *t)
+{
+	const struct stat *st;
+	/*
+	 * If we already have lstat() info, then try some
+	 * cheap tests to determine if this is a dir.
+	 */
+	if (t->flags & hasLstat) {
+		/* If lstat() says it's a dir, it must be a dir. */
+		st = tree_current_lstat(t);
+		if (st == NULL)
+			return 0;
+		if (S_ISDIR(st->st_mode))
+			return 1;
+		/* Not a dir; might be a link to a dir. */
+		/* If it's not a link, then it's not a link to a dir. */
+		if (!S_ISLNK(st->st_mode))
+			return 0;
+		/*
+		 * It's a link, but we don't know what it's a link to,
+		 * so we'll have to use stat().
+		 */
+	}
+
+	st = tree_current_stat(t);
+	/* If we can't stat it, it's not a dir. */
+	if (st == NULL)
+		return 0;
+	/* Use the definitive test.  Hopefully this is cached. */
+	return (S_ISDIR(st->st_mode));
+}
+
+/*
+ * Test whether current entry is a physical directory.  Usually, we
+ * already have at least one of stat() or lstat() in memory, so we
+ * use tricks to try to avoid an extra trip to the disk.
+ */
+static int
+tree_current_is_physical_dir(struct tree *t)
+{
+	const struct stat *st;
+
+	/*
+	 * If stat() says it isn't a dir, then it's not a dir.
+	 * If stat() data is cached, this check is free, so do it first.
+	 */
+	if (t->flags & hasStat) {
+		st = tree_current_stat(t);
+		if (st == NULL)
+			return (0);
+		if (!S_ISDIR(st->st_mode))
+			return (0);
+	}
+
+	/*
+	 * Either stat() said it was a dir (in which case, we have
+	 * to determine whether it's really a link to a dir) or
+	 * stat() info wasn't available.  So we use lstat(), which
+	 * hopefully is already cached.
+	 */
+
+	st = tree_current_lstat(t);
+	/* If we can't stat it, it's not a dir. */
+	if (st == NULL)
+		return 0;
+	/* Use the definitive test.  Hopefully this is cached. */
+	return (S_ISDIR(st->st_mode));
+}
+
+/*
+ * Test whether the same file has been in the tree as its parent.
+ */
+static int
+tree_target_is_same_as_parent(struct tree *t, const struct stat *st)
+{
+	struct tree_entry *te;
+
+	for (te = t->current->parent; te != NULL; te = te->parent) {
+		if (te->dev == (int64_t)st->st_dev &&
+		    te->ino == (int64_t)st->st_ino)
+			return (1);
+	}
+	return (0);
+}
+
+/*
+ * Test whether the current file is symbolic link target and
+ * on the other filesystem.
+ */
+static int
+tree_current_is_symblic_link_target(struct tree *t)
+{
+	static const struct stat *lst, *st;
+
+	lst = tree_current_lstat(t);
+	st = tree_current_stat(t);
+	return (st != NULL && lst != NULL &&
+	    (int64_t)st->st_dev == t->current_filesystem->dev &&
+	    st->st_dev != lst->st_dev);
+}
+
+/*
+ * Return the access path for the entry just returned from tree_next().
+ */
+static const char *
+tree_current_access_path(struct tree *t)
+{
+	return (t->basename);
+}
+
+/*
+ * Return the full path for the entry just returned from tree_next().
+ */
+static const char *
+tree_current_path(struct tree *t)
+{
+	return (t->path.s);
+}
+
+/*
+ * Terminate the traversal.
+ */
+static void
+tree_close(struct tree *t)
+{
+
+	if (t == NULL)
+		return;
+	if (t->entry_fd >= 0) {
+		close_and_restore_time(t->entry_fd, t, &t->restore_time);
+		t->entry_fd = -1;
+	}
+	/* Close the handle of readdir(). */
+	if (t->d != INVALID_DIR_HANDLE) {
+		closedir(t->d);
+		t->d = INVALID_DIR_HANDLE;
+	}
+	/* Release anything remaining in the stack. */
+	while (t->stack != NULL) {
+		if (t->stack->flags & isDirLink)
+			close(t->stack->symlink_parent_fd);
+		tree_pop(t);
+	}
+	if (t->working_dir_fd >= 0) {
+		close(t->working_dir_fd);
+		t->working_dir_fd = -1;
+	}
+	if (t->initial_dir_fd >= 0) {
+		close(t->initial_dir_fd);
+		t->initial_dir_fd = -1;
+	}
+}
+
+/*
+ * Release any resources.
+ */
+static void
+tree_free(struct tree *t)
+{
+	int i;
+
+	if (t == NULL)
+		return;
+	archive_string_free(&t->path);
+#if defined(USE_READDIR_R)
+	free(t->dirent);
+#endif
+	free(t->sparse_list);
+	for (i = 0; i < t->max_filesystem_id; i++)
+		free(t->filesystem_table[i].allocation_ptr);
+	free(t->filesystem_table);
+	free(t);
+}
+
+#endif
diff --git a/c/archive_read_disk_private.h b/c/archive_read_disk_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_read_disk_private.h
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 2003-2009 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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_read_disk_private.h 201105 2009-12-28 03:20:54Z kientzle $
+ */
+
+#ifndef ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
+#define ARCHIVE_READ_DISK_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_platform_acl.h"
+
+struct tree;
+struct archive_entry;
+
+struct archive_read_disk {
+	struct archive	archive;
+
+	/* Reused by archive_read_next_header() */
+	struct archive_entry *entry;
+
+	/*
+	 * Symlink mode is one of 'L'ogical, 'P'hysical, or 'H'ybrid,
+	 * following an old BSD convention.  'L' follows all symlinks,
+	 * 'P' follows none, 'H' follows symlinks only for the first
+	 * item.
+	 */
+	char	symlink_mode;
+
+	/*
+	 * Since symlink interaction changes, we need to track whether
+	 * we're following symlinks for the current item.  'L' mode above
+	 * sets this true, 'P' sets it false, 'H' changes it as we traverse.
+	 */
+	char	follow_symlinks;  /* Either 'L' or 'P'. */
+
+	/* Directory traversals. */
+	struct tree *tree;
+	int	(*open_on_current_dir)(struct tree*, const char *, int);
+	int	(*tree_current_dir_fd)(struct tree*);
+	int	(*tree_enter_working_dir)(struct tree*);
+
+	/* Bitfield with ARCHIVE_READDISK_* tunables */
+	int	flags;
+
+	const char * (*lookup_gname)(void *private, int64_t gid);
+	void	(*cleanup_gname)(void *private);
+	void	 *lookup_gname_data;
+	const char * (*lookup_uname)(void *private, int64_t uid);
+	void	(*cleanup_uname)(void *private);
+	void	 *lookup_uname_data;
+
+	int	(*metadata_filter_func)(struct archive *, void *,
+			struct archive_entry *);
+	void	*metadata_filter_data;
+
+	/* ARCHIVE_MATCH object. */
+	struct archive	*matching;
+	/* Callback function, this will be invoked when ARCHIVE_MATCH
+	 * archive_match_*_excluded_ae return true. */
+	void	(*excluded_cb_func)(struct archive *, void *,
+			 struct archive_entry *);
+	void	*excluded_cb_data;
+};
+
+const char *
+archive_read_disk_entry_setup_path(struct archive_read_disk *,
+    struct archive_entry *, int *);
+
+int
+archive_read_disk_entry_setup_acls(struct archive_read_disk *,
+    struct archive_entry *, int *);
+#endif
diff --git a/c/archive_read_disk_set_standard_lookup.c b/c/archive_read_disk_set_standard_lookup.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_disk_set_standard_lookup.c
@@ -0,0 +1,313 @@
+/*-
+ * 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: head/lib/libarchive/archive_read_disk_set_standard_lookup.c 201109 2009-12-28 03:30:31Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+int
+archive_read_disk_set_standard_lookup(struct archive *a)
+{
+	archive_set_error(a, -1, "Standard lookups not available on Windows");
+	return (ARCHIVE_FATAL);
+}
+#else /* ! (_WIN32 && !__CYGWIN__) */
+#define	name_cache_size 127
+
+static const char * const NO_NAME = "(noname)";
+
+struct name_cache {
+	struct archive *archive;
+	char   *buff;
+	size_t  buff_size;
+	int	probes;
+	int	hits;
+	size_t	size;
+	struct {
+		id_t id;
+		const char *name;
+	} cache[name_cache_size];
+};
+
+static const char *	lookup_gname(void *, int64_t);
+static const char *	lookup_uname(void *, int64_t);
+static void	cleanup(void *);
+static const char *	lookup_gname_helper(struct name_cache *, id_t gid);
+static const char *	lookup_uname_helper(struct name_cache *, id_t uid);
+
+/*
+ * Installs functions that use getpwuid()/getgrgid()---along with
+ * a simple cache to accelerate such lookups---into the archive_read_disk
+ * object.  This is in a separate file because getpwuid()/getgrgid()
+ * can pull in a LOT of library code (including NIS/LDAP functions, which
+ * pull in DNS resolvers, etc).  This can easily top 500kB, which makes
+ * it inappropriate for some space-constrained applications.
+ *
+ * Applications that are size-sensitive may want to just use the
+ * real default functions (defined in archive_read_disk.c) that just
+ * use the uid/gid without the lookup.  Or define your own custom functions
+ * if you prefer.
+ */
+int
+archive_read_disk_set_standard_lookup(struct archive *a)
+{
+	struct name_cache *ucache = malloc(sizeof(struct name_cache));
+	struct name_cache *gcache = malloc(sizeof(struct name_cache));
+
+	if (ucache == NULL || gcache == NULL) {
+		archive_set_error(a, ENOMEM,
+		    "Can't allocate uname/gname lookup cache");
+		free(ucache);
+		free(gcache);
+		return (ARCHIVE_FATAL);
+	}
+
+	memset(ucache, 0, sizeof(*ucache));
+	ucache->archive = a;
+	ucache->size = name_cache_size;
+	memset(gcache, 0, sizeof(*gcache));
+	gcache->archive = a;
+	gcache->size = name_cache_size;
+
+	archive_read_disk_set_gname_lookup(a, gcache, lookup_gname, cleanup);
+	archive_read_disk_set_uname_lookup(a, ucache, lookup_uname, cleanup);
+
+	return (ARCHIVE_OK);
+}
+
+static void
+cleanup(void *data)
+{
+	struct name_cache *cache = (struct name_cache *)data;
+	size_t i;
+
+	if (cache != NULL) {
+		for (i = 0; i < cache->size; i++) {
+			if (cache->cache[i].name != NULL &&
+			    cache->cache[i].name != NO_NAME)
+				free((void *)(uintptr_t)cache->cache[i].name);
+		}
+		free(cache->buff);
+		free(cache);
+	}
+}
+
+/*
+ * Lookup uid/gid from uname/gname, return NULL if no match.
+ */
+static const char *
+lookup_name(struct name_cache *cache,
+    const char * (*lookup_fn)(struct name_cache *, id_t), id_t id)
+{
+	const char *name;
+	int slot;
+
+
+	cache->probes++;
+
+	slot = id % cache->size;
+	if (cache->cache[slot].name != NULL) {
+		if (cache->cache[slot].id == id) {
+			cache->hits++;
+			if (cache->cache[slot].name == NO_NAME)
+				return (NULL);
+			return (cache->cache[slot].name);
+		}
+		if (cache->cache[slot].name != NO_NAME)
+			free((void *)(uintptr_t)cache->cache[slot].name);
+		cache->cache[slot].name = NULL;
+	}
+
+	name = (lookup_fn)(cache, id);
+	if (name == NULL) {
+		/* Cache and return the negative response. */
+		cache->cache[slot].name = NO_NAME;
+		cache->cache[slot].id = id;
+		return (NULL);
+	}
+
+	cache->cache[slot].name = name;
+	cache->cache[slot].id = id;
+	return (cache->cache[slot].name);
+}
+
+static const char *
+lookup_uname(void *data, int64_t uid)
+{
+	struct name_cache *uname_cache = (struct name_cache *)data;
+	return (lookup_name(uname_cache,
+		    &lookup_uname_helper, (id_t)uid));
+}
+
+#if HAVE_GETPWUID_R
+static const char *
+lookup_uname_helper(struct name_cache *cache, id_t id)
+{
+	struct passwd	pwent, *result;
+	char * nbuff;
+	size_t nbuff_size;
+	int r;
+
+	if (cache->buff_size == 0) {
+		cache->buff_size = 256;
+		cache->buff = malloc(cache->buff_size);
+	}
+	if (cache->buff == NULL)
+		return (NULL);
+	for (;;) {
+		result = &pwent; /* Old getpwuid_r ignores last arg. */
+		r = getpwuid_r((uid_t)id, &pwent,
+			       cache->buff, cache->buff_size, &result);
+		if (r == 0)
+			break;
+		if (r != ERANGE)
+			break;
+		/* ERANGE means our buffer was too small, but POSIX
+		 * doesn't tell us how big the buffer should be, so
+		 * we just double it and try again.  Because the buffer
+		 * is kept around in the cache object, we shouldn't
+		 * have to do this very often. */
+		nbuff_size = cache->buff_size * 2;
+		nbuff = realloc(cache->buff, nbuff_size);
+		if (nbuff == NULL)
+			break;
+		cache->buff = nbuff;
+		cache->buff_size = nbuff_size;
+	}
+	if (r != 0) {
+		archive_set_error(cache->archive, errno,
+		    "Can't lookup user for id %d", (int)id);
+		return (NULL);
+	}
+	if (result == NULL)
+		return (NULL);
+
+	return strdup(result->pw_name);
+}
+#else
+static const char *
+lookup_uname_helper(struct name_cache *cache, id_t id)
+{
+	struct passwd	*result;
+	(void)cache; /* UNUSED */
+
+	result = getpwuid((uid_t)id);
+
+	if (result == NULL)
+		return (NULL);
+
+	return strdup(result->pw_name);
+}
+#endif
+
+static const char *
+lookup_gname(void *data, int64_t gid)
+{
+	struct name_cache *gname_cache = (struct name_cache *)data;
+	return (lookup_name(gname_cache,
+		    &lookup_gname_helper, (id_t)gid));
+}
+
+#if HAVE_GETGRGID_R
+static const char *
+lookup_gname_helper(struct name_cache *cache, id_t id)
+{
+	struct group	grent, *result;
+	char * nbuff;
+	size_t nbuff_size;
+	int r;
+
+	if (cache->buff_size == 0) {
+		cache->buff_size = 256;
+		cache->buff = malloc(cache->buff_size);
+	}
+	if (cache->buff == NULL)
+		return (NULL);
+	for (;;) {
+		result = &grent; /* Old getgrgid_r ignores last arg. */
+		r = getgrgid_r((gid_t)id, &grent,
+			       cache->buff, cache->buff_size, &result);
+		if (r == 0)
+			break;
+		if (r != ERANGE)
+			break;
+		/* ERANGE means our buffer was too small, but POSIX
+		 * doesn't tell us how big the buffer should be, so
+		 * we just double it and try again. */
+		nbuff_size = cache->buff_size * 2;
+		nbuff = realloc(cache->buff, nbuff_size);
+		if (nbuff == NULL)
+			break;
+		cache->buff = nbuff;
+		cache->buff_size = nbuff_size;
+	}
+	if (r != 0) {
+		archive_set_error(cache->archive, errno,
+		    "Can't lookup group for id %d", (int)id);
+		return (NULL);
+	}
+	if (result == NULL)
+		return (NULL);
+
+	return strdup(result->gr_name);
+}
+#else
+static const char *
+lookup_gname_helper(struct name_cache *cache, id_t id)
+{
+	struct group	*result;
+	(void)cache; /* UNUSED */
+
+	result = getgrgid((gid_t)id);
+
+	if (result == NULL)
+		return (NULL);
+
+	return strdup(result->gr_name);
+}
+#endif
+
+#endif /* ! (_WIN32 && !__CYGWIN__) */
diff --git a/c/archive_read_disk_windows.c b/c/archive_read_disk_windows.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_disk_windows.c
@@ -0,0 +1,2477 @@
+/*-
+ * Copyright (c) 2003-2009 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <winioctl.h>
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_disk_private.h"
+
+#ifndef O_BINARY
+#define O_BINARY	0
+#endif
+#ifndef IO_REPARSE_TAG_SYMLINK
+/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */
+#define	IO_REPARSE_TAG_SYMLINK 0xA000000CL
+#endif
+
+/*-
+ * This is a new directory-walking system that addresses a number
+ * of problems I've had with fts(3).  In particular, it has no
+ * pathname-length limits (other than the size of 'int'), handles
+ * deep logical traversals, uses considerably less memory, and has
+ * an opaque interface (easier to modify in the future).
+ *
+ * Internally, it keeps a single list of "tree_entry" items that
+ * represent filesystem objects that require further attention.
+ * Non-directories are not kept in memory: they are pulled from
+ * readdir(), returned to the client, then freed as soon as possible.
+ * Any directory entry to be traversed gets pushed onto the stack.
+ *
+ * There is surprisingly little information that needs to be kept for
+ * each item on the stack.  Just the name, depth (represented here as the
+ * string length of the parent directory's pathname), and some markers
+ * indicating how to get back to the parent (via chdir("..") for a
+ * regular dir or via fchdir(2) for a symlink).
+ */
+
+struct restore_time {
+	const wchar_t		*full_path;
+	FILETIME		 lastWriteTime;
+	FILETIME		 lastAccessTime;
+	mode_t			 filetype;
+};
+
+struct tree_entry {
+	int			 depth;
+	struct tree_entry	*next;
+	struct tree_entry	*parent;
+	size_t			 full_path_dir_length;
+	struct archive_wstring	 name;
+	struct archive_wstring	 full_path;
+	size_t			 dirname_length;
+	int64_t			 dev;
+	int64_t			 ino;
+	int			 flags;
+	int			 filesystem_id;
+	/* How to restore time of a directory. */
+	struct restore_time	 restore_time;
+};
+
+struct filesystem {
+	int64_t		dev;
+	int		synthetic;
+	int		remote;
+	DWORD		bytesPerSector;
+};
+
+/* Definitions for tree_entry.flags bitmap. */
+#define	isDir		1  /* This entry is a regular directory. */
+#define	isDirLink	2  /* This entry is a symbolic link to a directory. */
+#define	needsFirstVisit	4  /* This is an initial entry. */
+#define	needsDescent	8  /* This entry needs to be previsited. */
+#define	needsOpen	16 /* This is a directory that needs to be opened. */
+#define	needsAscent	32 /* This entry needs to be postvisited. */
+
+/*
+ * On Windows, "first visit" is handled as a pattern to be handed to
+ * _findfirst().  This is consistent with Windows conventions that
+ * file patterns are handled within the application.  On Posix,
+ * "first visit" is just returned to the client.
+ */
+
+#define MAX_OVERLAPPED	8
+#define READ_BUFFER_SIZE	(1024 * 64) /* Default to 64KB per https://technet.microsoft.com/en-us/library/cc938632.aspx */
+#define DIRECT_IO	0/* Disabled */
+#define ASYNC_IO	1/* Enabled */
+
+/*
+ * Local data for this package.
+ */
+struct tree {
+	struct tree_entry	*stack;
+	struct tree_entry	*current;
+	HANDLE d;
+	WIN32_FIND_DATAW	_findData;
+	WIN32_FIND_DATAW	*findData;
+	int			 flags;
+	int			 visit_type;
+	/* Error code from last failed operation. */
+	int			 tree_errno;
+
+	/* A full path with "\\?\" prefix. */
+	struct archive_wstring	 full_path;
+	size_t			 full_path_dir_length;
+	/* Dynamically-sized buffer for holding path */
+	struct archive_wstring	 path;
+
+	/* Last path element */
+	const wchar_t		*basename;
+	/* Leading dir length */
+	size_t			 dirname_length;
+
+	int	 depth;
+
+	BY_HANDLE_FILE_INFORMATION	lst;
+	BY_HANDLE_FILE_INFORMATION	st;
+	int			 descend;
+	/* How to restore time of a file. */
+	struct restore_time	restore_time;
+
+	struct entry_sparse {
+		int64_t		 length;
+		int64_t		 offset;
+	}			*sparse_list, *current_sparse;
+	int			 sparse_count;
+	int			 sparse_list_size;
+
+	char			 initial_symlink_mode;
+	char			 symlink_mode;
+	struct filesystem	*current_filesystem;
+	struct filesystem	*filesystem_table;
+	int			 initial_filesystem_id;
+	int			 current_filesystem_id;
+	int			 max_filesystem_id;
+	int			 allocated_filesystem;
+
+	HANDLE			 entry_fh;
+	int			 entry_eof;
+	int64_t			 entry_remaining_bytes;
+	int64_t			 entry_total;
+
+	int			 ol_idx_doing;
+	int			 ol_idx_done;
+	int			 ol_num_doing;
+	int			 ol_num_done;
+	int64_t			 ol_remaining_bytes;
+	int64_t			 ol_total;
+	struct la_overlapped {
+		OVERLAPPED	 ol;
+		struct archive * _a;
+		unsigned char	*buff;
+		size_t		 buff_size;
+		int64_t		 offset;
+		size_t		 bytes_expected;
+		size_t		 bytes_transferred;
+	}			 ol[MAX_OVERLAPPED];
+	int			 direct_io;
+	int			 async_io;
+};
+
+#define bhfi_dev(bhfi)	((bhfi)->dwVolumeSerialNumber)
+/* Treat FileIndex as i-node. We should remove a sequence number
+ * which is high-16-bits of nFileIndexHigh. */
+#define bhfi_ino(bhfi)	\
+	((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \
+    + (bhfi)->nFileIndexLow)
+
+/* Definitions for tree.flags bitmap. */
+#define	hasStat		16 /* The st entry is valid. */
+#define	hasLstat	32 /* The lst entry is valid. */
+#define	needsRestoreTimes 128
+
+static int
+tree_dir_next_windows(struct tree *t, const wchar_t *pattern);
+
+/* Initiate/terminate a tree traversal. */
+static struct tree *tree_open(const wchar_t *, int, int);
+static struct tree *tree_reopen(struct tree *, const wchar_t *, int);
+static void tree_close(struct tree *);
+static void tree_free(struct tree *);
+static void tree_push(struct tree *, const wchar_t *, const wchar_t *,
+		int, int64_t, int64_t, struct restore_time *);
+
+/*
+ * tree_next() returns Zero if there is no next entry, non-zero if
+ * there is.  Note that directories are visited three times.
+ * Directories are always visited first as part of enumerating their
+ * parent; that is a "regular" visit.  If tree_descend() is invoked at
+ * that time, the directory is added to a work list and will
+ * subsequently be visited two more times: once just after descending
+ * into the directory ("postdescent") and again just after ascending
+ * back to the parent ("postascent").
+ *
+ * TREE_ERROR_DIR is returned if the descent failed (because the
+ * directory couldn't be opened, for instance).  This is returned
+ * instead of TREE_POSTDESCENT/TREE_POSTASCENT.  TREE_ERROR_DIR is not a
+ * fatal error, but it does imply that the relevant subtree won't be
+ * visited.  TREE_ERROR_FATAL is returned for an error that left the
+ * traversal completely hosed.  Right now, this is only returned for
+ * chdir() failures during ascent.
+ */
+#define	TREE_REGULAR		1
+#define	TREE_POSTDESCENT	2
+#define	TREE_POSTASCENT		3
+#define	TREE_ERROR_DIR		-1
+#define	TREE_ERROR_FATAL	-2
+
+static int tree_next(struct tree *);
+
+/*
+ * Return information about the current entry.
+ */
+
+/*
+ * The current full pathname, length of the full pathname, and a name
+ * that can be used to access the file.  Because tree does use chdir
+ * extensively, the access path is almost never the same as the full
+ * current path.
+ *
+ */
+static const wchar_t *tree_current_path(struct tree *);
+static const wchar_t *tree_current_access_path(struct tree *);
+
+/*
+ * Request the lstat() or stat() data for the current path.  Since the
+ * tree package needs to do some of this anyway, and caches the
+ * results, you should take advantage of it here if you need it rather
+ * than make a redundant stat() or lstat() call of your own.
+ */
+static const BY_HANDLE_FILE_INFORMATION *tree_current_stat(struct tree *);
+static const BY_HANDLE_FILE_INFORMATION *tree_current_lstat(struct tree *);
+
+/* The following functions use tricks to avoid a certain number of
+ * stat()/lstat() calls. */
+/* "is_physical_dir" is equivalent to S_ISDIR(tree_current_lstat()->st_mode) */
+static int tree_current_is_physical_dir(struct tree *);
+/* "is_physical_link" is equivalent to S_ISLNK(tree_current_lstat()->st_mode) */
+static int tree_current_is_physical_link(struct tree *);
+/* Instead of archive_entry_copy_stat for BY_HANDLE_FILE_INFORMATION */
+static void tree_archive_entry_copy_bhfi(struct archive_entry *,
+		    struct tree *, const BY_HANDLE_FILE_INFORMATION *);
+/* "is_dir" is equivalent to S_ISDIR(tree_current_stat()->st_mode) */
+static int tree_current_is_dir(struct tree *);
+static int update_current_filesystem(struct archive_read_disk *a,
+		    int64_t dev);
+static int setup_current_filesystem(struct archive_read_disk *);
+static int tree_target_is_same_as_parent(struct tree *,
+		    const BY_HANDLE_FILE_INFORMATION *);
+
+static int	_archive_read_disk_open_w(struct archive *, const wchar_t *);
+static int	_archive_read_free(struct archive *);
+static int	_archive_read_close(struct archive *);
+static int	_archive_read_data_block(struct archive *,
+		    const void **, size_t *, int64_t *);
+static int	_archive_read_next_header(struct archive *,
+		    struct archive_entry **);
+static int	_archive_read_next_header2(struct archive *,
+		    struct archive_entry *);
+static const char *trivial_lookup_gname(void *, int64_t gid);
+static const char *trivial_lookup_uname(void *, int64_t uid);
+static int	setup_sparse(struct archive_read_disk *, struct archive_entry *);
+static int	close_and_restore_time(HANDLE, struct tree *,
+		    struct restore_time *);
+static int	setup_sparse_from_disk(struct archive_read_disk *,
+		    struct archive_entry *, HANDLE);
+static int	la_linkname_from_handle(HANDLE, wchar_t **, int *);
+static int	la_linkname_from_pathw(const wchar_t *, wchar_t **, int *);
+static void	entry_symlink_from_pathw(struct archive_entry *,
+		    const wchar_t *path);
+
+typedef struct _REPARSE_DATA_BUFFER {
+	ULONG	ReparseTag;
+	USHORT ReparseDataLength;
+	USHORT	Reserved;
+	union {
+		struct {
+			USHORT	SubstituteNameOffset;
+			USHORT	SubstituteNameLength;
+			USHORT	PrintNameOffset;
+			USHORT	PrintNameLength;
+			ULONG	Flags;
+			WCHAR	PathBuffer[1];
+		} SymbolicLinkReparseBuffer;
+		struct {
+			USHORT	SubstituteNameOffset;
+			USHORT	SubstituteNameLength;
+			USHORT	PrintNameOffset;
+			USHORT	PrintNameLength;
+			WCHAR	PathBuffer[1];
+		} MountPointReparseBuffer;
+		struct {
+			UCHAR	DataBuffer[1];
+		} GenericReparseBuffer;
+	} DUMMYUNIONNAME;
+} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+/*
+ * Reads the target of a symbolic link
+ *
+ * Returns 0 on success and -1 on failure
+ * outbuf is allocated in the function
+ */
+static int
+la_linkname_from_handle(HANDLE h, wchar_t **linkname, int *linktype)
+{
+	DWORD inbytes;
+	REPARSE_DATA_BUFFER *buf;
+	BY_HANDLE_FILE_INFORMATION st;
+	size_t len;
+	BOOL ret;
+	BYTE *indata;
+	wchar_t *tbuf;
+
+	ret = GetFileInformationByHandle(h, &st);
+	if (ret == 0 ||
+	    (st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
+		return (-1);
+	}
+
+	indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
+	ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata,
+	    1024, &inbytes, NULL);
+	if (ret == 0) {
+		la_dosmaperr(GetLastError());
+		free(indata);
+		return (-1);
+	}
+
+	buf = (REPARSE_DATA_BUFFER *) indata;
+	if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) {
+		free(indata);
+		/* File is not a symbolic link */
+		errno = EINVAL;
+		return (-1);
+	}
+
+	len = buf->SymbolicLinkReparseBuffer.SubstituteNameLength;
+	if (len <= 0) {
+		free(indata);
+		return (-1);
+	}
+
+	tbuf = malloc(len + 1 * sizeof(wchar_t));
+	if (tbuf == NULL) {
+		free(indata);
+		return (-1);
+	}
+
+	memcpy(tbuf, &((BYTE *)buf->SymbolicLinkReparseBuffer.PathBuffer)
+	    [buf->SymbolicLinkReparseBuffer.SubstituteNameOffset], len);
+	free(indata);
+
+	tbuf[len / sizeof(wchar_t)] = L'\0';
+
+	*linkname = tbuf;
+
+	/*
+	 * Translate backslashes to slashes for libarchive internal use
+	 */
+	while(*tbuf != L'\0') {
+		if (*tbuf == L'\\')
+			*tbuf = L'/';
+		tbuf++;
+	}
+
+	if ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+		*linktype = AE_SYMLINK_TYPE_FILE;
+	else
+		*linktype = AE_SYMLINK_TYPE_DIRECTORY;
+
+	return (0);
+}
+
+/*
+ * Returns AE_SYMLINK_TYPE_FILE, AE_SYMLINK_TYPE_DIRECTORY or -1 on error
+ */
+static int
+la_linkname_from_pathw(const wchar_t *path, wchar_t **outbuf, int *linktype)
+{
+	HANDLE h;
+	const DWORD flag = FILE_FLAG_BACKUP_SEMANTICS |
+	    FILE_FLAG_OPEN_REPARSE_POINT;
+	int ret;
+
+	h = CreateFileW(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag,
+	    NULL);
+	if (h == INVALID_HANDLE_VALUE) {
+		la_dosmaperr(GetLastError());
+		return (-1);
+	}
+
+	ret = la_linkname_from_handle(h, outbuf, linktype);
+	CloseHandle(h);
+
+	return (ret);
+}
+
+static void
+entry_symlink_from_pathw(struct archive_entry *entry, const wchar_t *path)
+{
+	wchar_t *linkname = NULL;
+	int ret, linktype;
+
+	ret = la_linkname_from_pathw(path, &linkname, &linktype);
+	if (ret != 0)
+		return;
+	if (linktype >= 0) {
+		archive_entry_copy_symlink_w(entry, linkname);
+		archive_entry_set_symlink_type(entry, linktype);
+	}
+	free(linkname);
+
+	return;
+}
+
+static struct archive_vtable *
+archive_read_disk_vtable(void)
+{
+	static struct archive_vtable av;
+	static int inited = 0;
+
+	if (!inited) {
+		av.archive_free = _archive_read_free;
+		av.archive_close = _archive_read_close;
+		av.archive_read_data_block = _archive_read_data_block;
+		av.archive_read_next_header = _archive_read_next_header;
+		av.archive_read_next_header2 = _archive_read_next_header2;
+		inited = 1;
+	}
+	return (&av);
+}
+
+const char *
+archive_read_disk_gname(struct archive *_a, la_int64_t gid)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+		ARCHIVE_STATE_ANY, "archive_read_disk_gname"))
+		return (NULL);
+	if (a->lookup_gname == NULL)
+		return (NULL);
+	return ((*a->lookup_gname)(a->lookup_gname_data, gid));
+}
+
+const char *
+archive_read_disk_uname(struct archive *_a, la_int64_t uid)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	if (ARCHIVE_OK != __archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+		ARCHIVE_STATE_ANY, "archive_read_disk_uname"))
+		return (NULL);
+	if (a->lookup_uname == NULL)
+		return (NULL);
+	return ((*a->lookup_uname)(a->lookup_uname_data, uid));
+}
+
+int
+archive_read_disk_set_gname_lookup(struct archive *_a,
+    void *private_data,
+    const char * (*lookup_gname)(void *private, la_int64_t gid),
+    void (*cleanup_gname)(void *private))
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_gname_lookup");
+
+	if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+		(a->cleanup_gname)(a->lookup_gname_data);
+
+	a->lookup_gname = lookup_gname;
+	a->cleanup_gname = cleanup_gname;
+	a->lookup_gname_data = private_data;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_uname_lookup(struct archive *_a,
+    void *private_data,
+    const char * (*lookup_uname)(void *private, int64_t uid),
+    void (*cleanup_uname)(void *private))
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_uname_lookup");
+
+	if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+		(a->cleanup_uname)(a->lookup_uname_data);
+
+	a->lookup_uname = lookup_uname;
+	a->cleanup_uname = cleanup_uname;
+	a->lookup_uname_data = private_data;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Create a new archive_read_disk object and initialize it with global state.
+ */
+struct archive *
+archive_read_disk_new(void)
+{
+	struct archive_read_disk *a;
+
+	a = (struct archive_read_disk *)calloc(1, sizeof(*a));
+	if (a == NULL)
+		return (NULL);
+	a->archive.magic = ARCHIVE_READ_DISK_MAGIC;
+	a->archive.state = ARCHIVE_STATE_NEW;
+	a->archive.vtable = archive_read_disk_vtable();
+	a->entry = archive_entry_new2(&a->archive);
+	a->lookup_uname = trivial_lookup_uname;
+	a->lookup_gname = trivial_lookup_gname;
+	a->flags = ARCHIVE_READDISK_MAC_COPYFILE;
+	return (&a->archive);
+}
+
+static int
+_archive_read_free(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	int r;
+
+	if (_a == NULL)
+		return (ARCHIVE_OK);
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_free");
+
+	if (a->archive.state != ARCHIVE_STATE_CLOSED)
+		r = _archive_read_close(&a->archive);
+	else
+		r = ARCHIVE_OK;
+
+	tree_free(a->tree);
+	if (a->cleanup_gname != NULL && a->lookup_gname_data != NULL)
+		(a->cleanup_gname)(a->lookup_gname_data);
+	if (a->cleanup_uname != NULL && a->lookup_uname_data != NULL)
+		(a->cleanup_uname)(a->lookup_uname_data);
+	archive_string_free(&a->archive.error_string);
+	archive_entry_free(a->entry);
+	a->archive.magic = 0;
+	free(a);
+	return (r);
+}
+
+static int
+_archive_read_close(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_read_close");
+
+	if (a->archive.state != ARCHIVE_STATE_FATAL)
+		a->archive.state = ARCHIVE_STATE_CLOSED;
+
+	tree_close(a->tree);
+
+	return (ARCHIVE_OK);
+}
+
+static void
+setup_symlink_mode(struct archive_read_disk *a, char symlink_mode, 
+    int follow_symlinks)
+{
+	a->symlink_mode = symlink_mode;
+	a->follow_symlinks = follow_symlinks;
+	if (a->tree != NULL) {
+		a->tree->initial_symlink_mode = a->symlink_mode;
+		a->tree->symlink_mode = a->symlink_mode;
+	}
+}
+
+int
+archive_read_disk_set_symlink_logical(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_logical");
+	setup_symlink_mode(a, 'L', 1);
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_physical(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_physical");
+	setup_symlink_mode(a, 'P', 0);
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_symlink_hybrid(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_symlink_hybrid");
+	setup_symlink_mode(a, 'H', 1);/* Follow symlinks initially. */
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_atime_restored(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_restore_atime");
+	a->flags |= ARCHIVE_READDISK_RESTORE_ATIME;
+	if (a->tree != NULL)
+		a->tree->flags |= needsRestoreTimes;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_behavior(struct archive *_a, int flags)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	int r = ARCHIVE_OK;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_honor_nodump");
+
+	a->flags = flags;
+
+	if (flags & ARCHIVE_READDISK_RESTORE_ATIME)
+		r = archive_read_disk_set_atime_restored(_a);
+	else {
+		if (a->tree != NULL)
+			a->tree->flags &= ~needsRestoreTimes;
+	}
+	return (r);
+}
+
+/*
+ * Trivial implementations of gname/uname lookup functions.
+ * These are normally overridden by the client, but these stub
+ * versions ensure that we always have something that works.
+ */
+static const char *
+trivial_lookup_gname(void *private_data, int64_t gid)
+{
+	(void)private_data; /* UNUSED */
+	(void)gid; /* UNUSED */
+	return (NULL);
+}
+
+static const char *
+trivial_lookup_uname(void *private_data, int64_t uid)
+{
+	(void)private_data; /* UNUSED */
+	(void)uid; /* UNUSED */
+	return (NULL);
+}
+
+static int64_t
+align_num_per_sector(struct tree *t, int64_t size)
+{
+	int64_t surplus;
+
+	size += t->current_filesystem->bytesPerSector -1;
+	surplus = size % t->current_filesystem->bytesPerSector;
+	size -= surplus;
+	return (size);
+}
+
+static int
+start_next_async_read(struct archive_read_disk *a, struct tree *t)
+{
+	struct la_overlapped *olp;
+	DWORD buffbytes, rbytes;
+
+	if (t->ol_remaining_bytes == 0)
+		return (ARCHIVE_EOF);
+
+	olp = &(t->ol[t->ol_idx_doing]);
+	t->ol_idx_doing = (t->ol_idx_doing + 1) % MAX_OVERLAPPED;
+
+	/* Allocate read buffer. */
+	if (olp->buff == NULL) {
+		void *p;
+		size_t s = (size_t)align_num_per_sector(t, READ_BUFFER_SIZE);
+		p = VirtualAlloc(NULL, s, MEM_COMMIT, PAGE_READWRITE);
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Couldn't allocate memory");
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		}
+		olp->buff = p;
+		olp->buff_size = s;
+		olp->_a = &a->archive;
+		olp->ol.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
+		if (olp->ol.hEvent == NULL) {
+			la_dosmaperr(GetLastError());
+			archive_set_error(&a->archive, errno,
+			    "CreateEvent failed");
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		}
+	} else
+		ResetEvent(olp->ol.hEvent);
+
+	buffbytes = (DWORD)olp->buff_size;
+	if (buffbytes > t->current_sparse->length)
+		buffbytes = (DWORD)t->current_sparse->length;
+
+	/* Skip hole. */
+	if (t->current_sparse->offset > t->ol_total) {
+		t->ol_remaining_bytes -=
+			t->current_sparse->offset - t->ol_total;
+	}
+
+	olp->offset = t->current_sparse->offset;
+	olp->ol.Offset = (DWORD)(olp->offset & 0xffffffff);
+	olp->ol.OffsetHigh = (DWORD)(olp->offset >> 32);
+
+	if (t->ol_remaining_bytes > buffbytes) {
+		olp->bytes_expected = buffbytes;
+		t->ol_remaining_bytes -= buffbytes;
+	} else {
+		olp->bytes_expected = (size_t)t->ol_remaining_bytes;
+		t->ol_remaining_bytes = 0;
+	}
+	olp->bytes_transferred = 0;
+	t->current_sparse->offset += buffbytes;
+	t->current_sparse->length -= buffbytes;
+	t->ol_total = t->current_sparse->offset;
+	if (t->current_sparse->length == 0 && t->ol_remaining_bytes > 0)
+		t->current_sparse++;
+
+	if (!ReadFile(t->entry_fh, olp->buff, buffbytes, &rbytes, &(olp->ol))) {
+		DWORD lasterr;
+
+		lasterr = GetLastError();
+		if (lasterr == ERROR_HANDLE_EOF) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Reading file truncated");
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		} else if (lasterr != ERROR_IO_PENDING) {
+			if (lasterr == ERROR_NO_DATA)
+				errno = EAGAIN;
+			else if (lasterr == ERROR_ACCESS_DENIED)
+				errno = EBADF;
+			else
+				la_dosmaperr(lasterr);
+			archive_set_error(&a->archive, errno, "Read error");
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		}
+	} else
+		olp->bytes_transferred = rbytes;
+	t->ol_num_doing++;
+
+	return (t->ol_remaining_bytes == 0)? ARCHIVE_EOF: ARCHIVE_OK;
+}
+
+static void
+cancel_async(struct tree *t)
+{
+	if (t->ol_num_doing != t->ol_num_done) {
+		CancelIo(t->entry_fh);
+		t->ol_num_doing = t->ol_num_done = 0;
+	}
+}
+
+static int
+_archive_read_data_block(struct archive *_a, const void **buff,
+    size_t *size, int64_t *offset)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	struct tree *t = a->tree;
+	struct la_overlapped *olp;
+	DWORD bytes_transferred;
+	int r = ARCHIVE_FATAL;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_data_block");
+
+	if (t->entry_eof || t->entry_remaining_bytes <= 0) {
+		r = ARCHIVE_EOF;
+		goto abort_read_data;
+	}
+
+	/*
+	 * Make a request to read the file in asynchronous.
+	 */
+	if (t->ol_num_doing == 0) {
+		do {
+			r = start_next_async_read(a, t);
+			if (r == ARCHIVE_FATAL)
+				goto abort_read_data;
+			if (!t->async_io)
+				break;
+		} while (r == ARCHIVE_OK && t->ol_num_doing < MAX_OVERLAPPED);
+	} else {
+		if ((r = start_next_async_read(a, t)) == ARCHIVE_FATAL)
+			goto abort_read_data;
+	}
+
+	olp = &(t->ol[t->ol_idx_done]);
+	t->ol_idx_done = (t->ol_idx_done + 1) % MAX_OVERLAPPED;
+	if (olp->bytes_transferred)
+		bytes_transferred = (DWORD)olp->bytes_transferred;
+	else if (!GetOverlappedResult(t->entry_fh, &(olp->ol),
+	    &bytes_transferred, TRUE)) {
+		la_dosmaperr(GetLastError());
+		archive_set_error(&a->archive, errno,
+		    "GetOverlappedResult failed");
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		r = ARCHIVE_FATAL;
+		goto abort_read_data;
+	}
+	t->ol_num_done++;
+
+	if (bytes_transferred == 0 ||
+	    olp->bytes_expected != bytes_transferred) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Reading file truncated");
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		r = ARCHIVE_FATAL;
+		goto abort_read_data;
+	}
+
+	*buff = olp->buff;
+	*size = bytes_transferred;
+	*offset = olp->offset;
+	if (olp->offset > t->entry_total)
+		t->entry_remaining_bytes -= olp->offset - t->entry_total;
+	t->entry_total = olp->offset + *size;
+	t->entry_remaining_bytes -= *size;
+	if (t->entry_remaining_bytes == 0) {
+		/* Close the current file descriptor */
+		close_and_restore_time(t->entry_fh, t, &t->restore_time);
+		t->entry_fh = INVALID_HANDLE_VALUE;
+		t->entry_eof = 1;
+	}
+	return (ARCHIVE_OK);
+
+abort_read_data:
+	*buff = NULL;
+	*size = 0;
+	*offset = t->entry_total;
+	if (t->entry_fh != INVALID_HANDLE_VALUE) {
+		cancel_async(t);
+		/* Close the current file descriptor */
+		close_and_restore_time(t->entry_fh, t, &t->restore_time);
+		t->entry_fh = INVALID_HANDLE_VALUE;
+	}
+	return (r);
+}
+
+static int
+next_entry(struct archive_read_disk *a, struct tree *t,
+    struct archive_entry *entry)
+{
+	const BY_HANDLE_FILE_INFORMATION *st;
+	const BY_HANDLE_FILE_INFORMATION *lst;
+	const char*name;
+	int descend, r;
+
+	st = NULL;
+	lst = NULL;
+	t->descend = 0;
+	do {
+		switch (tree_next(t)) {
+		case TREE_ERROR_FATAL:
+			archive_set_error(&a->archive, t->tree_errno,
+			    "%ls: Unable to continue traversing directory tree",
+			    tree_current_path(t));
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		case TREE_ERROR_DIR:
+			archive_set_error(&a->archive, t->tree_errno,
+			    "%ls: Couldn't visit directory",
+			    tree_current_path(t));
+			return (ARCHIVE_FAILED);
+		case 0:
+			return (ARCHIVE_EOF);
+		case TREE_POSTDESCENT:
+		case TREE_POSTASCENT:
+			break;
+		case TREE_REGULAR:
+			lst = tree_current_lstat(t);
+			if (lst == NULL) {
+				archive_set_error(&a->archive, t->tree_errno,
+				    "%ls: Cannot stat",
+				    tree_current_path(t));
+				return (ARCHIVE_FAILED);
+			}
+			break;
+		}	
+	} while (lst == NULL);
+
+	archive_entry_copy_pathname_w(entry, tree_current_path(t));
+
+	/*
+	 * Perform path matching.
+	 */
+	if (a->matching) {
+		r = archive_match_path_excluded(a->matching, entry);
+		if (r < 0) {
+			archive_set_error(&(a->archive), errno,
+			    "Failed : %s", archive_error_string(a->matching));
+			return (r);
+		}
+		if (r) {
+			if (a->excluded_cb_func)
+				a->excluded_cb_func(&(a->archive),
+				    a->excluded_cb_data, entry);
+			return (ARCHIVE_RETRY);
+		}
+	}
+
+	/*
+	 * Distinguish 'L'/'P'/'H' symlink following.
+	 */
+	switch(t->symlink_mode) {
+	case 'H':
+		/* 'H': After the first item, rest like 'P'. */
+		t->symlink_mode = 'P';
+		/* 'H': First item (from command line) like 'L'. */
+		/* FALLTHROUGH */
+	case 'L':
+		/* 'L': Do descend through a symlink to dir. */
+		descend = tree_current_is_dir(t);
+		/* 'L': Follow symlinks to files. */
+		a->symlink_mode = 'L';
+		a->follow_symlinks = 1;
+		/* 'L': Archive symlinks as targets, if we can. */
+		st = tree_current_stat(t);
+		if (st != NULL && !tree_target_is_same_as_parent(t, st))
+			break;
+		/* If stat fails, we have a broken symlink;
+		 * in that case, don't follow the link. */
+		/* FALLTHROUGH */
+	default:
+		/* 'P': Don't descend through a symlink to dir. */
+		descend = tree_current_is_physical_dir(t);
+		/* 'P': Don't follow symlinks to files. */
+		a->symlink_mode = 'P';
+		a->follow_symlinks = 0;
+		/* 'P': Archive symlinks as symlinks. */
+		st = lst;
+		break;
+	}
+
+	if (update_current_filesystem(a, bhfi_dev(st)) != ARCHIVE_OK) {
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+	if (t->initial_filesystem_id == -1)
+		t->initial_filesystem_id = t->current_filesystem_id;
+	if (a->flags & ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS) {
+		if (t->initial_filesystem_id != t->current_filesystem_id)
+			return (ARCHIVE_RETRY);
+	}
+	t->descend = descend;
+
+	tree_archive_entry_copy_bhfi(entry, t, st);
+
+	/* Save the times to be restored. This must be in before
+	 * calling archive_read_disk_descend() or any chance of it,
+	 * especially, invoking a callback. */
+	t->restore_time.lastWriteTime = st->ftLastWriteTime;
+	t->restore_time.lastAccessTime = st->ftLastAccessTime;
+	t->restore_time.filetype = archive_entry_filetype(entry);
+
+	/*
+	 * Perform time matching.
+	 */
+	if (a->matching) {
+		r = archive_match_time_excluded(a->matching, entry);
+		if (r < 0) {
+			archive_set_error(&(a->archive), errno,
+			    "Failed : %s", archive_error_string(a->matching));
+			return (r);
+		}
+		if (r) {
+			if (a->excluded_cb_func)
+				a->excluded_cb_func(&(a->archive),
+				    a->excluded_cb_data, entry);
+			return (ARCHIVE_RETRY);
+		}
+	}
+
+	/* Lookup uname/gname */
+	name = archive_read_disk_uname(&(a->archive), archive_entry_uid(entry));
+	if (name != NULL)
+		archive_entry_copy_uname(entry, name);
+	name = archive_read_disk_gname(&(a->archive), archive_entry_gid(entry));
+	if (name != NULL)
+		archive_entry_copy_gname(entry, name);
+
+	/*
+	 * Perform owner matching.
+	 */
+	if (a->matching) {
+		r = archive_match_owner_excluded(a->matching, entry);
+		if (r < 0) {
+			archive_set_error(&(a->archive), errno,
+			    "Failed : %s", archive_error_string(a->matching));
+			return (r);
+		}
+		if (r) {
+			if (a->excluded_cb_func)
+				a->excluded_cb_func(&(a->archive),
+				    a->excluded_cb_data, entry);
+			return (ARCHIVE_RETRY);
+		}
+	}
+
+	/*
+	 * File attributes
+	 */
+	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
+		const int supported_attrs =
+		    FILE_ATTRIBUTE_READONLY |
+		    FILE_ATTRIBUTE_HIDDEN |
+		    FILE_ATTRIBUTE_SYSTEM;
+		DWORD file_attrs = st->dwFileAttributes & supported_attrs;
+		if (file_attrs != 0)
+			archive_entry_set_fflags(entry, file_attrs, 0);
+	}
+
+	/*
+	 * Invoke a meta data filter callback.
+	 */
+	if (a->metadata_filter_func) {
+		if (!a->metadata_filter_func(&(a->archive),
+		    a->metadata_filter_data, entry))
+			return (ARCHIVE_RETRY);
+	}
+
+	archive_entry_copy_sourcepath_w(entry, tree_current_access_path(t));
+
+	r = ARCHIVE_OK;
+	if (archive_entry_filetype(entry) == AE_IFREG &&
+	    archive_entry_size(entry) > 0) {
+		DWORD flags = FILE_FLAG_BACKUP_SEMANTICS;
+		if (t->async_io)
+			flags |= FILE_FLAG_OVERLAPPED;
+		if (t->direct_io)
+			flags |= FILE_FLAG_NO_BUFFERING;
+		else
+			flags |= FILE_FLAG_SEQUENTIAL_SCAN;
+		t->entry_fh = CreateFileW(tree_current_access_path(t),
+		    GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, flags, NULL);
+		if (t->entry_fh == INVALID_HANDLE_VALUE) {
+			la_dosmaperr(GetLastError());
+			archive_set_error(&a->archive, errno,
+			    "Couldn't open %ls", tree_current_path(a->tree));
+			return (ARCHIVE_FAILED);
+		}
+
+		/* Find sparse data from the disk. */
+		if (archive_entry_hardlink(entry) == NULL &&
+		    (st->dwFileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) != 0)
+			r = setup_sparse_from_disk(a, entry, t->entry_fh);
+	}
+	return (r);
+}
+
+static int
+_archive_read_next_header(struct archive *_a, struct archive_entry **entryp)
+{
+       int ret;
+       struct archive_read_disk *a = (struct archive_read_disk *)_a;
+       *entryp = NULL;
+       ret = _archive_read_next_header2(_a, a->entry);
+       *entryp = a->entry;
+       return ret;
+}
+
+static int
+_archive_read_next_header2(struct archive *_a, struct archive_entry *entry)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	struct tree *t;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_read_next_header2");
+
+	t = a->tree;
+	if (t->entry_fh != INVALID_HANDLE_VALUE) {
+		cancel_async(t);
+		close_and_restore_time(t->entry_fh, t, &t->restore_time);
+		t->entry_fh = INVALID_HANDLE_VALUE;
+	}
+
+	archive_entry_clear(entry);
+
+	while ((r = next_entry(a, t, entry)) == ARCHIVE_RETRY)
+		archive_entry_clear(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 (r) {
+	case ARCHIVE_EOF:
+		a->archive.state = ARCHIVE_STATE_EOF;
+		break;
+	case ARCHIVE_OK:
+	case ARCHIVE_WARN:
+		t->entry_total = 0;
+		if (archive_entry_filetype(entry) == AE_IFREG) {
+			t->entry_remaining_bytes = archive_entry_size(entry);
+			t->entry_eof = (t->entry_remaining_bytes == 0)? 1: 0;
+			if (!t->entry_eof &&
+			    setup_sparse(a, entry) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		} else {
+			t->entry_remaining_bytes = 0;
+			t->entry_eof = 1;
+		}
+		t->ol_idx_doing = t->ol_idx_done = 0;
+		t->ol_num_doing = t->ol_num_done = 0;
+		t->ol_remaining_bytes = t->entry_remaining_bytes;
+		t->ol_total = 0;
+		a->archive.state = ARCHIVE_STATE_DATA;
+		break;
+	case ARCHIVE_RETRY:
+		break;
+	case ARCHIVE_FATAL:
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		break;
+	}
+
+	__archive_reset_read_data(&a->archive);
+	return (r);
+}
+
+static int
+setup_sparse(struct archive_read_disk *a, struct archive_entry *entry)
+{
+	struct tree *t = a->tree;
+	int64_t aligned, length, offset;
+	int i;
+
+	t->sparse_count = archive_entry_sparse_reset(entry);
+	if (t->sparse_count+1 > t->sparse_list_size) {
+		free(t->sparse_list);
+		t->sparse_list_size = t->sparse_count + 1;
+		t->sparse_list = malloc(sizeof(t->sparse_list[0]) *
+		    t->sparse_list_size);
+		if (t->sparse_list == NULL) {
+			t->sparse_list_size = 0;
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate data");
+			a->archive.state = ARCHIVE_STATE_FATAL;
+			return (ARCHIVE_FATAL);
+		}
+	}
+	/*
+	 * Get sparse list and make sure those offsets and lengths are
+	 * aligned by a sector size.
+	 */
+	for (i = 0; i < t->sparse_count; i++) {
+		archive_entry_sparse_next(entry, &offset, &length);
+		aligned = align_num_per_sector(t, offset);
+		if (aligned != offset) {
+			aligned -= t->current_filesystem->bytesPerSector;
+			length += offset - aligned;
+		}
+		t->sparse_list[i].offset = aligned;
+		aligned = align_num_per_sector(t, length);
+		t->sparse_list[i].length = aligned;
+	}
+
+	aligned = align_num_per_sector(t, archive_entry_size(entry));
+	if (i == 0) {
+		t->sparse_list[i].offset = 0;
+		t->sparse_list[i].length = aligned;
+	} else {
+		int j, last = i;
+
+		t->sparse_list[i].offset = aligned;
+		t->sparse_list[i].length = 0;
+		for (i = 0; i < last; i++) {
+			if ((t->sparse_list[i].offset +
+			       t->sparse_list[i].length) <= 
+					t->sparse_list[i+1].offset)
+				continue;
+			/*
+			 * Now sparse_list[i+1] is overlapped by sparse_list[i].
+			 * Merge those two.
+			 */
+			length = t->sparse_list[i+1].offset -
+					t->sparse_list[i].offset;
+			t->sparse_list[i+1].offset = t->sparse_list[i].offset;
+			t->sparse_list[i+1].length += length;
+			/* Remove sparse_list[i]. */
+			for (j = i; j < last; j++) {
+				t->sparse_list[j].offset =
+				    t->sparse_list[j+1].offset;
+				t->sparse_list[j].length =
+				    t->sparse_list[j+1].length;
+			}
+			last--;
+		}
+	}
+	t->current_sparse = t->sparse_list;
+
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_matching(struct archive *_a, struct archive *_ma,
+    void (*_excluded_func)(struct archive *, void *, struct archive_entry *),
+    void *_client_data)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_read_disk_set_matching");
+	a->matching = _ma;
+	a->excluded_cb_func = _excluded_func;
+	a->excluded_cb_data = _client_data;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_set_metadata_filter_callback(struct archive *_a,
+    int (*_metadata_filter_func)(struct archive *, void *,
+    struct archive_entry *), void *_client_data)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_ANY,
+	    "archive_read_disk_set_metadata_filter_callback");
+
+	a->metadata_filter_func = _metadata_filter_func;
+	a->metadata_filter_data = _client_data;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_can_descend(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	struct tree *t = a->tree;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_read_disk_can_descend");
+
+	return (t->visit_type == TREE_REGULAR && t->descend);
+}
+
+/*
+ * Called by the client to mark the directory just returned from
+ * tree_next() as needing to be visited.
+ */
+int
+archive_read_disk_descend(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	struct tree *t = a->tree;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_read_disk_descend");
+
+	if (t->visit_type != TREE_REGULAR || !t->descend)
+		return (ARCHIVE_OK);
+
+	if (tree_current_is_physical_dir(t)) {
+		tree_push(t, t->basename, t->full_path.s,
+		    t->current_filesystem_id,
+		    bhfi_dev(&(t->lst)), bhfi_ino(&(t->lst)),
+		    &t->restore_time);
+		t->stack->flags |= isDir;
+	} else if (tree_current_is_dir(t)) {
+		tree_push(t, t->basename, t->full_path.s,
+		    t->current_filesystem_id,
+		    bhfi_dev(&(t->st)), bhfi_ino(&(t->st)),
+		    &t->restore_time);
+		t->stack->flags |= isDirLink;
+	}
+	t->descend = 0;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_disk_open(struct archive *_a, const char *pathname)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	struct archive_wstring wpath;
+	int ret;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+	    "archive_read_disk_open");
+	archive_clear_error(&a->archive);
+
+	/* Make a wchar_t string from a char string. */
+	archive_string_init(&wpath);
+	if (archive_wstring_append_from_mbs(&wpath, pathname,
+	    strlen(pathname)) != 0) {
+		if (errno == ENOMEM)
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+		else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Can't convert a path to a wchar_t string");
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		ret = ARCHIVE_FATAL;
+	} else
+		ret = _archive_read_disk_open_w(_a, wpath.s);
+
+	archive_wstring_free(&wpath);
+	return (ret);
+}
+
+int
+archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC,
+	    ARCHIVE_STATE_NEW | ARCHIVE_STATE_CLOSED,
+	    "archive_read_disk_open_w");
+	archive_clear_error(&a->archive);
+
+	return (_archive_read_disk_open_w(_a, pathname));
+}
+
+static int
+_archive_read_disk_open_w(struct archive *_a, const wchar_t *pathname)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	if (a->tree != NULL)
+		a->tree = tree_reopen(a->tree, pathname,
+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+	else
+		a->tree = tree_open(pathname, a->symlink_mode,
+		    a->flags & ARCHIVE_READDISK_RESTORE_ATIME);
+	if (a->tree == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate directory traversal data");
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+	a->archive.state = ARCHIVE_STATE_HEADER;
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Return a current filesystem ID which is index of the filesystem entry
+ * you've visited through archive_read_disk.
+ */
+int
+archive_read_disk_current_filesystem(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_disk_current_filesystem");
+
+	return (a->tree->current_filesystem_id);
+}
+
+static int
+update_current_filesystem(struct archive_read_disk *a, int64_t dev)
+{
+	struct tree *t = a->tree;
+	int i, fid;
+
+	if (t->current_filesystem != NULL &&
+	    t->current_filesystem->dev == dev)
+		return (ARCHIVE_OK);
+
+	for (i = 0; i < t->max_filesystem_id; i++) {
+		if (t->filesystem_table[i].dev == dev) {
+			/* There is the filesystem ID we've already generated. */
+			t->current_filesystem_id = i;
+			t->current_filesystem = &(t->filesystem_table[i]);
+			return (ARCHIVE_OK);
+		}
+	}
+
+	/*
+	 * There is a new filesystem, we generate a new ID for.
+	 */
+	fid = t->max_filesystem_id++;
+	if (t->max_filesystem_id > t->allocated_filesystem) {
+		size_t s;
+		void *p;
+
+		s = t->max_filesystem_id * 2;
+		p = realloc(t->filesystem_table,
+			s * sizeof(*t->filesystem_table));
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate tar data");
+			return (ARCHIVE_FATAL);
+		}
+		t->filesystem_table = (struct filesystem *)p;
+		t->allocated_filesystem = (int)s;
+	}
+	t->current_filesystem_id = fid;
+	t->current_filesystem = &(t->filesystem_table[fid]);
+	t->current_filesystem->dev = dev;
+
+	return (setup_current_filesystem(a));
+}
+
+/*
+ * Returns 1 if current filesystem is generated filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_synthetic(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_disk_current_filesystem");
+
+	return (a->tree->current_filesystem->synthetic);
+}
+
+/*
+ * Returns 1 if current filesystem is remote filesystem, 0 if it is not
+ * or -1 if it is unknown.
+ */
+int
+archive_read_disk_current_filesystem_is_remote(struct archive *_a)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+
+	archive_check_magic(_a, ARCHIVE_READ_DISK_MAGIC, ARCHIVE_STATE_DATA,
+	    "archive_read_disk_current_filesystem");
+
+	return (a->tree->current_filesystem->remote);
+}
+
+/*
+ * If symlink is broken, statfs or statvfs will fail.
+ * Use its directory path instead.
+ */
+static wchar_t *
+safe_path_for_statfs(struct tree *t)
+{
+	const wchar_t *path;
+	wchar_t *cp, *p = NULL;
+
+	path = tree_current_access_path(t);
+	if (tree_current_stat(t) == NULL) {
+		p = _wcsdup(path);
+		cp = wcsrchr(p, '/');
+		if (cp != NULL && wcslen(cp) >= 2) {
+			cp[1] = '.';
+			cp[2] = '\0';
+			path = p;
+		}
+	} else
+		p = _wcsdup(path);
+	return (p);
+}
+
+/*
+ * Get conditions of synthetic and remote on Windows
+ */
+static int
+setup_current_filesystem(struct archive_read_disk *a)
+{
+	struct tree *t = a->tree;
+	wchar_t vol[256];
+	wchar_t *path;
+
+	t->current_filesystem->synthetic = -1;/* Not supported */
+	path = safe_path_for_statfs(t);
+	if (!GetVolumePathNameW(path, vol, sizeof(vol)/sizeof(vol[0]))) {
+		free(path);
+		t->current_filesystem->remote = -1;
+		t->current_filesystem->bytesPerSector = 0;
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                        "GetVolumePathName failed: %d", (int)GetLastError());
+		return (ARCHIVE_FAILED);
+	}
+	free(path);
+	switch (GetDriveTypeW(vol)) {
+	case DRIVE_UNKNOWN:
+	case DRIVE_NO_ROOT_DIR:
+		t->current_filesystem->remote = -1;
+		break;
+	case DRIVE_REMOTE:
+		t->current_filesystem->remote = 1;
+		break;
+	default:
+		t->current_filesystem->remote = 0;
+		break;
+	}
+
+	if (!GetDiskFreeSpaceW(vol, NULL,
+	    &(t->current_filesystem->bytesPerSector), NULL, NULL)) {
+		t->current_filesystem->bytesPerSector = 0;
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                        "GetDiskFreeSpace failed: %d", (int)GetLastError());
+		return (ARCHIVE_FAILED);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
+close_and_restore_time(HANDLE h, struct tree *t, struct restore_time *rt)
+{
+	HANDLE handle;
+	int r = 0;
+
+	if (h == INVALID_HANDLE_VALUE && AE_IFLNK == rt->filetype)
+		return (0);
+
+	/* Close a file descriptor.
+	 * It will not be used for SetFileTime() because it has been opened
+	 * by a read only mode.
+	 */
+	if (h != INVALID_HANDLE_VALUE)
+		CloseHandle(h);
+	if ((t->flags & needsRestoreTimes) == 0)
+		return (r);
+
+	handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES,
+		    0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+	if (handle == INVALID_HANDLE_VALUE) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	if (SetFileTime(handle, NULL, &rt->lastAccessTime,
+	    &rt->lastWriteTime) == 0) {
+		errno = EINVAL;
+		r = -1;
+	} else
+		r = 0;
+	CloseHandle(handle);
+	return (r);
+}
+
+/*
+ * Add a directory path to the current stack.
+ */
+static void
+tree_push(struct tree *t, const wchar_t *path, const wchar_t *full_path,
+    int filesystem_id, int64_t dev, int64_t ino, struct restore_time *rt)
+{
+	struct tree_entry *te;
+
+	te = calloc(1, sizeof(*te));
+	te->next = t->stack;
+	te->parent = t->current;
+	if (te->parent)
+		te->depth = te->parent->depth + 1;
+	t->stack = te;
+	archive_string_init(&te->name);
+	archive_wstrcpy(&te->name, path);
+	archive_string_init(&te->full_path);
+	archive_wstrcpy(&te->full_path, full_path);
+	te->flags = needsDescent | needsOpen | needsAscent;
+	te->filesystem_id = filesystem_id;
+	te->dev = dev;
+	te->ino = ino;
+	te->dirname_length = t->dirname_length;
+	te->full_path_dir_length = t->full_path_dir_length;
+	te->restore_time.full_path = te->full_path.s;
+	if (rt != NULL) {
+		te->restore_time.lastWriteTime = rt->lastWriteTime;
+		te->restore_time.lastAccessTime = rt->lastAccessTime;
+		te->restore_time.filetype = rt->filetype;
+	}
+}
+
+/*
+ * Append a name to the current dir path.
+ */
+static void
+tree_append(struct tree *t, const wchar_t *name, size_t name_length)
+{
+	size_t size_needed;
+
+	t->path.s[t->dirname_length] = L'\0';
+	t->path.length = t->dirname_length;
+	/* Strip trailing '/' from name, unless entire name is "/". */
+	while (name_length > 1 && name[name_length - 1] == L'/')
+		name_length--;
+
+	/* Resize pathname buffer as needed. */
+	size_needed = name_length + t->dirname_length + 2;
+	archive_wstring_ensure(&t->path, size_needed);
+	/* Add a separating '/' if it's needed. */
+	if (t->dirname_length > 0 &&
+	    t->path.s[archive_strlen(&t->path)-1] != L'/')
+		archive_wstrappend_wchar(&t->path, L'/');
+	t->basename = t->path.s + archive_strlen(&t->path);
+	archive_wstrncat(&t->path, name, name_length);
+	t->restore_time.full_path = t->basename;
+	if (t->full_path_dir_length > 0) {
+		t->full_path.s[t->full_path_dir_length] = L'\0';
+		t->full_path.length = t->full_path_dir_length;
+		size_needed = name_length + t->full_path_dir_length + 2;
+		archive_wstring_ensure(&t->full_path, size_needed);
+		/* Add a separating '\' if it's needed. */
+		if (t->full_path.s[archive_strlen(&t->full_path)-1] != L'\\')
+			archive_wstrappend_wchar(&t->full_path, L'\\');
+		archive_wstrncat(&t->full_path, name, name_length);
+		t->restore_time.full_path = t->full_path.s;
+	}
+}
+
+/*
+ * Open a directory tree for traversal.
+ */
+static struct tree *
+tree_open(const wchar_t *path, int symlink_mode, int restore_time)
+{
+	struct tree *t;
+
+	t = calloc(1, sizeof(*t));
+	archive_string_init(&(t->full_path));
+	archive_string_init(&t->path);
+	archive_wstring_ensure(&t->path, 15);
+	t->initial_symlink_mode = symlink_mode;
+	return (tree_reopen(t, path, restore_time));
+}
+
+static struct tree *
+tree_reopen(struct tree *t, const wchar_t *path, int restore_time)
+{
+	struct archive_wstring ws;
+	wchar_t *pathname, *p, *base;
+
+	t->flags = (restore_time != 0)?needsRestoreTimes:0;
+	t->visit_type = 0;
+	t->tree_errno = 0;
+	t->full_path_dir_length = 0;
+	t->dirname_length = 0;
+	t->depth = 0;
+	t->descend = 0;
+	t->current = NULL;
+	t->d = INVALID_HANDLE_VALUE;
+	t->symlink_mode = t->initial_symlink_mode;
+	archive_string_empty(&(t->full_path));
+	archive_string_empty(&t->path);
+	t->entry_fh = INVALID_HANDLE_VALUE;
+	t->entry_eof = 0;
+	t->entry_remaining_bytes = 0;
+	t->initial_filesystem_id = -1;
+
+	/* Get wchar_t strings from char strings. */
+	archive_string_init(&ws);
+	archive_wstrcpy(&ws, path);
+	pathname = ws.s;
+	/* Get a full-path-name. */
+	p = __la_win_permissive_name_w(pathname);
+	if (p == NULL)
+		goto failed;
+	archive_wstrcpy(&(t->full_path), p);
+	free(p);
+
+	/* Convert path separators from '\' to '/' */
+	for (p = pathname; *p != L'\0'; ++p) {
+		if (*p == L'\\')
+			*p = L'/';
+	}
+	base = pathname;
+
+	/* First item is set up a lot like a symlink traversal. */
+	/* printf("Looking for wildcard in %s\n", path); */
+	if ((base[0] == L'/' && base[1] == L'/' &&
+	     base[2] == L'?' && base[3] == L'/' &&
+	     (wcschr(base+4, L'*') || wcschr(base+4, L'?'))) ||
+	    (!(base[0] == L'/' && base[1] == L'/' &&
+	       base[2] == L'?' && base[3] == L'/') &&
+	       (wcschr(base, L'*') || wcschr(base, L'?')))) {
+		// It has a wildcard in it...
+		// Separate the last element.
+		p = wcsrchr(base, L'/');
+		if (p != NULL) {
+			*p = L'\0';
+			tree_append(t, base, p - base);
+			t->dirname_length = archive_strlen(&t->path);
+			base = p + 1;
+		}
+		p = wcsrchr(t->full_path.s, L'\\');
+		if (p != NULL) {
+			*p = L'\0';
+			t->full_path.length = wcslen(t->full_path.s);
+			t->full_path_dir_length = archive_strlen(&t->full_path);
+		}
+	}
+	tree_push(t, base, t->full_path.s, 0, 0, 0, NULL);
+	archive_wstring_free(&ws);
+	t->stack->flags = needsFirstVisit;
+	/*
+	 * Debug flag for Direct IO(No buffering) or Async IO.
+	 * Those dependent on environment variable switches
+	 * will be removed until next release.
+	 */
+	{
+		const char *e;
+		if ((e = getenv("LIBARCHIVE_DIRECT_IO")) != NULL) {
+			if (e[0] == '0')
+				t->direct_io = 0;
+			else
+				t->direct_io = 1;
+			fprintf(stderr, "LIBARCHIVE_DIRECT_IO=%s\n",
+				(t->direct_io)?"Enabled":"Disabled");
+		} else
+			t->direct_io = DIRECT_IO;
+		if ((e = getenv("LIBARCHIVE_ASYNC_IO")) != NULL) {
+			if (e[0] == '0')
+				t->async_io = 0;
+			else
+				t->async_io = 1;
+			fprintf(stderr, "LIBARCHIVE_ASYNC_IO=%s\n",
+			    (t->async_io)?"Enabled":"Disabled");
+		} else
+			t->async_io = ASYNC_IO;
+	}
+	return (t);
+failed:
+	archive_wstring_free(&ws);
+	tree_free(t);
+	return (NULL);
+}
+
+static int
+tree_descent(struct tree *t)
+{
+	t->dirname_length = archive_strlen(&t->path);
+	t->full_path_dir_length = archive_strlen(&t->full_path);
+	t->depth++;
+	return (0);
+}
+
+/*
+ * We've finished a directory; ascend back to the parent.
+ */
+static int
+tree_ascend(struct tree *t)
+{
+	struct tree_entry *te;
+
+	te = t->stack;
+	t->depth--;
+	close_and_restore_time(INVALID_HANDLE_VALUE, t, &te->restore_time);
+	return (0);
+}
+
+/*
+ * Pop the working stack.
+ */
+static void
+tree_pop(struct tree *t)
+{
+	struct tree_entry *te;
+
+	t->full_path.s[t->full_path_dir_length] = L'\0';
+	t->full_path.length = t->full_path_dir_length;
+	t->path.s[t->dirname_length] = L'\0';
+	t->path.length = t->dirname_length;
+	if (t->stack == t->current && t->current != NULL)
+		t->current = t->current->parent;
+	te = t->stack;
+	t->stack = te->next;
+	t->dirname_length = te->dirname_length;
+	t->basename = t->path.s + t->dirname_length;
+	t->full_path_dir_length = te->full_path_dir_length;
+	while (t->basename[0] == L'/')
+		t->basename++;
+	archive_wstring_free(&te->name);
+	archive_wstring_free(&te->full_path);
+	free(te);
+}
+
+/*
+ * Get the next item in the tree traversal.
+ */
+static int
+tree_next(struct tree *t)
+{
+	int r;
+
+	while (t->stack != NULL) {
+		/* If there's an open dir, get the next entry from there. */
+		if (t->d != INVALID_HANDLE_VALUE) {
+			r = tree_dir_next_windows(t, NULL);
+			if (r == 0)
+				continue;
+			return (r);
+		}
+
+		if (t->stack->flags & needsFirstVisit) {
+			wchar_t *d = t->stack->name.s;
+			t->stack->flags &= ~needsFirstVisit;
+			if (!(d[0] == L'/' && d[1] == L'/' &&
+			      d[2] == L'?' && d[3] == L'/') &&
+			    (wcschr(d, L'*') || wcschr(d, L'?'))) {
+				r = tree_dir_next_windows(t, d);
+				if (r == 0)
+					continue;
+				return (r);
+			} else {
+				HANDLE h = FindFirstFileW(d, &t->_findData);
+				if (h == INVALID_HANDLE_VALUE) {
+					la_dosmaperr(GetLastError());
+					t->tree_errno = errno;
+					t->visit_type = TREE_ERROR_DIR;
+					return (t->visit_type);
+				}
+				t->findData = &t->_findData;
+				FindClose(h);
+			}
+			/* Top stack item needs a regular visit. */
+			t->current = t->stack;
+			tree_append(t, t->stack->name.s,
+			    archive_strlen(&(t->stack->name)));
+			//t->dirname_length = t->path_length;
+			//tree_pop(t);
+			t->stack->flags &= ~needsFirstVisit;
+			return (t->visit_type = TREE_REGULAR);
+		} else if (t->stack->flags & needsDescent) {
+			/* Top stack item is dir to descend into. */
+			t->current = t->stack;
+			tree_append(t, t->stack->name.s,
+			    archive_strlen(&(t->stack->name)));
+			t->stack->flags &= ~needsDescent;
+			r = tree_descent(t);
+			if (r != 0) {
+				tree_pop(t);
+				t->visit_type = r;
+			} else
+				t->visit_type = TREE_POSTDESCENT;
+			return (t->visit_type);
+		} else if (t->stack->flags & needsOpen) {
+			t->stack->flags &= ~needsOpen;
+			r = tree_dir_next_windows(t, L"*");
+			if (r == 0)
+				continue;
+			return (r);
+		} else if (t->stack->flags & needsAscent) {
+		        /* Top stack item is dir and we're done with it. */
+			r = tree_ascend(t);
+			tree_pop(t);
+			t->visit_type = r != 0 ? r : TREE_POSTASCENT;
+			return (t->visit_type);
+		} else {
+			/* Top item on stack is dead. */
+			tree_pop(t);
+			t->flags &= ~hasLstat;
+			t->flags &= ~hasStat;
+		}
+	}
+	return (t->visit_type = 0);
+}
+
+static int
+tree_dir_next_windows(struct tree *t, const wchar_t *pattern)
+{
+	const wchar_t *name;
+	size_t namelen;
+	int r;
+
+	for (;;) {
+		if (pattern != NULL) {
+			struct archive_wstring pt;
+
+			archive_string_init(&pt);
+			archive_wstring_ensure(&pt,
+			    archive_strlen(&(t->full_path))
+			      + 2 + wcslen(pattern));
+			archive_wstring_copy(&pt, &(t->full_path));
+			archive_wstrappend_wchar(&pt, L'\\');
+			archive_wstrcat(&pt, pattern);
+			t->d = FindFirstFileW(pt.s, &t->_findData);
+			archive_wstring_free(&pt);
+			if (t->d == INVALID_HANDLE_VALUE) {
+				la_dosmaperr(GetLastError());
+				t->tree_errno = errno;
+				r = tree_ascend(t); /* Undo "chdir" */
+				tree_pop(t);
+				t->visit_type = r != 0 ? r : TREE_ERROR_DIR;
+				return (t->visit_type);
+			}
+			t->findData = &t->_findData;
+			pattern = NULL;
+		} else if (!FindNextFileW(t->d, &t->_findData)) {
+			FindClose(t->d);
+			t->d = INVALID_HANDLE_VALUE;
+			t->findData = NULL;
+			return (0);
+		}
+		name = t->findData->cFileName;
+		namelen = wcslen(name);
+		t->flags &= ~hasLstat;
+		t->flags &= ~hasStat;
+		if (name[0] == L'.' && name[1] == L'\0')
+			continue;
+		if (name[0] == L'.' && name[1] == L'.' && name[2] == L'\0')
+			continue;
+		tree_append(t, name, namelen);
+		return (t->visit_type = TREE_REGULAR);
+	}
+}
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static void
+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)
+{
+	ULARGE_INTEGER utc;
+
+	utc.HighPart = filetime->dwHighDateTime;
+	utc.LowPart  = filetime->dwLowDateTime;
+	if (utc.QuadPart >= EPOC_TIME) {
+		utc.QuadPart -= EPOC_TIME;
+		/* milli seconds base */
+		*t = (time_t)(utc.QuadPart / 10000000);
+		/* nano seconds base */
+		*ns = (long)(utc.QuadPart % 10000000) * 100;
+	} else {
+		*t = 0;
+		*ns = 0;
+	}
+}
+
+static void
+entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path,
+	const WIN32_FIND_DATAW *findData,
+	const BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+	time_t secs;
+	long nsecs;
+	mode_t mode;
+
+	fileTimeToUtc(&bhfi->ftLastAccessTime, &secs, &nsecs);
+	archive_entry_set_atime(entry, secs, nsecs);
+	fileTimeToUtc(&bhfi->ftLastWriteTime, &secs, &nsecs);
+	archive_entry_set_mtime(entry, secs, nsecs);
+	fileTimeToUtc(&bhfi->ftCreationTime, &secs, &nsecs);
+	archive_entry_set_birthtime(entry, secs, nsecs);
+	archive_entry_set_ctime(entry, secs, nsecs);
+	archive_entry_set_dev(entry, bhfi_dev(bhfi));
+	archive_entry_set_ino64(entry, bhfi_ino(bhfi));
+	if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+		archive_entry_set_nlink(entry, bhfi->nNumberOfLinks + 1);
+	else
+		archive_entry_set_nlink(entry, bhfi->nNumberOfLinks);
+	archive_entry_set_size(entry,
+	    (((int64_t)bhfi->nFileSizeHigh) << 32)
+	    + bhfi->nFileSizeLow);
+	archive_entry_set_uid(entry, 0);
+	archive_entry_set_gid(entry, 0);
+	archive_entry_set_rdev(entry, 0);
+
+	mode = S_IRUSR | S_IRGRP | S_IROTH;
+	if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+		mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+	if ((bhfi->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+	    findData != NULL &&
+	    findData->dwReserved0 == IO_REPARSE_TAG_SYMLINK) {
+		mode |= S_IFLNK;
+		entry_symlink_from_pathw(entry, path);
+	} else if (bhfi->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+		mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+	else {
+		const wchar_t *p;
+
+		mode |= S_IFREG;
+		p = wcsrchr(path, L'.');
+		if (p != NULL && wcslen(p) == 4) {
+			switch (p[1]) {
+			case L'B': case L'b':
+				if ((p[2] == L'A' || p[2] == L'a' ) &&
+				    (p[3] == L'T' || p[3] == L't' ))
+					mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+				break;
+			case L'C': case L'c':
+				if (((p[2] == L'M' || p[2] == L'm' ) &&
+				    (p[3] == L'D' || p[3] == L'd' )))
+					mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+				break;
+			case L'E': case L'e':
+				if ((p[2] == L'X' || p[2] == L'x' ) &&
+				    (p[3] == L'E' || p[3] == L'e' ))
+					mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+	archive_entry_set_mode(entry, mode);
+}
+
+static void
+tree_archive_entry_copy_bhfi(struct archive_entry *entry, struct tree *t,
+	const BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+	entry_copy_bhfi(entry, tree_current_path(t), t->findData, bhfi);
+}
+
+static int
+tree_current_file_information(struct tree *t, BY_HANDLE_FILE_INFORMATION *st,
+ int sim_lstat)
+{
+	HANDLE h;
+	int r;
+	DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
+	
+	if (sim_lstat && tree_current_is_physical_link(t))
+		flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+	h = CreateFileW(tree_current_access_path(t), 0, FILE_SHARE_READ, NULL,
+	    OPEN_EXISTING, flag, NULL);
+	if (h == INVALID_HANDLE_VALUE) {
+		la_dosmaperr(GetLastError());
+		t->tree_errno = errno;
+		return (0);
+	}
+	r = GetFileInformationByHandle(h, st);
+	CloseHandle(h);
+	return (r);
+}
+
+/*
+ * Get the stat() data for the entry just returned from tree_next().
+ */
+static const BY_HANDLE_FILE_INFORMATION *
+tree_current_stat(struct tree *t)
+{
+	if (!(t->flags & hasStat)) {
+		if (!tree_current_file_information(t, &t->st, 0))
+			return NULL;
+		t->flags |= hasStat;
+	}
+	return (&t->st);
+}
+
+/*
+ * Get the lstat() data for the entry just returned from tree_next().
+ */
+static const BY_HANDLE_FILE_INFORMATION *
+tree_current_lstat(struct tree *t)
+{
+	if (!(t->flags & hasLstat)) {
+		if (!tree_current_file_information(t, &t->lst, 1))
+			return NULL;
+		t->flags |= hasLstat;
+	}
+	return (&t->lst);
+}
+
+/*
+ * Test whether current entry is a dir or link to a dir.
+ */
+static int
+tree_current_is_dir(struct tree *t)
+{
+	if (t->findData)
+		return (t->findData->dwFileAttributes
+		    & FILE_ATTRIBUTE_DIRECTORY);
+	return (0);
+}
+
+/*
+ * Test whether current entry is a physical directory.  Usually, we
+ * already have at least one of stat() or lstat() in memory, so we
+ * use tricks to try to avoid an extra trip to the disk.
+ */
+static int
+tree_current_is_physical_dir(struct tree *t)
+{
+	if (tree_current_is_physical_link(t))
+		return (0);
+	return (tree_current_is_dir(t));
+}
+
+/*
+ * Test whether current entry is a symbolic link.
+ */
+static int
+tree_current_is_physical_link(struct tree *t)
+{
+	if (t->findData)
+		return ((t->findData->dwFileAttributes
+			        & FILE_ATTRIBUTE_REPARSE_POINT) &&
+			(t->findData->dwReserved0
+			    == IO_REPARSE_TAG_SYMLINK));
+	return (0);
+}
+
+/*
+ * Test whether the same file has been in the tree as its parent.
+ */
+static int
+tree_target_is_same_as_parent(struct tree *t,
+    const BY_HANDLE_FILE_INFORMATION *st)
+{
+	struct tree_entry *te;
+	int64_t dev = bhfi_dev(st);
+	int64_t ino = bhfi_ino(st);
+
+	for (te = t->current->parent; te != NULL; te = te->parent) {
+		if (te->dev == dev && te->ino == ino)
+			return (1);
+	}
+	return (0);
+}
+
+/*
+ * Return the access path for the entry just returned from tree_next().
+ */
+static const wchar_t *
+tree_current_access_path(struct tree *t)
+{
+	return (t->full_path.s);
+}
+
+/*
+ * Return the full path for the entry just returned from tree_next().
+ */
+static const wchar_t *
+tree_current_path(struct tree *t)
+{
+	return (t->path.s);
+}
+
+/*
+ * Terminate the traversal.
+ */
+static void
+tree_close(struct tree *t)
+{
+
+	if (t == NULL)
+		return;
+	if (t->entry_fh != INVALID_HANDLE_VALUE) {
+		cancel_async(t);
+		close_and_restore_time(t->entry_fh, t, &t->restore_time);
+		t->entry_fh = INVALID_HANDLE_VALUE;
+	}
+	/* Close the handle of FindFirstFileW */
+	if (t->d != INVALID_HANDLE_VALUE) {
+		FindClose(t->d);
+		t->d = INVALID_HANDLE_VALUE;
+		t->findData = NULL;
+	}
+	/* Release anything remaining in the stack. */
+	while (t->stack != NULL)
+		tree_pop(t);
+}
+
+/*
+ * Release any resources.
+ */
+static void
+tree_free(struct tree *t)
+{
+	int i;
+
+	if (t == NULL)
+		return;
+	archive_wstring_free(&t->path);
+	archive_wstring_free(&t->full_path);
+	free(t->sparse_list);
+	free(t->filesystem_table);
+	for (i = 0; i < MAX_OVERLAPPED; i++) {
+		if (t->ol[i].buff)
+			VirtualFree(t->ol[i].buff, 0, MEM_RELEASE);
+		CloseHandle(t->ol[i].ol.hEvent);
+	}
+	free(t);
+}
+
+
+/*
+ * Populate the archive_entry with metadata from the disk.
+ */
+int
+archive_read_disk_entry_from_file(struct archive *_a,
+    struct archive_entry *entry, int fd, const struct stat *st)
+{
+	struct archive_read_disk *a = (struct archive_read_disk *)_a;
+	const wchar_t *path;
+	const wchar_t *wname;
+	const char *name;
+	HANDLE h;
+	BY_HANDLE_FILE_INFORMATION bhfi;
+	DWORD fileAttributes = 0;
+	int r;
+
+	archive_clear_error(_a);
+	wname = archive_entry_sourcepath_w(entry);
+	if (wname == NULL)
+		wname = archive_entry_pathname_w(entry);
+	if (wname == NULL) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Can't get a wide character version of the path");
+		return (ARCHIVE_FAILED);
+	}
+	path = __la_win_permissive_name_w(wname);
+
+	if (st == NULL) {
+		/*
+		 * Get metadata through GetFileInformationByHandle().
+		 */
+		if (fd >= 0) {
+			h = (HANDLE)_get_osfhandle(fd);
+			r = GetFileInformationByHandle(h, &bhfi);
+			if (r == 0) {
+				la_dosmaperr(GetLastError());
+				archive_set_error(&a->archive, errno,
+				    "Can't GetFileInformationByHandle");
+				return (ARCHIVE_FAILED);
+			}
+			entry_copy_bhfi(entry, path, NULL, &bhfi);
+		} else {
+			WIN32_FIND_DATAW findData;
+			DWORD flag, desiredAccess;
+	
+			h = FindFirstFileW(path, &findData);
+			if (h == INVALID_HANDLE_VALUE) {
+				la_dosmaperr(GetLastError());
+				archive_set_error(&a->archive, errno,
+				    "Can't FindFirstFileW");
+				return (ARCHIVE_FAILED);
+			}
+			FindClose(h);
+
+			flag = FILE_FLAG_BACKUP_SEMANTICS;
+			if (!a->follow_symlinks &&
+			    (findData.dwFileAttributes
+			      & FILE_ATTRIBUTE_REPARSE_POINT) &&
+				  (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
+				flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+				desiredAccess = 0;
+			} else if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+				desiredAccess = 0;
+			} else
+				desiredAccess = GENERIC_READ;
+
+			h = CreateFileW(path, desiredAccess, FILE_SHARE_READ, NULL,
+			    OPEN_EXISTING, flag, NULL);
+			if (h == INVALID_HANDLE_VALUE) {
+				la_dosmaperr(GetLastError());
+				archive_set_error(&a->archive, errno,
+				    "Can't CreateFileW");
+				return (ARCHIVE_FAILED);
+			}
+			r = GetFileInformationByHandle(h, &bhfi);
+			if (r == 0) {
+				la_dosmaperr(GetLastError());
+				archive_set_error(&a->archive, errno,
+				    "Can't GetFileInformationByHandle");
+				CloseHandle(h);
+				return (ARCHIVE_FAILED);
+			}
+			entry_copy_bhfi(entry, path, &findData, &bhfi);
+		}
+		fileAttributes = bhfi.dwFileAttributes;
+	} else {
+		archive_entry_copy_stat(entry, st);
+		if (st->st_mode & S_IFLNK)
+			entry_symlink_from_pathw(entry, path);
+		h = INVALID_HANDLE_VALUE;
+	}
+
+	/* Lookup uname/gname */
+	name = archive_read_disk_uname(_a, archive_entry_uid(entry));
+	if (name != NULL)
+		archive_entry_copy_uname(entry, name);
+	name = archive_read_disk_gname(_a, archive_entry_gid(entry));
+	if (name != NULL)
+		archive_entry_copy_gname(entry, name);
+
+	/*
+	 * File attributes
+	 */
+	if ((a->flags & ARCHIVE_READDISK_NO_FFLAGS) == 0) {
+		const int supported_attrs =
+		    FILE_ATTRIBUTE_READONLY |
+		    FILE_ATTRIBUTE_HIDDEN |
+		    FILE_ATTRIBUTE_SYSTEM;
+		DWORD file_attrs = fileAttributes & supported_attrs;
+		if (file_attrs != 0)
+			archive_entry_set_fflags(entry, file_attrs, 0);
+	}
+
+	/*
+	 * Can this file be sparse file ?
+	 */
+	if (archive_entry_filetype(entry) != AE_IFREG
+	    || archive_entry_size(entry) <= 0
+		|| archive_entry_hardlink(entry) != NULL) {
+		if (h != INVALID_HANDLE_VALUE && fd < 0)
+			CloseHandle(h);
+		return (ARCHIVE_OK);
+	}
+
+	if (h == INVALID_HANDLE_VALUE) {
+		if (fd >= 0) {
+			h = (HANDLE)_get_osfhandle(fd);
+		} else {
+			h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,
+			    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+			if (h == INVALID_HANDLE_VALUE) {
+				la_dosmaperr(GetLastError());
+				archive_set_error(&a->archive, errno,
+				    "Can't CreateFileW");
+				return (ARCHIVE_FAILED);
+			}
+		}
+		r = GetFileInformationByHandle(h, &bhfi);
+		if (r == 0) {
+			la_dosmaperr(GetLastError());
+			archive_set_error(&a->archive, errno,
+			    "Can't GetFileInformationByHandle");
+			if (h != INVALID_HANDLE_VALUE && fd < 0)
+				CloseHandle(h);
+			return (ARCHIVE_FAILED);
+		}
+		fileAttributes = bhfi.dwFileAttributes;
+	}
+
+	/* Sparse file must be set a mark, FILE_ATTRIBUTE_SPARSE_FILE */
+	if ((fileAttributes & FILE_ATTRIBUTE_SPARSE_FILE) == 0) {
+		if (fd < 0)
+			CloseHandle(h);
+		return (ARCHIVE_OK);
+	}
+
+	r = setup_sparse_from_disk(a, entry, h);
+	if (fd < 0)
+		CloseHandle(h);
+
+	return (r);
+}
+
+/*
+ * Windows sparse interface.
+ */
+#if defined(__MINGW32__) && !defined(FSCTL_QUERY_ALLOCATED_RANGES)
+#define FSCTL_QUERY_ALLOCATED_RANGES 0x940CF
+typedef struct {
+	LARGE_INTEGER FileOffset;
+	LARGE_INTEGER Length;
+} FILE_ALLOCATED_RANGE_BUFFER;
+#endif
+
+static int
+setup_sparse_from_disk(struct archive_read_disk *a,
+    struct archive_entry *entry, HANDLE handle)
+{
+	FILE_ALLOCATED_RANGE_BUFFER range, *outranges = NULL;
+	size_t outranges_size;
+	int64_t entry_size = archive_entry_size(entry);
+	int exit_sts = ARCHIVE_OK;
+
+	range.FileOffset.QuadPart = 0;
+	range.Length.QuadPart = entry_size;
+	outranges_size = 2048;
+	outranges = (FILE_ALLOCATED_RANGE_BUFFER *)malloc(outranges_size);
+	if (outranges == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+			"Couldn't allocate memory");
+		exit_sts = ARCHIVE_FATAL;
+		goto exit_setup_sparse;
+	}
+
+	for (;;) {
+		DWORD retbytes;
+		BOOL ret;
+
+		for (;;) {
+			ret = DeviceIoControl(handle,
+			    FSCTL_QUERY_ALLOCATED_RANGES,
+			    &range, sizeof(range), outranges,
+			    (DWORD)outranges_size, &retbytes, NULL);
+			if (ret == 0 && GetLastError() == ERROR_MORE_DATA) {
+				free(outranges);
+				outranges_size *= 2;
+				outranges = (FILE_ALLOCATED_RANGE_BUFFER *)
+				    malloc(outranges_size);
+				if (outranges == NULL) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "Couldn't allocate memory");
+					exit_sts = ARCHIVE_FATAL;
+					goto exit_setup_sparse;
+				}
+				continue;
+			} else
+				break;
+		}
+		if (ret != 0) {
+			if (retbytes > 0) {
+				DWORD i, n;
+
+				n = retbytes / sizeof(outranges[0]);
+				if (n == 1 &&
+				    outranges[0].FileOffset.QuadPart == 0 &&
+				    outranges[0].Length.QuadPart == entry_size)
+					break;/* This is not sparse. */
+				for (i = 0; i < n; i++)
+					archive_entry_sparse_add_entry(entry,
+					    outranges[i].FileOffset.QuadPart,
+						outranges[i].Length.QuadPart);
+				range.FileOffset.QuadPart =
+				    outranges[n-1].FileOffset.QuadPart
+				    + outranges[n-1].Length.QuadPart;
+				range.Length.QuadPart =
+				    entry_size - range.FileOffset.QuadPart;
+				if (range.Length.QuadPart > 0)
+					continue;
+			} else {
+				/* The entire file is a hole. Add one data block of size 0 at the end. */
+				archive_entry_sparse_add_entry(entry,
+				    entry_size,
+				    0);
+			}
+			break;
+		} else {
+			la_dosmaperr(GetLastError());
+			archive_set_error(&a->archive, errno,
+			    "DeviceIoControl Failed: %lu", GetLastError());
+			exit_sts = ARCHIVE_FAILED;
+			goto exit_setup_sparse;
+		}
+	}
+exit_setup_sparse:
+	free(outranges);
+
+	return (exit_sts);
+}
+
+#endif
diff --git a/c/archive_read_extract.c b/c/archive_read_extract.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_extract.c
@@ -0,0 +1,60 @@
+/*-
+ * 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.61 2008/05/26 17:00:22 kientzle Exp $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+int
+archive_read_extract(struct archive *_a, struct archive_entry *entry, int flags)
+{
+	struct archive_read_extract *extract;
+	struct archive_read * a = (struct archive_read *)_a;
+
+	extract = __archive_read_get_extract(a);
+	if (extract == NULL)
+		return (ARCHIVE_FATAL);
+
+	/* If we haven't initialized the archive_write_disk object, do it now. */
+	if (extract->ad == NULL) {
+		extract->ad = archive_write_disk_new();
+		if (extract->ad == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Can't extract");
+			return (ARCHIVE_FATAL);
+		}
+		archive_write_disk_set_standard_lookup(extract->ad);
+	}
+
+	archive_write_disk_set_options(extract->ad, flags);
+	return (archive_read_extract2(&a->archive, entry, extract->ad));
+}
diff --git a/c/archive_read_extract2.c b/c/archive_read_extract2.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_extract2.c
@@ -0,0 +1,155 @@
+/*-
+ * 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.61 2008/05/26 17:00:22 kientzle Exp $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+static int	copy_data(struct archive *ar, struct archive *aw);
+static int	archive_read_extract_cleanup(struct archive_read *);
+
+
+/* Retrieve an extract object without initialising the associated
+ * archive_write_disk object.
+ */
+struct archive_read_extract *
+__archive_read_get_extract(struct archive_read *a)
+{
+	if (a->extract == NULL) {
+		a->extract = (struct archive_read_extract *)calloc(1, sizeof(*a->extract));
+		if (a->extract == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Can't extract");
+			return (NULL);
+		}
+		a->cleanup_archive_extract = archive_read_extract_cleanup;
+	}
+	return (a->extract);
+}
+
+/*
+ * Cleanup function for archive_extract.
+ */
+static int
+archive_read_extract_cleanup(struct archive_read *a)
+{
+	int ret = ARCHIVE_OK;
+
+	if (a->extract->ad != NULL) {
+		ret = archive_write_free(a->extract->ad);
+	}
+	free(a->extract);
+	a->extract = NULL;
+	return (ret);
+}
+
+int
+archive_read_extract2(struct archive *_a, struct archive_entry *entry,
+    struct archive *ad)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	int r, r2;
+
+	/* Set up for this particular entry. */
+	if (a->skip_file_set)
+		archive_write_disk_set_skip_file(ad,
+		    a->skip_file_dev, a->skip_file_ino);
+	r = archive_write_header(ad, entry);
+	if (r < ARCHIVE_WARN)
+		r = ARCHIVE_WARN;
+	if (r != ARCHIVE_OK)
+		/* If _write_header failed, copy the error. */
+ 		archive_copy_error(&a->archive, ad);
+	else if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) > 0)
+		/* Otherwise, pour data into the entry. */
+		r = copy_data(_a, ad);
+	r2 = archive_write_finish_entry(ad);
+	if (r2 < ARCHIVE_WARN)
+		r2 = ARCHIVE_WARN;
+	/* Use the first message. */
+	if (r2 != ARCHIVE_OK && r == ARCHIVE_OK)
+		archive_copy_error(&a->archive, 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 archive_read_extract *extract = __archive_read_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)
+{
+	int64_t offset;
+	const void *buff;
+	struct archive_read_extract *extract;
+	size_t size;
+	int r;
+
+	extract = __archive_read_get_extract((struct archive_read *)ar);
+	if (extract == NULL)
+		return (ARCHIVE_FATAL);
+	for (;;) {
+		r = archive_read_data_block(ar, &buff, &size, &offset);
+		if (r == ARCHIVE_EOF)
+			return (ARCHIVE_OK);
+		if (r != ARCHIVE_OK)
+			return (r);
+		r = (int)archive_write_data_block(aw, buff, size, offset);
+		if (r < ARCHIVE_WARN)
+			r = ARCHIVE_WARN;
+		if (r < ARCHIVE_OK) {
+			archive_set_error(ar, archive_errno(aw),
+			    "%s", archive_error_string(aw));
+			return (r);
+		}
+		if (extract->extract_progress)
+			(extract->extract_progress)
+			    (extract->extract_progress_user_data);
+	}
+}
diff --git a/c/archive_read_open_fd.c b/c/archive_read_open_fd.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_open_fd.c
@@ -0,0 +1,211 @@
+/*-
+ * 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: head/lib/libarchive/archive_read_open_fd.c 201103 2009-12-28 03:13:49Z kientzle $");
+
+#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_IO_H
+#include <io.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;
+	char	 use_lseek;
+	void	*buffer;
+};
+
+static int	file_close(struct archive *, void *);
+static ssize_t	file_read(struct archive *, void *, const void **buff);
+static int64_t	file_seek(struct archive *, void *, int64_t request, int);
+static int64_t	file_skip(struct archive *, void *, int64_t request);
+
+int
+archive_read_open_fd(struct archive *a, int fd, size_t block_size)
+{
+	struct stat st;
+	struct read_fd_data *mine;
+	void *b;
+
+	archive_clear_error(a);
+	if (fstat(fd, &st) != 0) {
+		archive_set_error(a, errno, "Can't stat fd %d", fd);
+		return (ARCHIVE_FATAL);
+	}
+
+	mine = (struct read_fd_data *)calloc(1, sizeof(*mine));
+	b = malloc(block_size);
+	if (mine == NULL || b == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		free(mine);
+		free(b);
+		return (ARCHIVE_FATAL);
+	}
+	mine->block_size = block_size;
+	mine->buffer = b;
+	mine->fd = fd;
+	/*
+	 * Skip support is a performance optimization for anything
+	 * that supports lseek().  On FreeBSD, only regular files and
+	 * raw disk devices support lseek() and there's no portable
+	 * way to determine if a device is a raw disk device, so we
+	 * only enable this optimization for regular files.
+	 */
+	if (S_ISREG(st.st_mode)) {
+		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+		mine->use_lseek = 1;
+	}
+#if defined(__CYGWIN__) || defined(_WIN32)
+	setmode(mine->fd, O_BINARY);
+#endif
+
+	archive_read_set_read_callback(a, file_read);
+	archive_read_set_skip_callback(a, file_skip);
+	archive_read_set_seek_callback(a, file_seek);
+	archive_read_set_close_callback(a, file_close);
+	archive_read_set_callback_data(a, mine);
+	return (archive_read_open1(a));
+}
+
+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;
+	for (;;) {
+		bytes_read = read(mine->fd, mine->buffer, mine->block_size);
+		if (bytes_read < 0) {
+			if (errno == EINTR)
+				continue;
+			archive_set_error(a, errno, "Error reading fd %d",
+			    mine->fd);
+		}
+		return (bytes_read);
+	}
+}
+
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
+{
+	struct read_fd_data *mine = (struct read_fd_data *)client_data;
+	int64_t skip = request;
+	int64_t old_offset, new_offset;
+	int skip_bits = sizeof(skip) * 8 - 1;  /* off_t is a signed type. */
+
+	if (!mine->use_lseek)
+		return (0);
+
+	/* Reduce a request that would overflow the 'skip' variable. */
+	if (sizeof(request) > sizeof(skip)) {
+		int64_t max_skip =
+		    (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+		if (request > max_skip)
+			skip = max_skip;
+	}
+
+	/* Reduce request to the next smallest multiple of block_size */
+	request = (request / mine->block_size) * mine->block_size;
+	if (request == 0)
+		return (0);
+
+	if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0) &&
+	    ((new_offset = lseek(mine->fd, skip, SEEK_CUR)) >= 0))
+		return (new_offset - old_offset);
+
+	/* If seek failed once, it will probably fail again. */
+	mine->use_lseek = 0;
+
+	/* Let libarchive recover with read+discard. */
+	if (errno == ESPIPE)
+		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);
+}
+
+/*
+ * TODO: Store the offset and use it in the read callback.
+ */
+static int64_t
+file_seek(struct archive *a, void *client_data, int64_t request, int whence)
+{
+	struct read_fd_data *mine = (struct read_fd_data *)client_data;
+	int64_t r;
+
+	/* We use off_t here because lseek() is declared that way. */
+	/* See above for notes about when off_t is less than 64 bits. */
+	r = lseek(mine->fd, request, whence);
+	if (r >= 0)
+		return r;
+
+	if (errno == ESPIPE) {
+		archive_set_error(a, errno,
+		    "A file descriptor(%d) is not seekable(PIPE)", mine->fd);
+		return (ARCHIVE_FAILED);
+	} else {
+		/* If the input is corrupted or truncated, fail. */
+		archive_set_error(a, errno,
+		    "Error seeking in a file descriptor(%d)", mine->fd);
+		return (ARCHIVE_FATAL);
+	}
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+	struct read_fd_data *mine = (struct read_fd_data *)client_data;
+
+	(void)a; /* UNUSED */
+	free(mine->buffer);
+	free(mine);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_read_open_file.c b/c/archive_read_open_file.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_open_file.c
@@ -0,0 +1,180 @@
+/*-
+ * 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: head/lib/libarchive/archive_read_open_file.c 201093 2009-12-28 02:28:44Z kientzle $");
+
+#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_IO_H
+#include <io.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;
+	char	 can_skip;
+};
+
+static int	file_close(struct archive *, void *);
+static ssize_t	file_read(struct archive *, void *, const void **buff);
+static int64_t	file_skip(struct archive *, void *, int64_t request);
+
+int
+archive_read_open_FILE(struct archive *a, FILE *f)
+{
+	struct stat st;
+	struct read_FILE_data *mine;
+	size_t block_size = 128 * 1024;
+	void *b;
+
+	archive_clear_error(a);
+	mine = (struct read_FILE_data *)malloc(sizeof(*mine));
+	b = malloc(block_size);
+	if (mine == NULL || b == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		free(mine);
+		free(b);
+		return (ARCHIVE_FATAL);
+	}
+	mine->block_size = block_size;
+	mine->buffer = b;
+	mine->f = f;
+	/*
+	 * If we can't fstat() the file, it may just be that it's not
+	 * a file.  (On some platforms, FILE * objects can wrap I/O
+	 * streams that don't support fileno()).  As a result, fileno()
+	 * should be used cautiously.)
+	 */
+	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);
+		/* Enable the seek optimization only for regular files. */
+		mine->can_skip = 1;
+	} else
+		mine->can_skip = 0;
+
+#if defined(__CYGWIN__) || defined(_WIN32)
+	setmode(fileno(mine->f), O_BINARY);
+#endif
+
+	archive_read_set_read_callback(a, file_read);
+	archive_read_set_skip_callback(a, file_skip);
+	archive_read_set_close_callback(a, file_close);
+	archive_read_set_callback_data(a, mine);
+	return (archive_read_open1(a));
+}
+
+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;
+	size_t bytes_read;
+
+	*buff = mine->buffer;
+	bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f);
+	if (bytes_read < mine->block_size && ferror(mine->f)) {
+		archive_set_error(a, errno, "Error reading file");
+	}
+	return (bytes_read);
+}
+
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
+{
+	struct read_FILE_data *mine = (struct read_FILE_data *)client_data;
+#if HAVE_FSEEKO
+	off_t skip = (off_t)request;
+#elif HAVE__FSEEKI64
+	int64_t skip = request;
+#else
+	long skip = (long)request;
+#endif
+	int skip_bits = sizeof(skip) * 8 - 1;
+
+	(void)a; /* UNUSED */
+
+	/*
+	 * If we can't skip, return 0 as the amount we did step and
+	 * the caller will work around by reading and discarding.
+	 */
+	if (!mine->can_skip)
+		return (0);
+	if (request == 0)
+		return (0);
+
+	/* If request is too big for a long or an off_t, reduce it. */
+	if (sizeof(request) > sizeof(skip)) {
+		int64_t max_skip =
+		    (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+		if (request > max_skip)
+			skip = max_skip;
+	}
+
+#ifdef __ANDROID__
+        /* fileno() isn't safe on all platforms ... see above. */
+	if (lseek(fileno(mine->f), skip, SEEK_CUR) < 0)
+#elif HAVE_FSEEKO
+	if (fseeko(mine->f, skip, SEEK_CUR) != 0)
+#elif HAVE__FSEEKI64
+	if (_fseeki64(mine->f, skip, SEEK_CUR) != 0)
+#else
+	if (fseek(mine->f, skip, SEEK_CUR) != 0)
+#endif
+	{
+		mine->can_skip = 0;
+		return (0);
+	}
+	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 */
+	free(mine->buffer);
+	free(mine);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_read_open_filename.c b/c/archive_read_open_filename.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_open_filename.c
@@ -0,0 +1,586 @@
+/*-
+ * Copyright (c) 2003-2010 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: head/lib/libarchive/archive_read_open_filename.c 201093 2009-12-28 02:28:44Z kientzle $");
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#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_IO_H
+#include <io.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
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/disk.h>
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#elif defined(__DragonFly__)
+#include <sys/diskslice.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC	0
+#endif
+
+struct read_file_data {
+	int	 fd;
+	size_t	 block_size;
+	void	*buffer;
+	mode_t	 st_mode;  /* Mode bits for opened file. */
+	char	 use_lseek;
+	enum fnt_e { FNT_STDIN, FNT_MBS, FNT_WCS } filename_type;
+	union {
+		char	 m[1];/* MBS filename. */
+		wchar_t	 w[1];/* WCS filename. */
+	} filename; /* Must be last! */
+};
+
+static int	file_open(struct archive *, void *);
+static int	file_close(struct archive *, void *);
+static int file_close2(struct archive *, void *);
+static int file_switch(struct archive *, void *, void *);
+static ssize_t	file_read(struct archive *, void *, const void **buff);
+static int64_t	file_seek(struct archive *, void *, int64_t request, int);
+static int64_t	file_skip(struct archive *, void *, int64_t request);
+static int64_t	file_skip_lseek(struct archive *, void *, int64_t request);
+
+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)
+{
+	const char *filenames[2];
+	filenames[0] = filename;
+	filenames[1] = NULL;
+	return archive_read_open_filenames(a, filenames, block_size);
+}
+
+int
+archive_read_open_filenames(struct archive *a, const char **filenames,
+    size_t block_size)
+{
+	struct read_file_data *mine;
+	const char *filename = NULL;
+	if (filenames)
+		filename = *(filenames++);
+
+	archive_clear_error(a);
+	do
+	{
+		if (filename == NULL)
+			filename = "";
+		mine = (struct read_file_data *)calloc(1,
+			sizeof(*mine) + strlen(filename));
+		if (mine == NULL)
+			goto no_memory;
+		strcpy(mine->filename.m, filename);
+		mine->block_size = block_size;
+		mine->fd = -1;
+		mine->buffer = NULL;
+		mine->st_mode = mine->use_lseek = 0;
+		if (filename == NULL || filename[0] == '\0') {
+			mine->filename_type = FNT_STDIN;
+		} else
+			mine->filename_type = FNT_MBS;
+		if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK))
+			return (ARCHIVE_FATAL);
+		if (filenames == NULL)
+			break;
+		filename = *(filenames++);
+	} while (filename != NULL && filename[0] != '\0');
+	archive_read_set_open_callback(a, file_open);
+	archive_read_set_read_callback(a, file_read);
+	archive_read_set_skip_callback(a, file_skip);
+	archive_read_set_close_callback(a, file_close);
+	archive_read_set_switch_callback(a, file_switch);
+	archive_read_set_seek_callback(a, file_seek);
+
+	return (archive_read_open1(a));
+no_memory:
+	archive_set_error(a, ENOMEM, "No memory");
+	return (ARCHIVE_FATAL);
+}
+
+int
+archive_read_open_filename_w(struct archive *a, const wchar_t *wfilename,
+    size_t block_size)
+{
+	struct read_file_data *mine = (struct read_file_data *)calloc(1,
+		sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t));
+	if (!mine)
+	{
+		archive_set_error(a, ENOMEM, "No memory");
+		return (ARCHIVE_FATAL);
+	}
+	mine->fd = -1;
+	mine->block_size = block_size;
+
+	if (wfilename == NULL || wfilename[0] == L'\0') {
+		mine->filename_type = FNT_STDIN;
+	} else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		mine->filename_type = FNT_WCS;
+		wcscpy(mine->filename.w, wfilename);
+#else
+		/*
+		 * POSIX system does not support a wchar_t interface for
+		 * open() system call, so we have to translate a wchar_t
+		 * filename to multi-byte one and use it.
+		 */
+		struct archive_string fn;
+
+		archive_string_init(&fn);
+		if (archive_string_append_from_wcs(&fn, wfilename,
+		    wcslen(wfilename)) != 0) {
+			if (errno == ENOMEM)
+				archive_set_error(a, errno,
+				    "Can't allocate memory");
+			else
+				archive_set_error(a, EINVAL,
+				    "Failed to convert a wide-character"
+				    " filename to a multi-byte filename");
+			archive_string_free(&fn);
+			free(mine);
+			return (ARCHIVE_FATAL);
+		}
+		mine->filename_type = FNT_MBS;
+		strcpy(mine->filename.m, fn.s);
+		archive_string_free(&fn);
+#endif
+	}
+	if (archive_read_append_callback_data(a, mine) != (ARCHIVE_OK))
+		return (ARCHIVE_FATAL);
+	archive_read_set_open_callback(a, file_open);
+	archive_read_set_read_callback(a, file_read);
+	archive_read_set_skip_callback(a, file_skip);
+	archive_read_set_close_callback(a, file_close);
+	archive_read_set_switch_callback(a, file_switch);
+	archive_read_set_seek_callback(a, file_seek);
+
+	return (archive_read_open1(a));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+	struct stat st;
+	struct read_file_data *mine = (struct read_file_data *)client_data;
+	void *buffer;
+	const char *filename = NULL;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	const wchar_t *wfilename = NULL;
+#endif
+	int fd = -1;
+	int is_disk_like = 0;
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	off_t mediasize = 0; /* FreeBSD-specific, so off_t okay here. */
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+	struct disklabel dl;
+#elif defined(__DragonFly__)
+	struct partinfo pi;
+#endif
+
+	archive_clear_error(a);
+	if (mine->filename_type == FNT_STDIN) {
+		/* We used to delegate stdin support by
+		 * directly calling archive_read_open_fd(a,0,block_size)
+		 * here, but that doesn't (and shouldn't) handle the
+		 * end-of-file flush when reading stdout from a pipe.
+		 * Basically, read_open_fd() is intended for folks who
+		 * are willing to handle such details themselves.  This
+		 * API is intended to be a little smarter for folks who
+		 * want easy handling of the common case.
+		 */
+		fd = 0;
+#if defined(__CYGWIN__) || defined(_WIN32)
+		setmode(0, O_BINARY);
+#endif
+		filename = "";
+	} else if (mine->filename_type == FNT_MBS) {
+		filename = mine->filename.m;
+		fd = open(filename, O_RDONLY | O_BINARY | O_CLOEXEC);
+		__archive_ensure_cloexec_flag(fd);
+		if (fd < 0) {
+			archive_set_error(a, errno,
+			    "Failed to open '%s'", filename);
+			return (ARCHIVE_FATAL);
+		}
+	} else {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		wfilename = mine->filename.w;
+		fd = _wopen(wfilename, O_RDONLY | O_BINARY);
+		if (fd < 0 && errno == ENOENT) {
+			wchar_t *fullpath;
+			fullpath = __la_win_permissive_name_w(wfilename);
+			if (fullpath != NULL) {
+				fd = _wopen(fullpath, O_RDONLY | O_BINARY);
+				free(fullpath);
+			}
+		}
+		if (fd < 0) {
+			archive_set_error(a, errno,
+			    "Failed to open '%S'", wfilename);
+			return (ARCHIVE_FATAL);
+		}
+#else
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Unexpedted operation in archive_read_open_filename");
+		goto fail;
+#endif
+	}
+	if (fstat(fd, &st) != 0) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		if (mine->filename_type == FNT_WCS)
+			archive_set_error(a, errno, "Can't stat '%S'",
+			    wfilename);
+		else
+#endif
+			archive_set_error(a, errno, "Can't stat '%s'",
+			    filename);
+		goto fail;
+	}
+
+	/*
+	 * Determine whether the input looks like a disk device or a
+	 * tape device.  The results are used below to select an I/O
+	 * strategy:
+	 *  = "disk-like" devices support arbitrary lseek() and will
+	 *    support I/O requests of any size.  So we get easy skipping
+	 *    and can cheat on block sizes to get better performance.
+	 *  = "tape-like" devices require strict blocking and use
+	 *    specialized ioctls for seeking.
+	 *  = "socket-like" devices cannot seek at all but can improve
+	 *    performance by using nonblocking I/O to read "whatever is
+	 *    available right now".
+	 *
+	 * Right now, we only specially recognize disk-like devices,
+	 * but it should be straightforward to add probes and strategy
+	 * here for tape-like and socket-like devices.
+	 */
+	if (S_ISREG(st.st_mode)) {
+		/* Safety:  Tell the extractor not to overwrite the input. */
+		archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
+		/* Regular files act like disks. */
+		is_disk_like = 1;
+	}
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	/* FreeBSD: if it supports DIOCGMEDIASIZE ioctl, it's disk-like. */
+	else if (S_ISCHR(st.st_mode) &&
+	    ioctl(fd, DIOCGMEDIASIZE, &mediasize) == 0 &&
+	    mediasize > 0) {
+		is_disk_like = 1;
+	}
+#elif defined(__NetBSD__) || defined(__OpenBSD__)
+	/* Net/OpenBSD: if it supports DIOCGDINFO ioctl, it's disk-like. */
+	else if ((S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) &&
+	    ioctl(fd, DIOCGDINFO, &dl) == 0 &&
+	    dl.d_partitions[DISKPART(st.st_rdev)].p_size > 0) {
+		is_disk_like = 1;
+	}
+#elif defined(__DragonFly__)
+	/* DragonFly BSD:  if it supports DIOCGPART ioctl, it's disk-like. */
+	else if (S_ISCHR(st.st_mode) &&
+	    ioctl(fd, DIOCGPART, &pi) == 0 &&
+	    pi.media_size > 0) {
+		is_disk_like = 1;
+	}
+#elif defined(__linux__)
+	/* Linux:  All block devices are disk-like. */
+	else if (S_ISBLK(st.st_mode) &&
+	    lseek(fd, 0, SEEK_CUR) == 0 &&
+	    lseek(fd, 0, SEEK_SET) == 0 &&
+	    lseek(fd, 0, SEEK_END) > 0 &&
+	    lseek(fd, 0, SEEK_SET) == 0) {
+		is_disk_like = 1;
+	}
+#endif
+	/* TODO: Add an "is_tape_like" variable and appropriate tests. */
+
+	/* Disk-like devices prefer power-of-two block sizes.  */
+	/* Use provided block_size as a guide so users have some control. */
+	if (is_disk_like) {
+		size_t new_block_size = 64 * 1024;
+		while (new_block_size < mine->block_size
+		    && new_block_size < 64 * 1024 * 1024)
+			new_block_size *= 2;
+		mine->block_size = new_block_size;
+	}
+	buffer = malloc(mine->block_size);
+	if (buffer == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		goto fail;
+	}
+	mine->buffer = buffer;
+	mine->fd = fd;
+	/* Remember mode so close can decide whether to flush. */
+	mine->st_mode = st.st_mode;
+
+	/* Disk-like inputs can use lseek(). */
+	if (is_disk_like)
+		mine->use_lseek = 1;
+
+	return (ARCHIVE_OK);
+fail:
+	/*
+	 * Don't close file descriptors not opened or ones pointing referring
+	 * to `FNT_STDIN`.
+	 */
+	if (fd != -1 && fd != 0)
+		close(fd);
+	return (ARCHIVE_FATAL);
+}
+
+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;
+
+	/* TODO: If a recent lseek() operation has left us
+	 * mis-aligned, read and return a short block to try to get
+	 * us back in alignment. */
+
+	/* TODO: Someday, try mmap() here; if that succeeds, give
+	 * the entire file to libarchive as a single block.  That
+	 * could be a lot faster than block-by-block manual I/O. */
+
+	/* TODO: We might be able to improve performance on pipes and
+	 * sockets by setting non-blocking I/O and just accepting
+	 * whatever we get here instead of waiting for a full block
+	 * worth of data. */
+
+	*buff = mine->buffer;
+	for (;;) {
+		bytes_read = read(mine->fd, mine->buffer, mine->block_size);
+		if (bytes_read < 0) {
+			if (errno == EINTR)
+				continue;
+			else if (mine->filename_type == FNT_STDIN)
+				archive_set_error(a, errno,
+				    "Error reading stdin");
+			else if (mine->filename_type == FNT_MBS)
+				archive_set_error(a, errno,
+				    "Error reading '%s'", mine->filename.m);
+			else
+				archive_set_error(a, errno,
+				    "Error reading '%S'", mine->filename.w);
+		}
+		return (bytes_read);
+	}
+}
+
+/*
+ * Regular files and disk-like block devices can use simple lseek
+ * without needing to round the request to the block size.
+ *
+ * TODO: This can leave future reads mis-aligned.  Since we know the
+ * offset here, we should store it and use it in file_read() above
+ * to determine whether we should perform a short read to get back
+ * into alignment.  Long series of mis-aligned reads can negatively
+ * impact disk throughput.  (Of course, the performance impact should
+ * be carefully tested; extra code complexity is only worthwhile if
+ * it does provide measurable improvement.)
+ *
+ * TODO: Be lazy about the actual seek.  There are a few pathological
+ * cases where libarchive makes a bunch of seek requests in a row
+ * without any intervening reads.  This isn't a huge performance
+ * problem, since the kernel handles seeks lazily already, but
+ * it would be very slightly faster if we simply remembered the
+ * seek request here and then actually performed the seek at the
+ * top of the read callback above.
+ */
+static int64_t
+file_skip_lseek(struct archive *a, void *client_data, int64_t request)
+{
+	struct read_file_data *mine = (struct read_file_data *)client_data;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* We use _lseeki64() on Windows. */
+	int64_t old_offset, new_offset;
+#else
+	off_t old_offset, new_offset;
+#endif
+
+	/* We use off_t here because lseek() is declared that way. */
+
+	/* TODO: Deal with case where off_t isn't 64 bits.
+	 * This shouldn't be a problem on Linux or other POSIX
+	 * systems, since the configuration logic for libarchive
+	 * tries to obtain a 64-bit off_t.
+	 */
+	if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0 &&
+	    (new_offset = lseek(mine->fd, request, SEEK_CUR)) >= 0)
+		return (new_offset - old_offset);
+
+	/* If lseek() fails, don't bother trying again. */
+	mine->use_lseek = 0;
+
+	/* Let libarchive recover with read+discard */
+	if (errno == ESPIPE)
+		return (0);
+
+	/* If the input is corrupted or truncated, fail. */
+	if (mine->filename_type == FNT_STDIN)
+		archive_set_error(a, errno, "Error seeking in stdin");
+	else if (mine->filename_type == FNT_MBS)
+		archive_set_error(a, errno, "Error seeking in '%s'",
+		    mine->filename.m);
+	else
+		archive_set_error(a, errno, "Error seeking in '%S'",
+		    mine->filename.w);
+	return (-1);
+}
+
+
+/*
+ * TODO: Implement another file_skip_XXXX that uses MTIO ioctls to
+ * accelerate operation on tape drives.
+ */
+
+static int64_t
+file_skip(struct archive *a, void *client_data, int64_t request)
+{
+	struct read_file_data *mine = (struct read_file_data *)client_data;
+
+	/* Delegate skip requests. */
+	if (mine->use_lseek)
+		return (file_skip_lseek(a, client_data, request));
+
+	/* If we can't skip, return 0; libarchive will read+discard instead. */
+	return (0);
+}
+
+/*
+ * TODO: Store the offset and use it in the read callback.
+ */
+static int64_t
+file_seek(struct archive *a, void *client_data, int64_t request, int whence)
+{
+	struct read_file_data *mine = (struct read_file_data *)client_data;
+	int64_t r;
+
+	/* We use off_t here because lseek() is declared that way. */
+	/* See above for notes about when off_t is less than 64 bits. */
+	r = lseek(mine->fd, request, whence);
+	if (r >= 0)
+		return r;
+
+	/* If the input is corrupted or truncated, fail. */
+	if (mine->filename_type == FNT_STDIN)
+		archive_set_error(a, errno, "Error seeking in stdin");
+	else if (mine->filename_type == FNT_MBS)
+		archive_set_error(a, errno, "Error seeking in '%s'",
+		    mine->filename.m);
+	else
+		archive_set_error(a, errno, "Error seeking in '%S'",
+		    mine->filename.w);
+	return (ARCHIVE_FATAL);
+}
+
+static int
+file_close2(struct archive *a, void *client_data)
+{
+	struct read_file_data *mine = (struct read_file_data *)client_data;
+
+	(void)a; /* UNUSED */
+
+	/* Only flush and close if open succeeded. */
+	if (mine->fd >= 0) {
+		/*
+		 * Sometimes, we should flush the input before closing.
+		 *   Regular files: faster to just close without flush.
+		 *   Disk-like devices:  Ditto.
+		 *   Tapes: 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_type != FNT_STDIN)
+			close(mine->fd);
+	}
+	free(mine->buffer);
+	mine->buffer = NULL;
+	mine->fd = -1;
+	return (ARCHIVE_OK);
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+	struct read_file_data *mine = (struct read_file_data *)client_data;
+	file_close2(a, client_data);
+	free(mine);
+	return (ARCHIVE_OK);
+}
+
+static int
+file_switch(struct archive *a, void *client_data1, void *client_data2)
+{
+	file_close2(a, client_data1);
+	return file_open(a, client_data2);
+}
diff --git a/c/archive_read_open_memory.c b/c/archive_read_open_memory.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_open_memory.c
@@ -0,0 +1,186 @@
+/*-
+ * 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.6 2007/07/06 15:51:59 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 {
+	const unsigned char	*start;
+	const unsigned char	*p;
+	const unsigned char	*end;
+	ssize_t	 read_size;
+};
+
+static int	memory_read_close(struct archive *, void *);
+static int	memory_read_open(struct archive *, void *);
+static int64_t	memory_read_seek(struct archive *, void *, int64_t offset, int whence);
+static int64_t	memory_read_skip(struct archive *, void *, int64_t request);
+static ssize_t	memory_read(struct archive *, void *, const void **buff);
+
+int
+archive_read_open_memory(struct archive *a, const 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, const void *buff,
+    size_t size, size_t read_size)
+{
+	struct read_memory_data *mine;
+
+	mine = (struct read_memory_data *)calloc(1, sizeof(*mine));
+	if (mine == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		return (ARCHIVE_FATAL);
+	}
+	mine->start = mine->p = (const unsigned char *)buff;
+	mine->end = mine->start + size;
+	mine->read_size = read_size;
+	archive_read_set_open_callback(a, memory_read_open);
+	archive_read_set_read_callback(a, memory_read);
+	archive_read_set_seek_callback(a, memory_read_seek);
+	archive_read_set_skip_callback(a, memory_read_skip);
+	archive_read_set_close_callback(a, memory_read_close);
+	archive_read_set_callback_data(a, mine);
+	return (archive_read_open1(a));
+}
+
+/*
+ * 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->p;
+	size = mine->end - mine->p;
+	if (size > mine->read_size)
+		size = mine->read_size;
+        mine->p += 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.
+ */
+static int64_t
+memory_read_skip(struct archive *a, void *client_data, int64_t skip)
+{
+	struct read_memory_data *mine = (struct read_memory_data *)client_data;
+
+	(void)a; /* UNUSED */
+	if ((int64_t)skip > (int64_t)(mine->end - mine->p))
+		skip = mine->end - mine->p;
+	/* Round down to block size. */
+	skip /= mine->read_size;
+	skip *= mine->read_size;
+	mine->p += skip;
+	return (skip);
+}
+
+/*
+ * Seeking.
+ */
+static int64_t
+memory_read_seek(struct archive *a, void *client_data, int64_t offset, int whence)
+{
+	struct read_memory_data *mine = (struct read_memory_data *)client_data;
+
+	(void)a; /* UNUSED */
+	switch (whence) {
+	case SEEK_SET:
+		mine->p = mine->start + offset;
+		break;
+	case SEEK_CUR:
+		mine->p += offset;
+		break;
+	case SEEK_END:
+		mine->p = mine->end + offset;
+		break;
+	default:
+		return ARCHIVE_FATAL;
+	}
+	if (mine->p < mine->start) {
+		mine->p = mine->start;
+		return ARCHIVE_FAILED;
+	}
+	if (mine->p > mine->end) {
+		mine->p = mine->end;
+		return ARCHIVE_FAILED;
+	}
+	return (mine->p - mine->start);
+}
+
+/*
+ * 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/c/archive_read_private.h b/c/archive_read_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_read_private.h
@@ -0,0 +1,266 @@
+/*-
+ * 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: head/lib/libarchive/archive_read_private.h 201088 2009-12-28 02:18:55Z kientzle $
+ */
+
+#ifndef ARCHIVE_READ_PRIVATE_H_INCLUDED
+#define ARCHIVE_READ_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_private.h"
+
+struct archive_read;
+struct archive_read_filter_bidder;
+struct archive_read_filter;
+
+/*
+ * How bidding works for filters:
+ *   * The bid manager initializes the client-provided reader as the
+ *     first filter.
+ *   * It invokes the bidder for each registered filter with the
+ *     current head filter.
+ *   * The bidders can use archive_read_filter_ahead() to peek ahead
+ *     at the incoming data to compose their bids.
+ *   * The bid manager creates a new filter structure for the winning
+ *     bidder and gives the winning bidder a chance to initialize it.
+ *   * The new filter becomes the new top filter and we repeat the
+ *     process.
+ * This ends only when no bidder provides a non-zero bid.  Then
+ * we perform a similar dance with the registered format handlers.
+ */
+struct archive_read_filter_bidder {
+	/* Configuration data for the bidder. */
+	void *data;
+	/* Name of the filter */
+	const char *name;
+	/* Taste the upstream filter to see if we handle this. */
+	int (*bid)(struct archive_read_filter_bidder *,
+	    struct archive_read_filter *);
+	/* Initialize a newly-created filter. */
+	int (*init)(struct archive_read_filter *);
+	/* Set an option for the filter bidder. */
+	int (*options)(struct archive_read_filter_bidder *,
+	    const char *key, const char *value);
+	/* Release the bidder's configuration data. */
+	int (*free)(struct archive_read_filter_bidder *);
+};
+
+/*
+ * This structure is allocated within the archive_read core
+ * and initialized by archive_read and the init() method of the
+ * corresponding bidder above.
+ */
+struct archive_read_filter {
+	int64_t position;
+	/* Essentially all filters will need these values, so
+	 * just declare them here. */
+	struct archive_read_filter_bidder *bidder; /* My bidder. */
+	struct archive_read_filter *upstream; /* Who I read from. */
+	struct archive_read *archive; /* Associated archive. */
+	/* Open a block for reading */
+	int (*open)(struct archive_read_filter *self);
+	/* Return next block. */
+	ssize_t (*read)(struct archive_read_filter *, const void **);
+	/* Skip forward this many bytes. */
+	int64_t (*skip)(struct archive_read_filter *self, int64_t request);
+	/* Seek to an absolute location. */
+	int64_t (*seek)(struct archive_read_filter *self, int64_t offset, int whence);
+	/* Close (just this filter) and free(self). */
+	int (*close)(struct archive_read_filter *self);
+	/* Function that handles switching from reading one block to the next/prev */
+	int (*sswitch)(struct archive_read_filter *self, unsigned int iindex);
+	/* Read any header metadata if available. */
+	int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry);
+	/* My private data. */
+	void *data;
+
+	const char	*name;
+	int		 code;
+
+	/* Used by reblocking logic. */
+	char		*buffer;
+	size_t		 buffer_size;
+	char		*next;		/* Current read location. */
+	size_t		 avail;		/* Bytes in my buffer. */
+	const void	*client_buff;	/* Client buffer information. */
+	size_t		 client_total;
+	const char	*client_next;
+	size_t		 client_avail;
+	char		 end_of_file;
+	char		 closed;
+	char		 fatal;
+};
+
+/*
+ * The client looks a lot like a filter, so we just wrap it here.
+ *
+ * TODO: Make archive_read_filter and archive_read_client identical so
+ * that users of the library can easily register their own
+ * transformation filters.  This will probably break the API/ABI and
+ * so should be deferred at least until libarchive 3.0.
+ */
+struct archive_read_data_node {
+	int64_t begin_position;
+	int64_t total_size;
+	void *data;
+};
+struct archive_read_client {
+	archive_open_callback	*opener;
+	archive_read_callback	*reader;
+	archive_skip_callback	*skipper;
+	archive_seek_callback	*seeker;
+	archive_close_callback	*closer;
+	archive_switch_callback *switcher;
+	unsigned int nodes;
+	unsigned int cursor;
+	int64_t position;
+	struct archive_read_data_node *dataset;
+};
+struct archive_read_passphrase {
+	char	*passphrase;
+	struct archive_read_passphrase *next;
+};
+
+struct archive_read_extract {
+	struct archive *ad; /* archive_write_disk object */
+
+	/* Progress function invoked during extract. */
+	void			(*extract_progress)(void *);
+	void			 *extract_progress_user_data;
+};
+
+struct archive_read {
+	struct archive	archive;
+
+	struct archive_entry	*entry;
+
+	/* Dev/ino of the archive being read/written. */
+	int		  skip_file_set;
+	int64_t		  skip_file_dev;
+	int64_t		  skip_file_ino;
+
+	/* Callbacks to open/read/write/close client archive streams. */
+	struct archive_read_client client;
+
+	/* Registered filter bidders. */
+	struct archive_read_filter_bidder bidders[16];
+
+	/* Last filter in chain */
+	struct archive_read_filter *filter;
+
+	/* Whether to bypass filter bidding process */
+	int bypass_filter_bidding;
+
+	/* File offset of beginning of most recently-read header. */
+	int64_t		  header_position;
+
+	/* Nodes and offsets of compressed data block */
+	unsigned int data_start_node;
+	unsigned int data_end_node;
+
+	/*
+	 * Format detection is mostly the same as compression
+	 * detection, with one significant difference: 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.
+	 */
+
+	struct archive_format_descriptor {
+		void	 *data;
+		const char *name;
+		int	(*bid)(struct archive_read *, int best_bid);
+		int	(*options)(struct archive_read *, const char *key,
+		    const char *value);
+		int	(*read_header)(struct archive_read *, struct archive_entry *);
+		int	(*read_data)(struct archive_read *, const void **, size_t *, int64_t *);
+		int	(*read_data_skip)(struct archive_read *);
+		int64_t	(*seek_data)(struct archive_read *, int64_t, int);
+		int	(*cleanup)(struct archive_read *);
+		int	(*format_capabilties)(struct archive_read *);
+		int	(*has_encrypted_entries)(struct archive_read *);
+	}	formats[16];
+	struct archive_format_descriptor	*format; /* Active format. */
+
+	/*
+	 * Various information needed by archive_extract.
+	 */
+	struct archive_read_extract		*extract;
+	int			(*cleanup_archive_extract)(struct archive_read *);
+
+	/*
+	 * Decryption passphrase.
+	 */
+	struct {
+		struct archive_read_passphrase *first;
+		struct archive_read_passphrase **last;
+		int candidate;
+		archive_passphrase_callback *callback;
+		void *client_data;
+	}		passphrases;
+};
+
+int	__archive_read_register_format(struct archive_read *a,
+		void *format_data,
+		const char *name,
+		int (*bid)(struct archive_read *, int),
+		int (*options)(struct archive_read *, const char *, const char *),
+		int (*read_header)(struct archive_read *, struct archive_entry *),
+		int (*read_data)(struct archive_read *, const void **, size_t *, int64_t *),
+		int (*read_data_skip)(struct archive_read *),
+		int64_t (*seek_data)(struct archive_read *, int64_t, int),
+		int (*cleanup)(struct archive_read *),
+		int (*format_capabilities)(struct archive_read *),
+		int (*has_encrypted_entries)(struct archive_read *));
+
+int __archive_read_get_bidder(struct archive_read *a,
+    struct archive_read_filter_bidder **bidder);
+
+const void *__archive_read_ahead(struct archive_read *, size_t, ssize_t *);
+const void *__archive_read_filter_ahead(struct archive_read_filter *,
+    size_t, ssize_t *);
+int64_t	__archive_read_seek(struct archive_read*, int64_t, int);
+int64_t	__archive_read_filter_seek(struct archive_read_filter *, int64_t, int);
+int64_t	__archive_read_consume(struct archive_read *, int64_t);
+int64_t	__archive_read_filter_consume(struct archive_read_filter *, int64_t);
+int __archive_read_header(struct archive_read *, struct archive_entry *);
+int __archive_read_program(struct archive_read_filter *, const char *);
+void __archive_read_free_filters(struct archive_read *);
+struct archive_read_extract *__archive_read_get_extract(struct archive_read *);
+
+
+/*
+ * Get a decryption passphrase.
+ */
+void __archive_read_reset_passphrase(struct archive_read *a);
+const char * __archive_read_next_passphrase(struct archive_read *a);
+#endif
diff --git a/c/archive_read_set_format.c b/c/archive_read_set_format.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_set_format.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 2003-2012 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+int
+archive_read_set_format(struct archive *_a, int code)
+{
+  int r1, r2, slots, i;
+  char str[10];
+  struct archive_read *a = (struct archive_read *)_a;
+
+  if ((r1 = archive_read_support_format_by_code(_a, code)) < (ARCHIVE_OK))
+    return r1;
+
+  r1 = r2 = (ARCHIVE_OK);
+  if (a->format)
+    r2 = (ARCHIVE_WARN);
+  switch (code & ARCHIVE_FORMAT_BASE_MASK)
+  {
+    case ARCHIVE_FORMAT_7ZIP:
+      strcpy(str, "7zip");
+      break;
+    case ARCHIVE_FORMAT_AR:
+      strcpy(str, "ar");
+      break;
+    case ARCHIVE_FORMAT_CAB:
+      strcpy(str, "cab");
+      break;
+    case ARCHIVE_FORMAT_CPIO:
+      strcpy(str, "cpio");
+      break;
+    case ARCHIVE_FORMAT_ISO9660:
+      strcpy(str, "iso9660");
+      break;
+    case ARCHIVE_FORMAT_LHA:
+      strcpy(str, "lha");
+      break;
+    case ARCHIVE_FORMAT_MTREE:
+      strcpy(str, "mtree");
+      break;
+    case ARCHIVE_FORMAT_RAR:
+      strcpy(str, "rar");
+      break;
+    case ARCHIVE_FORMAT_RAR_V5:
+      strcpy(str, "rar5");
+      break;
+    case ARCHIVE_FORMAT_TAR:
+      strcpy(str, "tar");
+      break;
+    case ARCHIVE_FORMAT_XAR:
+      strcpy(str, "xar");
+      break;
+    case ARCHIVE_FORMAT_ZIP:
+      strcpy(str, "zip");
+      break;
+    default:
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+          "Invalid format code specified");
+      return (ARCHIVE_FATAL);
+  }
+
+  slots = sizeof(a->formats) / sizeof(a->formats[0]);
+  a->format = &(a->formats[0]);
+  for (i = 0; i < slots; i++, a->format++) {
+    if (!a->format->name || !strcmp(a->format->name, str))
+      break;
+  }
+  if (!a->format->name || strcmp(a->format->name, str))
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+        "Internal error: Unable to set format");
+    r1 = (ARCHIVE_FATAL);
+  }
+
+  return (r1 < r2) ? r1 : r2;
+}
diff --git a/c/archive_read_set_options.c b/c/archive_read_set_options.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_set_options.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 2011 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$");
+
+#include "archive_read_private.h"
+#include "archive_options_private.h"
+
+static int	archive_set_format_option(struct archive *a,
+		    const char *m, const char *o, const char *v);
+static int	archive_set_filter_option(struct archive *a,
+		    const char *m, const char *o, const char *v);
+static int	archive_set_option(struct archive *a,
+		    const char *m, const char *o, const char *v);
+
+int
+archive_read_set_format_option(struct archive *a, const char *m, const char *o,
+    const char *v)
+{
+	return _archive_set_option(a, m, o, v,
+	    ARCHIVE_READ_MAGIC, "archive_read_set_format_option",
+	    archive_set_format_option);
+}
+
+int
+archive_read_set_filter_option(struct archive *a, const char *m, const char *o,
+    const char *v)
+{
+	return _archive_set_option(a, m, o, v,
+	    ARCHIVE_READ_MAGIC, "archive_read_set_filter_option",
+	    archive_set_filter_option);
+}
+
+int
+archive_read_set_option(struct archive *a, const char *m, const char *o,
+    const char *v)
+{
+	return _archive_set_option(a, m, o, v,
+	    ARCHIVE_READ_MAGIC, "archive_read_set_option",
+	    archive_set_option);
+}
+
+int
+archive_read_set_options(struct archive *a, const char *options)
+{
+	return _archive_set_options(a, options,
+	    ARCHIVE_READ_MAGIC, "archive_read_set_options",
+	    archive_set_option);
+}
+
+static int
+archive_set_format_option(struct archive *_a, const char *m, const char *o,
+    const char *v)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	size_t i;
+	int r, rv = ARCHIVE_WARN, matched_modules = 0;
+
+	for (i = 0; i < sizeof(a->formats)/sizeof(a->formats[0]); i++) {
+		struct archive_format_descriptor *format = &a->formats[i];
+
+		if (format->options == NULL || format->name == NULL)
+			/* This format does not support option. */
+			continue;
+		if (m != NULL) {
+			if (strcmp(format->name, m) != 0)
+				continue;
+			++matched_modules;
+		}
+
+		a->format = format;
+		r = format->options(a, o, v);
+		a->format = NULL;
+
+		if (r == ARCHIVE_FATAL)
+			return (ARCHIVE_FATAL);
+
+		if (r == ARCHIVE_OK)
+			rv = ARCHIVE_OK;
+	}
+	/* If the format name didn't match, return a special code for
+	 * _archive_set_option[s]. */
+	if (m != NULL && matched_modules == 0)
+		return ARCHIVE_WARN - 1;
+	return (rv);
+}
+
+static int
+archive_set_filter_option(struct archive *_a, const char *m, const char *o,
+    const char *v)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter *filter;
+	struct archive_read_filter_bidder *bidder;
+	int r, rv = ARCHIVE_WARN, matched_modules = 0;
+
+	for (filter = a->filter; filter != NULL; filter = filter->upstream) {
+		bidder = filter->bidder;
+		if (bidder == NULL)
+			continue;
+		if (bidder->options == NULL)
+			/* This bidder does not support option */
+			continue;
+		if (m != NULL) {
+			if (strcmp(filter->name, m) != 0)
+				continue;
+			++matched_modules;
+		}
+
+		r = bidder->options(bidder, o, v);
+
+		if (r == ARCHIVE_FATAL)
+			return (ARCHIVE_FATAL);
+
+		if (r == ARCHIVE_OK)
+			rv = ARCHIVE_OK;
+	}
+	/* If the filter name didn't match, return a special code for
+	 * _archive_set_option[s]. */
+	if (m != NULL && matched_modules == 0)
+		return ARCHIVE_WARN - 1;
+	return (rv);
+}
+
+static int
+archive_set_option(struct archive *a, const char *m, const char *o,
+    const char *v)
+{
+	return _archive_set_either_option(a, m, o, v,
+	    archive_set_format_option,
+	    archive_set_filter_option);
+}
diff --git a/c/archive_read_support_filter_all.c b/c/archive_read_support_filter_all.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_all.c
@@ -0,0 +1,85 @@
+/*-
+ * Copyright (c) 2003-2011 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$");
+
+#include "archive.h"
+#include "archive_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_all(struct archive *a)
+{
+	return archive_read_support_filter_all(a);
+}
+#endif
+
+int
+archive_read_support_filter_all(struct archive *a)
+{
+	archive_check_magic(a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_all");
+
+	/* Bzip falls back to "bunzip2" command-line */
+	archive_read_support_filter_bzip2(a);
+	/* The decompress code doesn't use an outside library. */
+	archive_read_support_filter_compress(a);
+	/* Gzip decompress falls back to "gzip -d" command-line. */
+	archive_read_support_filter_gzip(a);
+	/* Lzip falls back to "unlzip" command-line program. */
+	archive_read_support_filter_lzip(a);
+	/* The LZMA file format has a very weak signature, so it
+	 * may not be feasible to keep this here, but we'll try.
+	 * This will come back out if there are problems. */
+	/* Lzma falls back to "unlzma" command-line program. */
+	archive_read_support_filter_lzma(a);
+	/* Xz falls back to "unxz" command-line program. */
+	archive_read_support_filter_xz(a);
+	/* The decode code doesn't use an outside library. */
+	archive_read_support_filter_uu(a);
+	/* The decode code doesn't use an outside library. */
+	archive_read_support_filter_rpm(a);
+	/* The decode code always uses "lrzip -q -d" command-line. */
+	archive_read_support_filter_lrzip(a);
+	/* Lzop decompress falls back to "lzop -d" command-line. */
+	archive_read_support_filter_lzop(a);
+	/* The decode code always uses "grzip -d" command-line. */
+	archive_read_support_filter_grzip(a);
+	/* Lz4 falls back to "lz4 -d" command-line program. */
+	archive_read_support_filter_lz4(a);
+	/* Zstd falls back to "zstd -d" command-line program. */
+	archive_read_support_filter_zstd(a);
+
+	/* Note: We always return ARCHIVE_OK here, even if some of the
+	 * above return ARCHIVE_WARN.  The intent here is to enable
+	 * "as much as possible."  Clients who need specific
+	 * compression should enable those individually so they can
+	 * verify the level of support. */
+	/* Clear any warning messages set by the above functions. */
+	archive_clear_error(a);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_read_support_filter_bzip2.c b/c/archive_read_support_filter_bzip2.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_bzip2.c
@@ -0,0 +1,371 @@
+/*-
+ * 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$");
+
+#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 defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+struct private_data {
+	bz_stream	 stream;
+	char		*out_block;
+	size_t		 out_block_size;
+	char		 valid; /* True = decompressor is initialized */
+	char		 eof; /* True = found end of compressed data. */
+};
+
+/* Bzip2 filter */
+static ssize_t	bzip2_filter_read(struct archive_read_filter *, const void **);
+static int	bzip2_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect bzip2 archives even if we can't decompress
+ * them.  (In fact, we like detecting them because we can give better
+ * error messages.)  So the bid framework here gets compiled even
+ * if bzlib is unavailable.
+ */
+static int	bzip2_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int	bzip2_reader_init(struct archive_read_filter *);
+static int	bzip2_reader_free(struct archive_read_filter_bidder *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_bzip2(struct archive *a)
+{
+	return archive_read_support_filter_bzip2(a);
+}
+#endif
+
+int
+archive_read_support_filter_bzip2(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *reader;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_bzip2");
+
+	if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	reader->data = NULL;
+	reader->name = "bzip2";
+	reader->bid = bzip2_reader_bid;
+	reader->init = bzip2_reader_init;
+	reader->options = NULL;
+	reader->free = bzip2_reader_free;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	return (ARCHIVE_OK);
+#else
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external bzip2 program");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+bzip2_reader_free(struct archive_read_filter_bidder *self){
+	(void)self; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+/*
+ * 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
+bzip2_reader_bid(struct archive_read_filter_bidder *self, struct archive_read_filter *filter)
+{
+	const unsigned char *buffer;
+	ssize_t avail;
+	int bits_checked;
+
+	(void)self; /* UNUSED */
+
+	/* Minimal bzip2 archive is 14 bytes. */
+	buffer = __archive_read_filter_ahead(filter, 14, &avail);
+	if (buffer == NULL)
+		return (0);
+
+	/* First three bytes must be "BZh" */
+	bits_checked = 0;
+	if (memcmp(buffer, "BZh", 3) != 0)
+		return (0);
+	bits_checked += 24;
+
+	/* Next follows a compression flag which must be an ASCII digit. */
+	if (buffer[3] < '1' || buffer[3] > '9')
+		return (0);
+	bits_checked += 5;
+
+	/* After BZh[1-9], there must be either a data block
+	 * which begins with 0x314159265359 or an end-of-data
+	 * marker of 0x177245385090. */
+	if (memcmp(buffer + 4, "\x31\x41\x59\x26\x53\x59", 6) == 0)
+		bits_checked += 48;
+	else if (memcmp(buffer + 4, "\x17\x72\x45\x38\x50\x90", 6) == 0)
+		bits_checked += 48;
+	else
+		return (0);
+
+	return (bits_checked);
+}
+
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
+
+/*
+ * If we don't have the library on this system, we can't actually do the
+ * decompression.  We can, however, still detect compressed archives
+ * and emit a useful message.
+ */
+static int
+bzip2_reader_init(struct archive_read_filter *self)
+{
+	int r;
+
+	r = __archive_read_program(self, "bzip2 -d");
+	/* Note: We set the format here even if __archive_read_program()
+	 * above fails.  We do, after all, know what the format is
+	 * even if we weren't able to read it. */
+	self->code = ARCHIVE_FILTER_BZIP2;
+	self->name = "bzip2";
+	return (r);
+}
+
+
+#else
+
+/*
+ * Setup the callbacks.
+ */
+static int
+bzip2_reader_init(struct archive_read_filter *self)
+{
+	static const size_t out_block_size = 64 * 1024;
+	void *out_block;
+	struct private_data *state;
+
+	self->code = ARCHIVE_FILTER_BZIP2;
+	self->name = "bzip2";
+
+	state = (struct private_data *)calloc(sizeof(*state), 1);
+	out_block = (unsigned char *)malloc(out_block_size);
+	if (state == NULL || out_block == NULL) {
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate data for bzip2 decompression");
+		free(out_block);
+		free(state);
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = state;
+	state->out_block_size = out_block_size;
+	state->out_block = out_block;
+	self->read = bzip2_filter_read;
+	self->skip = NULL; /* not supported */
+	self->close = bzip2_filter_close;
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Return the next block of decompressed data.
+ */
+static ssize_t
+bzip2_filter_read(struct archive_read_filter *self, const void **p)
+{
+	struct private_data *state;
+	size_t decompressed;
+	const char *read_buf;
+	ssize_t ret;
+
+	state = (struct private_data *)self->data;
+
+	if (state->eof) {
+		*p = NULL;
+		return (0);
+	}
+
+	/* Empty our output buffer. */
+	state->stream.next_out = state->out_block;
+	state->stream.avail_out = state->out_block_size;
+
+	/* Try to fill the output buffer. */
+	for (;;) {
+		if (!state->valid) {
+			if (bzip2_reader_bid(self->bidder, self->upstream) == 0) {
+				state->eof = 1;
+				*p = state->out_block;
+				decompressed = state->stream.next_out
+				    - state->out_block;
+				return (decompressed);
+			}
+			/* Initialize compression library. */
+			ret = BZ2_bzDecompressInit(&(state->stream),
+					   0 /* library verbosity */,
+					   0 /* don't use low-mem algorithm */);
+
+			/* If init fails, try low-memory algorithm instead. */
+			if (ret == BZ_MEM_ERROR)
+				ret = BZ2_bzDecompressInit(&(state->stream),
+					   0 /* library verbosity */,
+					   1 /* do use low-mem algo */);
+
+			if (ret != BZ_OK) {
+				const char *detail = NULL;
+				int err = ARCHIVE_ERRNO_MISC;
+				switch (ret) {
+				case BZ_PARAM_ERROR:
+					detail = "invalid setup parameter";
+					break;
+				case BZ_MEM_ERROR:
+					err = ENOMEM;
+					detail = "out of memory";
+					break;
+				case BZ_CONFIG_ERROR:
+					detail = "mis-compiled library";
+					break;
+				}
+				archive_set_error(&self->archive->archive, err,
+				    "Internal error initializing decompressor%s%s",
+				    detail == NULL ? "" : ": ",
+				    detail);
+				return (ARCHIVE_FATAL);
+			}
+			state->valid = 1;
+		}
+
+		/* stream.next_in is really const, but bzlib
+		 * doesn't declare it so. <sigh> */
+		read_buf =
+		    __archive_read_filter_ahead(self->upstream, 1, &ret);
+		if (read_buf == NULL) {
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "truncated bzip2 input");
+			return (ARCHIVE_FATAL);
+		}
+		state->stream.next_in = (char *)(uintptr_t)read_buf;
+		state->stream.avail_in = ret;
+		/* There is no more data, return whatever we have. */
+		if (ret == 0) {
+			state->eof = 1;
+			*p = state->out_block;
+			decompressed = state->stream.next_out
+			    - state->out_block;
+			return (decompressed);
+		}
+
+		/* Decompress as much as we can in one pass. */
+		ret = BZ2_bzDecompress(&(state->stream));
+		__archive_read_filter_consume(self->upstream,
+		    state->stream.next_in - read_buf);
+
+		switch (ret) {
+		case BZ_STREAM_END: /* Found end of stream. */
+			switch (BZ2_bzDecompressEnd(&(state->stream))) {
+			case BZ_OK:
+				break;
+			default:
+				archive_set_error(&(self->archive->archive),
+					  ARCHIVE_ERRNO_MISC,
+					  "Failed to clean up decompressor");
+				return (ARCHIVE_FATAL);
+			}
+			state->valid = 0;
+			/* FALLTHROUGH */
+		case BZ_OK: /* Decompressor made some progress. */
+			/* If we filled our buffer, update stats and return. */
+			if (state->stream.avail_out == 0) {
+				*p = state->out_block;
+				decompressed = state->stream.next_out
+				    - state->out_block;
+				return (decompressed);
+			}
+			break;
+		default: /* Return an error. */
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_MISC, "bzip decompression failed");
+			return (ARCHIVE_FATAL);
+		}
+	}
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+bzip2_filter_close(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	int ret = ARCHIVE_OK;
+
+	state = (struct private_data *)self->data;
+
+	if (state->valid) {
+		switch (BZ2_bzDecompressEnd(&state->stream)) {
+		case BZ_OK:
+			break;
+		default:
+			archive_set_error(&self->archive->archive,
+					  ARCHIVE_ERRNO_MISC,
+					  "Failed to clean up decompressor");
+			ret = ARCHIVE_FATAL;
+		}
+		state->valid = 0;
+	}
+
+	free(state->out_block);
+	free(state);
+	return (ret);
+}
+
+#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
diff --git a/c/archive_read_support_filter_compress.c b/c/archive_read_support_filter_compress.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_compress.c
@@ -0,0 +1,465 @@
+/*-
+ * 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$");
+
+#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;
+	size_t			 consume_unnotified;
+	int			 bit_buffer;
+	int			 bits_avail;
+	size_t			 bytes_in_section;
+
+	/* Output variables. */
+	size_t			 out_block_size;
+	void			*out_block;
+
+	/* 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	compress_bidder_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int	compress_bidder_init(struct archive_read_filter *);
+static int	compress_bidder_free(struct archive_read_filter_bidder *);
+
+static ssize_t	compress_filter_read(struct archive_read_filter *, const void **);
+static int	compress_filter_close(struct archive_read_filter *);
+
+static int	getbits(struct archive_read_filter *, int n);
+static int	next_code(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_compress(struct archive *a)
+{
+	return archive_read_support_filter_compress(a);
+}
+#endif
+
+int
+archive_read_support_filter_compress(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *bidder;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_compress");
+
+	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	bidder->data = NULL;
+	bidder->name = "compress (.Z)";
+	bidder->bid = compress_bidder_bid;
+	bidder->init = compress_bidder_init;
+	bidder->options = NULL;
+	bidder->free = compress_bidder_free;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Test whether we can handle this data.
+ * This logic returns zero if any part of the signature fails.
+ */
+static int
+compress_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	const unsigned char *buffer;
+	ssize_t avail;
+	int bits_checked;
+
+	(void)self; /* UNUSED */
+
+	/* Shortest valid compress file is 3 bytes. */
+	buffer = __archive_read_filter_ahead(filter, 3, &avail);
+
+	if (buffer == NULL)
+		return (0);
+
+	bits_checked = 0;
+	/* First two bytes are the magic value */
+	if (buffer[0] != 0x1F || buffer[1] != 0x9D)
+		return (0);
+	/* Third byte holds compression parameters. */
+	if (buffer[2] & 0x20) /* Reserved bit, must be zero. */
+		return (0);
+	if (buffer[2] & 0x40) /* Reserved bit, must be zero. */
+		return (0);
+	bits_checked += 18;
+
+	return (bits_checked);
+}
+
+/*
+ * Setup the callbacks.
+ */
+static int
+compress_bidder_init(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	static const size_t out_block_size = 64 * 1024;
+	void *out_block;
+	int code;
+
+	self->code = ARCHIVE_FILTER_COMPRESS;
+	self->name = "compress (.Z)";
+
+	state = (struct private_data *)calloc(sizeof(*state), 1);
+	out_block = malloc(out_block_size);
+	if (state == NULL || out_block == NULL) {
+		free(out_block);
+		free(state);
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate data for %s decompression",
+		    self->name);
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = state;
+	state->out_block_size = out_block_size;
+	state->out_block = out_block;
+	self->read = compress_filter_read;
+	self->skip = NULL; /* not supported */
+	self->close = compress_filter_close;
+
+	/* XXX MOVE THE FOLLOWING OUT OF INIT() XXX */
+
+	(void)getbits(self, 8); /* Skip first signature byte. */
+	(void)getbits(self, 8); /* Skip second signature byte. */
+
+	/* Get compression parameters. */
+	code = getbits(self, 8);
+	if ((code & 0x1f) > 16) {
+		archive_set_error(&self->archive->archive, -1,
+		    "Invalid compressed data");
+		return (ARCHIVE_FATAL);
+	}
+	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(self);
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Return a block of data from the decompression buffer.  Decompress more
+ * as necessary.
+ */
+static ssize_t
+compress_filter_read(struct archive_read_filter *self, const void **pblock)
+{
+	struct private_data *state;
+	unsigned char *p, *start, *end;
+	int ret;
+
+	state = (struct private_data *)self->data;
+	if (state->end_of_stream) {
+		*pblock = NULL;
+		return (0);
+	}
+	p = start = (unsigned char *)state->out_block;
+	end = start + state->out_block_size;
+
+	while (p < end && !state->end_of_stream) {
+		if (state->stackp > state->stack) {
+			*p++ = *--state->stackp;
+		} else {
+			ret = next_code(self);
+			if (ret == -1)
+				state->end_of_stream = ret;
+			else if (ret != ARCHIVE_OK)
+				return (ret);
+		}
+	}
+
+	*pblock = start;
+	return (p - start);
+}
+
+/*
+ * Clean up the reader.
+ */
+static int
+compress_bidder_free(struct archive_read_filter_bidder *self)
+{
+	self->data = NULL;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Close and release the filter.
+ */
+static int
+compress_filter_close(struct archive_read_filter *self)
+{
+	struct private_data *state = (struct private_data *)self->data;
+
+	free(state->out_block);
+	free(state);
+	return (ARCHIVE_OK);
+}
+
+/*
+ * 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_filter *self)
+{
+	struct private_data *state = (struct private_data *)self->data;
+	int code, newcode;
+
+	static int debug_buff[1024];
+	static unsigned debug_index;
+
+	code = newcode = getbits(self, 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(self, 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(self));
+	}
+
+	if (code > state->free_ent
+	    || (code == state->free_ent && state->oldcode < 0)) {
+		/* An invalid code is a fatal error. */
+		archive_set_error(&(self->archive->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_filter *self, int n)
+{
+	struct private_data *state = (struct private_data *)self->data;
+	int code;
+	ssize_t 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) {
+			if (state->consume_unnotified) {
+				__archive_read_filter_consume(self->upstream,
+					state->consume_unnotified);
+				state->consume_unnotified = 0;
+			}
+			state->next_in
+			    = __archive_read_filter_ahead(self->upstream,
+				1, &ret);
+			if (ret == 0)
+				return (-1);
+			if (ret < 0 || state->next_in == NULL)
+				return (ARCHIVE_FATAL);
+			state->consume_unnotified = 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/c/archive_read_support_filter_grzip.c b/c/archive_read_support_filter_grzip.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_grzip.c
@@ -0,0 +1,121 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * 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$");
+
+
+#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"
+
+static const unsigned char grzip_magic[] = {
+	0x47, 0x52, 0x5a, 0x69, 0x70, 0x49, 0x49, 0x00,
+	0x02, 0x04, 0x3a, 0x29 };
+
+static int	grzip_bidder_bid(struct archive_read_filter_bidder *,
+		    struct archive_read_filter *);
+static int	grzip_bidder_init(struct archive_read_filter *);
+
+
+static int
+grzip_reader_free(struct archive_read_filter_bidder *self)
+{
+	(void)self; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_support_filter_grzip(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *reader;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_grzip");
+
+	if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	reader->data = NULL;
+	reader->bid = grzip_bidder_bid;
+	reader->init = grzip_bidder_init;
+	reader->options = NULL;
+	reader->free = grzip_reader_free;
+	/* This filter always uses an external program. */
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external grzip program for grzip decompression");
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+grzip_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	const unsigned char *p;
+	ssize_t avail;
+
+	(void)self; /* UNUSED */
+
+	p = __archive_read_filter_ahead(filter, sizeof(grzip_magic), &avail);
+	if (p == NULL || avail == 0)
+		return (0);
+
+	if (memcmp(p, grzip_magic, sizeof(grzip_magic)))
+		return (0);
+
+	return (sizeof(grzip_magic) * 8);
+}
+
+static int
+grzip_bidder_init(struct archive_read_filter *self)
+{
+	int r;
+
+	r = __archive_read_program(self, "grzip -d");
+	/* Note: We set the format here even if __archive_read_program()
+	 * above fails.  We do, after all, know what the format is
+	 * even if we weren't able to read it. */
+	self->code = ARCHIVE_FILTER_GRZIP;
+	self->name = "grzip";
+	return (r);
+}
diff --git a/c/archive_read_support_filter_gzip.c b/c/archive_read_support_filter_gzip.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_gzip.c
@@ -0,0 +1,535 @@
+/*-
+ * 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$");
+
+
+#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_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#ifdef HAVE_ZLIB_H
+struct private_data {
+	z_stream	 stream;
+	char		 in_stream;
+	unsigned char	*out_block;
+	size_t		 out_block_size;
+	int64_t		 total_out;
+	unsigned long	 crc;
+	uint32_t	 mtime;
+	char		*name;
+	char		 eof; /* True = found end of compressed data. */
+};
+
+/* Gzip Filter. */
+static ssize_t	gzip_filter_read(struct archive_read_filter *, const void **);
+static int	gzip_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect gzip archives even if we can't decompress
+ * them.  (In fact, we like detecting them because we can give better
+ * error messages.)  So the bid framework here gets compiled even
+ * if zlib is unavailable.
+ *
+ * TODO: If zlib is unavailable, gzip_bidder_init() should
+ * use the compress_program framework to try to fire up an external
+ * gzip program.
+ */
+static int	gzip_bidder_bid(struct archive_read_filter_bidder *,
+		    struct archive_read_filter *);
+static int	gzip_bidder_init(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_gzip(struct archive *a)
+{
+	return archive_read_support_filter_gzip(a);
+}
+#endif
+
+int
+archive_read_support_filter_gzip(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *bidder;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_gzip");
+
+	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	bidder->data = NULL;
+	bidder->name = "gzip";
+	bidder->bid = gzip_bidder_bid;
+	bidder->init = gzip_bidder_init;
+	bidder->options = NULL;
+	bidder->free = NULL; /* No data, so no cleanup necessary. */
+	/* Signal the extent of gzip support with the return value here. */
+#if HAVE_ZLIB_H
+	return (ARCHIVE_OK);
+#else
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external gzip program");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Read and verify the header.
+ *
+ * Returns zero if the header couldn't be validated, else returns
+ * number of bytes in header.  If pbits is non-NULL, it receives a
+ * count of bits verified, suitable for use by bidder.
+ */
+static ssize_t
+peek_at_header(struct archive_read_filter *filter, int *pbits,
+#ifdef HAVE_ZLIB_H
+	       struct private_data *state
+#else
+	       void *state
+#endif
+	      )
+{
+	const unsigned char *p;
+	ssize_t avail, len;
+	int bits = 0;
+	int header_flags;
+#ifndef HAVE_ZLIB_H
+	(void)state; /* UNUSED */
+#endif
+
+	/* Start by looking at the first ten bytes of the header, which
+	 * is all fixed layout. */
+	len = 10;
+	p = __archive_read_filter_ahead(filter, len, &avail);
+	if (p == NULL || avail == 0)
+		return (0);
+	/* We only support deflation- third byte must be 0x08. */
+	if (memcmp(p, "\x1F\x8B\x08", 3) != 0)
+		return (0);
+	bits += 24;
+	if ((p[3] & 0xE0)!= 0)	/* No reserved flags set. */
+		return (0);
+	bits += 3;
+	header_flags = p[3];
+	/* Bytes 4-7 are mod time in little endian. */
+#ifdef HAVE_ZLIB_H
+	if (state)
+		state->mtime = archive_le32dec(p + 4);
+#endif
+	/* Byte 8 is deflate flags. */
+	/* XXXX TODO: return deflate flags back to consume_header for use
+	   in initializing the decompressor. */
+	/* Byte 9 is OS. */
+
+	/* Optional extra data:  2 byte length plus variable body. */
+	if (header_flags & 4) {
+		p = __archive_read_filter_ahead(filter, len + 2, &avail);
+		if (p == NULL)
+			return (0);
+		len += ((int)p[len + 1] << 8) | (int)p[len];
+		len += 2;
+	}
+
+	/* Null-terminated optional filename. */
+	if (header_flags & 8) {
+#ifdef HAVE_ZLIB_H
+		ssize_t file_start = len;
+#endif
+		do {
+			++len;
+			if (avail < len)
+				p = __archive_read_filter_ahead(filter,
+				    len, &avail);
+			if (p == NULL)
+				return (0);
+		} while (p[len - 1] != 0);
+
+#ifdef HAVE_ZLIB_H
+		if (state) {
+			/* Reset the name in case of repeat header reads. */
+			free(state->name);
+			state->name = strdup((const char *)&p[file_start]);
+		}
+#endif
+	}
+
+	/* Null-terminated optional comment. */
+	if (header_flags & 16) {
+		do {
+			++len;
+			if (avail < len)
+				p = __archive_read_filter_ahead(filter,
+				    len, &avail);
+			if (p == NULL)
+				return (0);
+		} while (p[len - 1] != 0);
+	}
+
+	/* Optional header CRC */
+	if ((header_flags & 2)) {
+		p = __archive_read_filter_ahead(filter, len + 2, &avail);
+		if (p == NULL)
+			return (0);
+#if 0
+	int hcrc = ((int)p[len + 1] << 8) | (int)p[len];
+	int crc = /* XXX TODO: Compute header CRC. */;
+	if (crc != hcrc)
+		return (0);
+	bits += 16;
+#endif
+		len += 2;
+	}
+
+	if (pbits != NULL)
+		*pbits = bits;
+	return (len);
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+gzip_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	int bits_checked;
+
+	(void)self; /* UNUSED */
+
+	if (peek_at_header(filter, &bits_checked, NULL))
+		return (bits_checked);
+	return (0);
+}
+
+#ifndef HAVE_ZLIB_H
+
+/*
+ * If we don't have the library on this system, we can't do the
+ * decompression directly.  We can, however, try to run "gzip -d"
+ * in case that's available.
+ */
+static int
+gzip_bidder_init(struct archive_read_filter *self)
+{
+	int r;
+
+	r = __archive_read_program(self, "gzip -d");
+	/* Note: We set the format here even if __archive_read_program()
+	 * above fails.  We do, after all, know what the format is
+	 * even if we weren't able to read it. */
+	self->code = ARCHIVE_FILTER_GZIP;
+	self->name = "gzip";
+	return (r);
+}
+
+#else
+
+static int
+gzip_read_header(struct archive_read_filter *self, struct archive_entry *entry)
+{
+	struct private_data *state;
+
+	state = (struct private_data *)self->data;
+
+	/* A mtime of 0 is considered invalid/missing. */
+	if (state->mtime != 0)
+		archive_entry_set_mtime(entry, state->mtime, 0);
+
+	/* If the name is available, extract it. */
+	if (state->name)
+		archive_entry_set_pathname(entry, state->name);
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Initialize the filter object.
+ */
+static int
+gzip_bidder_init(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	static const size_t out_block_size = 64 * 1024;
+	void *out_block;
+
+	self->code = ARCHIVE_FILTER_GZIP;
+	self->name = "gzip";
+
+	state = (struct private_data *)calloc(sizeof(*state), 1);
+	out_block = (unsigned char *)malloc(out_block_size);
+	if (state == NULL || out_block == NULL) {
+		free(out_block);
+		free(state);
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate data for gzip decompression");
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = state;
+	state->out_block_size = out_block_size;
+	state->out_block = out_block;
+	self->read = gzip_filter_read;
+	self->skip = NULL; /* not supported */
+	self->close = gzip_filter_close;
+#ifdef HAVE_ZLIB_H
+	self->read_header = gzip_read_header;
+#endif
+
+	state->in_stream = 0; /* We're not actually within a stream yet. */
+
+	return (ARCHIVE_OK);
+}
+
+static int
+consume_header(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	ssize_t avail;
+	size_t len;
+	int ret;
+
+	state = (struct private_data *)self->data;
+
+	/* If this is a real header, consume it. */
+	len = peek_at_header(self->upstream, NULL, state);
+	if (len == 0)
+		return (ARCHIVE_EOF);
+	__archive_read_filter_consume(self->upstream, len);
+
+	/* Initialize CRC accumulator. */
+	state->crc = crc32(0L, NULL, 0);
+
+	/* Initialize compression library. */
+	state->stream.next_in = (unsigned char *)(uintptr_t)
+	    __archive_read_filter_ahead(self->upstream, 1, &avail);
+	state->stream.avail_in = (uInt)avail;
+	ret = inflateInit2(&(state->stream),
+	    -15 /* Don't check for zlib header */);
+
+	/* Decipher the error code. */
+	switch (ret) {
+	case Z_OK:
+		state->in_stream = 1;
+		return (ARCHIVE_OK);
+	case Z_STREAM_ERROR:
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library: "
+		    "invalid setup parameter");
+		break;
+	case Z_MEM_ERROR:
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Internal error initializing compression library: "
+		    "out of memory");
+		break;
+	case Z_VERSION_ERROR:
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library: "
+		    "invalid library version");
+		break;
+	default:
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library: "
+		    " Zlib error %d", ret);
+		break;
+	}
+	return (ARCHIVE_FATAL);
+}
+
+static int
+consume_trailer(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	const unsigned char *p;
+	ssize_t avail;
+
+	state = (struct private_data *)self->data;
+
+	state->in_stream = 0;
+	switch (inflateEnd(&(state->stream))) {
+	case Z_OK:
+		break;
+	default:
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Failed to clean up gzip decompressor");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* GZip trailer is a fixed 8 byte structure. */
+	p = __archive_read_filter_ahead(self->upstream, 8, &avail);
+	if (p == NULL || avail == 0)
+		return (ARCHIVE_FATAL);
+
+	/* XXX TODO: Verify the length and CRC. */
+
+	/* We've verified the trailer, so consume it now. */
+	__archive_read_filter_consume(self->upstream, 8);
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+gzip_filter_read(struct archive_read_filter *self, const void **p)
+{
+	struct private_data *state;
+	size_t decompressed;
+	ssize_t avail_in, max_in;
+	int ret;
+
+	state = (struct private_data *)self->data;
+
+	/* Empty our output buffer. */
+	state->stream.next_out = state->out_block;
+	state->stream.avail_out = (uInt)state->out_block_size;
+
+	/* Try to fill the output buffer. */
+	while (state->stream.avail_out > 0 && !state->eof) {
+		/* If we're not in a stream, read a header
+		 * and initialize the decompression library. */
+		if (!state->in_stream) {
+			ret = consume_header(self);
+			if (ret == ARCHIVE_EOF) {
+				state->eof = 1;
+				break;
+			}
+			if (ret < ARCHIVE_OK)
+				return (ret);
+		}
+
+		/* Peek at the next available data. */
+		/* ZLib treats stream.next_in as const but doesn't declare
+		 * it so, hence this ugly cast. */
+		state->stream.next_in = (unsigned char *)(uintptr_t)
+		    __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+		if (state->stream.next_in == NULL) {
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "truncated gzip input");
+			return (ARCHIVE_FATAL);
+		}
+		if (UINT_MAX >= SSIZE_MAX)
+			max_in = SSIZE_MAX;
+		else
+			max_in = UINT_MAX;
+		if (avail_in > max_in)
+			avail_in = max_in;
+		state->stream.avail_in = (uInt)avail_in;
+
+		/* Decompress and consume some of that data. */
+		ret = inflate(&(state->stream), 0);
+		switch (ret) {
+		case Z_OK: /* Decompressor made some progress. */
+			__archive_read_filter_consume(self->upstream,
+			    avail_in - state->stream.avail_in);
+			break;
+		case Z_STREAM_END: /* Found end of stream. */
+			__archive_read_filter_consume(self->upstream,
+			    avail_in - state->stream.avail_in);
+			/* Consume the stream trailer; release the
+			 * decompression library. */
+			ret = consume_trailer(self);
+			if (ret < ARCHIVE_OK)
+				return (ret);
+			break;
+		default:
+			/* Return an error. */
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "gzip decompression failed");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	/* We've read as much as we can. */
+	decompressed = state->stream.next_out - state->out_block;
+	state->total_out += decompressed;
+	if (decompressed == 0)
+		*p = NULL;
+	else
+		*p = state->out_block;
+	return (decompressed);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+gzip_filter_close(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	int ret;
+
+	state = (struct private_data *)self->data;
+	ret = ARCHIVE_OK;
+
+	if (state->in_stream) {
+		switch (inflateEnd(&(state->stream))) {
+		case Z_OK:
+			break;
+		default:
+			archive_set_error(&(self->archive->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "Failed to clean up gzip compressor");
+			ret = ARCHIVE_FATAL;
+		}
+	}
+
+	free(state->name);
+	free(state->out_block);
+	free(state);
+	return (ret);
+}
+
+#endif /* HAVE_ZLIB_H */
diff --git a/c/archive_read_support_filter_lrzip.c b/c/archive_read_support_filter_lrzip.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_lrzip.c
@@ -0,0 +1,132 @@
+/*-
+ * 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$");
+
+
+#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"
+
+#define LRZIP_HEADER_MAGIC "LRZI"
+#define LRZIP_HEADER_MAGIC_LEN 4
+
+static int	lrzip_bidder_bid(struct archive_read_filter_bidder *,
+		    struct archive_read_filter *);
+static int	lrzip_bidder_init(struct archive_read_filter *);
+
+
+static int
+lrzip_reader_free(struct archive_read_filter_bidder *self)
+{
+	(void)self; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_support_filter_lrzip(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *reader;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_lrzip");
+
+	if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	reader->data = NULL;
+	reader->name = "lrzip";
+	reader->bid = lrzip_bidder_bid;
+	reader->init = lrzip_bidder_init;
+	reader->options = NULL;
+	reader->free = lrzip_reader_free;
+	/* This filter always uses an external program. */
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external lrzip program for lrzip decompression");
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+lrzip_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	const unsigned char *p;
+	ssize_t avail, len;
+	int i;
+
+	(void)self; /* UNUSED */
+	/* Start by looking at the first six bytes of the header, which
+	 * is all fixed layout. */
+	len = 6;
+	p = __archive_read_filter_ahead(filter, len, &avail);
+	if (p == NULL || avail == 0)
+		return (0);
+
+	if (memcmp(p, LRZIP_HEADER_MAGIC, LRZIP_HEADER_MAGIC_LEN))
+		return (0);
+
+	/* current major version is always 0, verify this */
+	if (p[LRZIP_HEADER_MAGIC_LEN])
+		return 0;
+	/* support only v0.6+ lrzip for sanity */
+	i = p[LRZIP_HEADER_MAGIC_LEN + 1];
+	if ((i < 6) || (i > 10))
+		return 0;
+
+	return (int)len;
+}
+
+static int
+lrzip_bidder_init(struct archive_read_filter *self)
+{
+	int r;
+
+	r = __archive_read_program(self, "lrzip -d -q");
+	/* Note: We set the format here even if __archive_read_program()
+	 * above fails.  We do, after all, know what the format is
+	 * even if we weren't able to read it. */
+	self->code = ARCHIVE_FILTER_LRZIP;
+	self->name = "lrzip";
+	return (r);
+}
diff --git a/c/archive_read_support_filter_lz4.c b/c/archive_read_support_filter_lz4.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_lz4.c
@@ -0,0 +1,742 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * 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$");
+
+#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_LZ4_H
+#include <lz4.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_xxhash.h"
+
+#define LZ4_MAGICNUMBER		0x184d2204
+#define LZ4_SKIPPABLED		0x184d2a50
+#define LZ4_LEGACY		0x184c2102
+
+#if defined(HAVE_LIBLZ4)
+struct private_data {
+	enum {  SELECT_STREAM,
+		READ_DEFAULT_STREAM,
+		READ_DEFAULT_BLOCK,
+		READ_LEGACY_STREAM,
+		READ_LEGACY_BLOCK,
+	}		stage;
+	struct {
+		unsigned block_independence:1;
+		unsigned block_checksum:3;
+		unsigned stream_size:1;
+		unsigned stream_checksum:1;
+		unsigned preset_dictionary:1;
+		int	 block_maximum_size;
+	} flags;
+	int64_t		 stream_size;
+	uint32_t	 dict_id;
+	char		*out_block;
+	size_t		 out_block_size;
+
+	/* Bytes read but not yet consumed via __archive_read_consume() */
+	size_t		 unconsumed;
+	size_t		 decoded_size;
+	void		*xxh32_state;
+
+	char		 valid; /* True = decompressor is initialized */
+	char		 eof; /* True = found end of compressed data. */
+};
+
+#define LEGACY_BLOCK_SIZE	(8 * 1024 * 1024)
+
+/* Lz4 filter */
+static ssize_t	lz4_filter_read(struct archive_read_filter *, const void **);
+static int	lz4_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect lz4 archives even if we can't decompress
+ * them.  (In fact, we like detecting them because we can give better
+ * error messages.)  So the bid framework here gets compiled even
+ * if liblz4 is unavailable.
+ */
+static int	lz4_reader_bid(struct archive_read_filter_bidder *, struct archive_read_filter *);
+static int	lz4_reader_init(struct archive_read_filter *);
+static int	lz4_reader_free(struct archive_read_filter_bidder *);
+#if defined(HAVE_LIBLZ4)
+static ssize_t  lz4_filter_read_default_stream(struct archive_read_filter *,
+		    const void **);
+static ssize_t  lz4_filter_read_legacy_stream(struct archive_read_filter *,
+		    const void **);
+#endif
+
+int
+archive_read_support_filter_lz4(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *reader;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_lz4");
+
+	if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	reader->data = NULL;
+	reader->name = "lz4";
+	reader->bid = lz4_reader_bid;
+	reader->init = lz4_reader_init;
+	reader->options = NULL;
+	reader->free = lz4_reader_free;
+#if defined(HAVE_LIBLZ4)
+	return (ARCHIVE_OK);
+#else
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external lz4 program");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+lz4_reader_free(struct archive_read_filter_bidder *self){
+	(void)self; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+/*
+ * 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
+lz4_reader_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	const unsigned char *buffer;
+	ssize_t avail;
+	int bits_checked;
+	uint32_t number;
+
+	(void)self; /* UNUSED */
+
+	/* Minimal lz4 archive is 11 bytes. */
+	buffer = __archive_read_filter_ahead(filter, 11, &avail);
+	if (buffer == NULL)
+		return (0);
+
+	/* First four bytes must be LZ4 magic numbers. */
+	bits_checked = 0;
+	if ((number = archive_le32dec(buffer)) == LZ4_MAGICNUMBER) {
+		unsigned char flag, BD;
+
+		bits_checked += 32;
+		/* Next follows a stream descriptor. */
+		/* Descriptor Flags. */
+		flag = buffer[4];
+		/* A version number must be "01". */
+		if (((flag & 0xc0) >> 6) != 1)
+			return (0);
+		/* A reserved bit must be "0". */
+		if (flag & 2)
+			return (0);
+		bits_checked += 8;
+		BD = buffer[5];
+		/* A block maximum size should be more than 3. */
+		if (((BD & 0x70) >> 4) < 4)
+			return (0);
+		/* Reserved bits must be "0". */
+		if (BD & ~0x70)
+			return (0);
+		bits_checked += 8;
+	} else if (number == LZ4_LEGACY) {
+		bits_checked += 32;
+	}
+	
+	return (bits_checked);
+}
+
+#if !defined(HAVE_LIBLZ4)
+
+/*
+ * If we don't have the library on this system, we can't actually do the
+ * decompression.  We can, however, still detect compressed archives
+ * and emit a useful message.
+ */
+static int
+lz4_reader_init(struct archive_read_filter *self)
+{
+	int r;
+
+	r = __archive_read_program(self, "lz4 -d -q");
+	/* Note: We set the format here even if __archive_read_program()
+	 * above fails.  We do, after all, know what the format is
+	 * even if we weren't able to read it. */
+	self->code = ARCHIVE_FILTER_LZ4;
+	self->name = "lz4";
+	return (r);
+}
+
+
+#else
+
+/*
+ * Setup the callbacks.
+ */
+static int
+lz4_reader_init(struct archive_read_filter *self)
+{
+	struct private_data *state;
+
+	self->code = ARCHIVE_FILTER_LZ4;
+	self->name = "lz4";
+
+	state = (struct private_data *)calloc(sizeof(*state), 1);
+	if (state == NULL) {
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate data for lz4 decompression");
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = state;
+	state->stage = SELECT_STREAM;
+	self->read = lz4_filter_read;
+	self->skip = NULL; /* not supported */
+	self->close = lz4_filter_close;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+lz4_allocate_out_block(struct archive_read_filter *self)
+{
+	struct private_data *state = (struct private_data *)self->data;
+	size_t out_block_size = state->flags.block_maximum_size;
+	void *out_block;
+
+	if (!state->flags.block_independence)
+		out_block_size += 64 * 1024;
+	if (state->out_block_size < out_block_size) {
+		free(state->out_block);
+		out_block = (unsigned char *)malloc(out_block_size);
+		state->out_block_size = out_block_size;
+		if (out_block == NULL) {
+			archive_set_error(&self->archive->archive, ENOMEM,
+			    "Can't allocate data for lz4 decompression");
+			return (ARCHIVE_FATAL);
+		}
+		state->out_block = out_block;
+	}
+	if (!state->flags.block_independence)
+		memset(state->out_block, 0, 64 * 1024);
+	return (ARCHIVE_OK);
+}
+
+static int
+lz4_allocate_out_block_for_legacy(struct archive_read_filter *self)
+{
+	struct private_data *state = (struct private_data *)self->data;
+	size_t out_block_size = LEGACY_BLOCK_SIZE;
+	void *out_block;
+
+	if (state->out_block_size < out_block_size) {
+		free(state->out_block);
+		out_block = (unsigned char *)malloc(out_block_size);
+		state->out_block_size = out_block_size;
+		if (out_block == NULL) {
+			archive_set_error(&self->archive->archive, ENOMEM,
+			    "Can't allocate data for lz4 decompression");
+			return (ARCHIVE_FATAL);
+		}
+		state->out_block = out_block;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Return the next block of decompressed data.
+ */
+static ssize_t
+lz4_filter_read(struct archive_read_filter *self, const void **p)
+{
+	struct private_data *state = (struct private_data *)self->data;
+	ssize_t ret;
+
+	if (state->eof) {
+		*p = NULL;
+		return (0);
+	}
+
+	__archive_read_filter_consume(self->upstream, state->unconsumed);
+	state->unconsumed = 0;
+
+	switch (state->stage) {
+	case SELECT_STREAM:
+		break;
+	case READ_DEFAULT_STREAM:
+	case READ_LEGACY_STREAM:
+		/* Reading a lz4 stream already failed. */
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC, "Invalid sequence.");
+		return (ARCHIVE_FATAL);
+	case READ_DEFAULT_BLOCK:
+		ret = lz4_filter_read_default_stream(self, p);
+		if (ret != 0 || state->stage != SELECT_STREAM)
+			return ret;
+		break;
+	case READ_LEGACY_BLOCK:
+		ret = lz4_filter_read_legacy_stream(self, p);
+		if (ret != 0 || state->stage != SELECT_STREAM)
+			return ret;
+		break;
+	default:
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC, "Program error.");
+		return (ARCHIVE_FATAL);
+		break;
+	}
+
+	while (state->stage == SELECT_STREAM) {
+		const char *read_buf;
+
+		/* Read a magic number. */
+		read_buf = __archive_read_filter_ahead(self->upstream, 4,
+				NULL);
+		if (read_buf == NULL) {
+			state->eof = 1;
+			*p = NULL;
+			return (0);
+		}
+		uint32_t number = archive_le32dec(read_buf);
+		__archive_read_filter_consume(self->upstream, 4);
+		if (number == LZ4_MAGICNUMBER)
+			return lz4_filter_read_default_stream(self, p);
+		else if (number == LZ4_LEGACY)
+			return lz4_filter_read_legacy_stream(self, p);
+		else if ((number & ~0xF) == LZ4_SKIPPABLED) {
+			read_buf = __archive_read_filter_ahead(
+				self->upstream, 4, NULL);
+			if (read_buf == NULL) {
+				archive_set_error(
+				    &self->archive->archive,
+		    		    ARCHIVE_ERRNO_MISC,
+				    "Malformed lz4 data");
+				return (ARCHIVE_FATAL);
+			}
+			uint32_t skip_bytes = archive_le32dec(read_buf);
+			__archive_read_filter_consume(self->upstream,
+				4 + skip_bytes);
+		} else {
+			/* Ignore following unrecognized data. */
+			state->eof = 1;
+			*p = NULL;
+			return (0);
+		}
+	}
+	state->eof = 1;
+	*p = NULL;
+	return (0);
+}
+
+static int
+lz4_filter_read_descriptor(struct archive_read_filter *self)
+{
+	struct private_data *state = (struct private_data *)self->data;
+	const char *read_buf;
+	ssize_t bytes_remaining;
+	ssize_t descriptor_bytes;
+	unsigned char flag, bd;
+	unsigned int chsum, chsum_verifier;
+
+	/* Make sure we have 2 bytes for flags. */
+	read_buf = __archive_read_filter_ahead(self->upstream, 2,
+	    &bytes_remaining);
+	if (read_buf == NULL) {
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "truncated lz4 input");
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	   Parse flags.
+	 */
+	flag = (unsigned char)read_buf[0];
+	/* Verify version number. */
+	if ((flag & 0xc0) != 1<<6)
+		goto malformed_error;
+	/* A reserved bit must be zero. */
+	if (flag & 0x02)
+		goto malformed_error;
+	state->flags.block_independence = (flag & 0x20) != 0;
+	state->flags.block_checksum = (flag & 0x10)?4:0;
+	state->flags.stream_size = (flag & 0x08) != 0;
+	state->flags.stream_checksum = (flag & 0x04) != 0;
+	state->flags.preset_dictionary = (flag & 0x01) != 0;
+
+	/* BD */
+	bd = (unsigned char)read_buf[1];
+	/* Reserved bits must be zero. */
+	if (bd & 0x8f)
+		goto malformed_error;
+	/* Get a maximum block size. */
+	switch (read_buf[1] >> 4) {
+	case 4: /* 64 KB */
+		state->flags.block_maximum_size = 64 * 1024;
+		break;
+	case 5: /* 256 KB */
+		state->flags.block_maximum_size = 256 * 1024;
+		break;
+	case 6: /* 1 MB */
+		state->flags.block_maximum_size = 1024 * 1024;
+		break;
+	case 7: /* 4 MB */
+		state->flags.block_maximum_size = 4 * 1024 * 1024;
+		break;
+	default:
+		goto malformed_error;
+	}
+
+	/* Read the whole descriptor in a stream block. */
+	descriptor_bytes = 3;
+	if (state->flags.stream_size)
+		descriptor_bytes += 8;
+	if (state->flags.preset_dictionary)
+		descriptor_bytes += 4;
+	if (bytes_remaining < descriptor_bytes) {
+		read_buf = __archive_read_filter_ahead(self->upstream,
+		    descriptor_bytes, &bytes_remaining);
+		if (read_buf == NULL) {
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "truncated lz4 input");
+			return (ARCHIVE_FATAL);
+		}
+	}
+	/* Check if a descriptor is corrupted */
+	chsum = __archive_xxhash.XXH32(read_buf, (int)descriptor_bytes -1, 0);
+	chsum = (chsum >> 8) & 0xff;
+	chsum_verifier = read_buf[descriptor_bytes-1] & 0xff;
+	if (chsum != chsum_verifier)
+		goto malformed_error;
+
+	__archive_read_filter_consume(self->upstream, descriptor_bytes);
+
+	/* Make sure we have a large enough buffer for uncompressed data. */
+	if (lz4_allocate_out_block(self) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	if (state->flags.stream_checksum)
+		state->xxh32_state = __archive_xxhash.XXH32_init(0);
+
+	state->decoded_size = 0;
+	/* Success */
+	return (ARCHIVE_OK);
+malformed_error:
+	archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+	    "malformed lz4 data");
+	return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+lz4_filter_read_data_block(struct archive_read_filter *self, const void **p)
+{
+	struct private_data *state = (struct private_data *)self->data;
+	ssize_t compressed_size;
+	const char *read_buf;
+	ssize_t bytes_remaining;
+	int checksum_size;
+	ssize_t uncompressed_size;
+	size_t prefix64k;
+
+	*p = NULL;
+
+	/* Make sure we have 4 bytes for a block size. */
+	read_buf = __archive_read_filter_ahead(self->upstream, 4,
+	    &bytes_remaining);
+	if (read_buf == NULL)
+		goto truncated_error;
+	compressed_size = archive_le32dec(read_buf);
+	if ((compressed_size & 0x7fffffff) > state->flags.block_maximum_size)
+		goto malformed_error;
+	/* A compressed size == 0 means the end of stream blocks. */
+	if (compressed_size == 0) {
+		__archive_read_filter_consume(self->upstream, 4);
+		return 0;
+	}
+
+	checksum_size = state->flags.block_checksum;
+	/* Check if the block is uncompressed. */
+	if (compressed_size & 0x80000000U) {
+		compressed_size &= 0x7fffffff;
+		uncompressed_size = compressed_size;
+	} else
+		uncompressed_size = 0;/* Unknown yet. */
+
+	/*
+	  Unfortunately, lz4 decompression API requires a whole block
+	  for its decompression speed, so we read a whole block and allocate
+	  a huge buffer used for decoded data.
+	*/
+	read_buf = __archive_read_filter_ahead(self->upstream,
+	    4 + compressed_size + checksum_size, &bytes_remaining);
+	if (read_buf == NULL)
+		goto truncated_error;
+
+	/* Optional processing, checking a block sum. */
+	if (checksum_size) {
+		unsigned int chsum = __archive_xxhash.XXH32(
+			read_buf + 4, (int)compressed_size, 0);
+		unsigned int chsum_block =
+		    archive_le32dec(read_buf + 4 + compressed_size);
+		if (chsum != chsum_block)
+			goto malformed_error;
+	}
+
+
+	/* If the block is uncompressed, there is nothing to do. */
+	if (uncompressed_size) {
+		/* Prepare a prefix 64k block for next block. */
+		if (!state->flags.block_independence) {
+			prefix64k = 64 * 1024;
+			if (uncompressed_size < (ssize_t)prefix64k) {
+				memcpy(state->out_block
+					+ prefix64k - uncompressed_size,
+				    read_buf + 4,
+				    uncompressed_size);
+				memset(state->out_block, 0,
+				    prefix64k - uncompressed_size);
+			} else {
+				memcpy(state->out_block,
+				    read_buf + 4
+					+ uncompressed_size - prefix64k,
+				    prefix64k);
+			}
+			state->decoded_size = 0;
+		}
+		state->unconsumed = 4 + uncompressed_size + checksum_size;
+		*p = read_buf + 4;
+		return uncompressed_size;
+	}
+
+	/*
+	   Decompress a block data.
+	 */
+	if (state->flags.block_independence) {
+		prefix64k = 0;
+		uncompressed_size = LZ4_decompress_safe(read_buf + 4,
+		    state->out_block, (int)compressed_size,
+		    state->flags.block_maximum_size);
+	} else {
+		prefix64k = 64 * 1024;
+		if (state->decoded_size) {
+			if (state->decoded_size < prefix64k) {
+				memmove(state->out_block
+					+ prefix64k - state->decoded_size,
+				    state->out_block + prefix64k,
+				    state->decoded_size);
+				memset(state->out_block, 0,
+				    prefix64k - state->decoded_size);
+			} else {
+				memmove(state->out_block,
+				    state->out_block + state->decoded_size,
+				    prefix64k);
+			}
+		}
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+		uncompressed_size = LZ4_decompress_safe_usingDict(
+		    read_buf + 4,
+		    state->out_block + prefix64k, (int)compressed_size,
+		    state->flags.block_maximum_size,
+		    state->out_block,
+		    prefix64k);
+#else
+		uncompressed_size = LZ4_decompress_safe_withPrefix64k(
+		    read_buf + 4,
+		    state->out_block + prefix64k, (int)compressed_size,
+		    state->flags.block_maximum_size);
+#endif
+	}
+
+	/* Check if an error occurred in the decompression process. */
+	if (uncompressed_size < 0) {
+		archive_set_error(&(self->archive->archive),
+		    ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
+		return (ARCHIVE_FATAL);
+	}
+
+	state->unconsumed = 4 + compressed_size + checksum_size;
+	*p = state->out_block + prefix64k;
+	state->decoded_size = uncompressed_size;
+	return uncompressed_size;
+
+malformed_error:
+	archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+	    "malformed lz4 data");
+	return (ARCHIVE_FATAL);
+truncated_error:
+	archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+	    "truncated lz4 input");
+	return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+lz4_filter_read_default_stream(struct archive_read_filter *self, const void **p)
+{
+	struct private_data *state = (struct private_data *)self->data;
+	const char *read_buf;
+	ssize_t bytes_remaining;
+	ssize_t ret;
+
+	if (state->stage == SELECT_STREAM) {
+		state->stage = READ_DEFAULT_STREAM;
+		/* First, read a descriptor. */
+		if((ret = lz4_filter_read_descriptor(self)) != ARCHIVE_OK)
+			return (ret);
+		state->stage = READ_DEFAULT_BLOCK;
+	}
+	/* Decompress a block. */
+	ret = lz4_filter_read_data_block(self, p);
+
+	/* If the end of block is detected, change the filter status
+	   to read next stream. */
+	if (ret == 0 && *p == NULL)
+		state->stage = SELECT_STREAM;
+
+	/* Optional processing, checking a stream sum. */
+	if (state->flags.stream_checksum) {
+		if (state->stage == SELECT_STREAM) {
+			unsigned int checksum;
+			unsigned int checksum_stream;
+			read_buf = __archive_read_filter_ahead(self->upstream,
+			    4, &bytes_remaining);
+			if (read_buf == NULL) {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC, "truncated lz4 input");
+				return (ARCHIVE_FATAL);
+			}
+			checksum = archive_le32dec(read_buf);
+			__archive_read_filter_consume(self->upstream, 4);
+			checksum_stream = __archive_xxhash.XXH32_digest(
+			    state->xxh32_state);
+			state->xxh32_state = NULL;
+			if (checksum != checksum_stream) {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "lz4 stream checksum error");
+				return (ARCHIVE_FATAL);
+			}
+		} else if (ret > 0)
+			__archive_xxhash.XXH32_update(state->xxh32_state,
+			    *p, (int)ret);
+	}
+	return (ret);
+}
+
+static ssize_t
+lz4_filter_read_legacy_stream(struct archive_read_filter *self, const void **p)
+{
+	struct private_data *state = (struct private_data *)self->data;
+	uint32_t compressed;
+	const char *read_buf;
+	ssize_t ret;
+
+	*p = NULL;
+	ret = lz4_allocate_out_block_for_legacy(self);
+	if (ret != ARCHIVE_OK)
+		return ret;
+
+	/* Make sure we have 4 bytes for a block size. */
+	read_buf = __archive_read_filter_ahead(self->upstream, 4, NULL);
+	if (read_buf == NULL) {
+		if (state->stage == SELECT_STREAM) {
+			state->stage = READ_LEGACY_STREAM;
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "truncated lz4 input");
+			return (ARCHIVE_FATAL);
+		}
+		state->stage = SELECT_STREAM;
+		return 0;
+	}
+	state->stage = READ_LEGACY_BLOCK;
+	compressed = archive_le32dec(read_buf);
+	if (compressed > LZ4_COMPRESSBOUND(LEGACY_BLOCK_SIZE)) {
+		state->stage = SELECT_STREAM;
+		return 0;
+	}
+
+	/* Make sure we have a whole block. */
+	read_buf = __archive_read_filter_ahead(self->upstream,
+	    4 + compressed, NULL);
+	if (read_buf == NULL) {
+		archive_set_error(&(self->archive->archive),
+		    ARCHIVE_ERRNO_MISC, "truncated lz4 input");
+		return (ARCHIVE_FATAL);
+	}
+	ret = LZ4_decompress_safe(read_buf + 4, state->out_block,
+	    compressed, (int)state->out_block_size);
+	if (ret < 0) {
+		archive_set_error(&(self->archive->archive),
+		    ARCHIVE_ERRNO_MISC, "lz4 decompression failed");
+		return (ARCHIVE_FATAL);
+	}
+	*p = state->out_block;
+	state->unconsumed = 4 + compressed;
+	return ret;
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+lz4_filter_close(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	int ret = ARCHIVE_OK;
+
+	state = (struct private_data *)self->data;
+	free(state->xxh32_state);
+	free(state->out_block);
+	free(state);
+	return (ret);
+}
+
+#endif /* HAVE_LIBLZ4 */
diff --git a/c/archive_read_support_filter_lzop.c b/c/archive_read_support_filter_lzop.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_lzop.c
@@ -0,0 +1,494 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * 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$");
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.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
+#ifdef HAVE_LZO_LZOCONF_H
+#include <lzo/lzoconf.h>
+#endif
+#ifdef HAVE_LZO_LZO1X_H
+#include <lzo/lzo1x.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h> /* for crc32 and adler32 */
+#endif
+
+#include "archive.h"
+#if !defined(HAVE_ZLIB_H) &&\
+     defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+#include "archive_crc32.h"
+#endif
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#ifndef HAVE_ZLIB_H
+#define adler32	lzo_adler32
+#endif
+
+#define LZOP_HEADER_MAGIC "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a"
+#define LZOP_HEADER_MAGIC_LEN 9
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+struct read_lzop {
+	unsigned char	*out_block;
+	size_t		 out_block_size;
+	int64_t		 total_out;
+	int		 flags;
+	uint32_t	 compressed_cksum;
+	uint32_t	 uncompressed_cksum;
+	size_t		 compressed_size;
+	size_t		 uncompressed_size;
+	size_t		 unconsumed_bytes;
+	char		 in_stream;
+	char		 eof; /* True = found end of compressed data. */
+};
+
+#define FILTER			0x0800
+#define CRC32_HEADER		0x1000
+#define EXTRA_FIELD		0x0040
+#define ADLER32_UNCOMPRESSED	0x0001
+#define ADLER32_COMPRESSED	0x0002
+#define CRC32_UNCOMPRESSED	0x0100
+#define CRC32_COMPRESSED	0x0200
+#define MAX_BLOCK_SIZE		(64 * 1024 * 1024)
+
+static ssize_t  lzop_filter_read(struct archive_read_filter *, const void **);
+static int	lzop_filter_close(struct archive_read_filter *);
+#endif
+
+static int lzop_bidder_bid(struct archive_read_filter_bidder *,
+    struct archive_read_filter *);
+static int lzop_bidder_init(struct archive_read_filter *);
+
+int
+archive_read_support_filter_lzop(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *reader;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_lzop");
+
+	if (__archive_read_get_bidder(a, &reader) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	reader->data = NULL;
+	reader->bid = lzop_bidder_bid;
+	reader->init = lzop_bidder_init;
+	reader->options = NULL;
+	reader->free = NULL;
+	/* Signal the extent of lzop support with the return value here. */
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+	return (ARCHIVE_OK);
+#else
+	/* Return ARCHIVE_WARN since this always uses an external program. */
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external lzop program for lzop decompression");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Bidder just verifies the header and returns the number of verified bits.
+ */
+static int
+lzop_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	const unsigned char *p;
+	ssize_t avail;
+
+	(void)self; /* UNUSED */
+
+	p = __archive_read_filter_ahead(filter, LZOP_HEADER_MAGIC_LEN, &avail);
+	if (p == NULL || avail == 0)
+		return (0);
+
+	if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN))
+		return (0);
+
+	return (LZOP_HEADER_MAGIC_LEN * 8);
+}
+
+#if !defined(HAVE_LZO_LZOCONF_H) || !defined(HAVE_LZO_LZO1X_H)
+/*
+ * If we don't have the library on this system, we can't do the
+ * decompression directly.  We can, however, try to run "lzop -d"
+ * in case that's available.
+ */
+static int
+lzop_bidder_init(struct archive_read_filter *self)
+{
+	int r;
+
+	r = __archive_read_program(self, "lzop -d");
+	/* Note: We set the format here even if __archive_read_program()
+	 * above fails.  We do, after all, know what the format is
+	 * even if we weren't able to read it. */
+	self->code = ARCHIVE_FILTER_LZOP;
+	self->name = "lzop";
+	return (r);
+}
+#else
+/*
+ * Initialize the filter object.
+ */
+static int
+lzop_bidder_init(struct archive_read_filter *self)
+{
+	struct read_lzop *state;
+
+	self->code = ARCHIVE_FILTER_LZOP;
+	self->name = "lzop";
+
+	state = (struct read_lzop *)calloc(sizeof(*state), 1);
+	if (state == NULL) {
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate data for lzop decompression");
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = state;
+	self->read = lzop_filter_read;
+	self->skip = NULL; /* not supported */
+	self->close = lzop_filter_close;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+consume_header(struct archive_read_filter *self)
+{
+	struct read_lzop *state = (struct read_lzop *)self->data;
+	const unsigned char *p, *_p;
+	unsigned checksum, flags, len, method, version;
+
+	/*
+	 * Check LZOP magic code.
+	 */
+	p = __archive_read_filter_ahead(self->upstream,
+		LZOP_HEADER_MAGIC_LEN, NULL);
+	if (p == NULL)
+		return (ARCHIVE_EOF);
+
+	if (memcmp(p, LZOP_HEADER_MAGIC, LZOP_HEADER_MAGIC_LEN))
+		return (ARCHIVE_EOF);
+	__archive_read_filter_consume(self->upstream,
+	    LZOP_HEADER_MAGIC_LEN);
+
+	p = __archive_read_filter_ahead(self->upstream, 29, NULL);
+	if (p == NULL)
+		goto truncated;
+	_p = p;
+	version = archive_be16dec(p);
+	p += 4;/* version(2 bytes) + library version(2 bytes) */
+
+	if (version >= 0x940) {
+		unsigned reqversion = archive_be16dec(p); p += 2;
+		if (reqversion < 0x900) {
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_MISC, "Invalid required version");
+			return (ARCHIVE_FAILED);
+		}
+	}
+
+	method = *p++;
+	if (method < 1 || method > 3) {
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "Unsupported method");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (version >= 0x940) {
+		unsigned level = *p++;
+#if 0
+		unsigned default_level[] = {0, 3, 1, 9};
+#endif
+		if (level == 0)
+			/* Method is 1..3 here due to check above. */
+#if 0	/* Avoid an error Clang Static Analyzer claims
+	  "Value stored to 'level' is never read". */
+			level = default_level[method];
+#else
+			;/* NOP */
+#endif
+		else if (level > 9) {
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_MISC, "Invalid level");
+			return (ARCHIVE_FAILED);
+		}
+	}
+
+	flags = archive_be32dec(p); p += 4;
+
+	if (flags & FILTER)
+		p += 4; /* Skip filter */
+	p += 4; /* Skip mode */
+	if (version >= 0x940)
+		p += 8; /* Skip mtime */
+	else
+		p += 4; /* Skip mtime */
+	len = *p++; /* Read filename length */
+	len += p - _p;
+	/* Make sure we have all bytes we need to calculate checksum. */
+	p = __archive_read_filter_ahead(self->upstream, len + 4, NULL);
+	if (p == NULL)
+		goto truncated;
+	if (flags & CRC32_HEADER)
+		checksum = crc32(crc32(0, NULL, 0), p, len);
+	else
+		checksum = adler32(adler32(0, NULL, 0), p, len);
+	if (archive_be32dec(p + len) != checksum)
+		goto corrupted;
+	__archive_read_filter_consume(self->upstream, len + 4);
+	if (flags & EXTRA_FIELD) {
+		/* Skip extra field */
+		p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+		if (p == NULL)
+			goto truncated;
+		len = archive_be32dec(p);
+		__archive_read_filter_consume(self->upstream, len + 4 + 4);
+	}
+	state->flags = flags;
+	state->in_stream = 1;
+	return (ARCHIVE_OK);
+truncated:
+	archive_set_error(&self->archive->archive,
+	    ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
+	return (ARCHIVE_FAILED);
+corrupted:
+	archive_set_error(&self->archive->archive,
+	    ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");
+	return (ARCHIVE_FAILED);
+}
+
+static int
+consume_block_info(struct archive_read_filter *self)
+{
+	struct read_lzop *state = (struct read_lzop *)self->data;
+	const unsigned char *p;
+	unsigned flags = state->flags;
+
+	p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+	if (p == NULL)
+		goto truncated;
+	state->uncompressed_size = archive_be32dec(p);
+	__archive_read_filter_consume(self->upstream, 4);
+	if (state->uncompressed_size == 0)
+		return (ARCHIVE_EOF);
+	if (state->uncompressed_size > MAX_BLOCK_SIZE)
+		goto corrupted;
+
+	p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+	if (p == NULL)
+		goto truncated;
+	state->compressed_size = archive_be32dec(p);
+	__archive_read_filter_consume(self->upstream, 4);
+	if (state->compressed_size > state->uncompressed_size)
+		goto corrupted;
+
+	if (flags & (CRC32_UNCOMPRESSED | ADLER32_UNCOMPRESSED)) {
+		p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+		if (p == NULL)
+			goto truncated;
+		state->compressed_cksum = state->uncompressed_cksum =
+		    archive_be32dec(p);
+		__archive_read_filter_consume(self->upstream, 4);
+	}
+	if ((flags & (CRC32_COMPRESSED | ADLER32_COMPRESSED)) &&
+	    state->compressed_size < state->uncompressed_size) {
+		p = __archive_read_filter_ahead(self->upstream, 4, NULL);
+		if (p == NULL)
+			goto truncated;
+		state->compressed_cksum = archive_be32dec(p);
+		__archive_read_filter_consume(self->upstream, 4);
+	}
+	return (ARCHIVE_OK);
+truncated:
+	archive_set_error(&self->archive->archive,
+	    ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
+	return (ARCHIVE_FAILED);
+corrupted:
+	archive_set_error(&self->archive->archive,
+	    ARCHIVE_ERRNO_FILE_FORMAT, "Corrupted lzop header");
+	return (ARCHIVE_FAILED);
+}
+
+static ssize_t
+lzop_filter_read(struct archive_read_filter *self, const void **p)
+{
+	struct read_lzop *state = (struct read_lzop *)self->data;
+	const void *b;
+	lzo_uint out_size;
+	uint32_t cksum;
+	int ret, r;
+
+	if (state->unconsumed_bytes) {
+		__archive_read_filter_consume(self->upstream,
+		    state->unconsumed_bytes);
+		state->unconsumed_bytes = 0;
+	}
+	if (state->eof)
+		return (0);
+
+	for (;;) {
+		if (!state->in_stream) {
+			ret = consume_header(self);
+			if (ret < ARCHIVE_OK)
+				return (ret);
+			if (ret == ARCHIVE_EOF) {
+				state->eof = 1;
+				return (0);
+			}
+		}
+		ret = consume_block_info(self);
+		if (ret < ARCHIVE_OK)
+			return (ret);
+		if (ret == ARCHIVE_EOF)
+			state->in_stream = 0;
+		else
+			break;
+	}
+
+	if (state->out_block == NULL ||
+	    state->out_block_size < state->uncompressed_size) {
+		void *new_block;
+
+		new_block = realloc(state->out_block, state->uncompressed_size);
+		if (new_block == NULL) {
+			archive_set_error(&self->archive->archive, ENOMEM,
+			    "Can't allocate data for lzop decompression");
+			return (ARCHIVE_FATAL);
+		}
+		state->out_block = new_block;
+		state->out_block_size = state->uncompressed_size;
+	}
+
+	b = __archive_read_filter_ahead(self->upstream,
+		state->compressed_size, NULL);
+	if (b == NULL) {
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT, "Truncated lzop data");
+		return (ARCHIVE_FATAL);
+	}
+	if (state->flags & CRC32_COMPRESSED)
+		cksum = crc32(crc32(0, NULL, 0), b, state->compressed_size);
+	else if (state->flags & ADLER32_COMPRESSED)
+		cksum = adler32(adler32(0, NULL, 0), b, state->compressed_size);
+	else
+		cksum = state->compressed_cksum;
+	if (cksum != state->compressed_cksum) {
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC, "Corrupted data");
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * If the both uncompressed size and compressed size are the same,
+	 * we do not decompress this block.
+	 */
+	if (state->uncompressed_size == state->compressed_size) {
+		*p = b;
+		state->total_out += state->compressed_size;
+		state->unconsumed_bytes = state->compressed_size;
+		return ((ssize_t)state->uncompressed_size);
+	}
+
+	/*
+	 * Drive lzo uncompression.
+	 */
+	out_size = (lzo_uint)state->uncompressed_size;
+	r = lzo1x_decompress_safe(b, (lzo_uint)state->compressed_size,
+		state->out_block, &out_size, NULL);
+	switch (r) {
+	case LZO_E_OK:
+		if (out_size == state->uncompressed_size)
+			break;
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC, "Corrupted data");
+		return (ARCHIVE_FATAL);
+	case LZO_E_OUT_OF_MEMORY:
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "lzop decompression failed: out of memory");
+		return (ARCHIVE_FATAL);
+	default:
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "lzop decompression failed: %d", r);
+		return (ARCHIVE_FATAL);
+	}
+
+	if (state->flags & CRC32_UNCOMPRESSED)
+		cksum = crc32(crc32(0, NULL, 0), state->out_block,
+		    state->uncompressed_size);
+	else if (state->flags & ADLER32_UNCOMPRESSED)
+		cksum = adler32(adler32(0, NULL, 0), state->out_block,
+		    state->uncompressed_size);
+	else
+		cksum = state->uncompressed_cksum;
+	if (cksum != state->uncompressed_cksum) {
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC, "Corrupted data");
+		return (ARCHIVE_FATAL);
+	}
+
+	__archive_read_filter_consume(self->upstream, state->compressed_size);
+	*p = state->out_block;
+	state->total_out += out_size;
+	return ((ssize_t)out_size);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+lzop_filter_close(struct archive_read_filter *self)
+{
+	struct read_lzop *state = (struct read_lzop *)self->data;
+
+	free(state->out_block);
+	free(state);
+	return (ARCHIVE_OK);
+}
+
+#endif
diff --git a/c/archive_read_support_filter_none.c b/c/archive_read_support_filter_none.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_none.c
@@ -0,0 +1,52 @@
+/*-
+ * 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$");
+
+#include "archive.h"
+#include "archive_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_none(struct archive *a)
+{
+	return archive_read_support_filter_none(a);
+}
+#endif
+
+/*
+ * Uncompressed streams are handled implicitly by the read core,
+ * so this is now a no-op.
+ */
+int
+archive_read_support_filter_none(struct archive *a)
+{
+	archive_check_magic(a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_none");
+
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_read_support_filter_program.c b/c/archive_read_support_filter_program.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_program.c
@@ -0,0 +1,503 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * 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$");
+
+#ifdef HAVE_SYS_WAIT_H
+#  include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#  include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#  include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#  include <limits.h>
+#endif
+#ifdef HAVE_SIGNAL_H
+#  include <signal.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_string.h"
+#include "archive_read_private.h"
+#include "filter_fork.h"
+
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_program(struct archive *a, const char *cmd)
+{
+	return archive_read_support_filter_program(a, cmd);
+}
+
+int
+archive_read_support_compression_program_signature(struct archive *a,
+    const char *cmd, const void *signature, size_t signature_len)
+{
+	return archive_read_support_filter_program_signature(a,
+	    cmd, signature, signature_len);
+}
+#endif
+
+int
+archive_read_support_filter_program(struct archive *a, const char *cmd)
+{
+	return (archive_read_support_filter_program_signature(a, cmd, NULL, 0));
+}
+
+/*
+ * The bidder object stores the command and the signature to watch for.
+ * The 'inhibit' entry here is used to ensure that unchecked filters never
+ * bid twice in the same pipeline.
+ */
+struct program_bidder {
+	char *description;
+	char *cmd;
+	void *signature;
+	size_t signature_len;
+	int inhibit;
+};
+
+static int	program_bidder_bid(struct archive_read_filter_bidder *,
+		    struct archive_read_filter *upstream);
+static int	program_bidder_init(struct archive_read_filter *);
+static int	program_bidder_free(struct archive_read_filter_bidder *);
+
+/*
+ * The actual filter needs to track input and output data.
+ */
+struct program_filter {
+	struct archive_string description;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	HANDLE		 child;
+#else
+	pid_t		 child;
+#endif
+	int		 exit_status;
+	int		 waitpid_return;
+	int		 child_stdin, child_stdout;
+
+	char		*out_buf;
+	size_t		 out_buf_len;
+};
+
+static ssize_t	program_filter_read(struct archive_read_filter *,
+		    const void **);
+static int	program_filter_close(struct archive_read_filter *);
+static void	free_state(struct program_bidder *);
+
+static int
+set_bidder_signature(struct archive_read_filter_bidder *bidder,
+    struct program_bidder *state, const void *signature, size_t signature_len)
+{
+
+	if (signature != NULL && signature_len > 0) {
+		state->signature_len = signature_len;
+		state->signature = malloc(signature_len);
+		memcpy(state->signature, signature, signature_len);
+	}
+
+	/*
+	 * Fill in the bidder object.
+	 */
+	bidder->data = state;
+	bidder->bid = program_bidder_bid;
+	bidder->init = program_bidder_init;
+	bidder->options = NULL;
+	bidder->free = program_bidder_free;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_support_filter_program_signature(struct archive *_a,
+    const char *cmd, const void *signature, size_t signature_len)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *bidder;
+	struct program_bidder *state;
+
+	/*
+	 * Get a bidder object from the read core.
+	 */
+	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/*
+	 * Allocate our private state.
+	 */
+	state = (struct program_bidder *)calloc(1, sizeof (*state));
+	if (state == NULL)
+		goto memerr;
+	state->cmd = strdup(cmd);
+	if (state->cmd == NULL)
+		goto memerr;
+
+	return set_bidder_signature(bidder, state, signature, signature_len);
+memerr:
+	free_state(state);
+	archive_set_error(_a, ENOMEM, "Can't allocate memory");
+	return (ARCHIVE_FATAL);
+}
+
+static int
+program_bidder_free(struct archive_read_filter_bidder *self)
+{
+	struct program_bidder *state = (struct program_bidder *)self->data;
+
+	free_state(state);
+	return (ARCHIVE_OK);
+}
+
+static void
+free_state(struct program_bidder *state)
+{
+
+	if (state) {
+		free(state->cmd);
+		free(state->signature);
+		free(state);
+	}
+}
+
+/*
+ * If we do have a signature, bid only if that matches.
+ *
+ * If there's no signature, we bid INT_MAX the first time
+ * we're called, then never bid again.
+ */
+static int
+program_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *upstream)
+{
+	struct program_bidder *state = self->data;
+	const char *p;
+
+	/* If we have a signature, use that to match. */
+	if (state->signature_len > 0) {
+		p = __archive_read_filter_ahead(upstream,
+		    state->signature_len, NULL);
+		if (p == NULL)
+			return (0);
+		/* No match, so don't bid. */
+		if (memcmp(p, state->signature, state->signature_len) != 0)
+			return (0);
+		return ((int)state->signature_len * 8);
+	}
+
+	/* Otherwise, bid once and then never bid again. */
+	if (state->inhibit)
+		return (0);
+	state->inhibit = 1;
+	return (INT_MAX);
+}
+
+/*
+ * Shut down the child, return ARCHIVE_OK if it exited normally.
+ *
+ * Note that the return value is sticky; if we're called again,
+ * we won't reap the child again, but we will return the same status
+ * (including error message if the child came to a bad end).
+ */
+static int
+child_stop(struct archive_read_filter *self, struct program_filter *state)
+{
+	/* Close our side of the I/O with the child. */
+	if (state->child_stdin != -1) {
+		close(state->child_stdin);
+		state->child_stdin = -1;
+	}
+	if (state->child_stdout != -1) {
+		close(state->child_stdout);
+		state->child_stdout = -1;
+	}
+
+	if (state->child != 0) {
+		/* Reap the child. */
+		do {
+			state->waitpid_return
+			    = waitpid(state->child, &state->exit_status, 0);
+		} while (state->waitpid_return == -1 && errno == EINTR);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		CloseHandle(state->child);
+#endif
+		state->child = 0;
+	}
+
+	if (state->waitpid_return < 0) {
+		/* waitpid() failed?  This is ugly. */
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "Child process exited badly");
+		return (ARCHIVE_WARN);
+	}
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+	if (WIFSIGNALED(state->exit_status)) {
+#ifdef SIGPIPE
+		/* If the child died because we stopped reading before
+		 * it was done, that's okay.  Some archive formats
+		 * have padding at the end that we routinely ignore. */
+		/* The alternative to this would be to add a step
+		 * before close(child_stdout) above to read from the
+		 * child until the child has no more to write. */
+		if (WTERMSIG(state->exit_status) == SIGPIPE)
+			return (ARCHIVE_OK);
+#endif
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "Child process exited with signal %d",
+		    WTERMSIG(state->exit_status));
+		return (ARCHIVE_WARN);
+	}
+#endif /* !_WIN32 || __CYGWIN__ */
+
+	if (WIFEXITED(state->exit_status)) {
+		if (WEXITSTATUS(state->exit_status) == 0)
+			return (ARCHIVE_OK);
+
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Child process exited with status %d",
+		    WEXITSTATUS(state->exit_status));
+		return (ARCHIVE_WARN);
+	}
+
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Use select() to decide whether the child is ready for read or write.
+ */
+static ssize_t
+child_read(struct archive_read_filter *self, char *buf, size_t buf_len)
+{
+	struct program_filter *state = self->data;
+	ssize_t ret, requested, avail;
+	const char *p;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	HANDLE handle = (HANDLE)_get_osfhandle(state->child_stdout);
+#endif
+
+	requested = buf_len > SSIZE_MAX ? SSIZE_MAX : buf_len;
+
+	for (;;) {
+		do {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+			/* Avoid infinity wait.
+			 * Note: If there is no data in the pipe, ReadFile()
+			 * called in read() never returns and so we won't
+			 * write remaining encoded data to the pipe.
+			 * Note: This way may cause performance problem.
+			 * we are looking forward to great code to resolve
+			 * this.  */
+			DWORD pipe_avail = -1;
+			int cnt = 2;
+
+			while (PeekNamedPipe(handle, NULL, 0, NULL,
+			    &pipe_avail, NULL) != 0 && pipe_avail == 0 &&
+			    cnt--)
+				Sleep(5);
+			if (pipe_avail == 0) {
+				ret = -1;
+				errno = EAGAIN;
+				break;
+			}
+#endif
+			ret = read(state->child_stdout, buf, requested);
+		} while (ret == -1 && errno == EINTR);
+
+		if (ret > 0)
+			return (ret);
+		if (ret == 0 || (ret == -1 && errno == EPIPE))
+			/* Child has closed its output; reap the child
+			 * and return the status. */
+			return (child_stop(self, state));
+		if (ret == -1 && errno != EAGAIN)
+			return (-1);
+
+		if (state->child_stdin == -1) {
+			/* Block until child has some I/O ready. */
+			__archive_check_child(state->child_stdin,
+			    state->child_stdout);
+			continue;
+		}
+
+		/* Get some more data from upstream. */
+		p = __archive_read_filter_ahead(self->upstream, 1, &avail);
+		if (p == NULL) {
+			close(state->child_stdin);
+			state->child_stdin = -1;
+			fcntl(state->child_stdout, F_SETFL, 0);
+			if (avail < 0)
+				return (avail);
+			continue;
+		}
+
+		do {
+			ret = write(state->child_stdin, p, avail);
+		} while (ret == -1 && errno == EINTR);
+
+		if (ret > 0) {
+			/* Consume whatever we managed to write. */
+			__archive_read_filter_consume(self->upstream, ret);
+		} else if (ret == -1 && errno == EAGAIN) {
+			/* Block until child has some I/O ready. */
+			__archive_check_child(state->child_stdin,
+			    state->child_stdout);
+		} else {
+			/* Write failed. */
+			close(state->child_stdin);
+			state->child_stdin = -1;
+			fcntl(state->child_stdout, F_SETFL, 0);
+			/* If it was a bad error, we're done; otherwise
+			 * it was EPIPE or EOF, and we can still read
+			 * from the child. */
+			if (ret == -1 && errno != EPIPE)
+				return (-1);
+		}
+	}
+}
+
+int
+__archive_read_program(struct archive_read_filter *self, const char *cmd)
+{
+	struct program_filter	*state;
+	static const size_t out_buf_len = 65536;
+	char *out_buf;
+	const char *prefix = "Program: ";
+	int ret;
+	size_t l;
+
+	l = strlen(prefix) + strlen(cmd) + 1;
+	state = (struct program_filter *)calloc(1, sizeof(*state));
+	out_buf = (char *)malloc(out_buf_len);
+	if (state == NULL || out_buf == NULL ||
+	    archive_string_ensure(&state->description, l) == NULL) {
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate input data");
+		if (state != NULL) {
+			archive_string_free(&state->description);
+			free(state);
+		}
+		free(out_buf);
+		return (ARCHIVE_FATAL);
+	}
+	archive_strcpy(&state->description, prefix);
+	archive_strcat(&state->description, cmd);
+
+	self->code = ARCHIVE_FILTER_PROGRAM;
+	self->name = state->description.s;
+
+	state->out_buf = out_buf;
+	state->out_buf_len = out_buf_len;
+
+	ret = __archive_create_child(cmd, &state->child_stdin,
+	    &state->child_stdout, &state->child);
+	if (ret != ARCHIVE_OK) {
+		free(state->out_buf);
+		archive_string_free(&state->description);
+		free(state);
+		archive_set_error(&self->archive->archive, EINVAL,
+		    "Can't initialize filter; unable to run program \"%s\"",
+		    cmd);
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = state;
+	self->read = program_filter_read;
+	self->skip = NULL;
+	self->close = program_filter_close;
+
+	/* XXX Check that we can read at least one byte? */
+	return (ARCHIVE_OK);
+}
+
+static int
+program_bidder_init(struct archive_read_filter *self)
+{
+	struct program_bidder   *bidder_state;
+
+	bidder_state = (struct program_bidder *)self->bidder->data;
+	return (__archive_read_program(self, bidder_state->cmd));
+}
+
+static ssize_t
+program_filter_read(struct archive_read_filter *self, const void **buff)
+{
+	struct program_filter *state;
+	ssize_t bytes;
+	size_t total;
+	char *p;
+
+	state = (struct program_filter *)self->data;
+
+	total = 0;
+	p = state->out_buf;
+	while (state->child_stdout != -1 && total < state->out_buf_len) {
+		bytes = child_read(self, p, state->out_buf_len - total);
+		if (bytes < 0)
+			/* No recovery is possible if we can no longer
+			 * read from the child. */
+			return (ARCHIVE_FATAL);
+		if (bytes == 0)
+			/* We got EOF from the child. */
+			break;
+		total += bytes;
+		p += bytes;
+	}
+
+	*buff = state->out_buf;
+	return (total);
+}
+
+static int
+program_filter_close(struct archive_read_filter *self)
+{
+	struct program_filter	*state;
+	int e;
+
+	state = (struct program_filter *)self->data;
+	e = child_stop(self, state);
+
+	/* Release our private data. */
+	free(state->out_buf);
+	archive_string_free(&state->description);
+	free(state);
+
+	return (e);
+}
diff --git a/c/archive_read_support_filter_rpm.c b/c/archive_read_support_filter_rpm.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_rpm.c
@@ -0,0 +1,289 @@
+/*-
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * 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"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct rpm {
+	int64_t		 total_in;
+	size_t		 hpos;
+	size_t		 hlen;
+	unsigned char	 header[16];
+	enum {
+		ST_LEAD,	/* Skipping 'Lead' section. */
+		ST_HEADER,	/* Reading 'Header' section;
+				 * first 16 bytes. */
+		ST_HEADER_DATA,	/* Skipping 'Header' section. */
+		ST_PADDING,	/* Skipping padding data after the
+				 * 'Header' section. */
+		ST_ARCHIVE	/* Reading 'Archive' section. */
+	}		 state;
+	int		 first_header;
+};
+#define RPM_LEAD_SIZE	96	/* Size of 'Lead' section. */
+
+static int	rpm_bidder_bid(struct archive_read_filter_bidder *,
+		    struct archive_read_filter *);
+static int	rpm_bidder_init(struct archive_read_filter *);
+
+static ssize_t	rpm_filter_read(struct archive_read_filter *,
+		    const void **);
+static int	rpm_filter_close(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_rpm(struct archive *a)
+{
+	return archive_read_support_filter_rpm(a);
+}
+#endif
+
+int
+archive_read_support_filter_rpm(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *bidder;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_rpm");
+
+	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	bidder->data = NULL;
+	bidder->name = "rpm";
+	bidder->bid = rpm_bidder_bid;
+	bidder->init = rpm_bidder_init;
+	bidder->options = NULL;
+	bidder->free = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+rpm_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	const unsigned char *b;
+	ssize_t avail;
+	int bits_checked;
+
+	(void)self; /* UNUSED */
+
+	b = __archive_read_filter_ahead(filter, 8, &avail);
+	if (b == NULL)
+		return (0);
+
+	bits_checked = 0;
+	/*
+	 * Verify Header Magic Bytes : 0XED 0XAB 0XEE 0XDB
+	 */
+	if (memcmp(b, "\xED\xAB\xEE\xDB", 4) != 0)
+		return (0);
+	bits_checked += 32;
+	/*
+	 * Check major version.
+	 */
+	if (b[4] != 3 && b[4] != 4)
+		return (0);
+	bits_checked += 8;
+	/*
+	 * Check package type; binary or source.
+	 */
+	if (b[6] != 0)
+		return (0);
+	bits_checked += 8;
+	if (b[7] != 0 && b[7] != 1)
+		return (0);
+	bits_checked += 8;
+
+	return (bits_checked);
+}
+
+static int
+rpm_bidder_init(struct archive_read_filter *self)
+{
+	struct rpm   *rpm;
+
+	self->code = ARCHIVE_FILTER_RPM;
+	self->name = "rpm";
+	self->read = rpm_filter_read;
+	self->skip = NULL; /* not supported */
+	self->close = rpm_filter_close;
+
+	rpm = (struct rpm *)calloc(sizeof(*rpm), 1);
+	if (rpm == NULL) {
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate data for rpm");
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = rpm;
+	rpm->state = ST_LEAD;
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+rpm_filter_read(struct archive_read_filter *self, const void **buff)
+{
+	struct rpm *rpm;
+	const unsigned char *b;
+	ssize_t avail_in, total;
+	size_t used, n;
+	uint32_t section;
+	uint32_t bytes;
+
+	rpm = (struct rpm *)self->data;
+	*buff = NULL;
+	total = avail_in = 0;
+	b = NULL;
+	used = 0;
+	do {
+		if (b == NULL) {
+			b = __archive_read_filter_ahead(self->upstream, 1,
+			    &avail_in);
+			if (b == NULL) {
+				if (avail_in < 0)
+					return (ARCHIVE_FATAL);
+				else
+					break;
+			}
+		}
+
+		switch (rpm->state) {
+		case ST_LEAD:
+			if (rpm->total_in + avail_in < RPM_LEAD_SIZE)
+				used += avail_in;
+			else {
+				n = (size_t)(RPM_LEAD_SIZE - rpm->total_in);
+				used += n;
+				b += n;
+				rpm->state = ST_HEADER;
+				rpm->hpos = 0;
+				rpm->hlen = 0;
+				rpm->first_header = 1;
+			}
+			break;
+		case ST_HEADER:
+			n = 16 - rpm->hpos;
+			if (n > avail_in - used)
+				n = avail_in - used;
+			memcpy(rpm->header+rpm->hpos, b, n);
+			b += n;
+			used += n;
+			rpm->hpos += n;
+
+			if (rpm->hpos == 16) {
+				if (rpm->header[0] != 0x8e ||
+				    rpm->header[1] != 0xad ||
+				    rpm->header[2] != 0xe8 ||
+				    rpm->header[3] != 0x01) {
+					if (rpm->first_header) {
+						archive_set_error(
+						    &self->archive->archive,
+						    ARCHIVE_ERRNO_FILE_FORMAT,
+						    "Unrecoginized rpm header");
+						return (ARCHIVE_FATAL);
+					}
+					rpm->state = ST_ARCHIVE;
+					*buff = rpm->header;
+					total = rpm->hpos;
+					break;
+				}
+				/* Calculate 'Header' length. */
+				section = archive_be32dec(rpm->header+8);
+				bytes = archive_be32dec(rpm->header+12);
+				rpm->hlen = 16 + section * 16 + bytes;
+				rpm->state = ST_HEADER_DATA;
+				rpm->first_header = 0;
+			}
+			break;
+		case ST_HEADER_DATA:
+			n = rpm->hlen - rpm->hpos;
+			if (n > avail_in - used)
+				n = avail_in - used;
+			b += n;
+			used += n;
+			rpm->hpos += n;
+			if (rpm->hpos == rpm->hlen)
+				rpm->state = ST_PADDING;
+			break;
+		case ST_PADDING:
+			while (used < (size_t)avail_in) {
+				if (*b != 0) {
+					/* Read next header. */
+					rpm->state = ST_HEADER;
+					rpm->hpos = 0;
+					rpm->hlen = 0;
+					break;
+				}
+				b++;
+				used++;
+			}
+			break;
+		case ST_ARCHIVE:
+			*buff = b;
+			total = avail_in;
+			used = avail_in;
+			break;
+		}
+		if (used == (size_t)avail_in) {
+			rpm->total_in += used;
+			__archive_read_filter_consume(self->upstream, used);
+			b = NULL;
+			used = 0;
+		}
+	} while (total == 0 && avail_in > 0);
+
+	if (used > 0 && b != NULL) {
+		rpm->total_in += used;
+		__archive_read_filter_consume(self->upstream, used);
+	}
+	return (total);
+}
+
+static int
+rpm_filter_close(struct archive_read_filter *self)
+{
+	struct rpm *rpm;
+
+	rpm = (struct rpm *)self->data;
+	free(rpm);
+
+	return (ARCHIVE_OK);
+}
+
diff --git a/c/archive_read_support_filter_uu.c b/c/archive_read_support_filter_uu.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_uu.c
@@ -0,0 +1,685 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * 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$");
+
+#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"
+
+/* Maximum lookahead during bid phase */
+#define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */
+
+struct uudecode {
+	int64_t		 total;
+	unsigned char	*in_buff;
+#define IN_BUFF_SIZE	(1024)
+	int		 in_cnt;
+	size_t		 in_allocated;
+	unsigned char	*out_buff;
+#define OUT_BUFF_SIZE	(64 * 1024)
+	int		 state;
+#define ST_FIND_HEAD	0
+#define ST_READ_UU	1
+#define ST_UUEND	2
+#define ST_READ_BASE64	3
+#define ST_IGNORE	4
+};
+
+static int	uudecode_bidder_bid(struct archive_read_filter_bidder *,
+		    struct archive_read_filter *filter);
+static int	uudecode_bidder_init(struct archive_read_filter *);
+
+static ssize_t	uudecode_filter_read(struct archive_read_filter *,
+		    const void **);
+static int	uudecode_filter_close(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_uu(struct archive *a)
+{
+	return archive_read_support_filter_uu(a);
+}
+#endif
+
+int
+archive_read_support_filter_uu(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *bidder;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_uu");
+
+	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	bidder->data = NULL;
+	bidder->name = "uu";
+	bidder->bid = uudecode_bidder_bid;
+	bidder->init = uudecode_bidder_init;
+	bidder->options = NULL;
+	bidder->free = NULL;
+	return (ARCHIVE_OK);
+}
+
+static const unsigned char ascii[256] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static const unsigned char uuchar[256] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
+	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static const unsigned char base64[256] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */
+	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */
+	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static const int base64num[128] = {
+	 0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0, /* 00 - 0F */
+	 0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0,  0,  0,  0,  0,  0, /* 10 - 1F */
+	 0,  0,  0,  0,  0,  0,  0,  0,
+	 0,  0,  0, 62,  0,  0,  0, 63, /* 20 - 2F */
+	52, 53, 54, 55, 56, 57, 58, 59,
+	60, 61,  0,  0,  0,  0,  0,  0, /* 30 - 3F */
+	 0,  0,  1,  2,  3,  4,  5,  6,
+	 7,  8,  9, 10, 11, 12, 13, 14, /* 40 - 4F */
+	15, 16, 17, 18, 19, 20, 21, 22,
+	23, 24, 25,  0,  0,  0,  0,  0, /* 50 - 5F */
+	 0, 26, 27, 28, 29, 30, 31, 32,
+	33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
+	41, 42, 43, 44, 45, 46, 47, 48,
+	49, 50, 51,  0,  0,  0,  0,  0, /* 70 - 7F */
+};
+
+static ssize_t
+get_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize)
+{
+	ssize_t len;
+
+	len = 0;
+	while (len < avail) {
+		switch (ascii[*b]) {
+		case 0:	/* Non-ascii character or control character. */
+			if (nlsize != NULL)
+				*nlsize = 0;
+			return (-1);
+		case '\r':
+			if (avail-len > 1 && b[1] == '\n') {
+				if (nlsize != NULL)
+					*nlsize = 2;
+				return (len+2);
+			}
+			/* FALL THROUGH */
+		case '\n':
+			if (nlsize != NULL)
+				*nlsize = 1;
+			return (len+1);
+		case 1:
+			b++;
+			len++;
+			break;
+		}
+	}
+	if (nlsize != NULL)
+		*nlsize = 0;
+	return (avail);
+}
+
+static ssize_t
+bid_get_line(struct archive_read_filter *filter,
+    const unsigned char **b, ssize_t *avail, ssize_t *ravail,
+    ssize_t *nl, size_t* nbytes_read)
+{
+	ssize_t len;
+	int quit;
+	
+	quit = 0;
+	if (*avail == 0) {
+		*nl = 0;
+		len = 0;
+	} else
+		len = get_line(*b, *avail, nl);
+
+	/*
+	 * Read bytes more while it does not reach the end of line.
+	 */
+	while (*nl == 0 && len == *avail && !quit &&
+	    *nbytes_read < UUENCODE_BID_MAX_READ) {
+		ssize_t diff = *ravail - *avail;
+		size_t nbytes_req = (*ravail+1023) & ~1023U;
+		ssize_t tested;
+
+		/* Increase reading bytes if it is not enough to at least
+		 * new two lines. */
+		if (nbytes_req < (size_t)*ravail + 160)
+			nbytes_req <<= 1;
+
+		*b = __archive_read_filter_ahead(filter, nbytes_req, avail);
+		if (*b == NULL) {
+			if (*ravail >= *avail)
+				return (0);
+			/* Reading bytes reaches the end of a stream. */
+			*b = __archive_read_filter_ahead(filter, *avail, avail);
+			quit = 1;
+		}
+		*nbytes_read = *avail;
+		*ravail = *avail;
+		*b += diff;
+		*avail -= diff;
+		tested = len;/* Skip some bytes we already determinated. */
+		len = get_line(*b + tested, *avail - tested, nl);
+		if (len >= 0)
+			len += tested;
+	}
+	return (len);
+}
+
+#define UUDECODE(c) (((c) - 0x20) & 0x3f)
+
+static int
+uudecode_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	const unsigned char *b;
+	ssize_t avail, ravail;
+	ssize_t len, nl;
+	int l;
+	int firstline;
+	size_t nbytes_read;
+
+	(void)self; /* UNUSED */
+
+	b = __archive_read_filter_ahead(filter, 1, &avail);
+	if (b == NULL)
+		return (0);
+
+	firstline = 20;
+	ravail = avail;
+	nbytes_read = avail;
+	for (;;) {
+		len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
+		if (len < 0 || nl == 0)
+			return (0); /* No match found. */
+		if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
+			l = 6;
+		else if (len -nl >= 18 && memcmp(b, "begin-base64 ", 13) == 0)
+			l = 13;
+		else
+			l = 0;
+
+		if (l > 0 && (b[l] < '0' || b[l] > '7' ||
+		    b[l+1] < '0' || b[l+1] > '7' ||
+		    b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' '))
+			l = 0;
+
+		b += len;
+		avail -= len;
+		if (l)
+			break;
+		firstline = 0;
+
+		/* Do not read more than UUENCODE_BID_MAX_READ bytes */
+		if (nbytes_read >= UUENCODE_BID_MAX_READ)
+			return (0);
+	}
+	if (!avail)
+		return (0);
+	len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read);
+	if (len < 0 || nl == 0)
+		return (0);/* There are non-ascii characters. */
+	avail -= len;
+
+	if (l == 6) {
+		/* "begin " */
+		if (!uuchar[*b])
+			return (0);
+		/* Get a length of decoded bytes. */
+		l = UUDECODE(*b++); len--;
+		if (l > 45)
+			/* Normally, maximum length is 45(character 'M'). */
+			return (0);
+		if (l > len - nl)
+			return (0); /* Line too short. */
+		while (l) {
+			if (!uuchar[*b++])
+				return (0);
+			--len;
+			--l;
+		}
+		if (len-nl == 1 &&
+		    (uuchar[*b] ||		 /* Check sum. */
+		     (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */
+			++b;
+			--len;
+		}
+		b += nl;
+		if (avail && uuchar[*b])
+			return (firstline+30);
+	} else if (l == 13) {
+		/* "begin-base64 " */
+		while (len-nl > 0) {
+			if (!base64[*b++])
+				return (0);
+			--len;
+		}
+		b += nl;
+
+		if (avail >= 5 && memcmp(b, "====\n", 5) == 0)
+			return (firstline+40);
+		if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0)
+			return (firstline+40);
+		if (avail > 0 && base64[*b])
+			return (firstline+30);
+	}
+
+	return (0);
+}
+
+static int
+uudecode_bidder_init(struct archive_read_filter *self)
+{
+	struct uudecode   *uudecode;
+	void *out_buff;
+	void *in_buff;
+
+	self->code = ARCHIVE_FILTER_UU;
+	self->name = "uu";
+	self->read = uudecode_filter_read;
+	self->skip = NULL; /* not supported */
+	self->close = uudecode_filter_close;
+
+	uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1);
+	out_buff = malloc(OUT_BUFF_SIZE);
+	in_buff = malloc(IN_BUFF_SIZE);
+	if (uudecode == NULL || out_buff == NULL || in_buff == NULL) {
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate data for uudecode");
+		free(uudecode);
+		free(out_buff);
+		free(in_buff);
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = uudecode;
+	uudecode->in_buff = in_buff;
+	uudecode->in_cnt = 0;
+	uudecode->in_allocated = IN_BUFF_SIZE;
+	uudecode->out_buff = out_buff;
+	uudecode->state = ST_FIND_HEAD;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+ensure_in_buff_size(struct archive_read_filter *self,
+    struct uudecode *uudecode, size_t size)
+{
+
+	if (size > uudecode->in_allocated) {
+		unsigned char *ptr;
+		size_t newsize;
+
+		/*
+		 * Calculate a new buffer size for in_buff.
+		 * Increase its value until it has enough size we need.
+		 */
+		newsize = uudecode->in_allocated;
+		do {
+			if (newsize < IN_BUFF_SIZE*32)
+				newsize <<= 1;
+			else
+				newsize += IN_BUFF_SIZE;
+		} while (size > newsize);
+		/* Allocate the new buffer. */
+		ptr = malloc(newsize);
+		if (ptr == NULL) {
+			free(ptr);
+			archive_set_error(&self->archive->archive,
+			    ENOMEM,
+    			    "Can't allocate data for uudecode");
+			return (ARCHIVE_FATAL);
+		}
+		/* Move the remaining data in in_buff into the new buffer. */
+		if (uudecode->in_cnt)
+			memmove(ptr, uudecode->in_buff, uudecode->in_cnt);
+		/* Replace in_buff with the new buffer. */
+		free(uudecode->in_buff);
+		uudecode->in_buff = ptr;
+		uudecode->in_allocated = newsize;
+	}
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+uudecode_filter_read(struct archive_read_filter *self, const void **buff)
+{
+	struct uudecode *uudecode;
+	const unsigned char *b, *d;
+	unsigned char *out;
+	ssize_t avail_in, ravail;
+	ssize_t used;
+	ssize_t total;
+	ssize_t len, llen, nl;
+
+	uudecode = (struct uudecode *)self->data;
+
+read_more:
+	d = __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+	if (d == NULL && avail_in < 0)
+		return (ARCHIVE_FATAL);
+	/* Quiet a code analyzer; make sure avail_in must be zero
+	 * when d is NULL. */
+	if (d == NULL)
+		avail_in = 0;
+	used = 0;
+	total = 0;
+	out = uudecode->out_buff;
+	ravail = avail_in;
+	if (uudecode->state == ST_IGNORE) {
+		used = avail_in;
+		goto finish;
+	}
+	if (uudecode->in_cnt) {
+		/*
+		 * If there is remaining data which is saved by
+		 * previous calling, use it first.
+		 */
+		if (ensure_in_buff_size(self, uudecode,
+		    avail_in + uudecode->in_cnt) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		memcpy(uudecode->in_buff + uudecode->in_cnt,
+		    d, avail_in);
+		d = uudecode->in_buff;
+		avail_in += uudecode->in_cnt;
+		uudecode->in_cnt = 0;
+	}
+	for (;used < avail_in; d += llen, used += llen) {
+		int64_t l, body;
+
+		b = d;
+		len = get_line(b, avail_in - used, &nl);
+		if (len < 0) {
+			/* Non-ascii character is found. */
+			if (uudecode->state == ST_FIND_HEAD &&
+			    (uudecode->total > 0 || total > 0)) {
+				uudecode->state = ST_IGNORE;
+				used = avail_in;
+				goto finish;
+			}
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "Insufficient compressed data");
+			return (ARCHIVE_FATAL);
+		}
+		llen = len;
+		if ((nl == 0) && (uudecode->state != ST_UUEND)) {
+			if (total == 0 && ravail <= 0) {
+				/* There is nothing more to read, fail */
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Missing format data");
+				return (ARCHIVE_FATAL);
+			}
+			/*
+			 * Save remaining data which does not contain
+			 * NL('\n','\r').
+			 */
+			if (ensure_in_buff_size(self, uudecode, len)
+			    != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			if (uudecode->in_buff != b)
+				memmove(uudecode->in_buff, b, len);
+			uudecode->in_cnt = (int)len;
+			if (total == 0) {
+				/* Do not return 0; it means end-of-file.
+				 * We should try to read bytes more. */
+				__archive_read_filter_consume(
+				    self->upstream, ravail);
+				goto read_more;
+			}
+			used += len;
+			break;
+		}
+		switch (uudecode->state) {
+		default:
+		case ST_FIND_HEAD:
+			/* Do not read more than UUENCODE_BID_MAX_READ bytes */
+			if (total + len >= UUENCODE_BID_MAX_READ) {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Invalid format data");
+				return (ARCHIVE_FATAL);
+			}
+			if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0)
+				l = 6;
+			else if (len - nl >= 18 &&
+			    memcmp(b, "begin-base64 ", 13) == 0)
+				l = 13;
+			else
+				l = 0;
+			if (l != 0 && b[l] >= '0' && b[l] <= '7' &&
+			    b[l+1] >= '0' && b[l+1] <= '7' &&
+			    b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') {
+				if (l == 6)
+					uudecode->state = ST_READ_UU;
+				else
+					uudecode->state = ST_READ_BASE64;
+			}
+			break;
+		case ST_READ_UU:
+			if (total + len * 2 > OUT_BUFF_SIZE)
+				goto finish;
+			body = len - nl;
+			if (!uuchar[*b] || body <= 0) {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Insufficient compressed data");
+				return (ARCHIVE_FATAL);
+			}
+			/* Get length of undecoded bytes of current line. */
+			l = UUDECODE(*b++);
+			body--;
+			if (l > body) {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Insufficient compressed data");
+				return (ARCHIVE_FATAL);
+			}
+			if (l == 0) {
+				uudecode->state = ST_UUEND;
+				break;
+			}
+			while (l > 0) {
+				int n = 0;
+
+				if (!uuchar[b[0]] || !uuchar[b[1]])
+					break;
+				n = UUDECODE(*b++) << 18;
+				n |= UUDECODE(*b++) << 12;
+				*out++ = n >> 16; total++;
+				--l;
+
+				if (l > 0) {
+					if (!uuchar[b[0]])
+						break;
+					n |= UUDECODE(*b++) << 6;
+					*out++ = (n >> 8) & 0xFF; total++;
+					--l;
+				}
+				if (l > 0) {
+					if (!uuchar[b[0]])
+						break;
+					n |= UUDECODE(*b++);
+					*out++ = n & 0xFF; total++;
+					--l;
+				}
+			}
+			if (l) {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Insufficient compressed data");
+				return (ARCHIVE_FATAL);
+			}
+			break;
+		case ST_UUEND:
+			if (len - nl == 3 && memcmp(b, "end ", 3) == 0)
+				uudecode->state = ST_FIND_HEAD;
+			else {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Insufficient compressed data");
+				return (ARCHIVE_FATAL);
+			}
+			break;
+		case ST_READ_BASE64:
+			if (total + len * 2 > OUT_BUFF_SIZE)
+				goto finish;
+			l = len - nl;
+			if (l >= 3 && b[0] == '=' && b[1] == '=' &&
+			    b[2] == '=') {
+				uudecode->state = ST_FIND_HEAD;
+				break;
+			}
+			while (l > 0) {
+				int n = 0;
+
+				if (!base64[b[0]] || !base64[b[1]])
+					break;
+				n = base64num[*b++] << 18;
+				n |= base64num[*b++] << 12;
+				*out++ = n >> 16; total++;
+				l -= 2;
+
+				if (l > 0) {
+					if (*b == '=')
+						break;
+					if (!base64[*b])
+						break;
+					n |= base64num[*b++] << 6;
+					*out++ = (n >> 8) & 0xFF; total++;
+					--l;
+				}
+				if (l > 0) {
+					if (*b == '=')
+						break;
+					if (!base64[*b])
+						break;
+					n |= base64num[*b++];
+					*out++ = n & 0xFF; total++;
+					--l;
+				}
+			}
+			if (l && *b != '=') {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Insufficient compressed data");
+				return (ARCHIVE_FATAL);
+			}
+			break;
+		}
+	}
+finish:
+	if (ravail < avail_in)
+		used -= avail_in - ravail;
+	__archive_read_filter_consume(self->upstream, used);
+
+	*buff = uudecode->out_buff;
+	uudecode->total += total;
+	return (total);
+}
+
+static int
+uudecode_filter_close(struct archive_read_filter *self)
+{
+	struct uudecode *uudecode;
+
+	uudecode = (struct uudecode *)self->data;
+	free(uudecode->in_buff);
+	free(uudecode->out_buff);
+	free(uudecode);
+
+	return (ARCHIVE_OK);
+}
+
diff --git a/c/archive_read_support_filter_xz.c b/c/archive_read_support_filter_xz.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_xz.c
@@ -0,0 +1,796 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2008 Tim Kientzle and Miklos Vajna
+ * 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$");
+
+#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
+#if HAVE_LZMA_H
+#include <lzma.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+
+struct private_data {
+	lzma_stream	 stream;
+	unsigned char	*out_block;
+	size_t		 out_block_size;
+	int64_t		 total_out;
+	char		 eof; /* True = found end of compressed data. */
+	char		 in_stream;
+
+	/* Following variables are used for lzip only. */
+	char		 lzip_ver;
+	uint32_t	 crc32;
+	int64_t		 member_in;
+	int64_t		 member_out;
+};
+
+#if LZMA_VERSION_MAJOR >= 5
+/* Effectively disable the limiter. */
+#define LZMA_MEMLIMIT	UINT64_MAX
+#else
+/* NOTE: This needs to check memory size which running system has. */
+#define LZMA_MEMLIMIT	(1U << 30)
+#endif
+
+/* Combined lzip/lzma/xz filter */
+static ssize_t	xz_filter_read(struct archive_read_filter *, const void **);
+static int	xz_filter_close(struct archive_read_filter *);
+static int	xz_lzma_bidder_init(struct archive_read_filter *);
+
+#endif
+
+/*
+ * Note that we can detect xz and lzma compressed files even if we
+ * can't decompress them.  (In fact, we like detecting them because we
+ * can give better error messages.)  So the bid framework here gets
+ * compiled even if no lzma library is available.
+ */
+static int	xz_bidder_bid(struct archive_read_filter_bidder *,
+		    struct archive_read_filter *);
+static int	xz_bidder_init(struct archive_read_filter *);
+static int	lzma_bidder_bid(struct archive_read_filter_bidder *,
+		    struct archive_read_filter *);
+static int	lzma_bidder_init(struct archive_read_filter *);
+static int	lzip_has_member(struct archive_read_filter *);
+static int	lzip_bidder_bid(struct archive_read_filter_bidder *,
+		    struct archive_read_filter *);
+static int	lzip_bidder_init(struct archive_read_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Deprecated; remove in libarchive 4.0 */
+int
+archive_read_support_compression_xz(struct archive *a)
+{
+	return archive_read_support_filter_xz(a);
+}
+#endif
+
+int
+archive_read_support_filter_xz(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *bidder;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_xz");
+
+	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	bidder->data = NULL;
+	bidder->name = "xz";
+	bidder->bid = xz_bidder_bid;
+	bidder->init = xz_bidder_init;
+	bidder->options = NULL;
+	bidder->free = NULL;
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+	return (ARCHIVE_OK);
+#else
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external xz program for xz decompression");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_read_support_compression_lzma(struct archive *a)
+{
+	return archive_read_support_filter_lzma(a);
+}
+#endif
+
+int
+archive_read_support_filter_lzma(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *bidder;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_lzma");
+
+	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	bidder->data = NULL;
+	bidder->name = "lzma";
+	bidder->bid = lzma_bidder_bid;
+	bidder->init = lzma_bidder_init;
+	bidder->options = NULL;
+	bidder->free = NULL;
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+	return (ARCHIVE_OK);
+#else
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external lzma program for lzma decompression");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_read_support_compression_lzip(struct archive *a)
+{
+	return archive_read_support_filter_lzip(a);
+}
+#endif
+
+int
+archive_read_support_filter_lzip(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *bidder;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_lzip");
+
+	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	bidder->data = NULL;
+	bidder->name = "lzip";
+	bidder->bid = lzip_bidder_bid;
+	bidder->init = lzip_bidder_init;
+	bidder->options = NULL;
+	bidder->free = NULL;
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+	return (ARCHIVE_OK);
+#else
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external lzip program for lzip decompression");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Test whether we can handle this data.
+ */
+static int
+xz_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	const unsigned char *buffer;
+	ssize_t avail;
+
+	(void)self; /* UNUSED */
+
+	buffer = __archive_read_filter_ahead(filter, 6, &avail);
+	if (buffer == NULL)
+		return (0);
+
+	/*
+	 * Verify Header Magic Bytes : FD 37 7A 58 5A 00
+	 */
+	if (memcmp(buffer, "\xFD\x37\x7A\x58\x5A\x00", 6) != 0)
+		return (0);
+
+	return (48);
+}
+
+/*
+ * Test whether we can handle this data.
+ *
+ * <sigh> LZMA has a rather poor file signature.  Zeros do not
+ * make good signature bytes as a rule, and the only non-zero byte
+ * here is an ASCII character.  For example, an uncompressed tar
+ * archive whose first file is ']' would satisfy this check.  It may
+ * be necessary to exclude LZMA from compression_all() because of
+ * this.  Clients of libarchive would then have to explicitly enable
+ * LZMA checking instead of (or in addition to) compression_all() when
+ * they have other evidence (file name, command-line option) to go on.
+ */
+static int
+lzma_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	const unsigned char *buffer;
+	ssize_t avail;
+	uint32_t dicsize;
+	uint64_t uncompressed_size;
+	int bits_checked;
+
+	(void)self; /* UNUSED */
+
+	buffer = __archive_read_filter_ahead(filter, 14, &avail);
+	if (buffer == NULL)
+		return (0);
+
+	/* First byte of raw LZMA stream is commonly 0x5d.
+	 * The first byte is a special number, which consists of
+	 * three parameters of LZMA compression, a number of literal
+	 * context bits(which is from 0 to 8, default is 3), a number
+	 * of literal pos bits(which is from 0 to 4, default is 0),
+	 * a number of pos bits(which is from 0 to 4, default is 2).
+	 * The first byte is made by
+	 * (pos bits * 5 + literal pos bit) * 9 + * literal contest bit,
+	 * and so the default value in this field is
+	 * (2 * 5 + 0) * 9 + 3 = 0x5d.
+	 * lzma of LZMA SDK has options to change those parameters.
+	 * It means a range of this field is from 0 to 224. And lzma of
+	 * XZ Utils with option -e records 0x5e in this field. */
+	/* NOTE: If this checking of the first byte increases false
+	 * recognition, we should allow only 0x5d and 0x5e for the first
+	 * byte of LZMA stream. */
+	bits_checked = 0;
+	if (buffer[0] > (4 * 5 + 4) * 9 + 8)
+		return (0);
+	/* Most likely value in the first byte of LZMA stream. */
+	if (buffer[0] == 0x5d || buffer[0] == 0x5e)
+		bits_checked += 8;
+
+	/* Sixth through fourteenth bytes are uncompressed size,
+	 * stored in little-endian order. `-1' means uncompressed
+	 * size is unknown and lzma of XZ Utils always records `-1'
+	 * in this field. */
+	uncompressed_size = archive_le64dec(buffer+5);
+	if (uncompressed_size == (uint64_t)ARCHIVE_LITERAL_LL(-1))
+		bits_checked += 64;
+
+	/* Second through fifth bytes are dictionary size, stored in
+	 * little-endian order. The minimum dictionary size is
+	 * 1 << 12(4KiB) which the lzma of LZMA SDK uses with option
+	 * -d12 and the maximum dictionary size is 1 << 27(128MiB)
+	 * which the one uses with option -d27.
+	 * NOTE: A comment of LZMA SDK source code says this dictionary
+	 * range is from 1 << 12 to 1 << 30. */
+	dicsize = archive_le32dec(buffer+1);
+	switch (dicsize) {
+	case 0x00001000:/* lzma of LZMA SDK option -d12. */
+	case 0x00002000:/* lzma of LZMA SDK option -d13. */
+	case 0x00004000:/* lzma of LZMA SDK option -d14. */
+	case 0x00008000:/* lzma of LZMA SDK option -d15. */
+	case 0x00010000:/* lzma of XZ Utils option -0 and -1.
+			 * lzma of LZMA SDK option -d16. */
+	case 0x00020000:/* lzma of LZMA SDK option -d17. */
+	case 0x00040000:/* lzma of LZMA SDK option -d18. */
+	case 0x00080000:/* lzma of XZ Utils option -2.
+			 * lzma of LZMA SDK option -d19. */
+	case 0x00100000:/* lzma of XZ Utils option -3.
+			 * lzma of LZMA SDK option -d20. */
+	case 0x00200000:/* lzma of XZ Utils option -4.
+			 * lzma of LZMA SDK option -d21. */
+	case 0x00400000:/* lzma of XZ Utils option -5.
+			 * lzma of LZMA SDK option -d22. */
+	case 0x00800000:/* lzma of XZ Utils option -6.
+			 * lzma of LZMA SDK option -d23. */
+	case 0x01000000:/* lzma of XZ Utils option -7.
+			 * lzma of LZMA SDK option -d24. */
+	case 0x02000000:/* lzma of XZ Utils option -8.
+			 * lzma of LZMA SDK option -d25. */
+	case 0x04000000:/* lzma of XZ Utils option -9.
+			 * lzma of LZMA SDK option -d26. */
+	case 0x08000000:/* lzma of LZMA SDK option -d27. */
+		bits_checked += 32;
+		break;
+	default:
+		/* If a memory usage for encoding was not enough on
+		 * the platform where LZMA stream was made, lzma of
+		 * XZ Utils automatically decreased the dictionary
+		 * size to enough memory for encoding by 1Mi bytes
+		 * (1 << 20).*/
+		if (dicsize <= 0x03F00000 && dicsize >= 0x00300000 &&
+		    (dicsize & ((1 << 20)-1)) == 0 &&
+		    bits_checked == 8 + 64) {
+			bits_checked += 32;
+			break;
+		}
+		/* Otherwise dictionary size is unlikely. But it is
+		 * possible that someone makes lzma stream with
+		 * liblzma/LZMA SDK in one's dictionary size. */
+		return (0);
+	}
+
+	/* TODO: The above test is still very weak.  It would be
+	 * good to do better. */
+
+	return (bits_checked);
+}
+
+static int
+lzip_has_member(struct archive_read_filter *filter)
+{
+	const unsigned char *buffer;
+	ssize_t avail;
+	int bits_checked;
+	int log2dic;
+
+	buffer = __archive_read_filter_ahead(filter, 6, &avail);
+	if (buffer == NULL)
+		return (0);
+
+	/*
+	 * Verify Header Magic Bytes : 4C 5A 49 50 (`LZIP')
+	 */
+	bits_checked = 0;
+	if (memcmp(buffer, "LZIP", 4) != 0)
+		return (0);
+	bits_checked += 32;
+
+	/* A version number must be 0 or 1 */
+	if (buffer[4] != 0 && buffer[4] != 1)
+		return (0);
+	bits_checked += 8;
+
+	/* Dictionary size. */
+	log2dic = buffer[5] & 0x1f;
+	if (log2dic < 12 || log2dic > 27)
+		return (0);
+	bits_checked += 8;
+
+	return (bits_checked);
+}
+
+static int
+lzip_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+
+	(void)self; /* UNUSED */
+	return (lzip_has_member(filter));
+}
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+
+/*
+ * liblzma 4.999.7 and later support both lzma and xz streams.
+ */
+static int
+xz_bidder_init(struct archive_read_filter *self)
+{
+	self->code = ARCHIVE_FILTER_XZ;
+	self->name = "xz";
+	return (xz_lzma_bidder_init(self));
+}
+
+static int
+lzma_bidder_init(struct archive_read_filter *self)
+{
+	self->code = ARCHIVE_FILTER_LZMA;
+	self->name = "lzma";
+	return (xz_lzma_bidder_init(self));
+}
+
+static int
+lzip_bidder_init(struct archive_read_filter *self)
+{
+	self->code = ARCHIVE_FILTER_LZIP;
+	self->name = "lzip";
+	return (xz_lzma_bidder_init(self));
+}
+
+/*
+ * Set an error code and choose an error message
+ */
+static void
+set_error(struct archive_read_filter *self, int ret)
+{
+
+	switch (ret) {
+	case LZMA_STREAM_END: /* Found end of stream. */
+	case LZMA_OK: /* Decompressor made some progress. */
+		break;
+	case LZMA_MEM_ERROR:
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Lzma library error: Cannot allocate memory");
+		break;
+	case LZMA_MEMLIMIT_ERROR:
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Lzma library error: Out of memory");
+		break;
+	case LZMA_FORMAT_ERROR:
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Lzma library error: format not recognized");
+		break;
+	case LZMA_OPTIONS_ERROR:
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Lzma library error: Invalid options");
+		break;
+	case LZMA_DATA_ERROR:
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Lzma library error: Corrupted input data");
+		break;
+	case LZMA_BUF_ERROR:
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Lzma library error:  No progress is possible");
+		break;
+	default:
+		/* Return an error. */
+		archive_set_error(&self->archive->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Lzma decompression failed:  Unknown error");
+		break;
+	}
+}
+
+/*
+ * Setup the callbacks.
+ */
+static int
+xz_lzma_bidder_init(struct archive_read_filter *self)
+{
+	static const size_t out_block_size = 64 * 1024;
+	void *out_block;
+	struct private_data *state;
+	int ret;
+
+	state = (struct private_data *)calloc(sizeof(*state), 1);
+	out_block = (unsigned char *)malloc(out_block_size);
+	if (state == NULL || out_block == NULL) {
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate data for xz decompression");
+		free(out_block);
+		free(state);
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = state;
+	state->out_block_size = out_block_size;
+	state->out_block = out_block;
+	self->read = xz_filter_read;
+	self->skip = NULL; /* not supported */
+	self->close = xz_filter_close;
+
+	state->stream.avail_in = 0;
+
+	state->stream.next_out = state->out_block;
+	state->stream.avail_out = state->out_block_size;
+
+	state->crc32 = 0;
+	if (self->code == ARCHIVE_FILTER_LZIP) {
+		/*
+		 * We have to read a lzip header and use it to initialize
+		 * compression library, thus we cannot initialize the
+		 * library for lzip here.
+		 */
+		state->in_stream = 0;
+		return (ARCHIVE_OK);
+	} else
+		state->in_stream = 1;
+
+	/* Initialize compression library. */
+	if (self->code == ARCHIVE_FILTER_XZ)
+		ret = lzma_stream_decoder(&(state->stream),
+		    LZMA_MEMLIMIT,/* memlimit */
+		    LZMA_CONCATENATED);
+	else
+		ret = lzma_alone_decoder(&(state->stream),
+		    LZMA_MEMLIMIT);/* memlimit */
+
+	if (ret == LZMA_OK)
+		return (ARCHIVE_OK);
+
+	/* Library setup failed: Choose an error message and clean up. */
+	set_error(self, ret);
+
+	free(state->out_block);
+	free(state);
+	self->data = NULL;
+	return (ARCHIVE_FATAL);
+}
+
+static int
+lzip_init(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	const unsigned char *h;
+	lzma_filter filters[2];
+	unsigned char props[5];
+	ssize_t avail_in;
+	uint32_t dicsize;
+	int log2dic, ret;
+
+	state = (struct private_data *)self->data;
+	h = __archive_read_filter_ahead(self->upstream, 6, &avail_in);
+	if (h == NULL)
+		return (ARCHIVE_FATAL);
+
+	/* Get a version number. */
+	state->lzip_ver = h[4];
+
+	/*
+	 * Setup lzma property.
+	 */
+	props[0] = 0x5d;
+
+	/* Get dictionary size. */
+	log2dic = h[5] & 0x1f;
+	if (log2dic < 12 || log2dic > 27)
+		return (ARCHIVE_FATAL);
+	dicsize = 1U << log2dic;
+	if (log2dic > 12)
+		dicsize -= (dicsize / 16) * (h[5] >> 5);
+	archive_le32enc(props+1, dicsize);
+
+	/* Consume lzip header. */
+	__archive_read_filter_consume(self->upstream, 6);
+	state->member_in = 6;
+
+	filters[0].id = LZMA_FILTER_LZMA1;
+	filters[0].options = NULL;
+	filters[1].id = LZMA_VLI_UNKNOWN;
+	filters[1].options = NULL;
+
+	ret = lzma_properties_decode(&filters[0], NULL, props, sizeof(props));
+	if (ret != LZMA_OK) {
+		set_error(self, ret);
+		return (ARCHIVE_FATAL);
+	}
+	ret = lzma_raw_decoder(&(state->stream), filters);
+	free(filters[0].options);
+	if (ret != LZMA_OK) {
+		set_error(self, ret);
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+lzip_tail(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	const unsigned char *f;
+	ssize_t avail_in;
+	int tail;
+
+	state = (struct private_data *)self->data;
+	if (state->lzip_ver == 0)
+		tail = 12;
+	else
+		tail = 20;
+	f = __archive_read_filter_ahead(self->upstream, tail, &avail_in);
+	if (f == NULL && avail_in < 0)
+		return (ARCHIVE_FATAL);
+	if (f == NULL || avail_in < tail) {
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "Lzip: Remaining data is less bytes");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Check the crc32 value of the uncompressed data of the current
+	 * member */
+	if (state->crc32 != archive_le32dec(f)) {
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "Lzip: CRC32 error");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Check the uncompressed size of the current member */
+	if ((uint64_t)state->member_out != archive_le64dec(f + 4)) {
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "Lzip: Uncompressed size error");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Check the total size of the current member */
+	if (state->lzip_ver == 1 &&
+	    (uint64_t)state->member_in + tail != archive_le64dec(f + 12)) {
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "Lzip: Member size error");
+		return (ARCHIVE_FAILED);
+	}
+	__archive_read_filter_consume(self->upstream, tail);
+
+	/* If current lzip data consists of multi member, try decompressing
+	 * a next member. */
+	if (lzip_has_member(self->upstream) != 0) {
+		state->in_stream = 0;
+		state->crc32 = 0;
+		state->member_out = 0;
+		state->member_in = 0;
+		state->eof = 0;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Return the next block of decompressed data.
+ */
+static ssize_t
+xz_filter_read(struct archive_read_filter *self, const void **p)
+{
+	struct private_data *state;
+	size_t decompressed;
+	ssize_t avail_in;
+	int ret;
+
+	state = (struct private_data *)self->data;
+
+	/* Empty our output buffer. */
+	state->stream.next_out = state->out_block;
+	state->stream.avail_out = state->out_block_size;
+
+	/* Try to fill the output buffer. */
+	while (state->stream.avail_out > 0 && !state->eof) {
+		if (!state->in_stream) {
+			/*
+			 * Initialize liblzma for lzip
+			 */
+			ret = lzip_init(self);
+			if (ret != ARCHIVE_OK)
+				return (ret);
+			state->in_stream = 1;
+		}
+		state->stream.next_in =
+		    __archive_read_filter_ahead(self->upstream, 1, &avail_in);
+		if (state->stream.next_in == NULL && avail_in < 0) {
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "truncated input");
+			return (ARCHIVE_FATAL);
+		}
+		state->stream.avail_in = avail_in;
+
+		/* Decompress as much as we can in one pass. */
+		ret = lzma_code(&(state->stream),
+		    (state->stream.avail_in == 0)? LZMA_FINISH: LZMA_RUN);
+		switch (ret) {
+		case LZMA_STREAM_END: /* Found end of stream. */
+			state->eof = 1;
+			/* FALL THROUGH */
+		case LZMA_OK: /* Decompressor made some progress. */
+			__archive_read_filter_consume(self->upstream,
+			    avail_in - state->stream.avail_in);
+			state->member_in +=
+			    avail_in - state->stream.avail_in;
+			break;
+		default:
+			set_error(self, ret);
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	decompressed = state->stream.next_out - state->out_block;
+	state->total_out += decompressed;
+	state->member_out += decompressed;
+	if (decompressed == 0)
+		*p = NULL;
+	else {
+		*p = state->out_block;
+		if (self->code == ARCHIVE_FILTER_LZIP) {
+			state->crc32 = lzma_crc32(state->out_block,
+			    decompressed, state->crc32);
+			if (state->eof) {
+				ret = lzip_tail(self);
+				if (ret != ARCHIVE_OK)
+					return (ret);
+			}
+		}
+	}
+	return (decompressed);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+xz_filter_close(struct archive_read_filter *self)
+{
+	struct private_data *state;
+
+	state = (struct private_data *)self->data;
+	lzma_end(&(state->stream));
+	free(state->out_block);
+	free(state);
+	return (ARCHIVE_OK);
+}
+
+#else
+
+/*
+ *
+ * If we have no suitable library on this system, we can't actually do
+ * the decompression.  We can, however, still detect compressed
+ * archives and emit a useful message.
+ *
+ */
+static int
+lzma_bidder_init(struct archive_read_filter *self)
+{
+	int r;
+
+	r = __archive_read_program(self, "lzma -d -qq");
+	/* Note: We set the format here even if __archive_read_program()
+	 * above fails.  We do, after all, know what the format is
+	 * even if we weren't able to read it. */
+	self->code = ARCHIVE_FILTER_LZMA;
+	self->name = "lzma";
+	return (r);
+}
+
+static int
+xz_bidder_init(struct archive_read_filter *self)
+{
+	int r;
+
+	r = __archive_read_program(self, "xz -d -qq");
+	/* Note: We set the format here even if __archive_read_program()
+	 * above fails.  We do, after all, know what the format is
+	 * even if we weren't able to read it. */
+	self->code = ARCHIVE_FILTER_XZ;
+	self->name = "xz";
+	return (r);
+}
+
+static int
+lzip_bidder_init(struct archive_read_filter *self)
+{
+	int r;
+
+	r = __archive_read_program(self, "lzip -d -q");
+	/* Note: We set the format here even if __archive_read_program()
+	 * above fails.  We do, after all, know what the format is
+	 * even if we weren't able to read it. */
+	self->code = ARCHIVE_FILTER_LZIP;
+	self->name = "lzip";
+	return (r);
+}
+
+#endif /* HAVE_LZMA_H */
diff --git a/c/archive_read_support_filter_zstd.c b/c/archive_read_support_filter_zstd.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_zstd.c
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 2009-2011 Sean Purcell
+ * 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#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
+#if HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+
+struct private_data {
+	ZSTD_DStream	*dstream;
+	unsigned char	*out_block;
+	size_t		 out_block_size;
+	int64_t		 total_out;
+	char		 in_frame; /* True = in the middle of a zstd frame. */
+	char		 eof; /* True = found end of compressed data. */
+};
+
+/* Zstd Filter. */
+static ssize_t	zstd_filter_read(struct archive_read_filter *, const void**);
+static int	zstd_filter_close(struct archive_read_filter *);
+#endif
+
+/*
+ * Note that we can detect zstd compressed files even if we can't decompress
+ * them.  (In fact, we like detecting them because we can give better error
+ * messages.)  So the bid framework here gets compiled even if no zstd library
+ * is available.
+ */
+static int	zstd_bidder_bid(struct archive_read_filter_bidder *,
+		    struct archive_read_filter *);
+static int	zstd_bidder_init(struct archive_read_filter *);
+
+int
+archive_read_support_filter_zstd(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct archive_read_filter_bidder *bidder;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_zstd");
+
+	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	bidder->data = NULL;
+	bidder->name = "zstd";
+	bidder->bid = zstd_bidder_bid;
+	bidder->init = zstd_bidder_init;
+	bidder->options = NULL;
+	bidder->free = NULL;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+	return (ARCHIVE_OK);
+#else
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external zstd program for zstd decompression");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Test whether we can handle this data.
+ */
+static int
+zstd_bidder_bid(struct archive_read_filter_bidder *self,
+    struct archive_read_filter *filter)
+{
+	const unsigned char *buffer;
+	ssize_t avail;
+	unsigned prefix;
+
+	/* Zstd frame magic values */
+	const unsigned zstd_magic = 0xFD2FB528U;
+	const unsigned zstd_magic_skippable_start = 0x184D2A50U;
+	const unsigned zstd_magic_skippable_mask = 0xFFFFFFF0;
+
+	(void) self; /* UNUSED */
+
+	buffer = __archive_read_filter_ahead(filter, 4, &avail);
+	if (buffer == NULL)
+		return (0);
+
+	prefix = archive_le32dec(buffer);
+	if (prefix == zstd_magic)
+		return (32);
+	if ((prefix & zstd_magic_skippable_mask) == zstd_magic_skippable_start)
+		return (32);
+
+	return (0);
+}
+
+#if !(HAVE_ZSTD_H && HAVE_LIBZSTD)
+
+/*
+ * If we don't have the library on this system, we can't do the
+ * decompression directly.  We can, however, try to run "zstd -d"
+ * in case that's available.
+ */
+static int
+zstd_bidder_init(struct archive_read_filter *self)
+{
+	int r;
+
+	r = __archive_read_program(self, "zstd -d -qq");
+	/* Note: We set the format here even if __archive_read_program()
+	 * above fails.  We do, after all, know what the format is
+	 * even if we weren't able to read it. */
+	self->code = ARCHIVE_FILTER_ZSTD;
+	self->name = "zstd";
+	return (r);
+}
+
+#else
+
+/*
+ * Initialize the filter object
+ */
+static int
+zstd_bidder_init(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	const size_t out_block_size = ZSTD_DStreamOutSize();
+	void *out_block;
+	ZSTD_DStream *dstream;
+
+	self->code = ARCHIVE_FILTER_ZSTD;
+	self->name = "zstd";
+
+	state = (struct private_data *)calloc(sizeof(*state), 1);
+	out_block = (unsigned char *)malloc(out_block_size);
+	dstream = ZSTD_createDStream();
+
+	if (state == NULL || out_block == NULL || dstream == NULL) {
+		free(out_block);
+		free(state);
+		ZSTD_freeDStream(dstream); /* supports free on NULL */
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate data for zstd decompression");
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = state;
+
+	state->out_block_size = out_block_size;
+	state->out_block = out_block;
+	state->dstream = dstream;
+	self->read = zstd_filter_read;
+	self->skip = NULL; /* not supported */
+	self->close = zstd_filter_close;
+
+	state->eof = 0;
+	state->in_frame = 0;
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+zstd_filter_read(struct archive_read_filter *self, const void **p)
+{
+	struct private_data *state;
+	size_t decompressed;
+	ssize_t avail_in;
+	ZSTD_outBuffer out;
+	ZSTD_inBuffer in;
+
+	state = (struct private_data *)self->data;
+
+	out = (ZSTD_outBuffer) { state->out_block, state->out_block_size, 0 };
+
+	/* Try to fill the output buffer. */
+	while (out.pos < out.size && !state->eof) {
+		if (!state->in_frame) {
+			const size_t ret = ZSTD_initDStream(state->dstream);
+			if (ZSTD_isError(ret)) {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Error initializing zstd decompressor: %s",
+				    ZSTD_getErrorName(ret));
+				return (ARCHIVE_FATAL);
+			}
+		}
+		in.src = __archive_read_filter_ahead(self->upstream, 1,
+		    &avail_in);
+		if (avail_in < 0) {
+			return avail_in;
+		}
+		if (in.src == NULL && avail_in == 0) {
+			if (!state->in_frame) {
+				/* end of stream */
+				state->eof = 1;
+				break;
+			} else {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Truncated zstd input");
+				return (ARCHIVE_FATAL);
+			}
+		}
+		in.size = avail_in;
+		in.pos = 0;
+
+		{
+			const size_t ret =
+			    ZSTD_decompressStream(state->dstream, &out, &in);
+
+			if (ZSTD_isError(ret)) {
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Zstd decompression failed: %s",
+				    ZSTD_getErrorName(ret));
+				return (ARCHIVE_FATAL);
+			}
+
+			/* Decompressor made some progress */
+			__archive_read_filter_consume(self->upstream, in.pos);
+
+			/* ret guaranteed to be > 0 if frame isn't done yet */
+			state->in_frame = (ret != 0);
+		}
+	}
+
+	decompressed = out.pos;
+	state->total_out += decompressed;
+	if (decompressed == 0)
+		*p = NULL;
+	else
+		*p = state->out_block;
+	return (decompressed);
+}
+
+/*
+ * Clean up the decompressor.
+ */
+static int
+zstd_filter_close(struct archive_read_filter *self)
+{
+	struct private_data *state;
+
+	state = (struct private_data *)self->data;
+
+	ZSTD_freeDStream(state->dstream);
+	free(state->out_block);
+	free(state);
+
+	return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H && HAVE_LIBZSTD */
diff --git a/c/archive_read_support_format_7zip.c b/c/archive_read_support_format_7zip.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_7zip.c
@@ -0,0 +1,3873 @@
+/*-
+ * Copyright (c) 2011 Michihiro NAKAJIMA
+ * 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_endian.h"
+
+#ifndef HAVE_ZLIB_H
+#include "archive_crc32.h"
+#endif
+
+#define _7ZIP_SIGNATURE	"7z\xBC\xAF\x27\x1C"
+#define SFX_MIN_ADDR	0x27000
+#define SFX_MAX_ADDR	0x60000
+
+
+/*
+ * Codec ID
+ */
+#define _7Z_COPY	0
+#define _7Z_LZMA	0x030101
+#define _7Z_LZMA2	0x21
+#define _7Z_DEFLATE	0x040108
+#define _7Z_BZ2		0x040202
+#define _7Z_PPMD	0x030401
+#define _7Z_DELTA	0x03
+#define _7Z_CRYPTO_MAIN_ZIP			0x06F10101 /* Main Zip crypto algo */
+#define _7Z_CRYPTO_RAR_29			0x06F10303 /* Rar29 AES-128 + (modified SHA-1) */
+#define _7Z_CRYPTO_AES_256_SHA_256	0x06F10701 /* AES-256 + SHA-256 */
+
+
+#define _7Z_X86		0x03030103
+#define _7Z_X86_BCJ2	0x0303011B
+#define _7Z_POWERPC	0x03030205
+#define _7Z_IA64	0x03030401
+#define _7Z_ARM		0x03030501
+#define _7Z_ARMTHUMB	0x03030701
+#define _7Z_SPARC	0x03030805
+
+/*
+ * 7-Zip header property IDs.
+ */
+#define kEnd			0x00
+#define kHeader			0x01
+#define kArchiveProperties	0x02
+#define kAdditionalStreamsInfo	0x03
+#define kMainStreamsInfo	0x04
+#define kFilesInfo		0x05
+#define kPackInfo		0x06
+#define kUnPackInfo		0x07
+#define kSubStreamsInfo		0x08
+#define kSize			0x09
+#define kCRC			0x0A
+#define kFolder			0x0B
+#define kCodersUnPackSize	0x0C
+#define kNumUnPackStream	0x0D
+#define kEmptyStream		0x0E
+#define kEmptyFile		0x0F
+#define kAnti			0x10
+#define kName			0x11
+#define kCTime			0x12
+#define kATime			0x13
+#define kMTime			0x14
+#define kAttributes		0x15
+#define kEncodedHeader		0x17
+#define kDummy			0x19
+
+struct _7z_digests {
+	unsigned char	*defineds;
+	uint32_t	*digests;
+};
+
+
+struct _7z_folder {
+	uint64_t		 numCoders;
+	struct _7z_coder {
+		unsigned long	 codec;
+		uint64_t	 numInStreams;
+		uint64_t	 numOutStreams;
+		uint64_t	 propertiesSize;
+		unsigned char	*properties;
+	} *coders;
+	uint64_t		 numBindPairs;
+	struct {
+		uint64_t	 inIndex;
+		uint64_t	 outIndex;
+	} *bindPairs;
+	uint64_t		 numPackedStreams;
+	uint64_t		*packedStreams;
+	uint64_t		 numInStreams;
+	uint64_t		 numOutStreams;
+	uint64_t		*unPackSize;
+	unsigned char		 digest_defined;
+	uint32_t		 digest;
+	uint64_t		 numUnpackStreams;
+	uint32_t		 packIndex;
+	/* Unoperated bytes. */
+	uint64_t		 skipped_bytes;
+};
+
+struct _7z_coders_info {
+	uint64_t		 numFolders;
+	struct _7z_folder	*folders;
+	uint64_t		 dataStreamIndex;
+};
+
+struct _7z_pack_info {
+	uint64_t		 pos;
+	uint64_t		 numPackStreams;
+	uint64_t		*sizes;
+	struct _7z_digests	 digest;
+	/* Calculated from pos and numPackStreams. */
+	uint64_t		*positions;
+};
+
+struct _7z_substream_info {
+	size_t			 unpack_streams;
+	uint64_t		*unpackSizes;
+	unsigned char		*digestsDefined;
+	uint32_t		*digests;
+};
+
+struct _7z_stream_info {
+	struct _7z_pack_info	 pi;
+	struct _7z_coders_info	 ci;
+	struct _7z_substream_info ss;
+};
+
+struct _7z_header_info {
+	uint64_t		 dataIndex;
+
+	unsigned char		*emptyStreamBools;
+	unsigned char		*emptyFileBools;
+	unsigned char		*antiBools;
+	unsigned char		*attrBools;
+};
+
+struct _7zip_entry {
+	size_t			 name_len;
+	unsigned char		*utf16name;
+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
+	const wchar_t		*wname;
+#endif
+	uint32_t		 folderIndex;
+	uint32_t		 ssIndex;
+	unsigned		 flg;
+#define MTIME_IS_SET	(1<<0)
+#define ATIME_IS_SET	(1<<1)
+#define CTIME_IS_SET	(1<<2)
+#define CRC32_IS_SET	(1<<3)
+#define HAS_STREAM	(1<<4)
+
+	time_t			 mtime;
+	time_t			 atime;
+	time_t			 ctime;
+	long			 mtime_ns;
+	long			 atime_ns;
+	long			 ctime_ns;
+	uint32_t		 mode;
+	uint32_t		 attr;
+};
+
+struct _7zip {
+	/* Structural information about the archive. */
+	struct _7z_stream_info	 si;
+
+	int			 header_is_being_read;
+	int			 header_is_encoded;
+	uint64_t		 header_bytes_remaining;
+	unsigned long		 header_crc32;
+	/* Header offset to check that reading points of the file contents
+	 * will not exceed the header. */
+	uint64_t		 header_offset;
+	/* Base offset of the archive file for a seek in case reading SFX. */
+	uint64_t		 seek_base;
+
+	/* List of entries */
+	size_t			 entries_remaining;
+	uint64_t		 numFiles;
+	struct _7zip_entry	*entries;
+	struct _7zip_entry	*entry;
+	unsigned char		*entry_names;
+
+	/* entry_bytes_remaining is the number of bytes we expect. */
+	int64_t			 entry_offset;
+	uint64_t		 entry_bytes_remaining;
+
+	/* Running CRC32 of the decompressed data */
+	unsigned long		 entry_crc32;
+
+	/* Flags to mark progress of decompression. */
+	char			 end_of_entry;
+
+	/* Uncompressed buffer control.  */
+#define UBUFF_SIZE	(64 * 1024)
+	unsigned char 		*uncompressed_buffer;
+	unsigned char 		*uncompressed_buffer_pointer;
+	size_t 			 uncompressed_buffer_size;
+	size_t			 uncompressed_buffer_bytes_remaining;
+
+	/* Offset of the compressed data. */
+	int64_t			 stream_offset;
+
+	/*
+	 * Decompressing control data.
+	 */
+	unsigned		 folder_index;
+	uint64_t		 folder_outbytes_remaining;
+	unsigned		 pack_stream_index;
+	unsigned		 pack_stream_remaining;
+	uint64_t		 pack_stream_inbytes_remaining;
+	size_t			 pack_stream_bytes_unconsumed;
+
+	/* The codec information of a folder. */
+	unsigned long		 codec;
+	unsigned long		 codec2;
+
+	/*
+	 * Decompressor controllers.
+	 */
+	/* Decoding LZMA1 and LZMA2 data. */
+#ifdef HAVE_LZMA_H
+	lzma_stream		 lzstream;
+	int			 lzstream_valid;
+#endif
+	/* Decoding bzip2 data. */
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	bz_stream		 bzstream;
+	int			 bzstream_valid;
+#endif
+	/* Decoding deflate data. */
+#ifdef HAVE_ZLIB_H
+	z_stream		 stream;
+	int			 stream_valid;
+#endif
+	/* Decoding PPMd data. */
+	int			 ppmd7_stat;
+	CPpmd7			 ppmd7_context;
+	CPpmd7z_RangeDec	 range_dec;
+	IByteIn			 bytein;
+	struct {
+		const unsigned char	*next_in;
+		int64_t			 avail_in;
+		int64_t			 total_in;
+		unsigned char		*next_out;
+		int64_t			 avail_out;
+		int64_t			 total_out;
+		int			 overconsumed;
+	} ppstream;
+	int			 ppmd7_valid;
+
+	/* Decoding BCJ and BCJ2 data. */
+	uint32_t		 bcj_state;
+	size_t			 odd_bcj_size;
+	unsigned char		 odd_bcj[4];
+	/* Decoding BCJ data. */
+	size_t			 bcj_prevPosT;
+	uint32_t		 bcj_prevMask;
+	uint32_t		 bcj_ip;
+
+	/* Decoding BCJ2 data. */
+	size_t			 main_stream_bytes_remaining;
+	unsigned char		*sub_stream_buff[3];
+	size_t			 sub_stream_size[3];
+	size_t			 sub_stream_bytes_remaining[3];
+	unsigned char		*tmp_stream_buff;
+	size_t			 tmp_stream_buff_size;
+	size_t			 tmp_stream_bytes_avail;
+	size_t			 tmp_stream_bytes_remaining;
+#ifdef _LZMA_PROB32
+#define CProb uint32_t
+#else
+#define CProb uint16_t
+#endif
+	CProb			 bcj2_p[256 + 2];
+	uint8_t			 bcj2_prevByte;
+	uint32_t		 bcj2_range;
+	uint32_t		 bcj2_code;
+	uint64_t		 bcj2_outPos;
+
+	/* Filename character-set conversion data. */
+	struct archive_string_conv *sconv;
+
+	char			 format_name[64];
+
+	/* Custom value that is non-zero if this archive contains encrypted entries. */
+	int			 has_encrypted_entries;
+};
+
+/* Maximum entry size. This limitation prevents reading intentional
+ * corrupted 7-zip files on assuming there are not so many entries in
+ * the files. */
+#define UMAX_ENTRY	ARCHIVE_LITERAL_ULL(100000000)
+
+static int	archive_read_format_7zip_has_encrypted_entries(struct archive_read *);
+static int	archive_read_support_format_7zip_capabilities(struct archive_read *a);
+static int	archive_read_format_7zip_bid(struct archive_read *, int);
+static int	archive_read_format_7zip_cleanup(struct archive_read *);
+static int	archive_read_format_7zip_read_data(struct archive_read *,
+		    const void **, size_t *, int64_t *);
+static int	archive_read_format_7zip_read_data_skip(struct archive_read *);
+static int	archive_read_format_7zip_read_header(struct archive_read *,
+		    struct archive_entry *);
+static int	check_7zip_header_in_sfx(const char *);
+static unsigned long decode_codec_id(const unsigned char *, size_t);
+static int	decode_encoded_header_info(struct archive_read *,
+		    struct _7z_stream_info *);
+static int	decompress(struct archive_read *, struct _7zip *,
+		    void *, size_t *, const void *, size_t *);
+static ssize_t	extract_pack_stream(struct archive_read *, size_t);
+static void	fileTimeToUtc(uint64_t, time_t *, long *);
+static uint64_t folder_uncompressed_size(struct _7z_folder *);
+static void	free_CodersInfo(struct _7z_coders_info *);
+static void	free_Digest(struct _7z_digests *);
+static void	free_Folder(struct _7z_folder *);
+static void	free_Header(struct _7z_header_info *);
+static void	free_PackInfo(struct _7z_pack_info *);
+static void	free_StreamsInfo(struct _7z_stream_info *);
+static void	free_SubStreamsInfo(struct _7z_substream_info *);
+static int	free_decompression(struct archive_read *, struct _7zip *);
+static ssize_t	get_uncompressed_data(struct archive_read *, const void **,
+		    size_t, size_t);
+static const unsigned char * header_bytes(struct archive_read *, size_t);
+static int	init_decompression(struct archive_read *, struct _7zip *,
+		    const struct _7z_coder *, const struct _7z_coder *);
+static int	parse_7zip_uint64(struct archive_read *, uint64_t *);
+static int	read_Bools(struct archive_read *, unsigned char *, size_t);
+static int	read_CodersInfo(struct archive_read *,
+		    struct _7z_coders_info *);
+static int	read_Digests(struct archive_read *, struct _7z_digests *,
+		    size_t);
+static int	read_Folder(struct archive_read *, struct _7z_folder *);
+static int	read_Header(struct archive_read *, struct _7z_header_info *,
+		    int);
+static int	read_PackInfo(struct archive_read *, struct _7z_pack_info *);
+static int	read_StreamsInfo(struct archive_read *,
+		    struct _7z_stream_info *);
+static int	read_SubStreamsInfo(struct archive_read *,
+		    struct _7z_substream_info *, struct _7z_folder *, size_t);
+static int	read_Times(struct archive_read *, struct _7z_header_info *,
+		    int);
+static void	read_consume(struct archive_read *);
+static ssize_t	read_stream(struct archive_read *, const void **, size_t,
+		    size_t);
+static int	seek_pack(struct archive_read *);
+static int64_t	skip_stream(struct archive_read *, size_t);
+static int	skip_sfx(struct archive_read *, ssize_t);
+static int	slurp_central_directory(struct archive_read *, struct _7zip *,
+		    struct _7z_header_info *);
+static int	setup_decode_folder(struct archive_read *, struct _7z_folder *,
+		    int);
+static void	x86_Init(struct _7zip *);
+static size_t	x86_Convert(struct _7zip *, uint8_t *, size_t);
+static ssize_t		Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
+
+
+int
+archive_read_support_format_7zip(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct _7zip *zip;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_7zip");
+
+	zip = calloc(1, sizeof(*zip));
+	if (zip == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate 7zip data");
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Until enough data has been read, we cannot tell about
+	 * any encrypted entries yet.
+	 */
+	zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+
+
+	r = __archive_read_register_format(a,
+	    zip,
+	    "7zip",
+	    archive_read_format_7zip_bid,
+	    NULL,
+	    archive_read_format_7zip_read_header,
+	    archive_read_format_7zip_read_data,
+	    archive_read_format_7zip_read_data_skip,
+	    NULL,
+	    archive_read_format_7zip_cleanup,
+	    archive_read_support_format_7zip_capabilities,
+	    archive_read_format_7zip_has_encrypted_entries);
+
+	if (r != ARCHIVE_OK)
+		free(zip);
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_support_format_7zip_capabilities(struct archive_read * a)
+{
+	(void)a; /* UNUSED */
+	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
+			ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+
+static int
+archive_read_format_7zip_has_encrypted_entries(struct archive_read *_a)
+{
+	if (_a && _a->format) {
+		struct _7zip * zip = (struct _7zip *)_a->format->data;
+		if (zip) {
+			return zip->has_encrypted_entries;
+		}
+	}
+	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+static int
+archive_read_format_7zip_bid(struct archive_read *a, int best_bid)
+{
+	const char *p;
+
+	/* If someone has already bid more than 32, then avoid
+	   trashing the look-ahead buffers with a seek. */
+	if (best_bid > 32)
+		return (-1);
+
+	if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)
+		return (0);
+
+	/* If first six bytes are the 7-Zip signature,
+	 * return the bid right now. */
+	if (memcmp(p, _7ZIP_SIGNATURE, 6) == 0)
+		return (48);
+
+	/*
+	 * It may a 7-Zip SFX archive file. If first two bytes are
+	 * 'M' and 'Z' available on Windows or first four bytes are
+	 * "\x7F\x45LF" available on posix like system, seek the 7-Zip
+	 * signature. Although we will perform a seek when reading
+	 * a header, what we do not use __archive_read_seek() here is
+	 * due to a bidding performance.
+	 */
+	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+		ssize_t offset = SFX_MIN_ADDR;
+		ssize_t window = 4096;
+		ssize_t bytes_avail;
+		while (offset + window <= (SFX_MAX_ADDR)) {
+			const char *buff = __archive_read_ahead(a,
+					offset + window, &bytes_avail);
+			if (buff == NULL) {
+				/* Remaining bytes are less than window. */
+				window >>= 1;
+				if (window < 0x40)
+					return (0);
+				continue;
+			}
+			p = buff + offset;
+			while (p + 32 < buff + bytes_avail) {
+				int step = check_7zip_header_in_sfx(p);
+				if (step == 0)
+					return (48);
+				p += step;
+			}
+			offset = p - buff;
+		}
+	}
+	return (0);
+}
+
+static int
+check_7zip_header_in_sfx(const char *p)
+{
+	switch ((unsigned char)p[5]) {
+	case 0x1C:
+		if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0)
+			return (6);
+		/*
+		 * Test the CRC because its extraction code has 7-Zip
+		 * Magic Code, so we should do this in order not to
+		 * make a mis-detection.
+		 */
+		if (crc32(0, (const unsigned char *)p + 12, 20)
+			!= archive_le32dec(p + 8))
+			return (6);
+		/* Hit the header! */
+		return (0);
+	case 0x37: return (5);
+	case 0x7A: return (4);
+	case 0xBC: return (3);
+	case 0xAF: return (2);
+	case 0x27: return (1);
+	default: return (6);
+	}
+}
+
+static int
+skip_sfx(struct archive_read *a, ssize_t bytes_avail)
+{
+	const void *h;
+	const char *p, *q;
+	size_t skip, offset;
+	ssize_t bytes, window;
+
+	/*
+	 * If bytes_avail > SFX_MIN_ADDR we do not have to call
+	 * __archive_read_seek() at this time since we have
+	 * already had enough data.
+	 */
+	if (bytes_avail > SFX_MIN_ADDR)
+		__archive_read_consume(a, SFX_MIN_ADDR);
+	else if (__archive_read_seek(a, SFX_MIN_ADDR, SEEK_SET) < 0)
+		return (ARCHIVE_FATAL);
+
+	offset = 0;
+	window = 1;
+	while (offset + window <= SFX_MAX_ADDR - SFX_MIN_ADDR) {
+		h = __archive_read_ahead(a, window, &bytes);
+		if (h == NULL) {
+			/* Remaining bytes are less than window. */
+			window >>= 1;
+			if (window < 0x40)
+				goto fatal;
+			continue;
+		}
+		if (bytes < 6) {
+			/* This case might happen when window == 1. */
+			window = 4096;
+			continue;
+		}
+		p = (const char *)h;
+		q = p + bytes;
+
+		/*
+		 * Scan ahead until we find something that looks
+		 * like the 7-Zip header.
+		 */
+		while (p + 32 < q) {
+			int step = check_7zip_header_in_sfx(p);
+			if (step == 0) {
+				struct _7zip *zip =
+				    (struct _7zip *)a->format->data;
+				skip = p - (const char *)h;
+				__archive_read_consume(a, skip);
+				zip->seek_base = SFX_MIN_ADDR + offset + skip;
+				return (ARCHIVE_OK);
+			}
+			p += step;
+		}
+		skip = p - (const char *)h;
+		__archive_read_consume(a, skip);
+		offset += skip;
+		if (window == 1)
+			window = 4096;
+	}
+fatal:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Couldn't find out 7-Zip header");
+	return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_7zip_read_header(struct archive_read *a,
+	struct archive_entry *entry)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	struct _7zip_entry *zip_entry;
+	int r, ret = ARCHIVE_OK;
+	struct _7z_folder *folder = 0;
+	uint64_t fidx = 0;
+
+	/*
+	 * It should be sufficient to call archive_read_next_header() for
+	 * a reader to determine if an entry is encrypted or not. If the
+	 * encryption of an entry is only detectable when calling
+	 * archive_read_data(), so be it. We'll do the same check there
+	 * as well.
+	 */
+	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+		zip->has_encrypted_entries = 0;
+	}
+
+	a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
+	if (a->archive.archive_format_name == NULL)
+		a->archive.archive_format_name = "7-Zip";
+
+	if (zip->entries == NULL) {
+		struct _7z_header_info header;
+
+		memset(&header, 0, sizeof(header));
+		r = slurp_central_directory(a, zip, &header);
+		free_Header(&header);
+		if (r != ARCHIVE_OK)
+			return (r);
+		zip->entries_remaining = (size_t)zip->numFiles;
+		zip->entry = zip->entries;
+	} else {
+		++zip->entry;
+	}
+	zip_entry = zip->entry;
+
+	if (zip->entries_remaining <= 0 || zip_entry == NULL)
+		return ARCHIVE_EOF;
+	--zip->entries_remaining;
+
+	zip->entry_offset = 0;
+	zip->end_of_entry = 0;
+	zip->entry_crc32 = crc32(0, NULL, 0);
+
+	/* Setup a string conversion for a filename. */
+	if (zip->sconv == NULL) {
+		zip->sconv = archive_string_conversion_from_charset(
+		    &a->archive, "UTF-16LE", 1);
+		if (zip->sconv == NULL)
+			return (ARCHIVE_FATAL);
+	}
+
+	/* Figure out if the entry is encrypted by looking at the folder
+	   that is associated to the current 7zip entry. If the folder
+	   has a coder with a _7Z_CRYPTO codec then the folder is encrypted.
+	   Hence the entry must also be encrypted. */
+	if (zip_entry && zip_entry->folderIndex < zip->si.ci.numFolders) {
+		folder = &(zip->si.ci.folders[zip_entry->folderIndex]);
+		for (fidx=0; folder && fidx<folder->numCoders; fidx++) {
+			switch(folder->coders[fidx].codec) {
+				case _7Z_CRYPTO_MAIN_ZIP:
+				case _7Z_CRYPTO_RAR_29:
+				case _7Z_CRYPTO_AES_256_SHA_256: {
+					archive_entry_set_is_data_encrypted(entry, 1);
+					zip->has_encrypted_entries = 1;
+					break;
+				}
+			}
+		}
+	}
+
+	/* Now that we've checked for encryption, if there were still no
+	 * encrypted entries found we can say for sure that there are none.
+	 */
+	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+		zip->has_encrypted_entries = 0;
+	}
+
+	if (archive_entry_copy_pathname_l(entry,
+	    (const char *)zip_entry->utf16name,
+	    zip_entry->name_len, zip->sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Pathname cannot be converted "
+		    "from %s to current locale.",
+		    archive_string_conversion_charset_name(zip->sconv));
+		ret = ARCHIVE_WARN;
+	}
+
+	/* Populate some additional entry fields: */
+	archive_entry_set_mode(entry, zip_entry->mode);
+	if (zip_entry->flg & MTIME_IS_SET)
+		archive_entry_set_mtime(entry, zip_entry->mtime,
+			zip_entry->mtime_ns);
+	if (zip_entry->flg & CTIME_IS_SET)
+		archive_entry_set_ctime(entry, zip_entry->ctime,
+		    zip_entry->ctime_ns);
+	if (zip_entry->flg & ATIME_IS_SET)
+		archive_entry_set_atime(entry, zip_entry->atime,
+		    zip_entry->atime_ns);
+	if (zip_entry->ssIndex != (uint32_t)-1) {
+		zip->entry_bytes_remaining =
+		    zip->si.ss.unpackSizes[zip_entry->ssIndex];
+		archive_entry_set_size(entry, zip->entry_bytes_remaining);
+	} else {
+		zip->entry_bytes_remaining = 0;
+		archive_entry_set_size(entry, 0);
+	}
+
+	/* If there's no body, force read_data() to return EOF immediately. */
+	if (zip->entry_bytes_remaining < 1)
+		zip->end_of_entry = 1;
+
+	if ((zip_entry->mode & AE_IFMT) == AE_IFLNK) {
+		unsigned char *symname = NULL;
+		size_t symsize = 0;
+
+		/*
+		 * Symbolic-name is recorded as its contents. We have to
+		 * read the contents at this time.
+		 */
+		while (zip->entry_bytes_remaining > 0) {
+			const void *buff;
+			unsigned char *mem;
+			size_t size;
+			int64_t offset;
+
+			r = archive_read_format_7zip_read_data(a, &buff,
+				&size, &offset);
+			if (r < ARCHIVE_WARN) {
+				free(symname);
+				return (r);
+			}
+			mem = realloc(symname, symsize + size + 1);
+			if (mem == NULL) {
+				free(symname);
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Symname");
+				return (ARCHIVE_FATAL);
+			}
+			symname = mem;
+			memcpy(symname+symsize, buff, size);
+			symsize += size;
+		}
+		if (symsize == 0) {
+			/* If there is no symname, handle it as a regular
+			 * file. */
+			zip_entry->mode &= ~AE_IFMT;
+			zip_entry->mode |= AE_IFREG;
+			archive_entry_set_mode(entry, zip_entry->mode);
+		} else {
+			symname[symsize] = '\0';
+			archive_entry_copy_symlink(entry,
+			    (const char *)symname);
+		}
+		free(symname);
+		archive_entry_set_size(entry, 0);
+	}
+
+	/* Set up a more descriptive format name. */
+	sprintf(zip->format_name, "7-Zip");
+	a->archive.archive_format_name = zip->format_name;
+
+	return (ret);
+}
+
+static int
+archive_read_format_7zip_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	struct _7zip *zip;
+	ssize_t bytes;
+	int ret = ARCHIVE_OK;
+
+	zip = (struct _7zip *)(a->format->data);
+
+	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+		zip->has_encrypted_entries = 0;
+	}
+
+	if (zip->pack_stream_bytes_unconsumed)
+		read_consume(a);
+
+	*offset = zip->entry_offset;
+	*size = 0;
+	*buff = NULL;
+	/*
+	 * If we hit end-of-entry last time, clean up and return
+	 * ARCHIVE_EOF this time.
+	 */
+	if (zip->end_of_entry)
+		return (ARCHIVE_EOF);
+
+	bytes = read_stream(a, buff,
+		(size_t)zip->entry_bytes_remaining, 0);
+	if (bytes < 0)
+		return ((int)bytes);
+	if (bytes == 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated 7-Zip file body");
+		return (ARCHIVE_FATAL);
+	}
+	zip->entry_bytes_remaining -= bytes;
+	if (zip->entry_bytes_remaining == 0)
+		zip->end_of_entry = 1;
+
+	/* Update checksum */
+	if ((zip->entry->flg & CRC32_IS_SET) && bytes)
+		zip->entry_crc32 = crc32(zip->entry_crc32, *buff,
+		    (unsigned)bytes);
+
+	/* If we hit the end, swallow any end-of-data marker. */
+	if (zip->end_of_entry) {
+		/* Check computed CRC against file contents. */
+		if ((zip->entry->flg & CRC32_IS_SET) &&
+			zip->si.ss.digests[zip->entry->ssIndex] !=
+		    zip->entry_crc32) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "7-Zip bad CRC: 0x%lx should be 0x%lx",
+			    (unsigned long)zip->entry_crc32,
+			    (unsigned long)zip->si.ss.digests[
+			    		zip->entry->ssIndex]);
+			ret = ARCHIVE_WARN;
+		}
+	}
+
+	*size = bytes;
+	*offset = zip->entry_offset;
+	zip->entry_offset += bytes;
+
+	return (ret);
+}
+
+static int
+archive_read_format_7zip_read_data_skip(struct archive_read *a)
+{
+	struct _7zip *zip;
+	int64_t bytes_skipped;
+
+	zip = (struct _7zip *)(a->format->data);
+
+	if (zip->pack_stream_bytes_unconsumed)
+		read_consume(a);
+
+	/* If we've already read to end of data, we're done. */
+	if (zip->end_of_entry)
+		return (ARCHIVE_OK);
+
+	/*
+	 * If the length is at the beginning, we can skip the
+	 * compressed data much more quickly.
+	 */
+	bytes_skipped = skip_stream(a, (size_t)zip->entry_bytes_remaining);
+	if (bytes_skipped < 0)
+		return (ARCHIVE_FATAL);
+	zip->entry_bytes_remaining = 0;
+
+	/* This entry is finished and done. */
+	zip->end_of_entry = 1;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_7zip_cleanup(struct archive_read *a)
+{
+	struct _7zip *zip;
+
+	zip = (struct _7zip *)(a->format->data);
+	free_StreamsInfo(&(zip->si));
+	free(zip->entries);
+	free(zip->entry_names);
+	free_decompression(a, zip);
+	free(zip->uncompressed_buffer);
+	free(zip->sub_stream_buff[0]);
+	free(zip->sub_stream_buff[1]);
+	free(zip->sub_stream_buff[2]);
+	free(zip->tmp_stream_buff);
+	free(zip);
+	(a->format->data) = NULL;
+	return (ARCHIVE_OK);
+}
+
+static void
+read_consume(struct archive_read *a)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+
+	if (zip->pack_stream_bytes_unconsumed) {
+		__archive_read_consume(a, zip->pack_stream_bytes_unconsumed);
+		zip->stream_offset += zip->pack_stream_bytes_unconsumed;
+		zip->pack_stream_bytes_unconsumed = 0;
+	}
+}
+
+#ifdef HAVE_LZMA_H
+
+/*
+ * Set an error code and choose an error message for liblzma.
+ */
+static void
+set_error(struct archive_read *a, int ret)
+{
+
+	switch (ret) {
+	case LZMA_STREAM_END: /* Found end of stream. */
+	case LZMA_OK: /* Decompressor made some progress. */
+		break;
+	case LZMA_MEM_ERROR:
+		archive_set_error(&a->archive, ENOMEM,
+		    "Lzma library error: Cannot allocate memory");
+		break;
+	case LZMA_MEMLIMIT_ERROR:
+		archive_set_error(&a->archive, ENOMEM,
+		    "Lzma library error: Out of memory");
+		break;
+	case LZMA_FORMAT_ERROR:
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Lzma library error: format not recognized");
+		break;
+	case LZMA_OPTIONS_ERROR:
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Lzma library error: Invalid options");
+		break;
+	case LZMA_DATA_ERROR:
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Lzma library error: Corrupted input data");
+		break;
+	case LZMA_BUF_ERROR:
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Lzma library error:  No progress is possible");
+		break;
+	default:
+		/* Return an error. */
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "Lzma decompression failed:  Unknown error");
+		break;
+	}
+}
+
+#endif
+
+static unsigned long
+decode_codec_id(const unsigned char *codecId, size_t id_size)
+{
+	unsigned i;
+	unsigned long id = 0;
+
+	for (i = 0; i < id_size; i++) {
+		id <<= 8;
+		id += codecId[i];
+	}
+	return (id);
+}
+
+static Byte
+ppmd_read(void *p)
+{
+	struct archive_read *a = ((IByteIn*)p)->a;
+	struct _7zip *zip = (struct _7zip *)(a->format->data);
+	Byte b;
+
+	if (zip->ppstream.avail_in == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated RAR file data");
+		zip->ppstream.overconsumed = 1;
+		return (0);
+	}
+	b = *zip->ppstream.next_in++;
+	zip->ppstream.avail_in--;
+	zip->ppstream.total_in++;
+	return (b);
+}
+
+static int
+init_decompression(struct archive_read *a, struct _7zip *zip,
+    const struct _7z_coder *coder1, const struct _7z_coder *coder2)
+{
+	int r;
+
+	zip->codec = coder1->codec;
+	zip->codec2 = -1;
+
+	switch (zip->codec) {
+	case _7Z_COPY:
+	case _7Z_BZ2:
+	case _7Z_DEFLATE:
+	case _7Z_PPMD:
+		if (coder2 != NULL) {
+			if (coder2->codec != _7Z_X86 &&
+			    coder2->codec != _7Z_X86_BCJ2) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Unsupported filter %lx for %lx",
+				    coder2->codec, coder1->codec);
+				return (ARCHIVE_FAILED);
+			}
+			zip->codec2 = coder2->codec;
+			zip->bcj_state = 0;
+			if (coder2->codec == _7Z_X86)
+				x86_Init(zip);
+		}
+		break;
+	default:
+		break;
+	}
+
+	switch (zip->codec) {
+	case _7Z_COPY:
+		break;
+
+	case _7Z_LZMA: case _7Z_LZMA2:
+#ifdef HAVE_LZMA_H
+#if LZMA_VERSION_MAJOR >= 5
+/* Effectively disable the limiter. */
+#define LZMA_MEMLIMIT   UINT64_MAX
+#else
+/* NOTE: This needs to check memory size which running system has. */
+#define LZMA_MEMLIMIT   (1U << 30)
+#endif
+	{
+		lzma_options_delta delta_opt;
+		lzma_filter filters[LZMA_FILTERS_MAX], *ff;
+		int fi = 0;
+
+		if (zip->lzstream_valid) {
+			lzma_end(&(zip->lzstream));
+			zip->lzstream_valid = 0;
+		}
+
+		/*
+		 * NOTE: liblzma incompletely handle the BCJ+LZMA compressed
+		 * data made by 7-Zip because 7-Zip does not add End-Of-
+		 * Payload Marker(EOPM) at the end of LZMA compressed data,
+		 * and so liblzma cannot know the end of the compressed data
+		 * without EOPM. So consequently liblzma will not return last
+		 * three or four bytes of uncompressed data because
+		 * LZMA_FILTER_X86 filter does not handle input data if its
+		 * data size is less than five bytes. If liblzma detect EOPM
+		 * or know the uncompressed data size, liblzma will flush out
+		 * the remaining that three or four bytes of uncompressed
+		 * data. That is why we have to use our converting program
+		 * for BCJ+LZMA. If we were able to tell the uncompressed
+		 * size to liblzma when using lzma_raw_decoder() liblzma
+		 * could correctly deal with BCJ+LZMA. But unfortunately
+		 * there is no way to do that.
+		 * Discussion about this can be found at XZ Utils forum.
+		 */
+		if (coder2 != NULL) {
+			zip->codec2 = coder2->codec;
+
+			filters[fi].options = NULL;
+			switch (zip->codec2) {
+			case _7Z_X86:
+				if (zip->codec == _7Z_LZMA2) {
+					filters[fi].id = LZMA_FILTER_X86;
+					fi++;
+				} else
+					/* Use our filter. */
+					x86_Init(zip);
+				break;
+			case _7Z_X86_BCJ2:
+				/* Use our filter. */
+				zip->bcj_state = 0;
+				break;
+			case _7Z_DELTA:
+				if (coder2->propertiesSize != 1) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_MISC,
+					    "Invalid Delta parameter");
+					return (ARCHIVE_FAILED);
+				}
+				filters[fi].id = LZMA_FILTER_DELTA;
+				memset(&delta_opt, 0, sizeof(delta_opt));
+				delta_opt.type = LZMA_DELTA_TYPE_BYTE;
+				delta_opt.dist =
+				    (uint32_t)coder2->properties[0] + 1;
+				filters[fi].options = &delta_opt;
+				fi++;
+				break;
+			/* Following filters have not been tested yet. */
+			case _7Z_POWERPC:
+				filters[fi].id = LZMA_FILTER_POWERPC;
+				fi++;
+				break;
+			case _7Z_IA64:
+				filters[fi].id = LZMA_FILTER_IA64;
+				fi++;
+				break;
+			case _7Z_ARM:
+				filters[fi].id = LZMA_FILTER_ARM;
+				fi++;
+				break;
+			case _7Z_ARMTHUMB:
+				filters[fi].id = LZMA_FILTER_ARMTHUMB;
+				fi++;
+				break;
+			case _7Z_SPARC:
+				filters[fi].id = LZMA_FILTER_SPARC;
+				fi++;
+				break;
+			default:
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Unexpected codec ID: %lX", zip->codec2);
+				return (ARCHIVE_FAILED);
+			}
+		}
+
+		if (zip->codec == _7Z_LZMA2)
+			filters[fi].id = LZMA_FILTER_LZMA2;
+		else
+			filters[fi].id = LZMA_FILTER_LZMA1;
+		filters[fi].options = NULL;
+		ff = &filters[fi];
+		r = lzma_properties_decode(&filters[fi], NULL,
+		    coder1->properties, (size_t)coder1->propertiesSize);
+		if (r != LZMA_OK) {
+			set_error(a, r);
+			return (ARCHIVE_FAILED);
+		}
+		fi++;
+
+		filters[fi].id = LZMA_VLI_UNKNOWN;
+		filters[fi].options = NULL;
+		r = lzma_raw_decoder(&(zip->lzstream), filters);
+		free(ff->options);
+		if (r != LZMA_OK) {
+			set_error(a, r);
+			return (ARCHIVE_FAILED);
+		}
+		zip->lzstream_valid = 1;
+		zip->lzstream.total_in = 0;
+		zip->lzstream.total_out = 0;
+		break;
+	}
+#else
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "LZMA codec is unsupported");
+		return (ARCHIVE_FAILED);
+#endif
+	case _7Z_BZ2:
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+		if (zip->bzstream_valid) {
+			BZ2_bzDecompressEnd(&(zip->bzstream));
+			zip->bzstream_valid = 0;
+		}
+		r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 0);
+		if (r == BZ_MEM_ERROR)
+			r = BZ2_bzDecompressInit(&(zip->bzstream), 0, 1);
+		if (r != BZ_OK) {
+			int err = ARCHIVE_ERRNO_MISC;
+			const char *detail = NULL;
+			switch (r) {
+			case BZ_PARAM_ERROR:
+				detail = "invalid setup parameter";
+				break;
+			case BZ_MEM_ERROR:
+				err = ENOMEM;
+				detail = "out of memory";
+				break;
+			case BZ_CONFIG_ERROR:
+				detail = "mis-compiled library";
+				break;
+			}
+			archive_set_error(&a->archive, err,
+			    "Internal error initializing decompressor: %s",
+			    detail != NULL ? detail : "??");
+			zip->bzstream_valid = 0;
+			return (ARCHIVE_FAILED);
+		}
+		zip->bzstream_valid = 1;
+		zip->bzstream.total_in_lo32 = 0;
+		zip->bzstream.total_in_hi32 = 0;
+		zip->bzstream.total_out_lo32 = 0;
+		zip->bzstream.total_out_hi32 = 0;
+		break;
+#else
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "BZ2 codec is unsupported");
+		return (ARCHIVE_FAILED);
+#endif
+	case _7Z_DEFLATE:
+#ifdef HAVE_ZLIB_H
+		if (zip->stream_valid)
+			r = inflateReset(&(zip->stream));
+		else
+			r = inflateInit2(&(zip->stream),
+			    -15 /* Don't check for zlib header */);
+		if (r != Z_OK) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Couldn't initialize zlib stream.");
+			return (ARCHIVE_FAILED);
+		}
+		zip->stream_valid = 1;
+		zip->stream.total_in = 0;
+		zip->stream.total_out = 0;
+		break;
+#else
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "DEFLATE codec is unsupported");
+		return (ARCHIVE_FAILED);
+#endif
+	case _7Z_PPMD:
+	{
+		unsigned order;
+		uint32_t msize;
+
+		if (zip->ppmd7_valid) {
+			__archive_ppmd7_functions.Ppmd7_Free(
+			    &zip->ppmd7_context);
+			zip->ppmd7_valid = 0;
+		}
+
+		if (coder1->propertiesSize < 5) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Malformed PPMd parameter");
+			return (ARCHIVE_FAILED);
+		}
+		order = coder1->properties[0];
+		msize = archive_le32dec(&(coder1->properties[1]));
+		if (order < PPMD7_MIN_ORDER || order > PPMD7_MAX_ORDER ||
+		    msize < PPMD7_MIN_MEM_SIZE || msize > PPMD7_MAX_MEM_SIZE) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Malformed PPMd parameter");
+			return (ARCHIVE_FAILED);
+		}
+		__archive_ppmd7_functions.Ppmd7_Construct(&zip->ppmd7_context);
+		r = __archive_ppmd7_functions.Ppmd7_Alloc(
+			&zip->ppmd7_context, msize);
+		if (r == 0) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Coludn't allocate memory for PPMd");
+			return (ARCHIVE_FATAL);
+		}
+		__archive_ppmd7_functions.Ppmd7_Init(
+			&zip->ppmd7_context, order);
+		__archive_ppmd7_functions.Ppmd7z_RangeDec_CreateVTable(
+			&zip->range_dec);
+		zip->ppmd7_valid = 1;
+		zip->ppmd7_stat = 0;
+		zip->ppstream.overconsumed = 0;
+		zip->ppstream.total_in = 0;
+		zip->ppstream.total_out = 0;
+		break;
+	}
+	case _7Z_X86:
+	case _7Z_X86_BCJ2:
+	case _7Z_POWERPC:
+	case _7Z_IA64:
+	case _7Z_ARM:
+	case _7Z_ARMTHUMB:
+	case _7Z_SPARC:
+	case _7Z_DELTA:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Unexpected codec ID: %lX", zip->codec);
+		return (ARCHIVE_FAILED);
+	case _7Z_CRYPTO_MAIN_ZIP:
+	case _7Z_CRYPTO_RAR_29:
+	case _7Z_CRYPTO_AES_256_SHA_256:
+		if (a->entry) {
+			archive_entry_set_is_metadata_encrypted(a->entry, 1);
+			archive_entry_set_is_data_encrypted(a->entry, 1);
+			zip->has_encrypted_entries = 1;
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Crypto codec not supported yet (ID: 0x%lX)", zip->codec);
+		return (ARCHIVE_FAILED);
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Unknown codec ID: %lX", zip->codec);
+		return (ARCHIVE_FAILED);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
+decompress(struct archive_read *a, struct _7zip *zip,
+    void *buff, size_t *outbytes, const void *b, size_t *used)
+{
+	const uint8_t *t_next_in;
+	uint8_t *t_next_out;
+	size_t o_avail_in, o_avail_out;
+	size_t t_avail_in, t_avail_out;
+	uint8_t *bcj2_next_out;
+	size_t bcj2_avail_out;
+	int r, ret = ARCHIVE_OK;
+
+	t_avail_in = o_avail_in = *used;
+	t_avail_out = o_avail_out = *outbytes;
+	t_next_in = b;
+	t_next_out = buff;
+
+	if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) {
+		int i;
+
+		/* Do not copy out the BCJ remaining bytes when the output
+		 * buffer size is less than five bytes. */
+		if (o_avail_in != 0 && t_avail_out < 5 && zip->odd_bcj_size) {
+			*used = 0;
+			*outbytes = 0;
+			return (ret);
+		}
+		for (i = 0; zip->odd_bcj_size > 0 && t_avail_out; i++) {
+			*t_next_out++ = zip->odd_bcj[i];
+			t_avail_out--;
+			zip->odd_bcj_size--;
+		}
+		if (o_avail_in == 0 || t_avail_out == 0) {
+			*used = o_avail_in - t_avail_in;
+			*outbytes = o_avail_out - t_avail_out;
+			if (o_avail_in == 0)
+				ret = ARCHIVE_EOF;
+			return (ret);
+		}
+	}
+
+	bcj2_next_out = t_next_out;
+	bcj2_avail_out = t_avail_out;
+	if (zip->codec2 == _7Z_X86_BCJ2) {
+		/*
+		 * Decord a remaining decompressed main stream for BCJ2.
+		 */
+		if (zip->tmp_stream_bytes_remaining) {
+			ssize_t bytes;
+			size_t remaining = zip->tmp_stream_bytes_remaining;
+			bytes = Bcj2_Decode(zip, t_next_out, t_avail_out);
+			if (bytes < 0) {
+				archive_set_error(&(a->archive),
+				    ARCHIVE_ERRNO_MISC,
+				    "BCJ2 conversion Failed");
+				return (ARCHIVE_FAILED);
+			}
+			zip->main_stream_bytes_remaining -=
+			    remaining - zip->tmp_stream_bytes_remaining;
+			t_avail_out -= bytes;
+			if (o_avail_in == 0 || t_avail_out == 0) {
+				*used = 0;
+				*outbytes = o_avail_out - t_avail_out;
+				if (o_avail_in == 0 &&
+				    zip->tmp_stream_bytes_remaining)
+					ret = ARCHIVE_EOF;
+				return (ret);
+			}
+			t_next_out += bytes;
+			bcj2_next_out = t_next_out;
+			bcj2_avail_out = t_avail_out;
+		}
+		t_next_out = zip->tmp_stream_buff;
+		t_avail_out = zip->tmp_stream_buff_size;
+	}
+
+	switch (zip->codec) {
+	case _7Z_COPY:
+	{
+		size_t bytes =
+		    (t_avail_in > t_avail_out)?t_avail_out:t_avail_in;
+
+		memcpy(t_next_out, t_next_in, bytes);
+		t_avail_in -= bytes;
+		t_avail_out -= bytes;
+		if (o_avail_in == 0)
+			ret = ARCHIVE_EOF;
+		break;
+	}
+#ifdef HAVE_LZMA_H
+	case _7Z_LZMA: case _7Z_LZMA2:
+		zip->lzstream.next_in = t_next_in;
+		zip->lzstream.avail_in = t_avail_in;
+		zip->lzstream.next_out = t_next_out;
+		zip->lzstream.avail_out = t_avail_out;
+
+		r = lzma_code(&(zip->lzstream), LZMA_RUN);
+		switch (r) {
+		case LZMA_STREAM_END: /* Found end of stream. */
+			lzma_end(&(zip->lzstream));
+			zip->lzstream_valid = 0;
+			ret = ARCHIVE_EOF;
+			break;
+		case LZMA_OK: /* Decompressor made some progress. */
+			break;
+		default:
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+				"Decompression failed(%d)",
+			    r);
+			return (ARCHIVE_FAILED);
+		}
+		t_avail_in = zip->lzstream.avail_in;
+		t_avail_out = zip->lzstream.avail_out;
+		break;
+#endif
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	case _7Z_BZ2:
+		zip->bzstream.next_in = (char *)(uintptr_t)t_next_in;
+		zip->bzstream.avail_in = t_avail_in;
+		zip->bzstream.next_out = (char *)(uintptr_t)t_next_out;
+		zip->bzstream.avail_out = t_avail_out;
+		r = BZ2_bzDecompress(&(zip->bzstream));
+		switch (r) {
+		case BZ_STREAM_END: /* Found end of stream. */
+			switch (BZ2_bzDecompressEnd(&(zip->bzstream))) {
+			case BZ_OK:
+				break;
+			default:
+				archive_set_error(&(a->archive),
+				    ARCHIVE_ERRNO_MISC,
+				    "Failed to clean up decompressor");
+				return (ARCHIVE_FAILED);
+			}
+			zip->bzstream_valid = 0;
+			ret = ARCHIVE_EOF;
+			break;
+		case BZ_OK: /* Decompressor made some progress. */
+			break;
+		default:
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "bzip decompression failed");
+			return (ARCHIVE_FAILED);
+		}
+		t_avail_in = zip->bzstream.avail_in;
+		t_avail_out = zip->bzstream.avail_out;
+		break;
+#endif
+#ifdef HAVE_ZLIB_H
+	case _7Z_DEFLATE:
+		zip->stream.next_in = (Bytef *)(uintptr_t)t_next_in;
+		zip->stream.avail_in = (uInt)t_avail_in;
+		zip->stream.next_out = t_next_out;
+		zip->stream.avail_out = (uInt)t_avail_out;
+		r = inflate(&(zip->stream), 0);
+		switch (r) {
+		case Z_STREAM_END: /* Found end of stream. */
+			ret = ARCHIVE_EOF;
+			break;
+		case Z_OK: /* Decompressor made some progress.*/
+			break;
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "File decompression failed (%d)", r);
+			return (ARCHIVE_FAILED);
+		}
+		t_avail_in = zip->stream.avail_in;
+		t_avail_out = zip->stream.avail_out;
+		break;
+#endif
+	case _7Z_PPMD:
+	{
+		uint64_t flush_bytes;
+
+		if (!zip->ppmd7_valid || zip->ppmd7_stat < 0 ||
+		    t_avail_out <= 0) {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "Decompression internal error");
+			return (ARCHIVE_FAILED);
+		}
+		zip->ppstream.next_in = t_next_in;
+		zip->ppstream.avail_in = t_avail_in;
+		zip->ppstream.next_out = t_next_out;
+		zip->ppstream.avail_out = t_avail_out;
+		if (zip->ppmd7_stat == 0) {
+			zip->bytein.a = a;
+			zip->bytein.Read = &ppmd_read;
+			zip->range_dec.Stream = &zip->bytein;
+			r = __archive_ppmd7_functions.Ppmd7z_RangeDec_Init(
+				&(zip->range_dec));
+			if (r == 0) {
+				zip->ppmd7_stat = -1;
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Failed to initialize PPMd range decorder");
+				return (ARCHIVE_FAILED);
+			}
+			if (zip->ppstream.overconsumed) {
+				zip->ppmd7_stat = -1;
+				return (ARCHIVE_FAILED);
+			}
+			zip->ppmd7_stat = 1;
+		}
+
+		if (t_avail_in == 0)
+			/* XXX Flush out remaining decoded data XXX */
+			flush_bytes = zip->folder_outbytes_remaining;
+		else
+			flush_bytes = 0;
+
+		do {
+			int sym;
+
+			sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+				&(zip->ppmd7_context), &(zip->range_dec.p));
+			if (sym < 0) {
+				zip->ppmd7_stat = -1;
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Failed to decode PPMd");
+				return (ARCHIVE_FAILED);
+			}
+			if (zip->ppstream.overconsumed) {
+				zip->ppmd7_stat = -1;
+				return (ARCHIVE_FAILED);
+			}
+			*zip->ppstream.next_out++ = (unsigned char)sym;
+			zip->ppstream.avail_out--;
+			zip->ppstream.total_out++;
+			if (flush_bytes)
+				flush_bytes--;
+		} while (zip->ppstream.avail_out &&
+			(zip->ppstream.avail_in || flush_bytes));
+
+		t_avail_in = (size_t)zip->ppstream.avail_in;
+		t_avail_out = (size_t)zip->ppstream.avail_out;
+		break;
+	}
+	default:
+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+		    "Decompression internal error");
+		return (ARCHIVE_FAILED);
+	}
+	if (ret != ARCHIVE_OK && ret != ARCHIVE_EOF)
+		return (ret);
+
+	*used = o_avail_in - t_avail_in;
+	*outbytes = o_avail_out - t_avail_out;
+
+	/*
+	 * Decord BCJ.
+	 */
+	if (zip->codec != _7Z_LZMA2 && zip->codec2 == _7Z_X86) {
+		size_t l = x86_Convert(zip, buff, *outbytes);
+		zip->odd_bcj_size = *outbytes - l;
+		if (zip->odd_bcj_size > 0 && zip->odd_bcj_size <= 4 &&
+		    o_avail_in && ret != ARCHIVE_EOF) {
+			memcpy(zip->odd_bcj, ((unsigned char *)buff) + l,
+			    zip->odd_bcj_size);
+			*outbytes = l;
+		} else
+			zip->odd_bcj_size = 0;
+	}
+
+	/*
+	 * Decord BCJ2 with a decompressed main stream.
+	 */
+	if (zip->codec2 == _7Z_X86_BCJ2) {
+		ssize_t bytes;
+
+		zip->tmp_stream_bytes_avail =
+		    zip->tmp_stream_buff_size - t_avail_out;
+		if (zip->tmp_stream_bytes_avail >
+		      zip->main_stream_bytes_remaining)
+			zip->tmp_stream_bytes_avail =
+			    zip->main_stream_bytes_remaining;
+		zip->tmp_stream_bytes_remaining = zip->tmp_stream_bytes_avail;
+		bytes = Bcj2_Decode(zip, bcj2_next_out, bcj2_avail_out);
+		if (bytes < 0) {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC, "BCJ2 conversion Failed");
+			return (ARCHIVE_FAILED);
+		}
+		zip->main_stream_bytes_remaining -=
+		    zip->tmp_stream_bytes_avail
+		      - zip->tmp_stream_bytes_remaining;
+		bcj2_avail_out -= bytes;
+		*outbytes = o_avail_out - bcj2_avail_out;
+	}
+
+	return (ret);
+}
+
+static int
+free_decompression(struct archive_read *a, struct _7zip *zip)
+{
+	int r = ARCHIVE_OK;
+
+#if !defined(HAVE_ZLIB_H) &&\
+	!(defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR))
+	(void)a;/* UNUSED */
+#endif
+#ifdef HAVE_LZMA_H
+	if (zip->lzstream_valid)
+		lzma_end(&(zip->lzstream));
+#endif
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	if (zip->bzstream_valid) {
+		if (BZ2_bzDecompressEnd(&(zip->bzstream)) != BZ_OK) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "Failed to clean up bzip2 decompressor");
+			r = ARCHIVE_FATAL;
+		}
+		zip->bzstream_valid = 0;
+	}
+#endif
+#ifdef HAVE_ZLIB_H
+	if (zip->stream_valid) {
+		if (inflateEnd(&(zip->stream)) != Z_OK) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "Failed to clean up zlib decompressor");
+			r = ARCHIVE_FATAL;
+		}
+		zip->stream_valid = 0;
+	}
+#endif
+	if (zip->ppmd7_valid) {
+		__archive_ppmd7_functions.Ppmd7_Free(
+			&zip->ppmd7_context);
+		zip->ppmd7_valid = 0;
+	}
+	return (r);
+}
+
+static int
+parse_7zip_uint64(struct archive_read *a, uint64_t *val)
+{
+	const unsigned char *p;
+	unsigned char avail, mask;
+	int i;
+
+	if ((p = header_bytes(a, 1)) == NULL)
+		return (-1);
+	avail = *p;
+	mask = 0x80;
+	*val = 0;
+	for (i = 0; i < 8; i++) {
+		if (avail & mask) {
+			if ((p = header_bytes(a, 1)) == NULL)
+				return (-1);
+			*val |= ((uint64_t)*p) << (8 * i);
+			mask >>= 1;
+			continue;
+		}
+		*val += ((uint64_t)(avail & (mask -1))) << (8 * i);
+		break;
+	}
+	return (0);
+}
+
+static int
+read_Bools(struct archive_read *a, unsigned char *data, size_t num)
+{
+	const unsigned char *p;
+	unsigned i, mask = 0, avail = 0;
+
+	for (i = 0; i < num; i++) {
+		if (mask == 0) {
+			if ((p = header_bytes(a, 1)) == NULL)
+				return (-1);
+			avail = *p;
+			mask = 0x80;
+		}
+		data[i] = (avail & mask)?1:0;
+		mask >>= 1;
+	}
+	return (0);
+}
+
+static void
+free_Digest(struct _7z_digests *d)
+{
+	free(d->defineds);
+	free(d->digests);
+}
+
+static int
+read_Digests(struct archive_read *a, struct _7z_digests *d, size_t num)
+{
+	const unsigned char *p;
+	unsigned i;
+
+	if (num == 0)
+		return (-1);
+	memset(d, 0, sizeof(*d));
+
+	d->defineds = malloc(num);
+	if (d->defineds == NULL)
+		return (-1);
+	/*
+	 * Read Bools.
+	 */
+	if ((p = header_bytes(a, 1)) == NULL)
+		return (-1);
+	if (*p == 0) {
+		if (read_Bools(a, d->defineds, num) < 0)
+			return (-1);
+	} else
+		/* All are defined */
+		memset(d->defineds, 1, num);
+
+	d->digests = calloc(num, sizeof(*d->digests));
+	if (d->digests == NULL)
+		return (-1);
+	for (i = 0; i < num; i++) {
+		if (d->defineds[i]) {
+			if ((p = header_bytes(a, 4)) == NULL)
+				return (-1);
+			d->digests[i] = archive_le32dec(p);
+		}
+	}
+
+	return (0);
+}
+
+static void
+free_PackInfo(struct _7z_pack_info *pi)
+{
+	free(pi->sizes);
+	free(pi->positions);
+	free_Digest(&(pi->digest));
+}
+
+static int
+read_PackInfo(struct archive_read *a, struct _7z_pack_info *pi)
+{
+	const unsigned char *p;
+	unsigned i;
+
+	memset(pi, 0, sizeof(*pi));
+
+	/*
+	 * Read PackPos.
+	 */
+	if (parse_7zip_uint64(a, &(pi->pos)) < 0)
+		return (-1);
+
+	/*
+	 * Read NumPackStreams.
+	 */
+	if (parse_7zip_uint64(a, &(pi->numPackStreams)) < 0)
+		return (-1);
+	if (pi->numPackStreams == 0)
+		return (-1);
+	if (UMAX_ENTRY < pi->numPackStreams)
+		return (-1);
+
+	/*
+	 * Read PackSizes[num]
+	 */
+	if ((p = header_bytes(a, 1)) == NULL)
+		return (-1);
+	if (*p == kEnd)
+		/* PackSizes[num] are not present. */
+		return (0);
+	if (*p != kSize)
+		return (-1);
+	pi->sizes = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
+	pi->positions = calloc((size_t)pi->numPackStreams, sizeof(uint64_t));
+	if (pi->sizes == NULL || pi->positions == NULL)
+		return (-1);
+
+	for (i = 0; i < pi->numPackStreams; i++) {
+		if (parse_7zip_uint64(a, &(pi->sizes[i])) < 0)
+			return (-1);
+	}
+
+	/*
+	 * Read PackStreamDigests[num]
+	 */
+	if ((p = header_bytes(a, 1)) == NULL)
+		return (-1);
+	if (*p == kEnd) {
+		/* PackStreamDigests[num] are not present. */
+		pi->digest.defineds =
+		    calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.defineds));
+		pi->digest.digests =
+		    calloc((size_t)pi->numPackStreams, sizeof(*pi->digest.digests));
+		if (pi->digest.defineds == NULL || pi->digest.digests == NULL)
+			return (-1);
+		return (0);
+	}
+
+	if (*p != kCRC)
+		return (-1);
+
+	if (read_Digests(a, &(pi->digest), (size_t)pi->numPackStreams) < 0)
+		return (-1);
+
+	/*
+	 *  Must be marked by kEnd.
+	 */
+	if ((p = header_bytes(a, 1)) == NULL)
+		return (-1);
+	if (*p != kEnd)
+		return (-1);
+	return (0);
+}
+
+static void
+free_Folder(struct _7z_folder *f)
+{
+	unsigned i;
+
+	if (f->coders) {
+		for (i = 0; i< f->numCoders; i++) {
+			free(f->coders[i].properties);
+		}
+		free(f->coders);
+	}
+	free(f->bindPairs);
+	free(f->packedStreams);
+	free(f->unPackSize);
+}
+
+static int
+read_Folder(struct archive_read *a, struct _7z_folder *f)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	const unsigned char *p;
+	uint64_t numInStreamsTotal = 0;
+	uint64_t numOutStreamsTotal = 0;
+	unsigned i;
+
+	memset(f, 0, sizeof(*f));
+
+	/*
+	 * Read NumCoders.
+	 */
+	if (parse_7zip_uint64(a, &(f->numCoders)) < 0)
+		return (-1);
+	if (f->numCoders > 4)
+		/* Too many coders. */
+		return (-1);
+
+	f->coders = calloc((size_t)f->numCoders, sizeof(*f->coders));
+	if (f->coders == NULL)
+		return (-1);
+	for (i = 0; i< f->numCoders; i++) {
+		size_t codec_size;
+		int simple, attr;
+
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+		/*
+		 * 0:3 CodecIdSize
+		 * 4:  0 - IsSimple
+		 *     1 - Is not Simple
+		 * 5:  0 - No Attributes
+		 *     1 - There are Attributes;
+		 * 7:  Must be zero.
+		 */
+		codec_size = *p & 0xf;
+		simple = (*p & 0x10)?0:1;
+		attr = *p & 0x20;
+		if (*p & 0x80)
+			return (-1);/* Not supported. */
+
+		/*
+		 * Read Decompression Method IDs.
+		 */
+		if ((p = header_bytes(a, codec_size)) == NULL)
+			return (-1);
+
+		f->coders[i].codec = decode_codec_id(p, codec_size);
+
+		if (simple) {
+			f->coders[i].numInStreams = 1;
+			f->coders[i].numOutStreams = 1;
+		} else {
+			if (parse_7zip_uint64(
+			    a, &(f->coders[i].numInStreams)) < 0)
+				return (-1);
+			if (UMAX_ENTRY < f->coders[i].numInStreams)
+				return (-1);
+			if (parse_7zip_uint64(
+			    a, &(f->coders[i].numOutStreams)) < 0)
+				return (-1);
+			if (UMAX_ENTRY < f->coders[i].numOutStreams)
+				return (-1);
+		}
+
+		if (attr) {
+			if (parse_7zip_uint64(
+			    a, &(f->coders[i].propertiesSize)) < 0)
+				return (-1);
+			if ((p = header_bytes(
+			    a, (size_t)f->coders[i].propertiesSize)) == NULL)
+				return (-1);
+			f->coders[i].properties =
+			    malloc((size_t)f->coders[i].propertiesSize);
+			if (f->coders[i].properties == NULL)
+				return (-1);
+			memcpy(f->coders[i].properties, p,
+			    (size_t)f->coders[i].propertiesSize);
+		}
+
+		numInStreamsTotal += f->coders[i].numInStreams;
+		numOutStreamsTotal += f->coders[i].numOutStreams;
+	}
+
+	if (numOutStreamsTotal == 0 ||
+	    numInStreamsTotal < numOutStreamsTotal-1)
+		return (-1);
+
+	f->numBindPairs = numOutStreamsTotal - 1;
+	if (zip->header_bytes_remaining < f->numBindPairs)
+			return (-1);
+	if (f->numBindPairs > 0) {
+		f->bindPairs =
+			calloc((size_t)f->numBindPairs, sizeof(*f->bindPairs));
+		if (f->bindPairs == NULL)
+			return (-1);
+	} else
+		f->bindPairs = NULL;
+	for (i = 0; i < f->numBindPairs; i++) {
+		if (parse_7zip_uint64(a, &(f->bindPairs[i].inIndex)) < 0)
+			return (-1);
+		if (UMAX_ENTRY < f->bindPairs[i].inIndex)
+			return (-1);
+		if (parse_7zip_uint64(a, &(f->bindPairs[i].outIndex)) < 0)
+			return (-1);
+		if (UMAX_ENTRY < f->bindPairs[i].outIndex)
+			return (-1);
+	}
+
+	f->numPackedStreams = numInStreamsTotal - f->numBindPairs;
+	f->packedStreams =
+	    calloc((size_t)f->numPackedStreams, sizeof(*f->packedStreams));
+	if (f->packedStreams == NULL)
+		return (-1);
+	if (f->numPackedStreams == 1) {
+		for (i = 0; i < numInStreamsTotal; i++) {
+			unsigned j;
+			for (j = 0; j < f->numBindPairs; j++) {
+				if (f->bindPairs[j].inIndex == i)
+					break;
+			}
+			if (j == f->numBindPairs)
+				break;
+		}
+		if (i == numInStreamsTotal)
+			return (-1);
+		f->packedStreams[0] = i;
+	} else {
+		for (i = 0; i < f->numPackedStreams; i++) {
+			if (parse_7zip_uint64(a, &(f->packedStreams[i])) < 0)
+				return (-1);
+			if (UMAX_ENTRY < f->packedStreams[i])
+				return (-1);
+		}
+	}
+	f->numInStreams = numInStreamsTotal;
+	f->numOutStreams = numOutStreamsTotal;
+
+	return (0);
+}
+
+static void
+free_CodersInfo(struct _7z_coders_info *ci)
+{
+	unsigned i;
+
+	if (ci->folders) {
+		for (i = 0; i < ci->numFolders; i++)
+			free_Folder(&(ci->folders[i]));
+		free(ci->folders);
+	}
+}
+
+static int
+read_CodersInfo(struct archive_read *a, struct _7z_coders_info *ci)
+{
+	const unsigned char *p;
+	struct _7z_digests digest;
+	unsigned i;
+
+	memset(ci, 0, sizeof(*ci));
+	memset(&digest, 0, sizeof(digest));
+
+	if ((p = header_bytes(a, 1)) == NULL)
+		goto failed;
+	if (*p != kFolder)
+		goto failed;
+
+	/*
+	 * Read NumFolders.
+	 */
+	if (parse_7zip_uint64(a, &(ci->numFolders)) < 0)
+		goto failed;
+	if (UMAX_ENTRY < ci->numFolders)
+		return (-1);
+
+	/*
+	 * Read External.
+	 */
+	if ((p = header_bytes(a, 1)) == NULL)
+		goto failed;
+	switch (*p) {
+	case 0:
+		ci->folders =
+			calloc((size_t)ci->numFolders, sizeof(*ci->folders));
+		if (ci->folders == NULL)
+			return (-1);
+		for (i = 0; i < ci->numFolders; i++) {
+			if (read_Folder(a, &(ci->folders[i])) < 0)
+				goto failed;
+		}
+		break;
+	case 1:
+		if (parse_7zip_uint64(a, &(ci->dataStreamIndex)) < 0)
+			return (-1);
+		if (UMAX_ENTRY < ci->dataStreamIndex)
+			return (-1);
+		if (ci->numFolders > 0) {
+			archive_set_error(&a->archive, -1,
+			    "Malformed 7-Zip archive");
+			goto failed;
+		}
+		break;
+	default:
+		archive_set_error(&a->archive, -1,
+		    "Malformed 7-Zip archive");
+		goto failed;
+	}
+
+	if ((p = header_bytes(a, 1)) == NULL)
+		goto failed;
+	if (*p != kCodersUnPackSize)
+		goto failed;
+
+	for (i = 0; i < ci->numFolders; i++) {
+		struct _7z_folder *folder = &(ci->folders[i]);
+		unsigned j;
+
+		folder->unPackSize =
+		    calloc((size_t)folder->numOutStreams, sizeof(*folder->unPackSize));
+		if (folder->unPackSize == NULL)
+			goto failed;
+		for (j = 0; j < folder->numOutStreams; j++) {
+			if (parse_7zip_uint64(a, &(folder->unPackSize[j])) < 0)
+				goto failed;
+		}
+	}
+
+	/*
+	 * Read CRCs.
+	 */
+	if ((p = header_bytes(a, 1)) == NULL)
+		goto failed;
+	if (*p == kEnd)
+		return (0);
+	if (*p != kCRC)
+		goto failed;
+	if (read_Digests(a, &digest, (size_t)ci->numFolders) < 0)
+		goto failed;
+	for (i = 0; i < ci->numFolders; i++) {
+		ci->folders[i].digest_defined = digest.defineds[i];
+		ci->folders[i].digest = digest.digests[i];
+	}
+
+	/*
+	 *  Must be kEnd.
+	 */
+	if ((p = header_bytes(a, 1)) == NULL)
+		goto failed;
+	if (*p != kEnd)
+		goto failed;
+	free_Digest(&digest);
+	return (0);
+failed:
+	free_Digest(&digest);
+	return (-1);
+}
+
+static uint64_t
+folder_uncompressed_size(struct _7z_folder *f)
+{
+	int n = (int)f->numOutStreams;
+	unsigned pairs = (unsigned)f->numBindPairs;
+
+	while (--n >= 0) {
+		unsigned i;
+		for (i = 0; i < pairs; i++) {
+			if (f->bindPairs[i].outIndex == (uint64_t)n)
+				break;
+		}
+		if (i >= pairs)
+			return (f->unPackSize[n]);
+	}
+	return (0);
+}
+
+static void
+free_SubStreamsInfo(struct _7z_substream_info *ss)
+{
+	free(ss->unpackSizes);
+	free(ss->digestsDefined);
+	free(ss->digests);
+}
+
+static int
+read_SubStreamsInfo(struct archive_read *a, struct _7z_substream_info *ss,
+    struct _7z_folder *f, size_t numFolders)
+{
+	const unsigned char *p;
+	uint64_t *usizes;
+	size_t unpack_streams;
+	int type;
+	unsigned i;
+	uint32_t numDigests;
+
+	memset(ss, 0, sizeof(*ss));
+
+	for (i = 0; i < numFolders; i++)
+		f[i].numUnpackStreams = 1;
+
+	if ((p = header_bytes(a, 1)) == NULL)
+		return (-1);
+	type = *p;
+
+	if (type == kNumUnPackStream) {
+		unpack_streams = 0;
+		for (i = 0; i < numFolders; i++) {
+			if (parse_7zip_uint64(a, &(f[i].numUnpackStreams)) < 0)
+				return (-1);
+			if (UMAX_ENTRY < f[i].numUnpackStreams)
+				return (-1);
+			if (unpack_streams > SIZE_MAX - UMAX_ENTRY) {
+				return (-1);
+			}
+			unpack_streams += (size_t)f[i].numUnpackStreams;
+		}
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+		type = *p;
+	} else
+		unpack_streams = numFolders;
+
+	ss->unpack_streams = unpack_streams;
+	if (unpack_streams) {
+		ss->unpackSizes = calloc(unpack_streams,
+		    sizeof(*ss->unpackSizes));
+		ss->digestsDefined = calloc(unpack_streams,
+		    sizeof(*ss->digestsDefined));
+		ss->digests = calloc(unpack_streams,
+		    sizeof(*ss->digests));
+		if (ss->unpackSizes == NULL || ss->digestsDefined == NULL ||
+		    ss->digests == NULL)
+			return (-1);
+	}
+
+	usizes = ss->unpackSizes;
+	for (i = 0; i < numFolders; i++) {
+		unsigned pack;
+		uint64_t sum;
+
+		if (f[i].numUnpackStreams == 0)
+			continue;
+
+		sum = 0;
+		if (type == kSize) {
+			for (pack = 1; pack < f[i].numUnpackStreams; pack++) {
+				if (parse_7zip_uint64(a, usizes) < 0)
+					return (-1);
+				sum += *usizes++;
+			}
+		}
+		*usizes++ = folder_uncompressed_size(&f[i]) - sum;
+	}
+
+	if (type == kSize) {
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+		type = *p;
+	}
+
+	for (i = 0; i < unpack_streams; i++) {
+		ss->digestsDefined[i] = 0;
+		ss->digests[i] = 0;
+	}
+
+	numDigests = 0;
+	for (i = 0; i < numFolders; i++) {
+		if (f[i].numUnpackStreams != 1 || !f[i].digest_defined)
+			numDigests += (uint32_t)f[i].numUnpackStreams;
+	}
+
+	if (type == kCRC) {
+		struct _7z_digests tmpDigests;
+		unsigned char *digestsDefined = ss->digestsDefined;
+		uint32_t * digests = ss->digests;
+		int di = 0;
+
+		memset(&tmpDigests, 0, sizeof(tmpDigests));
+		if (read_Digests(a, &(tmpDigests), numDigests) < 0) {
+			free_Digest(&tmpDigests);
+			return (-1);
+		}
+		for (i = 0; i < numFolders; i++) {
+			if (f[i].numUnpackStreams == 1 && f[i].digest_defined) {
+				*digestsDefined++ = 1;
+				*digests++ = f[i].digest;
+			} else {
+				unsigned j;
+
+				for (j = 0; j < f[i].numUnpackStreams;
+				    j++, di++) {
+					*digestsDefined++ =
+					    tmpDigests.defineds[di];
+					*digests++ =
+					    tmpDigests.digests[di];
+				}
+			}
+		}
+		free_Digest(&tmpDigests);
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+		type = *p;
+	}
+
+	/*
+	 *  Must be kEnd.
+	 */
+	if (type != kEnd)
+		return (-1);
+	return (0);
+}
+
+static void
+free_StreamsInfo(struct _7z_stream_info *si)
+{
+	free_PackInfo(&(si->pi));
+	free_CodersInfo(&(si->ci));
+	free_SubStreamsInfo(&(si->ss));
+}
+
+static int
+read_StreamsInfo(struct archive_read *a, struct _7z_stream_info *si)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	const unsigned char *p;
+	unsigned i;
+
+	memset(si, 0, sizeof(*si));
+
+	if ((p = header_bytes(a, 1)) == NULL)
+		return (-1);
+	if (*p == kPackInfo) {
+		uint64_t packPos;
+
+		if (read_PackInfo(a, &(si->pi)) < 0)
+			return (-1);
+
+		if (si->pi.positions == NULL || si->pi.sizes == NULL)
+			return (-1);
+		/*
+		 * Calculate packed stream positions.
+		 */
+		packPos = si->pi.pos;
+		for (i = 0; i < si->pi.numPackStreams; i++) {
+			si->pi.positions[i] = packPos;
+			packPos += si->pi.sizes[i];
+			if (packPos > zip->header_offset)
+				return (-1);
+		}
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+	}
+	if (*p == kUnPackInfo) {
+		uint32_t packIndex;
+		struct _7z_folder *f;
+
+		if (read_CodersInfo(a, &(si->ci)) < 0)
+			return (-1);
+
+		/*
+		 * Calculate packed stream indexes.
+		 */
+		packIndex = 0;
+		f = si->ci.folders;
+		for (i = 0; i < si->ci.numFolders; i++) {
+			f[i].packIndex = packIndex;
+			packIndex += (uint32_t)f[i].numPackedStreams;
+			if (packIndex > si->pi.numPackStreams)
+				return (-1);
+		}
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+	}
+
+	if (*p == kSubStreamsInfo) {
+		if (read_SubStreamsInfo(a, &(si->ss),
+		    si->ci.folders, (size_t)si->ci.numFolders) < 0)
+			return (-1);
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+	}
+
+	/*
+	 *  Must be kEnd.
+	 */
+	if (*p != kEnd)
+		return (-1);
+	return (0);
+}
+
+static void
+free_Header(struct _7z_header_info *h)
+{
+	free(h->emptyStreamBools);
+	free(h->emptyFileBools);
+	free(h->antiBools);
+	free(h->attrBools);
+}
+
+static int
+read_Header(struct archive_read *a, struct _7z_header_info *h,
+    int check_header_id)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	const unsigned char *p;
+	struct _7z_folder *folders;
+	struct _7z_stream_info *si = &(zip->si);
+	struct _7zip_entry *entries;
+	uint32_t folderIndex, indexInFolder;
+	unsigned i;
+	int eindex, empty_streams, sindex;
+
+	if (check_header_id) {
+		/*
+		 * Read Header.
+		 */
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+		if (*p != kHeader)
+			return (-1);
+	}
+
+	/*
+	 * Read ArchiveProperties.
+	 */
+	if ((p = header_bytes(a, 1)) == NULL)
+		return (-1);
+	if (*p == kArchiveProperties) {
+		for (;;) {
+			uint64_t size;
+			if ((p = header_bytes(a, 1)) == NULL)
+				return (-1);
+			if (*p == 0)
+				break;
+			if (parse_7zip_uint64(a, &size) < 0)
+				return (-1);
+		}
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+	}
+
+	/*
+	 * Read MainStreamsInfo.
+	 */
+	if (*p == kMainStreamsInfo) {
+		if (read_StreamsInfo(a, &(zip->si)) < 0)
+			return (-1);
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+	}
+	if (*p == kEnd)
+		return (0);
+
+	/*
+	 * Read FilesInfo.
+	 */
+	if (*p != kFilesInfo)
+		return (-1);
+
+	if (parse_7zip_uint64(a, &(zip->numFiles)) < 0)
+		return (-1);
+	if (UMAX_ENTRY < zip->numFiles)
+		return (-1);
+
+	zip->entries = calloc((size_t)zip->numFiles, sizeof(*zip->entries));
+	if (zip->entries == NULL)
+		return (-1);
+	entries = zip->entries;
+
+	empty_streams = 0;
+	for (;;) {
+		int type;
+		uint64_t size;
+		size_t ll;
+
+		if ((p = header_bytes(a, 1)) == NULL)
+			return (-1);
+		type = *p;
+		if (type == kEnd)
+			break;
+
+		if (parse_7zip_uint64(a, &size) < 0)
+			return (-1);
+		if (zip->header_bytes_remaining < size)
+			return (-1);
+		ll = (size_t)size;
+
+		switch (type) {
+		case kEmptyStream:
+			if (h->emptyStreamBools != NULL)
+				return (-1);
+			h->emptyStreamBools = calloc((size_t)zip->numFiles,
+			    sizeof(*h->emptyStreamBools));
+			if (h->emptyStreamBools == NULL)
+				return (-1);
+			if (read_Bools(
+			    a, h->emptyStreamBools, (size_t)zip->numFiles) < 0)
+				return (-1);
+			empty_streams = 0;
+			for (i = 0; i < zip->numFiles; i++) {
+				if (h->emptyStreamBools[i])
+					empty_streams++;
+			}
+			break;
+		case kEmptyFile:
+			if (empty_streams <= 0) {
+				/* Unexcepted sequence. Skip this. */
+				if (header_bytes(a, ll) == NULL)
+					return (-1);
+				break;
+			}
+			if (h->emptyFileBools != NULL)
+				return (-1);
+			h->emptyFileBools = calloc(empty_streams,
+			    sizeof(*h->emptyFileBools));
+			if (h->emptyFileBools == NULL)
+				return (-1);
+			if (read_Bools(a, h->emptyFileBools, empty_streams) < 0)
+				return (-1);
+			break;
+		case kAnti:
+			if (empty_streams <= 0) {
+				/* Unexcepted sequence. Skip this. */
+				if (header_bytes(a, ll) == NULL)
+					return (-1);
+				break;
+			}
+			if (h->antiBools != NULL)
+				return (-1);
+			h->antiBools = calloc(empty_streams,
+			    sizeof(*h->antiBools));
+			if (h->antiBools == NULL)
+				return (-1);
+			if (read_Bools(a, h->antiBools, empty_streams) < 0)
+				return (-1);
+			break;
+		case kCTime:
+		case kATime:
+		case kMTime:
+			if (read_Times(a, h, type) < 0)
+				return (-1);
+			break;
+		case kName:
+		{
+			unsigned char *np;
+			size_t nl, nb;
+
+			/* Skip one byte. */
+			if ((p = header_bytes(a, 1)) == NULL)
+				return (-1);
+			ll--;
+
+			if ((ll & 1) || ll < zip->numFiles * 4)
+				return (-1);
+
+			if (zip->entry_names != NULL)
+				return (-1);
+			zip->entry_names = malloc(ll);
+			if (zip->entry_names == NULL)
+				return (-1);
+			np = zip->entry_names;
+			nb = ll;
+			/*
+			 * Copy whole file names.
+			 * NOTE: This loop prevents from expanding
+			 * the uncompressed buffer in order not to
+			 * use extra memory resource.
+			 */
+			while (nb) {
+				size_t b;
+				if (nb > UBUFF_SIZE)
+					b = UBUFF_SIZE;
+				else
+					b = nb;
+				if ((p = header_bytes(a, b)) == NULL)
+					return (-1);
+				memcpy(np, p, b);
+				np += b;
+				nb -= b;
+			}
+			np = zip->entry_names;
+			nl = ll;
+
+			for (i = 0; i < zip->numFiles; i++) {
+				entries[i].utf16name = np;
+#if defined(_WIN32) && !defined(__CYGWIN__) && defined(_DEBUG)
+				entries[i].wname = (wchar_t *)np;
+#endif
+
+				/* Find a terminator. */
+				while (nl >= 2 && (np[0] || np[1])) {
+					np += 2;
+					nl -= 2;
+				}
+				if (nl < 2)
+					return (-1);/* Terminator not found */
+				entries[i].name_len = np - entries[i].utf16name;
+				np += 2;
+				nl -= 2;
+			}
+			break;
+		}
+		case kAttributes:
+		{
+			int allAreDefined;
+
+			if ((p = header_bytes(a, 2)) == NULL)
+				return (-1);
+			allAreDefined = *p;
+			if (h->attrBools != NULL)
+				return (-1);
+			h->attrBools = calloc((size_t)zip->numFiles,
+			    sizeof(*h->attrBools));
+			if (h->attrBools == NULL)
+				return (-1);
+			if (allAreDefined)
+				memset(h->attrBools, 1, (size_t)zip->numFiles);
+			else {
+				if (read_Bools(a, h->attrBools,
+				      (size_t)zip->numFiles) < 0)
+					return (-1);
+			}
+			for (i = 0; i < zip->numFiles; i++) {
+				if (h->attrBools[i]) {
+					if ((p = header_bytes(a, 4)) == NULL)
+						return (-1);
+					entries[i].attr = archive_le32dec(p);
+				}
+			}
+			break;
+		}
+		case kDummy:
+			if (ll == 0)
+				break;
+			__LA_FALLTHROUGH;
+		default:
+			if (header_bytes(a, ll) == NULL)
+				return (-1);
+			break;
+		}
+	}
+
+	/*
+	 * Set up entry's attributes.
+	 */
+	folders = si->ci.folders;
+	eindex = sindex = 0;
+	folderIndex = indexInFolder = 0;
+	for (i = 0; i < zip->numFiles; i++) {
+		if (h->emptyStreamBools == NULL || h->emptyStreamBools[i] == 0)
+			entries[i].flg |= HAS_STREAM;
+		/* The high 16 bits of attributes is a posix file mode. */
+		entries[i].mode = entries[i].attr >> 16;
+		if (entries[i].flg & HAS_STREAM) {
+			if ((size_t)sindex >= si->ss.unpack_streams)
+				return (-1);
+			if (entries[i].mode == 0)
+				entries[i].mode = AE_IFREG | 0666;
+			if (si->ss.digestsDefined[sindex])
+				entries[i].flg |= CRC32_IS_SET;
+			entries[i].ssIndex = sindex;
+			sindex++;
+		} else {
+			int dir;
+			if (h->emptyFileBools == NULL)
+				dir = 1;
+			else {
+				if (h->emptyFileBools[eindex])
+					dir = 0;
+				else
+					dir = 1;
+				eindex++;
+			}
+			if (entries[i].mode == 0) {
+				if (dir)
+					entries[i].mode = AE_IFDIR | 0777;
+				else
+					entries[i].mode = AE_IFREG | 0666;
+			} else if (dir &&
+			    (entries[i].mode & AE_IFMT) != AE_IFDIR) {
+				entries[i].mode &= ~AE_IFMT;
+				entries[i].mode |= AE_IFDIR;
+			}
+			if ((entries[i].mode & AE_IFMT) == AE_IFDIR &&
+			    entries[i].name_len >= 2 &&
+			    (entries[i].utf16name[entries[i].name_len-2] != '/' ||
+			     entries[i].utf16name[entries[i].name_len-1] != 0)) {
+				entries[i].utf16name[entries[i].name_len] = '/';
+				entries[i].utf16name[entries[i].name_len+1] = 0;
+				entries[i].name_len += 2;
+			}
+			entries[i].ssIndex = -1;
+		}
+		if (entries[i].attr & 0x01)
+			entries[i].mode &= ~0222;/* Read only. */
+
+		if ((entries[i].flg & HAS_STREAM) == 0 && indexInFolder == 0) {
+			/*
+			 * The entry is an empty file or a directory file,
+			 * those both have no contents.
+			 */
+			entries[i].folderIndex = -1;
+			continue;
+		}
+		if (indexInFolder == 0) {
+			for (;;) {
+				if (folderIndex >= si->ci.numFolders)
+					return (-1);
+				if (folders[folderIndex].numUnpackStreams)
+					break;
+				folderIndex++;
+			}
+		}
+		entries[i].folderIndex = folderIndex;
+		if ((entries[i].flg & HAS_STREAM) == 0)
+			continue;
+		indexInFolder++;
+		if (indexInFolder >= folders[folderIndex].numUnpackStreams) {
+			folderIndex++;
+			indexInFolder = 0;
+		}
+	}
+
+	return (0);
+}
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static void
+fileTimeToUtc(uint64_t fileTime, time_t *timep, long *ns)
+{
+
+	if (fileTime >= EPOC_TIME) {
+		fileTime -= EPOC_TIME;
+		/* milli seconds base */
+		*timep = (time_t)(fileTime / 10000000);
+		/* nano seconds base */
+		*ns = (long)(fileTime % 10000000) * 100;
+	} else {
+		*timep = 0;
+		*ns = 0;
+	}
+}
+
+static int
+read_Times(struct archive_read *a, struct _7z_header_info *h, int type)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	const unsigned char *p;
+	struct _7zip_entry *entries = zip->entries;
+	unsigned char *timeBools;
+	int allAreDefined;
+	unsigned i;
+
+	timeBools = calloc((size_t)zip->numFiles, sizeof(*timeBools));
+	if (timeBools == NULL)
+		return (-1);
+
+	/* Read allAreDefined. */
+	if ((p = header_bytes(a, 1)) == NULL)
+		goto failed;
+	allAreDefined = *p;
+	if (allAreDefined)
+		memset(timeBools, 1, (size_t)zip->numFiles);
+	else {
+		if (read_Bools(a, timeBools, (size_t)zip->numFiles) < 0)
+			goto failed;
+	}
+
+	/* Read external. */
+	if ((p = header_bytes(a, 1)) == NULL)
+		goto failed;
+	if (*p) {
+		if (parse_7zip_uint64(a, &(h->dataIndex)) < 0)
+			goto failed;
+		if (UMAX_ENTRY < h->dataIndex)
+			goto failed;
+	}
+
+	for (i = 0; i < zip->numFiles; i++) {
+		if (!timeBools[i])
+			continue;
+		if ((p = header_bytes(a, 8)) == NULL)
+			goto failed;
+		switch (type) {
+		case kCTime:
+			fileTimeToUtc(archive_le64dec(p),
+			    &(entries[i].ctime),
+			    &(entries[i].ctime_ns));
+			entries[i].flg |= CTIME_IS_SET;
+			break;
+		case kATime:
+			fileTimeToUtc(archive_le64dec(p),
+			    &(entries[i].atime),
+			    &(entries[i].atime_ns));
+			entries[i].flg |= ATIME_IS_SET;
+			break;
+		case kMTime:
+			fileTimeToUtc(archive_le64dec(p),
+			    &(entries[i].mtime),
+			    &(entries[i].mtime_ns));
+			entries[i].flg |= MTIME_IS_SET;
+			break;
+		}
+	}
+
+	free(timeBools);
+	return (0);
+failed:
+	free(timeBools);
+	return (-1);
+}
+
+static int
+decode_encoded_header_info(struct archive_read *a, struct _7z_stream_info *si)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+
+	errno = 0;
+	if (read_StreamsInfo(a, si) < 0) {
+		if (errno == ENOMEM)
+			archive_set_error(&a->archive, -1,
+			    "Couldn't allocate memory");
+		else
+			archive_set_error(&a->archive, -1,
+			    "Malformed 7-Zip archive");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (si->pi.numPackStreams == 0 || si->ci.numFolders == 0) {
+		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (zip->header_offset < si->pi.pos + si->pi.sizes[0] ||
+	    (int64_t)(si->pi.pos + si->pi.sizes[0]) < 0 ||
+	    si->pi.sizes[0] == 0 || (int64_t)si->pi.pos < 0) {
+		archive_set_error(&a->archive, -1, "Malformed Header offset");
+		return (ARCHIVE_FATAL);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static const unsigned char *
+header_bytes(struct archive_read *a, size_t rbytes)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	const unsigned char *p;
+
+	if (zip->header_bytes_remaining < rbytes)
+		return (NULL);
+	if (zip->pack_stream_bytes_unconsumed)
+		read_consume(a);
+
+	if (zip->header_is_encoded == 0) {
+		p = __archive_read_ahead(a, rbytes, NULL);
+		if (p == NULL)
+			return (NULL);
+		zip->header_bytes_remaining -= rbytes;
+		zip->pack_stream_bytes_unconsumed = rbytes;
+	} else {
+		const void *buff;
+		ssize_t bytes;
+
+		bytes = read_stream(a, &buff, rbytes, rbytes);
+		if (bytes <= 0)
+			return (NULL);
+		zip->header_bytes_remaining -= bytes;
+		p = buff;
+	}
+
+	/* Update checksum */
+	zip->header_crc32 = crc32(zip->header_crc32, p, (unsigned)rbytes);
+	return (p);
+}
+
+static int
+slurp_central_directory(struct archive_read *a, struct _7zip *zip,
+    struct _7z_header_info *header)
+{
+	const unsigned char *p;
+	uint64_t next_header_offset;
+	uint64_t next_header_size;
+	uint32_t next_header_crc;
+	ssize_t bytes_avail;
+	int check_header_crc, r;
+
+	if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL)
+		return (ARCHIVE_FATAL);
+
+	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+		/* This is an executable ? Must be self-extracting... */
+		r = skip_sfx(a, bytes_avail);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if ((p = __archive_read_ahead(a, 32, &bytes_avail)) == NULL)
+			return (ARCHIVE_FATAL);
+	}
+	zip->seek_base += 32;
+
+	if (memcmp(p, _7ZIP_SIGNATURE, 6) != 0) {
+		archive_set_error(&a->archive, -1, "Not 7-Zip archive file");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* CRC check. */
+	if (crc32(0, (const unsigned char *)p + 12, 20)
+	    != archive_le32dec(p + 8)) {
+		archive_set_error(&a->archive, -1, "Header CRC error");
+		return (ARCHIVE_FATAL);
+	}
+
+	next_header_offset = archive_le64dec(p + 12);
+	next_header_size = archive_le64dec(p + 20);
+	next_header_crc = archive_le32dec(p + 28);
+
+	if (next_header_size == 0)
+		/* There is no entry in an archive file. */
+		return (ARCHIVE_EOF);
+
+	if (((int64_t)next_header_offset) < 0) {
+		archive_set_error(&a->archive, -1, "Malformed 7-Zip archive");
+		return (ARCHIVE_FATAL);
+	}
+	__archive_read_consume(a, 32);
+	if (next_header_offset != 0) {
+		if (bytes_avail >= (ssize_t)next_header_offset)
+			__archive_read_consume(a, next_header_offset);
+		else if (__archive_read_seek(a,
+		    next_header_offset + zip->seek_base, SEEK_SET) < 0)
+			return (ARCHIVE_FATAL);
+	}
+	zip->stream_offset = next_header_offset;
+	zip->header_offset = next_header_offset;
+	zip->header_bytes_remaining = next_header_size;
+	zip->header_crc32 = 0;
+	zip->header_is_encoded = 0;
+	zip->header_is_being_read = 1;
+	zip->has_encrypted_entries = 0;
+	check_header_crc = 1;
+
+	if ((p = header_bytes(a, 1)) == NULL) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated 7-Zip file body");
+		return (ARCHIVE_FATAL);
+	}
+	/* Parse ArchiveProperties. */
+	switch (p[0]) {
+	case kEncodedHeader:
+		/*
+		 * The archive has an encoded header and we have to decode it
+		 * in order to parse the header correctly.
+		 */
+		r = decode_encoded_header_info(a, &(zip->si));
+
+		/* Check the EncodedHeader CRC.*/
+		if (r == 0 && zip->header_crc32 != next_header_crc) {
+			archive_set_error(&a->archive, -1,
+			    "Damaged 7-Zip archive");
+			r = -1;
+		}
+		if (r == 0) {
+			if (zip->si.ci.folders[0].digest_defined)
+				next_header_crc = zip->si.ci.folders[0].digest;
+			else
+				check_header_crc = 0;
+			if (zip->pack_stream_bytes_unconsumed)
+				read_consume(a);
+			r = setup_decode_folder(a, zip->si.ci.folders, 1);
+			if (r == 0) {
+				zip->header_bytes_remaining =
+					zip->folder_outbytes_remaining;
+				r = seek_pack(a);
+			}
+		}
+		/* Clean up StreamsInfo. */
+		free_StreamsInfo(&(zip->si));
+		memset(&(zip->si), 0, sizeof(zip->si));
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+		zip->header_is_encoded = 1;
+		zip->header_crc32 = 0;
+		/* FALL THROUGH */
+	case kHeader:
+		/*
+		 * Parse the header.
+		 */
+		errno = 0;
+		r = read_Header(a, header, zip->header_is_encoded);
+		if (r < 0) {
+			if (errno == ENOMEM)
+				archive_set_error(&a->archive, -1,
+				    "Couldn't allocate memory");
+			else
+				archive_set_error(&a->archive, -1,
+				    "Damaged 7-Zip archive");
+			return (ARCHIVE_FATAL);
+		}
+
+		/*
+		 *  Must be kEnd.
+		 */
+		if ((p = header_bytes(a, 1)) == NULL ||*p != kEnd) {
+			archive_set_error(&a->archive, -1,
+			    "Malformed 7-Zip archive");
+			return (ARCHIVE_FATAL);
+		}
+
+		/* Check the Header CRC.*/
+		if (check_header_crc && zip->header_crc32 != next_header_crc) {
+			archive_set_error(&a->archive, -1,
+			    "Malformed 7-Zip archive");
+			return (ARCHIVE_FATAL);
+		}
+		break;
+	default:
+		archive_set_error(&a->archive, -1,
+		    "Unexpected Property ID = %X", p[0]);
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Clean up variables be used for decoding the archive header */
+	zip->pack_stream_remaining = 0;
+	zip->pack_stream_index = 0;
+	zip->folder_outbytes_remaining = 0;
+	zip->uncompressed_buffer_bytes_remaining = 0;
+	zip->pack_stream_bytes_unconsumed = 0;
+	zip->header_is_being_read = 0;
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+get_uncompressed_data(struct archive_read *a, const void **buff, size_t size,
+    size_t minimum)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	ssize_t bytes_avail;
+
+	if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
+		/* Copy mode. */
+
+		*buff = __archive_read_ahead(a, minimum, &bytes_avail);
+		if (bytes_avail <= 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated 7-Zip file data");
+			return (ARCHIVE_FATAL);
+		}
+		if ((size_t)bytes_avail >
+		    zip->uncompressed_buffer_bytes_remaining)
+			bytes_avail = (ssize_t)
+			    zip->uncompressed_buffer_bytes_remaining;
+		if ((size_t)bytes_avail > size)
+			bytes_avail = (ssize_t)size;
+
+		zip->pack_stream_bytes_unconsumed = bytes_avail;
+	} else if (zip->uncompressed_buffer_pointer == NULL) {
+		/* Decompression has failed. */
+		archive_set_error(&(a->archive),
+		    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+		return (ARCHIVE_FATAL);
+	} else {
+		/* Packed mode. */
+		if (minimum > zip->uncompressed_buffer_bytes_remaining) {
+			/*
+			 * If remaining uncompressed data size is less than
+			 * the minimum size, fill the buffer up to the
+			 * minimum size.
+			 */
+			if (extract_pack_stream(a, minimum) < 0)
+				return (ARCHIVE_FATAL);
+		}
+		if (size > zip->uncompressed_buffer_bytes_remaining)
+			bytes_avail = (ssize_t)
+			    zip->uncompressed_buffer_bytes_remaining;
+		else
+			bytes_avail = (ssize_t)size;
+		*buff = zip->uncompressed_buffer_pointer;
+		zip->uncompressed_buffer_pointer += bytes_avail;
+	}
+	zip->uncompressed_buffer_bytes_remaining -= bytes_avail;
+	return (bytes_avail);
+}
+
+static ssize_t
+extract_pack_stream(struct archive_read *a, size_t minimum)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	ssize_t bytes_avail;
+	int r;
+
+	if (zip->codec == _7Z_COPY && zip->codec2 == (unsigned long)-1) {
+		if (minimum == 0)
+			minimum = 1;
+		if (__archive_read_ahead(a, minimum, &bytes_avail) == NULL
+		    || bytes_avail <= 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated 7-Zip file body");
+			return (ARCHIVE_FATAL);
+		}
+		if (bytes_avail > (ssize_t)zip->pack_stream_inbytes_remaining)
+			bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining;
+		zip->pack_stream_inbytes_remaining -= bytes_avail;
+		if (bytes_avail > (ssize_t)zip->folder_outbytes_remaining)
+			bytes_avail = (ssize_t)zip->folder_outbytes_remaining;
+		zip->folder_outbytes_remaining -= bytes_avail;
+		zip->uncompressed_buffer_bytes_remaining = bytes_avail;
+		return (ARCHIVE_OK);
+	}
+
+	/* If the buffer hasn't been allocated, allocate it now. */
+	if (zip->uncompressed_buffer == NULL) {
+		zip->uncompressed_buffer_size = UBUFF_SIZE;
+		if (zip->uncompressed_buffer_size < minimum) {
+			zip->uncompressed_buffer_size = minimum + 1023;
+			zip->uncompressed_buffer_size &= ~0x3ff;
+		}
+		zip->uncompressed_buffer =
+		    malloc(zip->uncompressed_buffer_size);
+		if (zip->uncompressed_buffer == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "No memory for 7-Zip decompression");
+			return (ARCHIVE_FATAL);
+		}
+		zip->uncompressed_buffer_bytes_remaining = 0;
+	} else if (zip->uncompressed_buffer_size < minimum ||
+	    zip->uncompressed_buffer_bytes_remaining < minimum) {
+		/*
+		 * Make sure the uncompressed buffer can have bytes
+		 * at least `minimum' bytes.
+		 * NOTE: This case happen when reading the header.
+		 */
+		size_t used;
+		if (zip->uncompressed_buffer_pointer != 0)
+			used = zip->uncompressed_buffer_pointer -
+				zip->uncompressed_buffer;
+		else
+			used = 0;
+		if (zip->uncompressed_buffer_size < minimum) {
+			/*
+			 * Expand the uncompressed buffer up to
+			 * the minimum size.
+			 */
+			void *p;
+			size_t new_size;
+
+			new_size = minimum + 1023;
+			new_size &= ~0x3ff;
+			p = realloc(zip->uncompressed_buffer, new_size);
+			if (p == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory for 7-Zip decompression");
+				return (ARCHIVE_FATAL);
+			}
+			zip->uncompressed_buffer = (unsigned char *)p;
+			zip->uncompressed_buffer_size = new_size;
+		}
+		/*
+		 * Move unconsumed bytes to the head.
+		 */
+		if (used) {
+			memmove(zip->uncompressed_buffer,
+				zip->uncompressed_buffer + used,
+				zip->uncompressed_buffer_bytes_remaining);
+		}
+	} else
+		zip->uncompressed_buffer_bytes_remaining = 0;
+	zip->uncompressed_buffer_pointer = NULL;
+	for (;;) {
+		size_t bytes_in, bytes_out;
+		const void *buff_in;
+		unsigned char *buff_out;
+		int end_of_data;
+
+		/*
+		 * Note: '1' here is a performance optimization.
+		 * Recall that the decompression layer returns a count of
+		 * available bytes; asking for more than that forces the
+		 * decompressor to combine reads by copying data.
+		 */
+		buff_in = __archive_read_ahead(a, 1, &bytes_avail);
+		if (bytes_avail <= 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated 7-Zip file body");
+			return (ARCHIVE_FATAL);
+		}
+
+		buff_out = zip->uncompressed_buffer
+			+ zip->uncompressed_buffer_bytes_remaining;
+		bytes_out = zip->uncompressed_buffer_size
+			- zip->uncompressed_buffer_bytes_remaining;
+		bytes_in = bytes_avail;
+		if (bytes_in > zip->pack_stream_inbytes_remaining)
+			bytes_in = (size_t)zip->pack_stream_inbytes_remaining;
+		/* Drive decompression. */
+		r = decompress(a, zip, buff_out, &bytes_out,
+			buff_in, &bytes_in);
+		switch (r) {
+		case ARCHIVE_OK:
+			end_of_data = 0;
+			break;
+		case ARCHIVE_EOF:
+			end_of_data = 1;
+			break;
+		default:
+			return (ARCHIVE_FATAL);
+		}
+		zip->pack_stream_inbytes_remaining -= bytes_in;
+		if (bytes_out > zip->folder_outbytes_remaining)
+			bytes_out = (size_t)zip->folder_outbytes_remaining;
+		zip->folder_outbytes_remaining -= bytes_out;
+		zip->uncompressed_buffer_bytes_remaining += bytes_out;
+		zip->pack_stream_bytes_unconsumed = bytes_in;
+
+		/*
+		 * Continue decompression until uncompressed_buffer is full.
+		 */
+		if (zip->uncompressed_buffer_bytes_remaining ==
+		    zip->uncompressed_buffer_size)
+			break;
+		if (zip->codec2 == _7Z_X86 && zip->odd_bcj_size &&
+		    zip->uncompressed_buffer_bytes_remaining + 5 >
+		    zip->uncompressed_buffer_size)
+			break;
+		if (zip->pack_stream_inbytes_remaining == 0 &&
+		    zip->folder_outbytes_remaining == 0)
+			break;
+		if (end_of_data || (bytes_in == 0 && bytes_out == 0)) {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+			return (ARCHIVE_FATAL);
+		}
+		read_consume(a);
+	}
+	if (zip->uncompressed_buffer_bytes_remaining < minimum) {
+		archive_set_error(&(a->archive),
+		    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+		return (ARCHIVE_FATAL);
+	}
+	zip->uncompressed_buffer_pointer = zip->uncompressed_buffer;
+	return (ARCHIVE_OK);
+}
+
+static int
+seek_pack(struct archive_read *a)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	int64_t pack_offset;
+
+	if (zip->pack_stream_remaining <= 0) {
+		archive_set_error(&(a->archive),
+		    ARCHIVE_ERRNO_MISC, "Damaged 7-Zip archive");
+		return (ARCHIVE_FATAL);
+	}
+	zip->pack_stream_inbytes_remaining =
+	    zip->si.pi.sizes[zip->pack_stream_index];
+	pack_offset = zip->si.pi.positions[zip->pack_stream_index];
+	if (zip->stream_offset != pack_offset) {
+		if (0 > __archive_read_seek(a, pack_offset + zip->seek_base,
+		    SEEK_SET))
+			return (ARCHIVE_FATAL);
+		zip->stream_offset = pack_offset;
+	}
+	zip->pack_stream_index++;
+	zip->pack_stream_remaining--;
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+read_stream(struct archive_read *a, const void **buff, size_t size,
+    size_t minimum)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	uint64_t skip_bytes = 0;
+	ssize_t r;
+
+	if (zip->uncompressed_buffer_bytes_remaining == 0) {
+		if (zip->pack_stream_inbytes_remaining > 0) {
+			r = extract_pack_stream(a, 0);
+			if (r < 0)
+				return (r);
+			return (get_uncompressed_data(a, buff, size, minimum));
+		} else if (zip->folder_outbytes_remaining > 0) {
+			/* Extract a remaining pack stream. */
+			r = extract_pack_stream(a, 0);
+			if (r < 0)
+				return (r);
+			return (get_uncompressed_data(a, buff, size, minimum));
+		}
+	} else
+		return (get_uncompressed_data(a, buff, size, minimum));
+
+	/*
+	 * Current pack stream has been consumed.
+	 */
+	if (zip->pack_stream_remaining == 0) {
+		if (zip->header_is_being_read) {
+			/* Invalid sequence. This might happen when
+			 * reading a malformed archive. */
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC, "Malformed 7-Zip archive");
+			return (ARCHIVE_FATAL);
+		}
+
+		/*
+		 * All current folder's pack streams have been
+		 * consumed. Switch to next folder.
+		 */
+		if (zip->folder_index == 0 &&
+		    (zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
+		     || zip->folder_index != zip->entry->folderIndex)) {
+			zip->folder_index = zip->entry->folderIndex;
+			skip_bytes =
+			    zip->si.ci.folders[zip->folder_index].skipped_bytes;
+		}
+
+		if (zip->folder_index >= zip->si.ci.numFolders) {
+			/*
+			 * We have consumed all folders and its pack streams.
+			 */
+			*buff = NULL;
+			return (0);
+		}
+		r = setup_decode_folder(a,
+			&(zip->si.ci.folders[zip->folder_index]), 0);
+		if (r != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+
+		zip->folder_index++;
+	}
+
+	/*
+	 * Switch to next pack stream.
+	 */
+	r = seek_pack(a);
+	if (r < 0)
+		return (r);
+
+	/* Extract a new pack stream. */
+	r = extract_pack_stream(a, 0);
+	if (r < 0)
+		return (r);
+
+	/*
+	 * Skip the bytes we already has skipped in skip_stream().
+	 */
+	while (skip_bytes) {
+		ssize_t skipped;
+
+		if (zip->uncompressed_buffer_bytes_remaining == 0) {
+			if (zip->pack_stream_inbytes_remaining > 0) {
+				r = extract_pack_stream(a, 0);
+				if (r < 0)
+					return (r);
+			} else if (zip->folder_outbytes_remaining > 0) {
+				/* Extract a remaining pack stream. */
+				r = extract_pack_stream(a, 0);
+				if (r < 0)
+					return (r);
+			} else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Truncated 7-Zip file body");
+				return (ARCHIVE_FATAL);
+			}
+		}
+		skipped = get_uncompressed_data(
+			a, buff, (size_t)skip_bytes, 0);
+		if (skipped < 0)
+			return (skipped);
+		skip_bytes -= skipped;
+		if (zip->pack_stream_bytes_unconsumed)
+			read_consume(a);
+	}
+
+	return (get_uncompressed_data(a, buff, size, minimum));
+}
+
+static int
+setup_decode_folder(struct archive_read *a, struct _7z_folder *folder,
+    int header)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	const struct _7z_coder *coder1, *coder2;
+	const char *cname = (header)?"archive header":"file content";
+	unsigned i;
+	int r, found_bcj2 = 0;
+
+	/*
+	 * Release the memory which the previous folder used for BCJ2.
+	 */
+	for (i = 0; i < 3; i++) {
+		free(zip->sub_stream_buff[i]);
+		zip->sub_stream_buff[i] = NULL;
+	}
+
+	/*
+	 * Initialize a stream reader.
+	 */
+	zip->pack_stream_remaining = (unsigned)folder->numPackedStreams;
+	zip->pack_stream_index = (unsigned)folder->packIndex;
+	zip->folder_outbytes_remaining = folder_uncompressed_size(folder);
+	zip->uncompressed_buffer_bytes_remaining = 0;
+
+	/*
+	 * Check coder types.
+	 */
+	for (i = 0; i < folder->numCoders; i++) {
+		switch(folder->coders[i].codec) {
+			case _7Z_CRYPTO_MAIN_ZIP:
+			case _7Z_CRYPTO_RAR_29:
+			case _7Z_CRYPTO_AES_256_SHA_256: {
+				/* For entry that is associated with this folder, mark
+				   it as encrypted (data+metadata). */
+				zip->has_encrypted_entries = 1;
+				if (a->entry) {
+					archive_entry_set_is_data_encrypted(a->entry, 1);
+					archive_entry_set_is_metadata_encrypted(a->entry, 1);
+				}
+				archive_set_error(&(a->archive),
+					ARCHIVE_ERRNO_MISC,
+					"The %s is encrypted, "
+					"but currently not supported", cname);
+				return (ARCHIVE_FATAL);
+			}
+			case _7Z_X86_BCJ2: {
+				found_bcj2++;
+				break;
+			}
+		}
+	}
+	/* Now that we've checked for encryption, if there were still no
+	 * encrypted entries found we can say for sure that there are none.
+	 */
+	if (zip->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+		zip->has_encrypted_entries = 0;
+	}
+
+	if ((folder->numCoders > 2 && !found_bcj2) || found_bcj2 > 1) {
+		archive_set_error(&(a->archive),
+		    ARCHIVE_ERRNO_MISC,
+		    "The %s is encoded with many filters, "
+		    "but currently not supported", cname);
+		return (ARCHIVE_FATAL);
+	}
+	coder1 = &(folder->coders[0]);
+	if (folder->numCoders == 2)
+		coder2 = &(folder->coders[1]);
+	else
+		coder2 = NULL;
+
+	if (found_bcj2) {
+		/*
+		 * Preparation to decode BCJ2.
+		 * Decoding BCJ2 requires four sources. Those are at least,
+		 * as far as I know, two types of the storage form.
+		 */
+		const struct _7z_coder *fc = folder->coders;
+		static const struct _7z_coder coder_copy = {0, 1, 1, 0, NULL};
+		const struct _7z_coder *scoder[3] =
+			{&coder_copy, &coder_copy, &coder_copy};
+		const void *buff;
+		ssize_t bytes;
+		unsigned char *b[3] = {NULL, NULL, NULL};
+		uint64_t sunpack[3] ={-1, -1, -1};
+		size_t s[3] = {0, 0, 0};
+		int idx[3] = {0, 1, 2};
+
+		if (folder->numCoders == 4 && fc[3].codec == _7Z_X86_BCJ2 &&
+		    folder->numInStreams == 7 && folder->numOutStreams == 4 &&
+		    zip->pack_stream_remaining == 4) {
+			/* Source type 1 made by 7zr or 7z with -m options. */
+			if (folder->bindPairs[0].inIndex == 5) {
+				/* The form made by 7zr */
+				idx[0] = 1; idx[1] = 2; idx[2] = 0;
+				scoder[1] = &(fc[1]);
+				scoder[2] = &(fc[0]);
+				sunpack[1] = folder->unPackSize[1];
+				sunpack[2] = folder->unPackSize[0];
+				coder1 = &(fc[2]);
+			} else {
+				/*
+				 * NOTE: Some patterns do not work.
+				 * work:
+				 *  7z a -m0=BCJ2 -m1=COPY -m2=COPY
+				 *       -m3=(any)
+				 *  7z a -m0=BCJ2 -m1=COPY -m2=(any)
+				 *       -m3=COPY
+				 *  7z a -m0=BCJ2 -m1=(any) -m2=COPY
+				 *       -m3=COPY
+				 * not work:
+				 *  other patterns.
+				 *
+				 * We have to handle this like `pipe' or
+				 * our libarchive7s filter frame work,
+				 * decoding the BCJ2 main stream sequentially,
+				 * m3 -> m2 -> m1 -> BCJ2.
+				 *
+				 */
+				if (fc[0].codec == _7Z_COPY &&
+				    fc[1].codec == _7Z_COPY)
+					coder1 = &(folder->coders[2]);
+				else if (fc[0].codec == _7Z_COPY &&
+				    fc[2].codec == _7Z_COPY)
+					coder1 = &(folder->coders[1]);
+				else if (fc[1].codec == _7Z_COPY &&
+				    fc[2].codec == _7Z_COPY)
+					coder1 = &(folder->coders[0]);
+				else {
+					archive_set_error(&(a->archive),
+					    ARCHIVE_ERRNO_MISC,
+					    "Unsupported form of "
+					    "BCJ2 streams");
+					return (ARCHIVE_FATAL);
+				}
+			}
+			coder2 = &(fc[3]);
+			zip->main_stream_bytes_remaining =
+				(size_t)folder->unPackSize[2];
+		} else if (coder2 != NULL && coder2->codec == _7Z_X86_BCJ2 &&
+		    zip->pack_stream_remaining == 4 &&
+		    folder->numInStreams == 5 && folder->numOutStreams == 2) {
+			/* Source type 0 made by 7z */
+			zip->main_stream_bytes_remaining =
+				(size_t)folder->unPackSize[0];
+		} else {
+			/* We got an unexpected form. */
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "Unsupported form of BCJ2 streams");
+			return (ARCHIVE_FATAL);
+		}
+
+		/* Skip the main stream at this time. */
+		if ((r = seek_pack(a)) < 0)
+			return (r);
+		zip->pack_stream_bytes_unconsumed =
+		    (size_t)zip->pack_stream_inbytes_remaining;
+		read_consume(a);
+
+		/* Read following three sub streams. */
+		for (i = 0; i < 3; i++) {
+			const struct _7z_coder *coder = scoder[i];
+
+			if ((r = seek_pack(a)) < 0) {
+				free(b[0]); free(b[1]); free(b[2]);
+				return (r);
+			}
+
+			if (sunpack[i] == (uint64_t)-1)
+				zip->folder_outbytes_remaining =
+				    zip->pack_stream_inbytes_remaining;
+			else
+				zip->folder_outbytes_remaining = sunpack[i];
+
+			r = init_decompression(a, zip, coder, NULL);
+			if (r != ARCHIVE_OK) {
+				free(b[0]); free(b[1]); free(b[2]);
+				return (ARCHIVE_FATAL);
+			}
+
+			/* Allocate memory for the decoded data of a sub
+			 * stream. */
+			b[i] = malloc((size_t)zip->folder_outbytes_remaining);
+			if (b[i] == NULL) {
+				free(b[0]); free(b[1]); free(b[2]);
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory for 7-Zip decompression");
+				return (ARCHIVE_FATAL);
+			}
+
+			/* Extract a sub stream. */
+			while (zip->pack_stream_inbytes_remaining > 0) {
+				r = (int)extract_pack_stream(a, 0);
+				if (r < 0) {
+					free(b[0]); free(b[1]); free(b[2]);
+					return (r);
+				}
+				bytes = get_uncompressed_data(a, &buff,
+				    zip->uncompressed_buffer_bytes_remaining,
+				    0);
+				if (bytes < 0) {
+					free(b[0]); free(b[1]); free(b[2]);
+					return ((int)bytes);
+				}
+				memcpy(b[i]+s[i], buff, bytes);
+				s[i] += bytes;
+				if (zip->pack_stream_bytes_unconsumed)
+					read_consume(a);
+			}
+		}
+
+		/* Set the sub streams to the right place. */
+		for (i = 0; i < 3; i++) {
+			zip->sub_stream_buff[i] = b[idx[i]];
+			zip->sub_stream_size[i] = s[idx[i]];
+			zip->sub_stream_bytes_remaining[i] = s[idx[i]];
+		}
+
+		/* Allocate memory used for decoded main stream bytes. */
+		if (zip->tmp_stream_buff == NULL) {
+			zip->tmp_stream_buff_size = 32 * 1024;
+			zip->tmp_stream_buff =
+			    malloc(zip->tmp_stream_buff_size);
+			if (zip->tmp_stream_buff == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory for 7-Zip decompression");
+				return (ARCHIVE_FATAL);
+			}
+		}
+		zip->tmp_stream_bytes_avail = 0;
+		zip->tmp_stream_bytes_remaining = 0;
+		zip->odd_bcj_size = 0;
+		zip->bcj2_outPos = 0;
+
+		/*
+		 * Reset a stream reader in order to read the main stream
+		 * of BCJ2.
+		 */
+		zip->pack_stream_remaining = 1;
+		zip->pack_stream_index = (unsigned)folder->packIndex;
+		zip->folder_outbytes_remaining =
+		    folder_uncompressed_size(folder);
+		zip->uncompressed_buffer_bytes_remaining = 0;
+	}
+
+	/*
+	 * Initialize the decompressor for the new folder's pack streams.
+	 */
+	r = init_decompression(a, zip, coder1, coder2);
+	if (r != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	return (ARCHIVE_OK);
+}
+
+static int64_t
+skip_stream(struct archive_read *a, size_t skip_bytes)
+{
+	struct _7zip *zip = (struct _7zip *)a->format->data;
+	const void *p;
+	int64_t skipped_bytes;
+	size_t bytes = skip_bytes;
+
+	if (zip->folder_index == 0) {
+		/*
+		 * Optimization for a list mode.
+		 * Avoid unnecessary decoding operations.
+		 */
+		zip->si.ci.folders[zip->entry->folderIndex].skipped_bytes
+		    += skip_bytes;
+		return (skip_bytes);
+	}
+
+	while (bytes) {
+		skipped_bytes = read_stream(a, &p, bytes, 0);
+		if (skipped_bytes < 0)
+			return (skipped_bytes);
+		if (skipped_bytes == 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated 7-Zip file body");
+			return (ARCHIVE_FATAL);
+		}
+		bytes -= (size_t)skipped_bytes;
+		if (zip->pack_stream_bytes_unconsumed)
+			read_consume(a);
+	}
+	return (skip_bytes);
+}
+
+/*
+ * Brought from LZMA SDK.
+ *
+ * Bra86.c -- Converter for x86 code (BCJ)
+ * 2008-10-04 : Igor Pavlov : Public domain
+ *
+ */
+
+#define Test86MSByte(b) ((b) == 0 || (b) == 0xFF)
+
+static void
+x86_Init(struct _7zip *zip)
+{
+	zip->bcj_state = 0;
+	zip->bcj_prevPosT = (size_t)0 - 1;
+	zip->bcj_prevMask = 0;
+	zip->bcj_ip = 5;
+}
+
+static size_t
+x86_Convert(struct _7zip *zip, uint8_t *data, size_t size)
+{
+	static const uint8_t kMaskToAllowedStatus[8] = {1, 1, 1, 0, 1, 0, 0, 0};
+	static const uint8_t kMaskToBitNumber[8] = {0, 1, 2, 2, 3, 3, 3, 3};
+	size_t bufferPos, prevPosT;
+	uint32_t ip, prevMask;
+
+	if (size < 5)
+		return 0;
+
+	bufferPos = 0;
+	prevPosT = zip->bcj_prevPosT;
+	prevMask = zip->bcj_prevMask;
+	ip = zip->bcj_ip;
+
+	for (;;) {
+		uint8_t *p = data + bufferPos;
+		uint8_t *limit = data + size - 4;
+
+		for (; p < limit; p++)
+			if ((*p & 0xFE) == 0xE8)
+				break;
+		bufferPos = (size_t)(p - data);
+		if (p >= limit)
+			break;
+		prevPosT = bufferPos - prevPosT;
+		if (prevPosT > 3)
+			prevMask = 0;
+		else {
+			prevMask = (prevMask << ((int)prevPosT - 1)) & 0x7;
+			if (prevMask != 0) {
+				unsigned char b =
+					p[4 - kMaskToBitNumber[prevMask]];
+				if (!kMaskToAllowedStatus[prevMask] ||
+				    Test86MSByte(b)) {
+					prevPosT = bufferPos;
+					prevMask = ((prevMask << 1) & 0x7) | 1;
+					bufferPos++;
+					continue;
+				}
+			}
+		}
+		prevPosT = bufferPos;
+
+		if (Test86MSByte(p[4])) {
+			uint32_t src = ((uint32_t)p[4] << 24) |
+				((uint32_t)p[3] << 16) | ((uint32_t)p[2] << 8) |
+				((uint32_t)p[1]);
+			uint32_t dest;
+			for (;;) {
+				uint8_t b;
+				int b_index;
+
+				dest = src - (ip + (uint32_t)bufferPos);
+				if (prevMask == 0)
+					break;
+				b_index = kMaskToBitNumber[prevMask] * 8;
+				b = (uint8_t)(dest >> (24 - b_index));
+				if (!Test86MSByte(b))
+					break;
+				src = dest ^ ((1 << (32 - b_index)) - 1);
+			}
+			p[4] = (uint8_t)(~(((dest >> 24) & 1) - 1));
+			p[3] = (uint8_t)(dest >> 16);
+			p[2] = (uint8_t)(dest >> 8);
+			p[1] = (uint8_t)dest;
+			bufferPos += 5;
+		} else {
+			prevMask = ((prevMask << 1) & 0x7) | 1;
+			bufferPos++;
+		}
+	}
+	zip->bcj_prevPosT = prevPosT;
+	zip->bcj_prevMask = prevMask;
+	zip->bcj_ip += (uint32_t)bufferPos;
+	return (bufferPos);
+}
+
+/*
+ * Brought from LZMA SDK.
+ *
+ * Bcj2.c -- Converter for x86 code (BCJ2)
+ * 2008-10-04 : Igor Pavlov : Public domain
+ *
+ */
+
+#define SZ_ERROR_DATA	 ARCHIVE_FAILED
+
+#define IsJcc(b0, b1) ((b0) == 0x0F && ((b1) & 0xF0) == 0x80)
+#define IsJ(b0, b1) ((b1 & 0xFE) == 0xE8 || IsJcc(b0, b1))
+
+#define kNumTopBits 24
+#define kTopValue ((uint32_t)1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+
+#define RC_READ_BYTE (*buffer++)
+#define RC_TEST { if (buffer == bufferLim) return SZ_ERROR_DATA; }
+#define RC_INIT2 zip->bcj2_code = 0; zip->bcj2_range = 0xFFFFFFFF; \
+  { int ii; for (ii = 0; ii < 5; ii++) { RC_TEST; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }}
+
+#define NORMALIZE if (zip->bcj2_range < kTopValue) { RC_TEST; zip->bcj2_range <<= 8; zip->bcj2_code = (zip->bcj2_code << 8) | RC_READ_BYTE; }
+
+#define IF_BIT_0(p) ttt = *(p); bound = (zip->bcj2_range >> kNumBitModelTotalBits) * ttt; if (zip->bcj2_code < bound)
+#define UPDATE_0(p) zip->bcj2_range = bound; *(p) = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); NORMALIZE;
+#define UPDATE_1(p) zip->bcj2_range -= bound; zip->bcj2_code -= bound; *(p) = (CProb)(ttt - (ttt >> kNumMoveBits)); NORMALIZE;
+
+static ssize_t
+Bcj2_Decode(struct _7zip *zip, uint8_t *outBuf, size_t outSize)
+{
+	size_t inPos = 0, outPos = 0;
+	const uint8_t *buf0, *buf1, *buf2, *buf3;
+	size_t size0, size1, size2, size3;
+	const uint8_t *buffer, *bufferLim;
+	unsigned int i, j;
+
+	size0 = zip->tmp_stream_bytes_remaining;
+	buf0 = zip->tmp_stream_buff + zip->tmp_stream_bytes_avail - size0;
+	size1 = zip->sub_stream_bytes_remaining[0];
+	buf1 = zip->sub_stream_buff[0] + zip->sub_stream_size[0] - size1;
+	size2 = zip->sub_stream_bytes_remaining[1];
+	buf2 = zip->sub_stream_buff[1] + zip->sub_stream_size[1] - size2;
+	size3 = zip->sub_stream_bytes_remaining[2];
+	buf3 = zip->sub_stream_buff[2] + zip->sub_stream_size[2] - size3;
+
+	buffer = buf3;
+	bufferLim = buffer + size3;
+
+	if (zip->bcj_state == 0) {
+		/*
+		 * Initialize.
+		 */
+		zip->bcj2_prevByte = 0;
+		for (i = 0;
+		    i < sizeof(zip->bcj2_p) / sizeof(zip->bcj2_p[0]); i++)
+			zip->bcj2_p[i] = kBitModelTotal >> 1;
+		RC_INIT2;
+		zip->bcj_state = 1;
+	}
+
+	/*
+	 * Gather the odd bytes of a previous call.
+	 */
+	for (i = 0; zip->odd_bcj_size > 0 && outPos < outSize; i++) {
+		outBuf[outPos++] = zip->odd_bcj[i];
+		zip->odd_bcj_size--;
+	}
+
+	if (outSize == 0) {
+		zip->bcj2_outPos += outPos;
+		return (outPos);
+	}
+
+	for (;;) {
+		uint8_t b;
+		CProb *prob;
+		uint32_t bound;
+		uint32_t ttt;
+
+		size_t limit = size0 - inPos;
+		if (outSize - outPos < limit)
+			limit = outSize - outPos;
+
+		if (zip->bcj_state == 1) {
+			while (limit != 0) {
+				uint8_t bb = buf0[inPos];
+				outBuf[outPos++] = bb;
+				if (IsJ(zip->bcj2_prevByte, bb)) {
+					zip->bcj_state = 2;
+					break;
+				}
+				inPos++;
+				zip->bcj2_prevByte = bb;
+				limit--;
+			}
+		}
+
+		if (limit == 0 || outPos == outSize)
+			break;
+		zip->bcj_state = 1;
+
+		b = buf0[inPos++];
+
+		if (b == 0xE8)
+			prob = zip->bcj2_p + zip->bcj2_prevByte;
+		else if (b == 0xE9)
+			prob = zip->bcj2_p + 256;
+		else
+			prob = zip->bcj2_p + 257;
+
+		IF_BIT_0(prob) {
+			UPDATE_0(prob)
+			zip->bcj2_prevByte = b;
+		} else {
+			uint32_t dest;
+			const uint8_t *v;
+			uint8_t out[4];
+
+			UPDATE_1(prob)
+			if (b == 0xE8) {
+				v = buf1;
+				if (size1 < 4)
+					return SZ_ERROR_DATA;
+				buf1 += 4;
+				size1 -= 4;
+			} else {
+				v = buf2;
+				if (size2 < 4)
+					return SZ_ERROR_DATA;
+				buf2 += 4;
+				size2 -= 4;
+			}
+			dest = (((uint32_t)v[0] << 24) |
+			    ((uint32_t)v[1] << 16) |
+			    ((uint32_t)v[2] << 8) |
+			    ((uint32_t)v[3])) -
+			    ((uint32_t)zip->bcj2_outPos + (uint32_t)outPos + 4);
+			out[0] = (uint8_t)dest;
+			out[1] = (uint8_t)(dest >> 8);
+			out[2] = (uint8_t)(dest >> 16);
+			out[3] = zip->bcj2_prevByte = (uint8_t)(dest >> 24);
+
+			for (i = 0; i < 4 && outPos < outSize; i++)
+				outBuf[outPos++] = out[i];
+			if (i < 4) {
+				/*
+				 * Save odd bytes which we could not add into
+				 * the output buffer because of out of space.
+				 */
+				zip->odd_bcj_size = 4 -i;
+				for (; i < 4; i++) {
+					j = i - 4 + (unsigned)zip->odd_bcj_size;
+					zip->odd_bcj[j] = out[i];
+				}
+				break;
+			}
+		}
+	}
+	zip->tmp_stream_bytes_remaining -= inPos;
+	zip->sub_stream_bytes_remaining[0] = size1;
+	zip->sub_stream_bytes_remaining[1] = size2;
+	zip->sub_stream_bytes_remaining[2] = bufferLim - buffer;
+	zip->bcj2_outPos += outPos;
+
+	return ((ssize_t)outPos);
+}
+
diff --git a/c/archive_read_support_format_all.c b/c/archive_read_support_format_all.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_all.c
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 2003-2011 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: head/lib/libarchive/archive_read_support_format_all.c 174991 2007-12-30 04:58:22Z kientzle $");
+
+#include "archive.h"
+#include "archive_private.h"
+
+int
+archive_read_support_format_all(struct archive *a)
+{
+	archive_check_magic(a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_all");
+
+	/* TODO: It would be nice to compute the ordering
+	 * here automatically so that people who enable just
+	 * a few formats can still get the benefits.  That
+	 * may just require the format registration to include
+	 * a "maximum read-ahead" value (anything that uses seek
+	 * would be essentially infinite read-ahead).  The core
+	 * bid management can then sort the bidders before calling
+	 * them.
+	 *
+	 * If you implement the above, please return the list below
+	 * to alphabetic order.
+	 */
+
+	/*
+	 * These bidders are all pretty cheap; they just examine a
+	 * small initial part of the archive.  If one of these bids
+	 * high, we can maybe avoid running any of the more expensive
+	 * bidders below.
+	 */
+	archive_read_support_format_ar(a);
+	archive_read_support_format_cpio(a);
+	archive_read_support_format_empty(a);
+	archive_read_support_format_lha(a);
+	archive_read_support_format_mtree(a);
+	archive_read_support_format_tar(a);
+	archive_read_support_format_xar(a);
+	archive_read_support_format_warc(a);
+
+	/*
+	 * Install expensive bidders last.  By doing them last, we
+	 * increase the chance that a high bid from someone else will
+	 * make it unnecessary for these to do anything at all.
+	 */
+	/* These three have potentially large look-ahead. */
+	archive_read_support_format_7zip(a);
+	archive_read_support_format_cab(a);
+	archive_read_support_format_rar(a);
+	archive_read_support_format_rar5(a);
+	archive_read_support_format_iso9660(a);
+	/* Seek is really bad, since it forces the read-ahead
+	 * logic to discard buffered data. */
+	archive_read_support_format_zip(a);
+
+	/* Note: We always return ARCHIVE_OK here, even if some of the
+	 * above return ARCHIVE_WARN.  The intent here is to enable
+	 * "as much as possible."  Clients who need specific
+	 * compression should enable those individually so they can
+	 * verify the level of support. */
+	/* Clear any warning messages set by the above functions. */
+	archive_clear_error(a);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_read_support_format_ar.c b/c/archive_read_support_format_ar.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_ar.c
@@ -0,0 +1,638 @@
+/*-
+ * Copyright (c) 2007 Kai Wang
+ * Copyright (c) 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
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_ar.c 201101 2009-12-28 03:06:27Z kientzle $");
+
+#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_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct ar {
+	int64_t	 entry_bytes_remaining;
+	/* unconsumed is purely to track data we've gotten from readahead,
+	 * but haven't yet marked as consumed.  Must be paired with
+	 * entry_bytes_remaining usage/modification.
+	 */
+	size_t   entry_bytes_unconsumed;
+	int64_t	 entry_offset;
+	int64_t	 entry_padding;
+	char	*strtab;
+	size_t	 strtab_size;
+	char	 read_global_header;
+};
+
+/*
+ * Define structure of the "ar" header.
+ */
+#define AR_name_offset 0
+#define AR_name_size 16
+#define AR_date_offset 16
+#define AR_date_size 12
+#define AR_uid_offset 28
+#define AR_uid_size 6
+#define AR_gid_offset 34
+#define AR_gid_size 6
+#define AR_mode_offset 40
+#define AR_mode_size 8
+#define AR_size_offset 48
+#define AR_size_size 10
+#define AR_fmag_offset 58
+#define AR_fmag_size 2
+
+static int	archive_read_format_ar_bid(struct archive_read *a, int);
+static int	archive_read_format_ar_cleanup(struct archive_read *a);
+static int	archive_read_format_ar_read_data(struct archive_read *a,
+		    const void **buff, size_t *size, int64_t *offset);
+static int	archive_read_format_ar_skip(struct archive_read *a);
+static int	archive_read_format_ar_read_header(struct archive_read *a,
+		    struct archive_entry *e);
+static uint64_t	ar_atol8(const char *p, unsigned char_cnt);
+static uint64_t	ar_atol10(const char *p, unsigned char_cnt);
+static int	ar_parse_gnu_filename_table(struct archive_read *a);
+static int	ar_parse_common_header(struct ar *ar, struct archive_entry *,
+		    const char *h);
+
+int
+archive_read_support_format_ar(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct ar *ar;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_ar");
+
+	ar = (struct ar *)calloc(1, sizeof(*ar));
+	if (ar == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate ar data");
+		return (ARCHIVE_FATAL);
+	}
+	ar->strtab = NULL;
+
+	r = __archive_read_register_format(a,
+	    ar,
+	    "ar",
+	    archive_read_format_ar_bid,
+	    NULL,
+	    archive_read_format_ar_read_header,
+	    archive_read_format_ar_read_data,
+	    archive_read_format_ar_skip,
+	    NULL,
+	    archive_read_format_ar_cleanup,
+	    NULL,
+	    NULL);
+
+	if (r != ARCHIVE_OK) {
+		free(ar);
+		return (r);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_cleanup(struct archive_read *a)
+{
+	struct ar *ar;
+
+	ar = (struct ar *)(a->format->data);
+	free(ar->strtab);
+	free(ar);
+	(a->format->data) = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_bid(struct archive_read *a, int best_bid)
+{
+	const void *h;
+
+	(void)best_bid; /* UNUSED */
+
+	/*
+	 * Verify the 8-byte file signature.
+	 * TODO: Do we need to check more than this?
+	 */
+	if ((h = __archive_read_ahead(a, 8, NULL)) == NULL)
+		return (-1);
+	if (memcmp(h, "!<arch>\n", 8) == 0) {
+		return (64);
+	}
+	return (-1);
+}
+
+static int
+_ar_read_header(struct archive_read *a, struct archive_entry *entry,
+	struct ar *ar, const char *h, size_t *unconsumed)
+{
+	char filename[AR_name_size + 1];
+	uint64_t number; /* Used to hold parsed numbers before validation. */
+	size_t bsd_name_length, entry_size;
+	char *p, *st;
+	const void *b;
+	int r;
+
+	/* Verify the magic signature on the file header. */
+	if (strncmp(h + AR_fmag_offset, "`\n", 2) != 0) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Incorrect file header signature");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Copy filename into work buffer. */
+	strncpy(filename, h + AR_name_offset, AR_name_size);
+	filename[AR_name_size] = '\0';
+
+	/*
+	 * Guess the format variant based on the filename.
+	 */
+	if (a->archive.archive_format == ARCHIVE_FORMAT_AR) {
+		/* We don't already know the variant, so let's guess. */
+		/*
+		 * Biggest clue is presence of '/': GNU starts special
+		 * filenames with '/', appends '/' as terminator to
+		 * non-special names, so anything with '/' should be
+		 * GNU except for BSD long filenames.
+		 */
+		if (strncmp(filename, "#1/", 3) == 0)
+			a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+		else if (strchr(filename, '/') != NULL)
+			a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
+		else if (strncmp(filename, "__.SYMDEF", 9) == 0)
+			a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+		/*
+		 * XXX Do GNU/SVR4 'ar' programs ever omit trailing '/'
+		 * if name exactly fills 16-byte field?  If so, we
+		 * can't assume entries without '/' are BSD. XXX
+		 */
+	}
+
+	/* Update format name from the code. */
+	if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU)
+		a->archive.archive_format_name = "ar (GNU/SVR4)";
+	else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD)
+		a->archive.archive_format_name = "ar (BSD)";
+	else
+		a->archive.archive_format_name = "ar";
+
+	/*
+	 * Remove trailing spaces from the filename.  GNU and BSD
+	 * variants both pad filename area out with spaces.
+	 * This will only be wrong if GNU/SVR4 'ar' implementations
+	 * omit trailing '/' for 16-char filenames and we have
+	 * a 16-char filename that ends in ' '.
+	 */
+	p = filename + AR_name_size - 1;
+	while (p >= filename && *p == ' ') {
+		*p = '\0';
+		p--;
+	}
+
+	/*
+	 * Remove trailing slash unless first character is '/'.
+	 * (BSD entries never end in '/', so this will only trim
+	 * GNU-format entries.  GNU special entries start with '/'
+	 * and are not terminated in '/', so we don't trim anything
+	 * that starts with '/'.)
+	 */
+	if (filename[0] != '/' && p > filename && *p == '/') {
+		*p = '\0';
+	}
+
+	if (p < filename) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Found entry with empty filename");
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * '//' is the GNU filename table.
+	 * Later entries can refer to names in this table.
+	 */
+	if (strcmp(filename, "//") == 0) {
+		/* This must come before any call to _read_ahead. */
+		ar_parse_common_header(ar, entry, h);
+		archive_entry_copy_pathname(entry, filename);
+		archive_entry_set_filetype(entry, AE_IFREG);
+		/* Get the size of the filename table. */
+		number = ar_atol10(h + AR_size_offset, AR_size_size);
+		if (number > SIZE_MAX || number > 1024 * 1024 * 1024) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Filename table too large");
+			return (ARCHIVE_FATAL);
+		}
+		entry_size = (size_t)number;
+		if (entry_size == 0) {
+			archive_set_error(&a->archive, EINVAL,
+			    "Invalid string table");
+			return (ARCHIVE_FATAL);
+		}
+		if (ar->strtab != NULL) {
+			archive_set_error(&a->archive, EINVAL,
+			    "More than one string tables exist");
+			return (ARCHIVE_FATAL);
+		}
+
+		/* Read the filename table into memory. */
+		st = malloc(entry_size);
+		if (st == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate filename table buffer");
+			return (ARCHIVE_FATAL);
+		}
+		ar->strtab = st;
+		ar->strtab_size = entry_size;
+
+		if (*unconsumed) {
+			__archive_read_consume(a, *unconsumed);
+			*unconsumed = 0;
+		}
+
+		if ((b = __archive_read_ahead(a, entry_size, NULL)) == NULL)
+			return (ARCHIVE_FATAL);
+		memcpy(st, b, entry_size);
+		__archive_read_consume(a, entry_size);
+		/* All contents are consumed. */
+		ar->entry_bytes_remaining = 0;
+		archive_entry_set_size(entry, ar->entry_bytes_remaining);
+
+		/* Parse the filename table. */
+		return (ar_parse_gnu_filename_table(a));
+	}
+
+	/*
+	 * GNU variant handles long filenames by storing /<number>
+	 * to indicate a name stored in the filename table.
+	 * XXX TODO: Verify that it's all digits... Don't be fooled
+	 * by "/9xyz" XXX
+	 */
+	if (filename[0] == '/' && filename[1] >= '0' && filename[1] <= '9') {
+		number = ar_atol10(h + AR_name_offset + 1, AR_name_size - 1);
+		/*
+		 * If we can't look up the real name, warn and return
+		 * the entry with the wrong name.
+		 */
+		if (ar->strtab == NULL || number >= ar->strtab_size) {
+			archive_set_error(&a->archive, EINVAL,
+			    "Can't find long filename for GNU/SVR4 archive entry");
+			archive_entry_copy_pathname(entry, filename);
+			/* Parse the time, owner, mode, size fields. */
+			ar_parse_common_header(ar, entry, h);
+			return (ARCHIVE_FATAL);
+		}
+
+		archive_entry_copy_pathname(entry, &ar->strtab[(size_t)number]);
+		/* Parse the time, owner, mode, size fields. */
+		return (ar_parse_common_header(ar, entry, h));
+	}
+
+	/*
+	 * BSD handles long filenames by storing "#1/" followed by the
+	 * length of filename as a decimal number, then prepends the
+	 * the filename to the file contents.
+	 */
+	if (strncmp(filename, "#1/", 3) == 0) {
+		/* Parse the time, owner, mode, size fields. */
+		/* This must occur before _read_ahead is called again. */
+		ar_parse_common_header(ar, entry, h);
+
+		/* Parse the size of the name, adjust the file size. */
+		number = ar_atol10(h + AR_name_offset + 3, AR_name_size - 3);
+		/* Sanity check the filename length:
+		 *   = Must be <= SIZE_MAX - 1
+		 *   = Must be <= 1MB
+		 *   = Cannot be bigger than the entire entry
+		 */
+		if (number > SIZE_MAX - 1
+		    || number > 1024 * 1024
+		    || (int64_t)number > ar->entry_bytes_remaining) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Bad input file size");
+			return (ARCHIVE_FATAL);
+		}
+		bsd_name_length = (size_t)number;
+		ar->entry_bytes_remaining -= bsd_name_length;
+		/* Adjust file size reported to client. */
+		archive_entry_set_size(entry, ar->entry_bytes_remaining);
+
+		if (*unconsumed) {
+			__archive_read_consume(a, *unconsumed);
+			*unconsumed = 0;
+		}
+
+		/* Read the long name into memory. */
+		if ((b = __archive_read_ahead(a, bsd_name_length, NULL)) == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Truncated input file");
+			return (ARCHIVE_FATAL);
+		}
+		/* Store it in the entry. */
+		p = (char *)malloc(bsd_name_length + 1);
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate fname buffer");
+			return (ARCHIVE_FATAL);
+		}
+		strncpy(p, b, bsd_name_length);
+		p[bsd_name_length] = '\0';
+
+		__archive_read_consume(a, bsd_name_length);
+
+		archive_entry_copy_pathname(entry, p);
+		free(p);
+		return (ARCHIVE_OK);
+	}
+
+	/*
+	 * "/" is the SVR4/GNU archive symbol table.
+	 * "/SYM64/" is the SVR4/GNU 64-bit variant archive symbol table.
+	 */
+	if (strcmp(filename, "/") == 0 || strcmp(filename, "/SYM64/") == 0) {
+		archive_entry_copy_pathname(entry, filename);
+		/* Parse the time, owner, mode, size fields. */
+		r = ar_parse_common_header(ar, entry, h);
+		/* Force the file type to a regular file. */
+		archive_entry_set_filetype(entry, AE_IFREG);
+		return (r);
+	}
+
+	/*
+	 * "__.SYMDEF" is a BSD archive symbol table.
+	 */
+	if (strcmp(filename, "__.SYMDEF") == 0) {
+		archive_entry_copy_pathname(entry, filename);
+		/* Parse the time, owner, mode, size fields. */
+		return (ar_parse_common_header(ar, entry, h));
+	}
+
+	/*
+	 * Otherwise, this is a standard entry.  The filename
+	 * has already been trimmed as much as possible, based
+	 * on our current knowledge of the format.
+	 */
+	archive_entry_copy_pathname(entry, filename);
+	return (ar_parse_common_header(ar, entry, h));
+}
+
+static int
+archive_read_format_ar_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct ar *ar = (struct ar*)(a->format->data);
+	size_t unconsumed;
+	const void *header_data;
+	int ret;
+
+	if (!ar->read_global_header) {
+		/*
+		 * We are now at the beginning of the archive,
+		 * so we need first consume the ar global header.
+		 */
+		__archive_read_consume(a, 8);
+		ar->read_global_header = 1;
+		/* Set a default format code for now. */
+		a->archive.archive_format = ARCHIVE_FORMAT_AR;
+	}
+
+	/* Read the header for the next file entry. */
+	if ((header_data = __archive_read_ahead(a, 60, NULL)) == NULL)
+		/* Broken header. */
+		return (ARCHIVE_EOF);
+	
+	unconsumed = 60;
+	
+	ret = _ar_read_header(a, entry, ar, (const char *)header_data, &unconsumed);
+
+	if (unconsumed)
+		__archive_read_consume(a, unconsumed);
+
+	return ret;
+}
+
+
+static int
+ar_parse_common_header(struct ar *ar, struct archive_entry *entry,
+    const char *h)
+{
+	uint64_t n;
+
+	/* Copy remaining header */
+	archive_entry_set_filetype(entry, AE_IFREG);
+	archive_entry_set_mtime(entry,
+	    (time_t)ar_atol10(h + AR_date_offset, AR_date_size), 0L);
+	archive_entry_set_uid(entry,
+	    (uid_t)ar_atol10(h + AR_uid_offset, AR_uid_size));
+	archive_entry_set_gid(entry,
+	    (gid_t)ar_atol10(h + AR_gid_offset, AR_gid_size));
+	archive_entry_set_mode(entry,
+	    (mode_t)ar_atol8(h + AR_mode_offset, AR_mode_size));
+	n = ar_atol10(h + AR_size_offset, AR_size_size);
+
+	ar->entry_offset = 0;
+	ar->entry_padding = n % 2;
+	archive_entry_set_size(entry, n);
+	ar->entry_bytes_remaining = n;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_ar_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	ssize_t bytes_read;
+	struct ar *ar;
+
+	ar = (struct ar *)(a->format->data);
+
+	if (ar->entry_bytes_unconsumed) {
+		__archive_read_consume(a, ar->entry_bytes_unconsumed);
+		ar->entry_bytes_unconsumed = 0;
+	}
+
+	if (ar->entry_bytes_remaining > 0) {
+		*buff = __archive_read_ahead(a, 1, &bytes_read);
+		if (bytes_read == 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Truncated ar archive");
+			return (ARCHIVE_FATAL);
+		}
+		if (bytes_read < 0)
+			return (ARCHIVE_FATAL);
+		if (bytes_read > ar->entry_bytes_remaining)
+			bytes_read = (ssize_t)ar->entry_bytes_remaining;
+		*size = bytes_read;
+		ar->entry_bytes_unconsumed = bytes_read;
+		*offset = ar->entry_offset;
+		ar->entry_offset += bytes_read;
+		ar->entry_bytes_remaining -= bytes_read;
+		return (ARCHIVE_OK);
+	} else {
+		int64_t skipped = __archive_read_consume(a, ar->entry_padding);
+		if (skipped >= 0) {
+			ar->entry_padding -= skipped;
+		}
+		if (ar->entry_padding) {
+			if (skipped >= 0) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+					"Truncated ar archive- failed consuming padding");
+			}
+			return (ARCHIVE_FATAL);
+		}
+		*buff = NULL;
+		*size = 0;
+		*offset = ar->entry_offset;
+		return (ARCHIVE_EOF);
+	}
+}
+
+static int
+archive_read_format_ar_skip(struct archive_read *a)
+{
+	int64_t bytes_skipped;
+	struct ar* ar;
+
+	ar = (struct ar *)(a->format->data);
+
+	bytes_skipped = __archive_read_consume(a,
+	    ar->entry_bytes_remaining + ar->entry_padding
+	    + ar->entry_bytes_unconsumed);
+	if (bytes_skipped < 0)
+		return (ARCHIVE_FATAL);
+
+	ar->entry_bytes_remaining = 0;
+	ar->entry_bytes_unconsumed = 0;
+	ar->entry_padding = 0;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+ar_parse_gnu_filename_table(struct archive_read *a)
+{
+	struct ar *ar;
+	char *p;
+	size_t size;
+
+	ar = (struct ar*)(a->format->data);
+	size = ar->strtab_size;
+
+	for (p = ar->strtab; p < ar->strtab + size - 1; ++p) {
+		if (*p == '/') {
+			*p++ = '\0';
+			if (*p != '\n')
+				goto bad_string_table;
+			*p = '\0';
+		}
+	}
+	/*
+	 * GNU ar always pads the table to an even size.
+	 * The pad character is either '\n' or '`'.
+	 */
+	if (p != ar->strtab + size && *p != '\n' && *p != '`')
+		goto bad_string_table;
+
+	/* Enforce zero termination. */
+	ar->strtab[size - 1] = '\0';
+
+	return (ARCHIVE_OK);
+
+bad_string_table:
+	archive_set_error(&a->archive, EINVAL,
+	    "Invalid string table");
+	free(ar->strtab);
+	ar->strtab = NULL;
+	return (ARCHIVE_FATAL);
+}
+
+static uint64_t
+ar_atol8(const char *p, unsigned char_cnt)
+{
+	uint64_t l, limit, last_digit_limit;
+	unsigned int digit, base;
+
+	base = 8;
+	limit = UINT64_MAX / base;
+	last_digit_limit = UINT64_MAX % base;
+
+	while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
+		p++;
+
+	l = 0;
+	digit = *p - '0';
+	while (*p >= '0' && digit < base  && char_cnt-- > 0) {
+		if (l>limit || (l == limit && digit > last_digit_limit)) {
+			l = UINT64_MAX; /* Truncate on overflow. */
+			break;
+		}
+		l = (l * base) + digit;
+		digit = *++p - '0';
+	}
+	return (l);
+}
+
+static uint64_t
+ar_atol10(const char *p, unsigned char_cnt)
+{
+	uint64_t l, limit, last_digit_limit;
+	unsigned int base, digit;
+
+	base = 10;
+	limit = UINT64_MAX / base;
+	last_digit_limit = UINT64_MAX % base;
+
+	while ((*p == ' ' || *p == '\t') && char_cnt-- > 0)
+		p++;
+	l = 0;
+	digit = *p - '0';
+	while (*p >= '0' && digit < base  && char_cnt-- > 0) {
+		if (l > limit || (l == limit && digit > last_digit_limit)) {
+			l = UINT64_MAX; /* Truncate on overflow. */
+			break;
+		}
+		l = (l * base) + digit;
+		digit = *++p - '0';
+	}
+	return (l);
+}
diff --git a/c/archive_read_support_format_by_code.c b/c/archive_read_support_format_by_code.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_by_code.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2003-2011 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$");
+
+#include "archive.h"
+#include "archive_private.h"
+
+int
+archive_read_support_format_by_code(struct archive *a, int format_code)
+{
+	archive_check_magic(a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_by_code");
+
+	switch (format_code & ARCHIVE_FORMAT_BASE_MASK) {
+	case ARCHIVE_FORMAT_7ZIP:
+		return archive_read_support_format_7zip(a);
+		break;
+	case ARCHIVE_FORMAT_AR:
+		return archive_read_support_format_ar(a);
+		break;
+	case ARCHIVE_FORMAT_CAB:
+		return archive_read_support_format_cab(a);
+		break;
+	case ARCHIVE_FORMAT_CPIO:
+		return archive_read_support_format_cpio(a);
+		break;
+	case ARCHIVE_FORMAT_ISO9660:
+		return archive_read_support_format_iso9660(a);
+		break;
+	case ARCHIVE_FORMAT_LHA:
+		return archive_read_support_format_lha(a);
+		break;
+	case ARCHIVE_FORMAT_MTREE:
+		return archive_read_support_format_mtree(a);
+		break;
+	case ARCHIVE_FORMAT_RAR:
+		return archive_read_support_format_rar(a);
+		break;
+	case ARCHIVE_FORMAT_RAR_V5:
+		return archive_read_support_format_rar5(a);
+		break;
+	case ARCHIVE_FORMAT_TAR:
+		return archive_read_support_format_tar(a);
+		break;
+	case ARCHIVE_FORMAT_XAR:
+		return archive_read_support_format_xar(a);
+		break;
+	case ARCHIVE_FORMAT_ZIP:
+		return archive_read_support_format_zip(a);
+		break;
+	}
+	return (ARCHIVE_FATAL);
+}
diff --git a/c/archive_read_support_format_cab.c b/c/archive_read_support_format_cab.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_cab.c
@@ -0,0 +1,3227 @@
+/*-
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * 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"
+
+#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_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_endian.h"
+
+
+struct lzx_dec {
+	/* Decoding status. */
+	int     		 state;
+
+	/*
+	 * Window to see last decoded data, from 32KBi to 2MBi.
+	 */
+	int			 w_size;
+	int			 w_mask;
+	/* Window buffer, which is a loop buffer. */
+	unsigned char		*w_buff;
+	/* The insert position to the window. */
+	int			 w_pos;
+	/* The position where we can copy decoded code from the window. */
+	int     		 copy_pos;
+	/* The length how many bytes we can copy decoded code from
+	 * the window. */
+	int     		 copy_len;
+	/* Translation reversal for x86 processor CALL byte sequence(E8).
+	 * This is used for LZX only. */
+	uint32_t		 translation_size;
+	char			 translation;
+	char			 block_type;
+#define VERBATIM_BLOCK		1
+#define ALIGNED_OFFSET_BLOCK	2
+#define UNCOMPRESSED_BLOCK	3
+	size_t			 block_size;
+	size_t			 block_bytes_avail;
+	/* Repeated offset. */
+	int			 r0, r1, r2;
+	unsigned char		 rbytes[4];
+	int			 rbytes_avail;
+	int			 length_header;
+	int			 position_slot;
+	int			 offset_bits;
+
+	struct lzx_pos_tbl {
+		int		 base;
+		int		 footer_bits;
+	}			*pos_tbl;
+	/*
+	 * Bit stream reader.
+	 */
+	struct lzx_br {
+#define CACHE_TYPE		uint64_t
+#define CACHE_BITS		(8 * sizeof(CACHE_TYPE))
+	 	/* Cache buffer. */
+		CACHE_TYPE	 cache_buffer;
+		/* Indicates how many bits avail in cache_buffer. */
+		int		 cache_avail;
+		unsigned char	 odd;
+		char		 have_odd;
+	} br;
+
+	/*
+	 * Huffman coding.
+	 */
+	struct huffman {
+		int		 len_size;
+		int		 freq[17];
+		unsigned char	*bitlen;
+
+		/*
+		 * Use a index table. It's faster than searching a huffman
+		 * coding tree, which is a binary tree. But a use of a large
+		 * index table causes L1 cache read miss many times.
+		 */
+		int		 max_bits;
+		int		 tbl_bits;
+		int		 tree_used;
+		/* Direct access table. */
+		uint16_t	*tbl;
+	}			 at, lt, mt, pt;
+
+	int			 loop;
+	int			 error;
+};
+
+static const int slots[] = {
+	30, 32, 34, 36, 38, 42, 50, 66, 98, 162, 290
+};
+#define SLOT_BASE	15
+#define SLOT_MAX	21/*->25*/
+
+struct lzx_stream {
+	const unsigned char	*next_in;
+	int64_t			 avail_in;
+	int64_t			 total_in;
+	unsigned char		*next_out;
+	int64_t			 avail_out;
+	int64_t			 total_out;
+	struct lzx_dec		*ds;
+};
+
+/*
+ * Cabinet file definitions.
+ */
+/* CFHEADER offset */
+#define CFHEADER_signature	0
+#define CFHEADER_cbCabinet	8
+#define CFHEADER_coffFiles	16
+#define CFHEADER_versionMinor	24
+#define CFHEADER_versionMajor	25
+#define CFHEADER_cFolders	26
+#define CFHEADER_cFiles		28
+#define CFHEADER_flags		30
+#define CFHEADER_setID		32
+#define CFHEADER_iCabinet	34
+#define CFHEADER_cbCFHeader	36
+#define CFHEADER_cbCFFolder	38
+#define CFHEADER_cbCFData	39
+
+/* CFFOLDER offset */
+#define CFFOLDER_coffCabStart	0
+#define CFFOLDER_cCFData	4
+#define CFFOLDER_typeCompress	6
+#define CFFOLDER_abReserve	8
+
+/* CFFILE offset */
+#define CFFILE_cbFile		0
+#define CFFILE_uoffFolderStart	4
+#define CFFILE_iFolder		8
+#define CFFILE_date_time	10
+#define CFFILE_attribs		14
+
+/* CFDATA offset */
+#define CFDATA_csum		0
+#define CFDATA_cbData		4
+#define CFDATA_cbUncomp		6
+
+static const char * const compression_name[] = {
+	"NONE",
+	"MSZIP",
+	"Quantum",
+	"LZX",
+};
+
+struct cfdata {
+	/* Sum value of this CFDATA. */
+	uint32_t		 sum;
+	uint16_t		 compressed_size;
+	uint16_t		 compressed_bytes_remaining;
+	uint16_t		 uncompressed_size;
+	uint16_t		 uncompressed_bytes_remaining;
+	/* To know how many bytes we have decompressed. */
+	uint16_t		 uncompressed_avail;
+	/* Offset from the beginning of compressed data of this CFDATA */
+	uint16_t		 read_offset;
+	int64_t			 unconsumed;
+	/* To keep memory image of this CFDATA to compute the sum. */
+	size_t			 memimage_size;
+	unsigned char		*memimage;
+	/* Result of calculation of sum. */
+	uint32_t		 sum_calculated;
+	unsigned char		 sum_extra[4];
+	int			 sum_extra_avail;
+	const void		*sum_ptr;
+};
+
+struct cffolder {
+	uint32_t		 cfdata_offset_in_cab;
+	uint16_t		 cfdata_count;
+	uint16_t		 comptype;
+#define COMPTYPE_NONE		0x0000
+#define COMPTYPE_MSZIP		0x0001
+#define COMPTYPE_QUANTUM	0x0002
+#define COMPTYPE_LZX		0x0003
+	uint16_t		 compdata;
+	const char		*compname;
+	/* At the time reading CFDATA */
+	struct cfdata		 cfdata;
+	int			 cfdata_index;
+	/* Flags to mark progress of decompression. */
+	char			 decompress_init;
+};
+
+struct cffile {
+	uint32_t		 uncompressed_size;
+	uint32_t		 offset;
+	time_t			 mtime;
+	uint16_t	 	 folder;
+#define iFoldCONTINUED_FROM_PREV	0xFFFD
+#define iFoldCONTINUED_TO_NEXT		0xFFFE
+#define iFoldCONTINUED_PREV_AND_NEXT	0xFFFF
+	unsigned char		 attr;
+#define ATTR_RDONLY		0x01
+#define ATTR_NAME_IS_UTF	0x80
+	struct archive_string 	 pathname;
+};
+
+struct cfheader {
+	/* Total bytes of all file size in a Cabinet. */
+	uint32_t		 total_bytes;
+	uint32_t		 files_offset;
+	uint16_t		 folder_count;
+	uint16_t		 file_count;
+	uint16_t		 flags;
+#define PREV_CABINET	0x0001
+#define NEXT_CABINET	0x0002
+#define RESERVE_PRESENT	0x0004
+	uint16_t		 setid;
+	uint16_t		 cabinet;
+	/* Version number. */
+	unsigned char		 major;
+	unsigned char		 minor;
+	unsigned char		 cffolder;
+	unsigned char		 cfdata;
+	/* All folders in a cabinet. */
+	struct cffolder		*folder_array;
+	/* All files in a cabinet. */
+	struct cffile		*file_array;
+	int			 file_index;
+};
+
+struct cab {
+	/* entry_bytes_remaining is the number of bytes we expect.	    */
+	int64_t			 entry_offset;
+	int64_t			 entry_bytes_remaining;
+	int64_t			 entry_unconsumed;
+	int64_t			 entry_compressed_bytes_read;
+	int64_t			 entry_uncompressed_bytes_read;
+	struct cffolder		*entry_cffolder;
+	struct cffile		*entry_cffile;
+	struct cfdata		*entry_cfdata;
+
+	/* Offset from beginning of a cabinet file. */
+	int64_t			 cab_offset;
+	struct cfheader		 cfheader;
+	struct archive_wstring	 ws;
+
+	/* Flag to mark progress that an archive was read their first header.*/
+	char			 found_header;
+	char			 end_of_archive;
+	char			 end_of_entry;
+	char			 end_of_entry_cleanup;
+	char			 read_data_invoked;
+	int64_t			 bytes_skipped;
+
+	unsigned char		*uncompressed_buffer;
+	size_t			 uncompressed_buffer_size;
+
+	int			 init_default_conversion;
+	struct archive_string_conv *sconv;
+	struct archive_string_conv *sconv_default;
+	struct archive_string_conv *sconv_utf8;
+	char			 format_name[64];
+
+#ifdef HAVE_ZLIB_H
+	z_stream		 stream;
+	char			 stream_valid;
+#endif
+	struct lzx_stream	 xstrm;
+};
+
+static int	archive_read_format_cab_bid(struct archive_read *, int);
+static int	archive_read_format_cab_options(struct archive_read *,
+		    const char *, const char *);
+static int	archive_read_format_cab_read_header(struct archive_read *,
+		    struct archive_entry *);
+static int	archive_read_format_cab_read_data(struct archive_read *,
+		    const void **, size_t *, int64_t *);
+static int	archive_read_format_cab_read_data_skip(struct archive_read *);
+static int	archive_read_format_cab_cleanup(struct archive_read *);
+
+static int	cab_skip_sfx(struct archive_read *);
+static time_t	cab_dos_time(const unsigned char *);
+static int	cab_read_data(struct archive_read *, const void **,
+		    size_t *, int64_t *);
+static int	cab_read_header(struct archive_read *);
+static uint32_t cab_checksum_cfdata_4(const void *, size_t bytes, uint32_t);
+static uint32_t cab_checksum_cfdata(const void *, size_t bytes, uint32_t);
+static void	cab_checksum_update(struct archive_read *, size_t);
+static int	cab_checksum_finish(struct archive_read *);
+static int	cab_next_cfdata(struct archive_read *);
+static const void *cab_read_ahead_cfdata(struct archive_read *, ssize_t *);
+static const void *cab_read_ahead_cfdata_none(struct archive_read *, ssize_t *);
+static const void *cab_read_ahead_cfdata_deflate(struct archive_read *,
+		    ssize_t *);
+static const void *cab_read_ahead_cfdata_lzx(struct archive_read *,
+		    ssize_t *);
+static int64_t	cab_consume_cfdata(struct archive_read *, int64_t);
+static int64_t	cab_minimum_consume_cfdata(struct archive_read *, int64_t);
+static int	lzx_decode_init(struct lzx_stream *, int);
+static int	lzx_read_blocks(struct lzx_stream *, int);
+static int	lzx_decode_blocks(struct lzx_stream *, int);
+static void	lzx_decode_free(struct lzx_stream *);
+static void	lzx_translation(struct lzx_stream *, void *, size_t, uint32_t);
+static void	lzx_cleanup_bitstream(struct lzx_stream *);
+static int	lzx_decode(struct lzx_stream *, int);
+static int	lzx_read_pre_tree(struct lzx_stream *);
+static int	lzx_read_bitlen(struct lzx_stream *, struct huffman *, int);
+static int	lzx_huffman_init(struct huffman *, size_t, int);
+static void	lzx_huffman_free(struct huffman *);
+static int	lzx_make_huffman_table(struct huffman *);
+static inline int lzx_decode_huffman(struct huffman *, unsigned);
+
+
+int
+archive_read_support_format_cab(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct cab *cab;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_cab");
+
+	cab = (struct cab *)calloc(1, sizeof(*cab));
+	if (cab == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate CAB data");
+		return (ARCHIVE_FATAL);
+	}
+	archive_string_init(&cab->ws);
+	archive_wstring_ensure(&cab->ws, 256);
+
+	r = __archive_read_register_format(a,
+	    cab,
+	    "cab",
+	    archive_read_format_cab_bid,
+	    archive_read_format_cab_options,
+	    archive_read_format_cab_read_header,
+	    archive_read_format_cab_read_data,
+	    archive_read_format_cab_read_data_skip,
+	    NULL,
+	    archive_read_format_cab_cleanup,
+	    NULL,
+	    NULL);
+
+	if (r != ARCHIVE_OK)
+		free(cab);
+	return (ARCHIVE_OK);
+}
+
+static int
+find_cab_magic(const char *p)
+{
+	switch (p[4]) {
+	case 0:
+		/*
+		 * Note: Self-Extraction program has 'MSCF' string in their
+		 * program. If we were finding 'MSCF' string only, we got
+		 * wrong place for Cabinet header, thus, we have to check
+		 * following four bytes which are reserved and must be set
+		 * to zero.
+		 */
+		if (memcmp(p, "MSCF\0\0\0\0", 8) == 0)
+			return 0;
+		return 5;
+	case 'F': return 1;
+	case 'C': return 2;
+	case 'S': return 3;
+	case 'M': return 4;
+	default:  return 5;
+	}
+}
+
+static int
+archive_read_format_cab_bid(struct archive_read *a, int best_bid)
+{
+	const char *p;
+	ssize_t bytes_avail, offset, window;
+
+	/* If there's already a better bid than we can ever
+	   make, don't bother testing. */
+	if (best_bid > 64)
+		return (-1);
+
+	if ((p = __archive_read_ahead(a, 8, NULL)) == NULL)
+		return (-1);
+
+	if (memcmp(p, "MSCF\0\0\0\0", 8) == 0)
+		return (64);
+
+	/*
+	 * Attempt to handle self-extracting archives
+	 * by noting a PE header and searching forward
+	 * up to 128k for a 'MSCF' marker.
+	 */
+	if (p[0] == 'M' && p[1] == 'Z') {
+		offset = 0;
+		window = 4096;
+		while (offset < (1024 * 128)) {
+			const char *h = __archive_read_ahead(a, offset + window,
+			    &bytes_avail);
+			if (h == NULL) {
+				/* Remaining bytes are less than window. */
+				window >>= 1;
+				if (window < 128)
+					return (0);
+				continue;
+			}
+			p = h + offset;
+			while (p + 8 < h + bytes_avail) {
+				int next;
+				if ((next = find_cab_magic(p)) == 0)
+					return (64);
+				p += next;
+			}
+			offset = p - h;
+		}
+	}
+	return (0);
+}
+
+static int
+archive_read_format_cab_options(struct archive_read *a,
+    const char *key, const char *val)
+{
+	struct cab *cab;
+	int ret = ARCHIVE_FAILED;
+
+	cab = (struct cab *)(a->format->data);
+	if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "cab: hdrcharset option needs a character-set name");
+		else {
+			cab->sconv = archive_string_conversion_from_charset(
+			    &a->archive, val, 0);
+			if (cab->sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+cab_skip_sfx(struct archive_read *a)
+{
+	const char *p, *q;
+	size_t skip;
+	ssize_t bytes, window;
+
+	window = 4096;
+	for (;;) {
+		const char *h = __archive_read_ahead(a, window, &bytes);
+		if (h == NULL) {
+			/* Remaining size are less than window. */
+			window >>= 1;
+			if (window < 128) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Couldn't find out CAB header");
+				return (ARCHIVE_FATAL);
+			}
+			continue;
+		}
+		p = h;
+		q = p + bytes;
+
+		/*
+		 * Scan ahead until we find something that looks
+		 * like the cab header.
+		 */
+		while (p + 8 < q) {
+			int next;
+			if ((next = find_cab_magic(p)) == 0) {
+				skip = p - h;
+				__archive_read_consume(a, skip);
+				return (ARCHIVE_OK);
+			}
+			p += next;
+		}
+		skip = p - h;
+		__archive_read_consume(a, skip);
+	}
+}
+
+static int
+truncated_error(struct archive_read *a)
+{
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Truncated CAB header");
+	return (ARCHIVE_FATAL);
+}
+
+static ssize_t
+cab_strnlen(const unsigned char *p, size_t maxlen)
+{
+	size_t i;
+
+	for (i = 0; i <= maxlen; i++) {
+		if (p[i] == 0)
+			break;
+	}
+	if (i > maxlen)
+		return (-1);/* invalid */
+	return ((ssize_t)i);
+}
+
+/* Read bytes as much as remaining. */
+static const void *
+cab_read_ahead_remaining(struct archive_read *a, size_t min, ssize_t *avail)
+{
+	const void *p;
+
+	while (min > 0) {
+		p = __archive_read_ahead(a, min, avail);
+		if (p != NULL)
+			return (p);
+		min--;
+	}
+	return (NULL);
+}
+
+/* Convert a path separator '\' -> '/' */
+static int
+cab_convert_path_separator_1(struct archive_string *fn, unsigned char attr)
+{
+	size_t i;
+	int mb;
+
+	/* Easy check if we have '\' in multi-byte string. */
+	mb = 0;
+	for (i = 0; i < archive_strlen(fn); i++) {
+		if (fn->s[i] == '\\') {
+			if (mb) {
+				/* This may be second byte of multi-byte
+				 * character. */
+				break;
+			}
+			fn->s[i] = '/';
+			mb = 0;
+		} else if ((fn->s[i] & 0x80) && !(attr & ATTR_NAME_IS_UTF))
+			mb = 1;
+		else
+			mb = 0;
+	}
+	if (i == archive_strlen(fn))
+		return (0);
+	return (-1);
+}
+
+/*
+ * Replace a character '\' with '/' in wide character.
+ */
+static void
+cab_convert_path_separator_2(struct cab *cab, struct archive_entry *entry)
+{
+	const wchar_t *wp;
+	size_t i;
+
+	/* If a conversion to wide character failed, force the replacement. */
+	if ((wp = archive_entry_pathname_w(entry)) != NULL) {
+		archive_wstrcpy(&(cab->ws), wp);
+		for (i = 0; i < archive_strlen(&(cab->ws)); i++) {
+			if (cab->ws.s[i] == L'\\')
+				cab->ws.s[i] = L'/';
+		}
+		archive_entry_copy_pathname_w(entry, cab->ws.s);
+	}
+}
+
+/*
+ * Read CFHEADER, CFFOLDER and CFFILE.
+ */
+static int
+cab_read_header(struct archive_read *a)
+{
+	const unsigned char *p;
+	struct cab *cab;
+	struct cfheader *hd;
+	size_t bytes, used;
+	ssize_t len;
+	int64_t skip;
+	int err, i;
+	int cur_folder, prev_folder;
+	uint32_t offset32;
+	
+	a->archive.archive_format = ARCHIVE_FORMAT_CAB;
+	if (a->archive.archive_format_name == NULL)
+		a->archive.archive_format_name = "CAB";
+
+	if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)
+		return (truncated_error(a));
+
+	cab = (struct cab *)(a->format->data);
+	if (cab->found_header == 0 &&
+	    p[0] == 'M' && p[1] == 'Z') {
+		/* This is an executable?  Must be self-extracting... */
+		err = cab_skip_sfx(a);
+		if (err < ARCHIVE_WARN)
+			return (err);
+
+		/* Re-read header after processing the SFX. */
+		if ((p = __archive_read_ahead(a, 42, NULL)) == NULL)
+			return (truncated_error(a));
+	}
+
+	cab->cab_offset = 0;
+	/*
+	 * Read CFHEADER.
+	 */
+	hd = &cab->cfheader;
+	if (p[CFHEADER_signature+0] != 'M' || p[CFHEADER_signature+1] != 'S' ||
+	    p[CFHEADER_signature+2] != 'C' || p[CFHEADER_signature+3] != 'F') {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Couldn't find out CAB header");
+		return (ARCHIVE_FATAL);
+	}
+	hd->total_bytes = archive_le32dec(p + CFHEADER_cbCabinet);
+	hd->files_offset = archive_le32dec(p + CFHEADER_coffFiles);
+	hd->minor = p[CFHEADER_versionMinor];
+	hd->major = p[CFHEADER_versionMajor];
+	hd->folder_count = archive_le16dec(p + CFHEADER_cFolders);
+	if (hd->folder_count == 0)
+		goto invalid;
+	hd->file_count = archive_le16dec(p + CFHEADER_cFiles);
+	if (hd->file_count == 0)
+		goto invalid;
+	hd->flags = archive_le16dec(p + CFHEADER_flags);
+	hd->setid = archive_le16dec(p + CFHEADER_setID);
+	hd->cabinet = archive_le16dec(p + CFHEADER_iCabinet);
+	used = CFHEADER_iCabinet + 2;
+	if (hd->flags & RESERVE_PRESENT) {
+		uint16_t cfheader;
+		cfheader = archive_le16dec(p + CFHEADER_cbCFHeader);
+		if (cfheader > 60000U)
+			goto invalid;
+		hd->cffolder = p[CFHEADER_cbCFFolder];
+		hd->cfdata = p[CFHEADER_cbCFData];
+		used += 4;/* cbCFHeader, cbCFFolder and cbCFData */
+		used += cfheader;/* abReserve */
+	} else
+		hd->cffolder = 0;/* Avoid compiling warning. */
+	if (hd->flags & PREV_CABINET) {
+		/* How many bytes are used for szCabinetPrev. */
+		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+			return (truncated_error(a));
+		if ((len = cab_strnlen(p + used, 255)) <= 0)
+			goto invalid;
+		used += len + 1;
+		/* How many bytes are used for szDiskPrev. */
+		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+			return (truncated_error(a));
+		if ((len = cab_strnlen(p + used, 255)) <= 0)
+			goto invalid;
+		used += len + 1;
+	}
+	if (hd->flags & NEXT_CABINET) {
+		/* How many bytes are used for szCabinetNext. */
+		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+			return (truncated_error(a));
+		if ((len = cab_strnlen(p + used, 255)) <= 0)
+			goto invalid;
+		used += len + 1;
+		/* How many bytes are used for szDiskNext. */
+		if ((p = __archive_read_ahead(a, used+256, NULL)) == NULL)
+			return (truncated_error(a));
+		if ((len = cab_strnlen(p + used, 255)) <= 0)
+			goto invalid;
+		used += len + 1;
+	}
+	__archive_read_consume(a, used);
+	cab->cab_offset += used;
+	used = 0;
+
+	/*
+	 * Read CFFOLDER.
+	 */
+	hd->folder_array = (struct cffolder *)calloc(
+	    hd->folder_count, sizeof(struct cffolder));
+	if (hd->folder_array == NULL)
+		goto nomem;
+	
+	bytes = 8;
+	if (hd->flags & RESERVE_PRESENT)
+		bytes += hd->cffolder;
+	bytes *= hd->folder_count;
+	if ((p = __archive_read_ahead(a, bytes, NULL)) == NULL)
+		return (truncated_error(a));
+	offset32 = 0;
+	for (i = 0; i < hd->folder_count; i++) {
+		struct cffolder *folder = &(hd->folder_array[i]);
+		folder->cfdata_offset_in_cab =
+		    archive_le32dec(p + CFFOLDER_coffCabStart);
+		folder->cfdata_count = archive_le16dec(p+CFFOLDER_cCFData);
+		folder->comptype =
+		    archive_le16dec(p+CFFOLDER_typeCompress) & 0x0F;
+		folder->compdata =
+		    archive_le16dec(p+CFFOLDER_typeCompress) >> 8;
+		/* Get a compression name. */
+		if (folder->comptype <
+		    sizeof(compression_name) / sizeof(compression_name[0]))
+			folder->compname = compression_name[folder->comptype];
+		else
+			folder->compname = "UNKNOWN";
+		p += 8;
+		used += 8;
+		if (hd->flags & RESERVE_PRESENT) {
+			p += hd->cffolder;/* abReserve */
+			used += hd->cffolder;
+		}
+		/*
+		 * Sanity check if each data is acceptable.
+		 */
+		if (offset32 >= folder->cfdata_offset_in_cab)
+			goto invalid;
+		offset32 = folder->cfdata_offset_in_cab;
+
+		/* Set a request to initialize zlib for the CFDATA of
+		 * this folder. */
+		folder->decompress_init = 0;
+	}
+	__archive_read_consume(a, used);
+	cab->cab_offset += used;
+
+	/*
+	 * Read CFFILE.
+	 */
+	/* Seek read pointer to the offset of CFFILE if needed. */
+	skip = (int64_t)hd->files_offset - cab->cab_offset;
+	if (skip <  0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid offset of CFFILE %jd < %jd",
+		    (intmax_t)hd->files_offset, (intmax_t)cab->cab_offset);
+		return (ARCHIVE_FATAL);
+	}
+	if (skip) {
+		__archive_read_consume(a, skip);
+		cab->cab_offset += skip;
+	}
+	/* Allocate memory for CFDATA */
+	hd->file_array = (struct cffile *)calloc(
+	    hd->file_count, sizeof(struct cffile));
+	if (hd->file_array == NULL)
+		goto nomem;
+
+	prev_folder = -1;
+	for (i = 0; i < hd->file_count; i++) {
+		struct cffile *file = &(hd->file_array[i]);
+		ssize_t avail;
+
+		if ((p = __archive_read_ahead(a, 16, NULL)) == NULL)
+			return (truncated_error(a));
+		file->uncompressed_size = archive_le32dec(p + CFFILE_cbFile);
+		file->offset = archive_le32dec(p + CFFILE_uoffFolderStart);
+		file->folder = archive_le16dec(p + CFFILE_iFolder);
+		file->mtime = cab_dos_time(p + CFFILE_date_time);
+		file->attr = (uint8_t)archive_le16dec(p + CFFILE_attribs);
+		__archive_read_consume(a, 16);
+
+		cab->cab_offset += 16;
+		if ((p = cab_read_ahead_remaining(a, 256, &avail)) == NULL)
+			return (truncated_error(a));
+		if ((len = cab_strnlen(p, avail-1)) <= 0)
+			goto invalid;
+
+		/* Copy a pathname.  */
+		archive_string_init(&(file->pathname));
+		archive_strncpy(&(file->pathname), p, len);
+		__archive_read_consume(a, len + 1);
+		cab->cab_offset += len + 1;
+
+		/*
+		 * Sanity check if each data is acceptable.
+		 */
+		if (file->uncompressed_size > 0x7FFF8000)
+			goto invalid;/* Too large */
+		if ((int64_t)file->offset + (int64_t)file->uncompressed_size
+		    > ARCHIVE_LITERAL_LL(0x7FFF8000))
+			goto invalid;/* Too large */
+		switch (file->folder) {
+		case iFoldCONTINUED_TO_NEXT:
+			/* This must be last file in a folder. */
+			if (i != hd->file_count -1)
+				goto invalid;
+			cur_folder = hd->folder_count -1;
+			break;
+		case iFoldCONTINUED_PREV_AND_NEXT:
+			/* This must be only one file in a folder. */
+			if (hd->file_count != 1)
+				goto invalid;
+			/* FALL THROUGH */
+		case iFoldCONTINUED_FROM_PREV:
+			/* This must be first file in a folder. */
+			if (i != 0)
+				goto invalid;
+			prev_folder = cur_folder = 0;
+			offset32 = file->offset;
+			break;
+		default:
+			if (file->folder >= hd->folder_count)
+				goto invalid;
+			cur_folder = file->folder;
+			break;
+		}
+		/* Dot not back track. */
+		if (cur_folder < prev_folder)
+			goto invalid;
+		if (cur_folder != prev_folder)
+			offset32 = 0;
+		prev_folder = cur_folder;
+
+		/* Make sure there are not any blanks from last file
+		 * contents. */
+		if (offset32 != file->offset)
+			goto invalid;
+		offset32 += file->uncompressed_size;
+
+		/* CFDATA is available for file contents. */
+		if (file->uncompressed_size > 0 &&
+		    hd->folder_array[cur_folder].cfdata_count == 0)
+			goto invalid;
+	}
+
+	if (hd->cabinet != 0 || hd->flags & (PREV_CABINET | NEXT_CABINET)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Multivolume cabinet file is unsupported");
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+invalid:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Invalid CAB header");
+	return (ARCHIVE_FATAL);
+nomem:
+	archive_set_error(&a->archive, ENOMEM,
+	    "Can't allocate memory for CAB data");
+	return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_cab_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct cab *cab;
+	struct cfheader *hd;
+	struct cffolder *prev_folder;
+	struct cffile *file;
+	struct archive_string_conv *sconv;
+	int err = ARCHIVE_OK, r;
+	
+	cab = (struct cab *)(a->format->data);
+	if (cab->found_header == 0) {
+		err = cab_read_header(a); 
+		if (err < ARCHIVE_WARN)
+			return (err);
+		/* We've found the header. */
+		cab->found_header = 1;
+	}
+	hd = &cab->cfheader;
+
+	if (hd->file_index >= hd->file_count) {
+		cab->end_of_archive = 1;
+		return (ARCHIVE_EOF);
+	}
+	file = &hd->file_array[hd->file_index++];
+
+	cab->end_of_entry = 0;
+	cab->end_of_entry_cleanup = 0;
+	cab->entry_compressed_bytes_read = 0;
+	cab->entry_uncompressed_bytes_read = 0;
+	cab->entry_unconsumed = 0;
+	cab->entry_cffile = file;
+
+	/*
+	 * Choose a proper folder.
+	 */
+	prev_folder = cab->entry_cffolder;
+	switch (file->folder) {
+	case iFoldCONTINUED_FROM_PREV:
+	case iFoldCONTINUED_PREV_AND_NEXT:
+		cab->entry_cffolder = &hd->folder_array[0];
+		break;
+	case iFoldCONTINUED_TO_NEXT:
+		cab->entry_cffolder = &hd->folder_array[hd->folder_count-1];
+		break;
+	default:
+		cab->entry_cffolder = &hd->folder_array[file->folder];
+		break;
+	}
+	/* If a cffolder of this file is changed, reset a cfdata to read
+	 * file contents from next cfdata. */
+	if (prev_folder != cab->entry_cffolder)
+		cab->entry_cfdata = NULL;
+
+	/* If a pathname is UTF-8, prepare a string conversion object
+	 * for UTF-8 and use it. */
+	if (file->attr & ATTR_NAME_IS_UTF) {
+		if (cab->sconv_utf8 == NULL) {
+			cab->sconv_utf8 =
+			    archive_string_conversion_from_charset(
+				&(a->archive), "UTF-8", 1);
+			if (cab->sconv_utf8 == NULL)
+				return (ARCHIVE_FATAL);
+		}
+		sconv = cab->sconv_utf8;
+	} else if (cab->sconv != NULL) {
+		/* Choose the conversion specified by the option. */
+		sconv = cab->sconv;
+	} else {
+		/* Choose the default conversion. */
+		if (!cab->init_default_conversion) {
+			cab->sconv_default =
+			    archive_string_default_conversion_for_read(
+			      &(a->archive));
+			cab->init_default_conversion = 1;
+		}
+		sconv = cab->sconv_default;
+	}
+
+	/*
+	 * Set a default value and common data
+	 */
+	r = cab_convert_path_separator_1(&(file->pathname), file->attr);
+	if (archive_entry_copy_pathname_l(entry, file->pathname.s,
+	    archive_strlen(&(file->pathname)), sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Pathname cannot be converted "
+		    "from %s to current locale.",
+		    archive_string_conversion_charset_name(sconv));
+		err = ARCHIVE_WARN;
+	}
+	if (r < 0) {
+		/* Convert a path separator '\' -> '/' */
+		cab_convert_path_separator_2(cab, entry);
+	}
+
+	archive_entry_set_size(entry, file->uncompressed_size);
+	if (file->attr & ATTR_RDONLY)
+		archive_entry_set_mode(entry, AE_IFREG | 0555);
+	else
+		archive_entry_set_mode(entry, AE_IFREG | 0666);
+	archive_entry_set_mtime(entry, file->mtime, 0);
+
+	cab->entry_bytes_remaining = file->uncompressed_size;
+	cab->entry_offset = 0;
+	/* We don't need compress data. */
+	if (file->uncompressed_size == 0)
+		cab->end_of_entry_cleanup = cab->end_of_entry = 1;
+
+	/* Set up a more descriptive format name. */
+	sprintf(cab->format_name, "CAB %d.%d (%s)",
+	    hd->major, hd->minor, cab->entry_cffolder->compname);
+	a->archive.archive_format_name = cab->format_name;
+
+	return (err);
+}
+
+static int
+archive_read_format_cab_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	int r;
+
+	switch (cab->entry_cffile->folder) {
+	case iFoldCONTINUED_FROM_PREV:
+	case iFoldCONTINUED_TO_NEXT:
+	case iFoldCONTINUED_PREV_AND_NEXT:
+		*buff = NULL;
+		*size = 0;
+		*offset = 0;
+		archive_clear_error(&a->archive);
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Cannot restore this file split in multivolume.");
+		return (ARCHIVE_FAILED);
+	default:
+		break;
+	}
+	if (cab->read_data_invoked == 0) {
+		if (cab->bytes_skipped) {
+			if (cab->entry_cfdata == NULL) {
+				r = cab_next_cfdata(a);
+				if (r < 0)
+					return (r);
+			}
+			if (cab_consume_cfdata(a, cab->bytes_skipped) < 0)
+				return (ARCHIVE_FATAL);
+			cab->bytes_skipped = 0;
+		}
+		cab->read_data_invoked = 1;
+	}
+	if (cab->entry_unconsumed) {
+		/* Consume as much as the compressor actually used. */
+		r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);
+		cab->entry_unconsumed = 0;
+		if (r < 0)
+			return (r);
+	}
+	if (cab->end_of_archive || cab->end_of_entry) {
+		if (!cab->end_of_entry_cleanup) {
+			/* End-of-entry cleanup done. */
+			cab->end_of_entry_cleanup = 1;
+		}
+		*offset = cab->entry_offset;
+		*size = 0;
+		*buff = NULL;
+		return (ARCHIVE_EOF);
+	}
+
+	return (cab_read_data(a, buff, size, offset));
+}
+
+static uint32_t
+cab_checksum_cfdata_4(const void *p, size_t bytes, uint32_t seed)
+{
+	const unsigned char *b;
+	unsigned u32num;
+	uint32_t sum;
+
+	u32num = (unsigned)bytes / 4;
+	sum = seed;
+	b = p;
+	for (;u32num > 0; --u32num) {
+		sum ^= archive_le32dec(b);
+		b += 4;
+	}
+	return (sum);
+}
+
+static uint32_t
+cab_checksum_cfdata(const void *p, size_t bytes, uint32_t seed)
+{
+	const unsigned char *b;
+	uint32_t sum;
+	uint32_t t;
+
+	sum = cab_checksum_cfdata_4(p, bytes, seed);
+	b = p;
+	b += bytes & ~3;
+	t = 0;
+	switch (bytes & 3) {
+	case 3:
+		t |= ((uint32_t)(*b++)) << 16;
+		/* FALL THROUGH */
+	case 2:
+		t |= ((uint32_t)(*b++)) << 8;
+		/* FALL THROUGH */
+	case 1:
+		t |= *b;
+		/* FALL THROUGH */
+	default:
+		break;
+	}
+	sum ^= t;
+
+	return (sum);
+}
+
+static void
+cab_checksum_update(struct archive_read *a, size_t bytes)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	struct cfdata *cfdata = cab->entry_cfdata;
+	const unsigned char *p;
+	size_t sumbytes;
+
+	if (cfdata->sum == 0 || cfdata->sum_ptr == NULL)
+		return;
+	/*
+	 * Calculate the sum of this CFDATA.
+	 * Make sure CFDATA must be calculated in four bytes.
+	 */
+	p = cfdata->sum_ptr;
+	sumbytes = bytes;
+	if (cfdata->sum_extra_avail) {
+		while (cfdata->sum_extra_avail < 4 && sumbytes > 0) {
+			cfdata->sum_extra[
+			    cfdata->sum_extra_avail++] = *p++;
+			sumbytes--;
+		}
+		if (cfdata->sum_extra_avail == 4) {
+			cfdata->sum_calculated = cab_checksum_cfdata_4(
+			    cfdata->sum_extra, 4, cfdata->sum_calculated);
+			cfdata->sum_extra_avail = 0;
+		}
+	}
+	if (sumbytes) {
+		int odd = sumbytes & 3;
+		if (sumbytes - odd > 0)
+			cfdata->sum_calculated = cab_checksum_cfdata_4(
+			    p, sumbytes - odd, cfdata->sum_calculated);
+		if (odd)
+			memcpy(cfdata->sum_extra, p + sumbytes - odd, odd);
+		cfdata->sum_extra_avail = odd;
+	}
+	cfdata->sum_ptr = NULL;
+}
+
+static int
+cab_checksum_finish(struct archive_read *a)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	struct cfdata *cfdata = cab->entry_cfdata;
+	int l;
+
+	/* Do not need to compute a sum. */
+	if (cfdata->sum == 0)
+		return (ARCHIVE_OK);
+
+	/*
+	 * Calculate the sum of remaining CFDATA.
+	 */
+	if (cfdata->sum_extra_avail) {
+		cfdata->sum_calculated =
+		    cab_checksum_cfdata(cfdata->sum_extra,
+		       cfdata->sum_extra_avail, cfdata->sum_calculated);
+		cfdata->sum_extra_avail = 0;
+	}
+
+	l = 4;
+	if (cab->cfheader.flags & RESERVE_PRESENT)
+		l += cab->cfheader.cfdata;
+	cfdata->sum_calculated = cab_checksum_cfdata(
+	    cfdata->memimage + CFDATA_cbData, l, cfdata->sum_calculated);
+	if (cfdata->sum_calculated != cfdata->sum) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Checksum error CFDATA[%d] %x:%x in %d bytes",
+		    cab->entry_cffolder->cfdata_index -1,
+		    cfdata->sum, cfdata->sum_calculated,
+		    cfdata->compressed_size);
+		return (ARCHIVE_FAILED);
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Read CFDATA if needed.
+ */
+static int
+cab_next_cfdata(struct archive_read *a)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	struct cfdata *cfdata = cab->entry_cfdata;
+
+	/* There are remaining bytes in current CFDATA, use it first. */
+	if (cfdata != NULL && cfdata->uncompressed_bytes_remaining > 0)
+		return (ARCHIVE_OK);
+
+	if (cfdata == NULL) {
+		int64_t skip;
+
+		cab->entry_cffolder->cfdata_index = 0;
+
+		/* Seek read pointer to the offset of CFDATA if needed. */
+		skip = cab->entry_cffolder->cfdata_offset_in_cab
+			- cab->cab_offset;
+		if (skip < 0) {
+			int folder_index;
+			switch (cab->entry_cffile->folder) {
+			case iFoldCONTINUED_FROM_PREV:
+			case iFoldCONTINUED_PREV_AND_NEXT:
+				folder_index = 0;
+				break;
+			case iFoldCONTINUED_TO_NEXT:
+				folder_index = cab->cfheader.folder_count-1;
+				break;
+			default:
+				folder_index = cab->entry_cffile->folder;
+				break;
+			}
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Invalid offset of CFDATA in folder(%d) %jd < %jd",
+			    folder_index,
+			    (intmax_t)cab->entry_cffolder->cfdata_offset_in_cab,
+			    (intmax_t)cab->cab_offset);
+			return (ARCHIVE_FATAL);
+		}
+		if (skip > 0) {
+			if (__archive_read_consume(a, skip) < 0)
+				return (ARCHIVE_FATAL);
+			cab->cab_offset =
+			    cab->entry_cffolder->cfdata_offset_in_cab;
+		}
+	}
+
+	/*
+	 * Read a CFDATA.
+	 */
+	if (cab->entry_cffolder->cfdata_index <
+	    cab->entry_cffolder->cfdata_count) {
+		const unsigned char *p;
+		int l;
+
+		cfdata = &(cab->entry_cffolder->cfdata);
+		cab->entry_cffolder->cfdata_index++;
+		cab->entry_cfdata = cfdata;
+		cfdata->sum_calculated = 0;
+		cfdata->sum_extra_avail = 0;
+		cfdata->sum_ptr = NULL;
+		l = 8;
+		if (cab->cfheader.flags & RESERVE_PRESENT)
+			l += cab->cfheader.cfdata;
+		if ((p = __archive_read_ahead(a, l, NULL)) == NULL)
+			return (truncated_error(a));
+		cfdata->sum = archive_le32dec(p + CFDATA_csum);
+		cfdata->compressed_size = archive_le16dec(p + CFDATA_cbData);
+		cfdata->compressed_bytes_remaining = cfdata->compressed_size;
+		cfdata->uncompressed_size =
+		    archive_le16dec(p + CFDATA_cbUncomp);
+		cfdata->uncompressed_bytes_remaining =
+		    cfdata->uncompressed_size;
+		cfdata->uncompressed_avail = 0;
+		cfdata->read_offset = 0;
+		cfdata->unconsumed = 0;
+
+		/*
+		 * Sanity check if data size is acceptable.
+		 */
+		if (cfdata->compressed_size == 0 ||
+		    cfdata->compressed_size > (0x8000+6144))
+			goto invalid;
+		if (cfdata->uncompressed_size > 0x8000)
+			goto invalid;
+		if (cfdata->uncompressed_size == 0) {
+			switch (cab->entry_cffile->folder) {
+			case iFoldCONTINUED_PREV_AND_NEXT:
+			case iFoldCONTINUED_TO_NEXT:
+				break;
+			case iFoldCONTINUED_FROM_PREV:
+			default:
+				goto invalid;
+			}
+		}
+		/* If CFDATA is not last in a folder, an uncompressed
+		 * size must be 0x8000(32KBi) */
+		if ((cab->entry_cffolder->cfdata_index <
+		     cab->entry_cffolder->cfdata_count) &&
+		       cfdata->uncompressed_size != 0x8000)
+			goto invalid;
+
+		/* A compressed data size and an uncompressed data size must
+		 * be the same in no compression mode. */
+		if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&
+		    cfdata->compressed_size != cfdata->uncompressed_size)
+			goto invalid;
+
+		/*
+		 * Save CFDATA image for sum check.
+		 */
+		if (cfdata->memimage_size < (size_t)l) {
+			free(cfdata->memimage);
+			cfdata->memimage = malloc(l);
+			if (cfdata->memimage == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for CAB data");
+				return (ARCHIVE_FATAL);
+			}
+			cfdata->memimage_size = l;
+		}
+		memcpy(cfdata->memimage, p, l);
+
+		/* Consume bytes as much as we used. */
+		__archive_read_consume(a, l);
+		cab->cab_offset += l;
+	} else if (cab->entry_cffolder->cfdata_count > 0) {
+		/* Run out of all CFDATA in a folder. */
+		cfdata->compressed_size = 0;
+		cfdata->uncompressed_size = 0;
+		cfdata->compressed_bytes_remaining = 0;
+		cfdata->uncompressed_bytes_remaining = 0;
+	} else {
+		/* Current folder does not have any CFDATA. */
+		cfdata = &(cab->entry_cffolder->cfdata);
+		cab->entry_cfdata = cfdata;
+		memset(cfdata, 0, sizeof(*cfdata));
+	}
+	return (ARCHIVE_OK);
+invalid:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Invalid CFDATA");
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Read ahead CFDATA.
+ */
+static const void *
+cab_read_ahead_cfdata(struct archive_read *a, ssize_t *avail)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	int err;
+
+	err = cab_next_cfdata(a);
+	if (err < ARCHIVE_OK) {
+		*avail = err;
+		return (NULL);
+	}
+
+	switch (cab->entry_cffolder->comptype) {
+	case COMPTYPE_NONE:
+		return (cab_read_ahead_cfdata_none(a, avail));
+	case COMPTYPE_MSZIP:
+		return (cab_read_ahead_cfdata_deflate(a, avail));
+	case COMPTYPE_LZX:
+		return (cab_read_ahead_cfdata_lzx(a, avail));
+	default: /* Unsupported compression. */
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unsupported CAB compression : %s",
+		    cab->entry_cffolder->compname);
+		*avail = ARCHIVE_FAILED;
+		return (NULL);
+	}
+}
+
+/*
+ * Read ahead CFDATA as uncompressed data.
+ */
+static const void *
+cab_read_ahead_cfdata_none(struct archive_read *a, ssize_t *avail)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	struct cfdata *cfdata;
+	const void *d;
+
+	cfdata = cab->entry_cfdata;
+
+	/*
+	 * Note: '1' here is a performance optimization.
+	 * Recall that the decompression layer returns a count of
+	 * available bytes; asking for more than that forces the
+	 * decompressor to combine reads by copying data.
+	 */
+	d = __archive_read_ahead(a, 1, avail);
+	if (*avail <= 0) {
+		*avail = truncated_error(a);
+		return (NULL);
+	}
+	if (*avail > cfdata->uncompressed_bytes_remaining)
+		*avail = cfdata->uncompressed_bytes_remaining;
+	cfdata->uncompressed_avail = cfdata->uncompressed_size;
+	cfdata->unconsumed = *avail;
+	cfdata->sum_ptr = d;
+	return (d);
+}
+
+/*
+ * Read ahead CFDATA as deflate data.
+ */
+#ifdef HAVE_ZLIB_H
+static const void *
+cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	struct cfdata *cfdata;
+	const void *d;
+	int r, mszip;
+	uint16_t uavail;
+	char eod = 0;
+
+	cfdata = cab->entry_cfdata;
+	/* If the buffer hasn't been allocated, allocate it now. */
+	if (cab->uncompressed_buffer == NULL) {
+		cab->uncompressed_buffer_size = 0x8000;
+		cab->uncompressed_buffer
+		    = (unsigned char *)malloc(cab->uncompressed_buffer_size);
+		if (cab->uncompressed_buffer == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "No memory for CAB reader");
+			*avail = ARCHIVE_FATAL;
+			return (NULL);
+		}
+	}
+
+	uavail = cfdata->uncompressed_avail;
+	if (uavail == cfdata->uncompressed_size) {
+		d = cab->uncompressed_buffer + cfdata->read_offset;
+		*avail = uavail - cfdata->read_offset;
+		return (d);
+	}
+
+	if (!cab->entry_cffolder->decompress_init) {
+		cab->stream.next_in = NULL;
+		cab->stream.avail_in = 0;
+		cab->stream.total_in = 0;
+		cab->stream.next_out = NULL;
+		cab->stream.avail_out = 0;
+		cab->stream.total_out = 0;
+		if (cab->stream_valid)
+			r = inflateReset(&cab->stream);
+		else
+			r = inflateInit2(&cab->stream,
+			    -15 /* Don't check for zlib header */);
+		if (r != Z_OK) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Can't initialize deflate decompression.");
+			*avail = ARCHIVE_FATAL;
+			return (NULL);
+		}
+		/* Stream structure has been set up. */
+		cab->stream_valid = 1;
+		/* We've initialized decompression for this stream. */
+		cab->entry_cffolder->decompress_init = 1;
+	}
+
+	if (cfdata->compressed_bytes_remaining == cfdata->compressed_size)
+		mszip = 2;
+	else
+		mszip = 0;
+	eod = 0;
+	cab->stream.total_out = uavail;
+	/*
+	 * We always uncompress all data in current CFDATA.
+	 */
+	while (!eod && cab->stream.total_out < cfdata->uncompressed_size) {
+		ssize_t bytes_avail;
+
+		cab->stream.next_out =
+		    cab->uncompressed_buffer + cab->stream.total_out;
+		cab->stream.avail_out =
+		    cfdata->uncompressed_size - cab->stream.total_out;
+
+		d = __archive_read_ahead(a, 1, &bytes_avail);
+		if (bytes_avail <= 0) {
+			*avail = truncated_error(a);
+			return (NULL);
+		}
+		if (bytes_avail > cfdata->compressed_bytes_remaining)
+			bytes_avail = cfdata->compressed_bytes_remaining;
+		/*
+		 * 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'.
+		 */
+		cab->stream.next_in = (Bytef *)(uintptr_t)d;
+		cab->stream.avail_in = (uInt)bytes_avail;
+		cab->stream.total_in = 0;
+
+		/* Cut out a tow-byte MSZIP signature(0x43, 0x4b). */
+		if (mszip > 0) {
+			if (bytes_avail <= 0)
+				goto nomszip;
+			if (bytes_avail <= mszip) {
+				if (mszip == 2) {
+					if (cab->stream.next_in[0] != 0x43)
+						goto nomszip;
+					if (bytes_avail > 1 &&
+					    cab->stream.next_in[1] != 0x4b)
+						goto nomszip;
+				} else if (cab->stream.next_in[0] != 0x4b)
+					goto nomszip;
+				cfdata->unconsumed = bytes_avail;
+				cfdata->sum_ptr = d;
+				if (cab_minimum_consume_cfdata(
+				    a, cfdata->unconsumed) < 0) {
+					*avail = ARCHIVE_FATAL;
+					return (NULL);
+				}
+				mszip -= (int)bytes_avail;
+				continue;
+			}
+			if (mszip == 1 && cab->stream.next_in[0] != 0x4b)
+				goto nomszip;
+			else if (mszip == 2 && (cab->stream.next_in[0] != 0x43 ||
+			    cab->stream.next_in[1] != 0x4b))
+				goto nomszip;
+			cab->stream.next_in += mszip;
+			cab->stream.avail_in -= mszip;
+			cab->stream.total_in += mszip;
+			mszip = 0;
+		}
+
+		r = inflate(&cab->stream, 0);
+		switch (r) {
+		case Z_OK:
+			break;
+		case Z_STREAM_END:
+			eod = 1;
+			break;
+		default:
+			goto zlibfailed;
+		}
+		cfdata->unconsumed = cab->stream.total_in;
+		cfdata->sum_ptr = d;
+		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+			*avail = ARCHIVE_FATAL;
+			return (NULL);
+		}
+	}
+	uavail = (uint16_t)cab->stream.total_out;
+
+	if (uavail < cfdata->uncompressed_size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid uncompressed size (%d < %d)",
+		    uavail, cfdata->uncompressed_size);
+		*avail = ARCHIVE_FATAL;
+		return (NULL);
+	}
+
+	/*
+	 * Note: I suspect there is a bug in makecab.exe because, in rare
+	 * case, compressed bytes are still remaining regardless we have
+	 * gotten all uncompressed bytes, which size is recorded in CFDATA,
+	 * as much as we need, and we have to use the garbage so as to
+	 * correctly compute the sum of CFDATA accordingly.
+	 */
+	if (cfdata->compressed_bytes_remaining > 0) {
+		ssize_t bytes_avail;
+
+		d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,
+		    &bytes_avail);
+		if (bytes_avail <= 0) {
+			*avail = truncated_error(a);
+			return (NULL);
+		}
+		cfdata->unconsumed = cfdata->compressed_bytes_remaining;
+		cfdata->sum_ptr = d;
+		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+			*avail = ARCHIVE_FATAL;
+			return (NULL);
+		}
+	}
+
+	/*
+	 * Set dictionary data for decompressing of next CFDATA, which
+	 * in the same folder. This is why we always do decompress CFDATA
+	 * even if beginning CFDATA or some of CFDATA are not used in
+	 * skipping file data.
+	 */
+	if (cab->entry_cffolder->cfdata_index <
+	    cab->entry_cffolder->cfdata_count) {
+		r = inflateReset(&cab->stream);
+		if (r != Z_OK)
+			goto zlibfailed;
+		r = inflateSetDictionary(&cab->stream,
+		    cab->uncompressed_buffer, cfdata->uncompressed_size);
+		if (r != Z_OK)
+			goto zlibfailed;
+	}
+
+	d = cab->uncompressed_buffer + cfdata->read_offset;
+	*avail = uavail - cfdata->read_offset;
+	cfdata->uncompressed_avail = uavail;
+
+	return (d);
+
+zlibfailed:
+	switch (r) {
+	case Z_MEM_ERROR:
+		archive_set_error(&a->archive, ENOMEM,
+		    "Out of memory for deflate decompression");
+		break;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Deflate decompression failed (%d)", r);
+		break;
+	}
+	*avail = ARCHIVE_FATAL;
+	return (NULL);
+nomszip:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "CFDATA incorrect(no MSZIP signature)");
+	*avail = ARCHIVE_FATAL;
+	return (NULL);
+}
+
+#else /* HAVE_ZLIB_H */
+
+static const void *
+cab_read_ahead_cfdata_deflate(struct archive_read *a, ssize_t *avail)
+{
+	*avail = ARCHIVE_FATAL;
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "libarchive compiled without deflate support (no libz)");
+	return (NULL);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static const void *
+cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	struct cfdata *cfdata;
+	const void *d;
+	int r;
+	uint16_t uavail;
+
+	cfdata = cab->entry_cfdata;
+	/* If the buffer hasn't been allocated, allocate it now. */
+	if (cab->uncompressed_buffer == NULL) {
+		cab->uncompressed_buffer_size = 0x8000;
+		cab->uncompressed_buffer
+		    = (unsigned char *)malloc(cab->uncompressed_buffer_size);
+		if (cab->uncompressed_buffer == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "No memory for CAB reader");
+			*avail = ARCHIVE_FATAL;
+			return (NULL);
+		}
+	}
+
+	uavail = cfdata->uncompressed_avail;
+	if (uavail == cfdata->uncompressed_size) {
+		d = cab->uncompressed_buffer + cfdata->read_offset;
+		*avail = uavail - cfdata->read_offset;
+		return (d);
+	}
+
+	if (!cab->entry_cffolder->decompress_init) {
+		r = lzx_decode_init(&cab->xstrm,
+		    cab->entry_cffolder->compdata);
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Can't initialize LZX decompression.");
+			*avail = ARCHIVE_FATAL;
+			return (NULL);
+		}
+		/* We've initialized decompression for this stream. */
+		cab->entry_cffolder->decompress_init = 1;
+	}
+
+	/* Clean up remaining bits of previous CFDATA. */
+	lzx_cleanup_bitstream(&cab->xstrm);
+	cab->xstrm.total_out = uavail;
+	while (cab->xstrm.total_out < cfdata->uncompressed_size) {
+		ssize_t bytes_avail;
+
+		cab->xstrm.next_out =
+		    cab->uncompressed_buffer + cab->xstrm.total_out;
+		cab->xstrm.avail_out =
+		    cfdata->uncompressed_size - cab->xstrm.total_out;
+
+		d = __archive_read_ahead(a, 1, &bytes_avail);
+		if (bytes_avail <= 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated CAB file data");
+			*avail = ARCHIVE_FATAL;
+			return (NULL);
+		}
+		if (bytes_avail > cfdata->compressed_bytes_remaining)
+			bytes_avail = cfdata->compressed_bytes_remaining;
+
+		cab->xstrm.next_in = d;
+		cab->xstrm.avail_in = bytes_avail;
+		cab->xstrm.total_in = 0;
+		r = lzx_decode(&cab->xstrm,
+		    cfdata->compressed_bytes_remaining == bytes_avail);
+		switch (r) {
+		case ARCHIVE_OK:
+		case ARCHIVE_EOF:
+			break;
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "LZX decompression failed (%d)", r);
+			*avail = ARCHIVE_FATAL;
+			return (NULL);
+		}
+		cfdata->unconsumed = cab->xstrm.total_in;
+		cfdata->sum_ptr = d;
+		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+			*avail = ARCHIVE_FATAL;
+			return (NULL);
+		}
+	}
+
+	uavail = (uint16_t)cab->xstrm.total_out;
+	/*
+	 * Make sure a read pointer advances to next CFDATA.
+	 */
+	if (cfdata->compressed_bytes_remaining > 0) {
+		ssize_t bytes_avail;
+
+		d = __archive_read_ahead(a, cfdata->compressed_bytes_remaining,
+		    &bytes_avail);
+		if (bytes_avail <= 0) {
+			*avail = truncated_error(a);
+			return (NULL);
+		}
+		cfdata->unconsumed = cfdata->compressed_bytes_remaining;
+		cfdata->sum_ptr = d;
+		if (cab_minimum_consume_cfdata(a, cfdata->unconsumed) < 0) {
+			*avail = ARCHIVE_FATAL;
+			return (NULL);
+		}
+	}
+
+	/*
+	 * Translation reversal of x86 processor CALL byte sequence(E8).
+	 */
+	lzx_translation(&cab->xstrm, cab->uncompressed_buffer,
+	    cfdata->uncompressed_size,
+	    (cab->entry_cffolder->cfdata_index-1) * 0x8000);
+
+	d = cab->uncompressed_buffer + cfdata->read_offset;
+	*avail = uavail - cfdata->read_offset;
+	cfdata->uncompressed_avail = uavail;
+
+	return (d);
+}
+
+/*
+ * Consume CFDATA.
+ * We always decompress CFDATA to consume CFDATA as much as we need
+ * in uncompressed bytes because all CFDATA in a folder are related
+ * so we do not skip any CFDATA without decompressing.
+ * Note: If the folder of a CFFILE is iFoldCONTINUED_PREV_AND_NEXT or
+ * iFoldCONTINUED_FROM_PREV, we won't decompress because a CFDATA for
+ * the CFFILE is remaining bytes of previous Multivolume CAB file.
+ */
+static int64_t
+cab_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	struct cfdata *cfdata;
+	int64_t cbytes, rbytes;
+	int err;
+
+	rbytes = cab_minimum_consume_cfdata(a, consumed_bytes);
+	if (rbytes < 0)
+		return (ARCHIVE_FATAL);
+
+	cfdata = cab->entry_cfdata;
+	while (rbytes > 0) {
+		ssize_t avail;
+
+		if (cfdata->compressed_size == 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Invalid CFDATA");
+			return (ARCHIVE_FATAL);
+		}
+		cbytes = cfdata->uncompressed_bytes_remaining;
+		if (cbytes > rbytes)
+			cbytes = rbytes;
+		rbytes -= cbytes;
+
+		if (cfdata->uncompressed_avail == 0 &&
+		   (cab->entry_cffile->folder == iFoldCONTINUED_PREV_AND_NEXT ||
+		    cab->entry_cffile->folder == iFoldCONTINUED_FROM_PREV)) {
+			/* We have not read any data yet. */
+			if (cbytes == cfdata->uncompressed_bytes_remaining) {
+				/* Skip whole current CFDATA. */
+				__archive_read_consume(a,
+				    cfdata->compressed_size);
+				cab->cab_offset += cfdata->compressed_size;
+				cfdata->compressed_bytes_remaining = 0;
+				cfdata->uncompressed_bytes_remaining = 0;
+				err = cab_next_cfdata(a);
+				if (err < 0)
+					return (err);
+				cfdata = cab->entry_cfdata;
+				if (cfdata->uncompressed_size == 0) {
+					switch (cab->entry_cffile->folder) {
+					case iFoldCONTINUED_PREV_AND_NEXT:
+					case iFoldCONTINUED_TO_NEXT:
+					case iFoldCONTINUED_FROM_PREV:
+						rbytes = 0;
+						break;
+					default:
+						break;
+					}
+				}
+				continue;
+			}
+			cfdata->read_offset += (uint16_t)cbytes;
+			cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
+			break;
+		} else if (cbytes == 0) {
+			err = cab_next_cfdata(a);
+			if (err < 0)
+				return (err);
+			cfdata = cab->entry_cfdata;
+			if (cfdata->uncompressed_size == 0) {
+				switch (cab->entry_cffile->folder) {
+				case iFoldCONTINUED_PREV_AND_NEXT:
+				case iFoldCONTINUED_TO_NEXT:
+				case iFoldCONTINUED_FROM_PREV:
+					return (ARCHIVE_FATAL);
+				default:
+					break;
+				}
+			}
+			continue;
+		}
+		while (cbytes > 0) {
+			(void)cab_read_ahead_cfdata(a, &avail);
+			if (avail <= 0)
+				return (ARCHIVE_FATAL);
+			if (avail > cbytes)
+				avail = (ssize_t)cbytes;
+			if (cab_minimum_consume_cfdata(a, avail) < 0)
+				return (ARCHIVE_FATAL);
+			cbytes -= avail;
+		}
+	}
+	return (consumed_bytes);
+}
+
+/*
+ * Consume CFDATA as much as we have already gotten and
+ * compute the sum of CFDATA.
+ */
+static int64_t
+cab_minimum_consume_cfdata(struct archive_read *a, int64_t consumed_bytes)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	struct cfdata *cfdata;
+	int64_t cbytes, rbytes;
+	int err;
+
+	cfdata = cab->entry_cfdata;
+	rbytes = consumed_bytes;
+	if (cab->entry_cffolder->comptype == COMPTYPE_NONE) {
+		if (consumed_bytes < cfdata->unconsumed)
+			cbytes = consumed_bytes;
+		else
+			cbytes = cfdata->unconsumed;
+		rbytes -= cbytes; 
+		cfdata->read_offset += (uint16_t)cbytes;
+		cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
+		cfdata->unconsumed -= cbytes;
+	} else {
+		cbytes = cfdata->uncompressed_avail - cfdata->read_offset;
+		if (cbytes > 0) {
+			if (consumed_bytes < cbytes)
+				cbytes = consumed_bytes;
+			rbytes -= cbytes;
+			cfdata->read_offset += (uint16_t)cbytes;
+			cfdata->uncompressed_bytes_remaining -= (uint16_t)cbytes;
+		}
+
+		if (cfdata->unconsumed) {
+			cbytes = cfdata->unconsumed;
+			cfdata->unconsumed = 0;
+		} else
+			cbytes = 0;
+	}
+	if (cbytes) {
+		/* Compute the sum. */
+		cab_checksum_update(a, (size_t)cbytes);
+
+		/* Consume as much as the compressor actually used. */
+		__archive_read_consume(a, cbytes);
+		cab->cab_offset += cbytes;
+		cfdata->compressed_bytes_remaining -= (uint16_t)cbytes;
+		if (cfdata->compressed_bytes_remaining == 0) {
+			err = cab_checksum_finish(a);
+			if (err < 0)
+				return (err);
+		}
+	}
+	return (rbytes);
+}
+
+/*
+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
+ * cab->end_of_entry if it consumes all of the data.
+ */
+static int
+cab_read_data(struct archive_read *a, const void **buff,
+    size_t *size, int64_t *offset)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	ssize_t bytes_avail;
+
+	if (cab->entry_bytes_remaining == 0) {
+		*buff = NULL;
+		*size = 0;
+		*offset = cab->entry_offset;
+		cab->end_of_entry = 1;
+		return (ARCHIVE_OK);
+	}
+
+	*buff = cab_read_ahead_cfdata(a, &bytes_avail);
+	if (bytes_avail <= 0) {
+		*buff = NULL;
+		*size = 0;
+		*offset = 0;
+		if (bytes_avail == 0 &&
+		    cab->entry_cfdata->uncompressed_size == 0) {
+			/* All of CFDATA in a folder has been handled. */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT, "Invalid CFDATA");
+			return (ARCHIVE_FATAL);
+		} else
+			return ((int)bytes_avail);
+	}
+	if (bytes_avail > cab->entry_bytes_remaining)
+		bytes_avail = (ssize_t)cab->entry_bytes_remaining;
+
+	*size = bytes_avail;
+	*offset = cab->entry_offset;
+	cab->entry_offset += bytes_avail;
+	cab->entry_bytes_remaining -= bytes_avail;
+	if (cab->entry_bytes_remaining == 0)
+		cab->end_of_entry = 1;
+	cab->entry_unconsumed = bytes_avail;
+	if (cab->entry_cffolder->comptype == COMPTYPE_NONE) {
+		/* Don't consume more than current entry used. */
+		if (cab->entry_cfdata->unconsumed > cab->entry_unconsumed)
+			cab->entry_cfdata->unconsumed = cab->entry_unconsumed;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cab_read_data_skip(struct archive_read *a)
+{
+	struct cab *cab;
+	int64_t bytes_skipped;
+	int r;
+
+	cab = (struct cab *)(a->format->data);
+
+	if (cab->end_of_archive)
+		return (ARCHIVE_EOF);
+
+	if (!cab->read_data_invoked) {
+		cab->bytes_skipped += cab->entry_bytes_remaining;
+		cab->entry_bytes_remaining = 0;
+		/* This entry is finished and done. */
+		cab->end_of_entry_cleanup = cab->end_of_entry = 1;
+		return (ARCHIVE_OK);
+	}
+
+	if (cab->entry_unconsumed) {
+		/* Consume as much as the compressor actually used. */
+		r = (int)cab_consume_cfdata(a, cab->entry_unconsumed);
+		cab->entry_unconsumed = 0;
+		if (r < 0)
+			return (r);
+	} else if (cab->entry_cfdata == NULL) {
+		r = cab_next_cfdata(a);
+		if (r < 0)
+			return (r);
+	}
+
+	/* if we've already read to end of data, we're done. */
+	if (cab->end_of_entry_cleanup)
+		return (ARCHIVE_OK);
+
+	/*
+	 * If the length is at the beginning, we can skip the
+	 * compressed data much more quickly.
+	 */
+	bytes_skipped = cab_consume_cfdata(a, cab->entry_bytes_remaining);
+	if (bytes_skipped < 0)
+		return (ARCHIVE_FATAL);
+
+	/* If the compression type is none(uncompressed), we've already
+	 * consumed data as much as the current entry size. */
+	if (cab->entry_cffolder->comptype == COMPTYPE_NONE &&
+	    cab->entry_cfdata != NULL)
+		cab->entry_cfdata->unconsumed = 0;
+
+	/* This entry is finished and done. */
+	cab->end_of_entry_cleanup = cab->end_of_entry = 1;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cab_cleanup(struct archive_read *a)
+{
+	struct cab *cab = (struct cab *)(a->format->data);
+	struct cfheader *hd = &cab->cfheader;
+	int i;
+
+	if (hd->folder_array != NULL) {
+		for (i = 0; i < hd->folder_count; i++)
+			free(hd->folder_array[i].cfdata.memimage);
+		free(hd->folder_array);
+	}
+	if (hd->file_array != NULL) {
+		for (i = 0; i < cab->cfheader.file_count; i++)
+			archive_string_free(&(hd->file_array[i].pathname));
+		free(hd->file_array);
+	}
+#ifdef HAVE_ZLIB_H
+	if (cab->stream_valid)
+		inflateEnd(&cab->stream);
+#endif
+	lzx_decode_free(&cab->xstrm);
+	archive_wstring_free(&cab->ws);
+	free(cab->uncompressed_buffer);
+	free(cab);
+	(a->format->data) = NULL;
+	return (ARCHIVE_OK);
+}
+
+/* Convert an MSDOS-style date/time into Unix-style time. */
+static time_t
+cab_dos_time(const unsigned char *p)
+{
+	int msTime, msDate;
+	struct tm ts;
+
+	msDate = archive_le16dec(p);
+	msTime = archive_le16dec(p+2);
+
+	memset(&ts, 0, sizeof(ts));
+	ts.tm_year = ((msDate >> 9) & 0x7f) + 80;   /* Years since 1900. */
+	ts.tm_mon = ((msDate >> 5) & 0x0f) - 1;     /* Month number.     */
+	ts.tm_mday = msDate & 0x1f;		    /* Day of month.     */
+	ts.tm_hour = (msTime >> 11) & 0x1f;
+	ts.tm_min = (msTime >> 5) & 0x3f;
+	ts.tm_sec = (msTime << 1) & 0x3e;
+	ts.tm_isdst = -1;
+	return (mktime(&ts));
+}
+
+/*****************************************************************
+ *
+ * LZX decompression code.
+ *
+ *****************************************************************/
+
+/*
+ * Initialize LZX decoder.
+ *
+ * Returns ARCHIVE_OK if initialization was successful.
+ * Returns ARCHIVE_FAILED if w_bits has unsupported value.
+ * Returns ARCHIVE_FATAL if initialization failed; memory allocation
+ * error occurred.
+ */
+static int
+lzx_decode_init(struct lzx_stream *strm, int w_bits)
+{
+	struct lzx_dec *ds;
+	int slot, w_size, w_slot;
+	int base, footer;
+	int base_inc[18];
+
+	if (strm->ds == NULL) {
+		strm->ds = calloc(1, sizeof(*strm->ds));
+		if (strm->ds == NULL)
+			return (ARCHIVE_FATAL);
+	}
+	ds = strm->ds;
+	ds->error = ARCHIVE_FAILED;
+
+	/* Allow bits from 15(32KBi) up to 21(2MBi) */
+	if (w_bits < SLOT_BASE || w_bits > SLOT_MAX)
+		return (ARCHIVE_FAILED);
+
+	ds->error = ARCHIVE_FATAL;
+
+	/*
+	 * Alloc window
+	 */
+	w_size = ds->w_size;
+	w_slot = slots[w_bits - SLOT_BASE];
+	ds->w_size = 1U << w_bits;
+	ds->w_mask = ds->w_size -1;
+	if (ds->w_buff == NULL || w_size != ds->w_size) {
+		free(ds->w_buff);
+		ds->w_buff = malloc(ds->w_size);
+		if (ds->w_buff == NULL)
+			return (ARCHIVE_FATAL);
+		free(ds->pos_tbl);
+		ds->pos_tbl = malloc(sizeof(ds->pos_tbl[0]) * w_slot);
+		if (ds->pos_tbl == NULL)
+			return (ARCHIVE_FATAL);
+		lzx_huffman_free(&(ds->mt));
+	}
+
+	for (footer = 0; footer < 18; footer++)
+		base_inc[footer] = 1 << footer;
+	base = footer = 0;
+	for (slot = 0; slot < w_slot; slot++) {
+		int n;
+		if (footer == 0)
+			base = slot;
+		else
+			base += base_inc[footer];
+		if (footer < 17) {
+			footer = -2;
+			for (n = base; n; n >>= 1)
+				footer++;
+			if (footer <= 0)
+				footer = 0;
+		}
+		ds->pos_tbl[slot].base = base;
+		ds->pos_tbl[slot].footer_bits = footer;
+	}
+
+	ds->w_pos = 0;
+	ds->state = 0;
+	ds->br.cache_buffer = 0;
+	ds->br.cache_avail = 0;
+	ds->r0 = ds->r1 = ds->r2 = 1;
+
+	/* Initialize aligned offset tree. */
+	if (lzx_huffman_init(&(ds->at), 8, 8) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Initialize pre-tree. */
+	if (lzx_huffman_init(&(ds->pt), 20, 10) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Initialize Main tree. */
+	if (lzx_huffman_init(&(ds->mt), 256+(w_slot<<3), 16)
+	    != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Initialize Length tree. */
+	if (lzx_huffman_init(&(ds->lt), 249, 16) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	ds->error = 0;
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Release LZX decoder.
+ */
+static void
+lzx_decode_free(struct lzx_stream *strm)
+{
+
+	if (strm->ds == NULL)
+		return;
+	free(strm->ds->w_buff);
+	free(strm->ds->pos_tbl);
+	lzx_huffman_free(&(strm->ds->at));
+	lzx_huffman_free(&(strm->ds->pt));
+	lzx_huffman_free(&(strm->ds->mt));
+	lzx_huffman_free(&(strm->ds->lt));
+	free(strm->ds);
+	strm->ds = NULL;
+}
+
+/*
+ * E8 Call Translation reversal.
+ */
+static void
+lzx_translation(struct lzx_stream *strm, void *p, size_t size, uint32_t offset)
+{
+	struct lzx_dec *ds = strm->ds;
+	unsigned char *b, *end;
+
+	if (!ds->translation || size <= 10)
+		return;
+	b = p;
+	end = b + size - 10;
+	while (b < end && (b = memchr(b, 0xE8, end - b)) != NULL) {
+		size_t i = b - (unsigned char *)p;
+		int32_t cp, displacement, value;
+
+		cp = (int32_t)(offset + (uint32_t)i);
+		value = archive_le32dec(&b[1]);
+		if (value >= -cp && value < (int32_t)ds->translation_size) {
+			if (value >= 0)
+				displacement = value - cp;
+			else
+				displacement = value + ds->translation_size;
+			archive_le32enc(&b[1], (uint32_t)displacement);
+		}
+		b += 5;
+	}
+}
+
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define lzx_br_has(br, n)	((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define lzx_br_bits(br, n)				\
+	(((uint32_t)((br)->cache_buffer >>		\
+		((br)->cache_avail - (n)))) & cache_masks[n])
+#define lzx_br_bits_forced(br, n)			\
+	(((uint32_t)((br)->cache_buffer <<		\
+		((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ *  True  : completed, there is enough data in the cache buffer.
+ *  False : we met that strm->next_in is empty, we have to get following
+ *          bytes. */
+#define lzx_br_read_ahead_0(strm, br, n)	\
+	(lzx_br_has((br), (n)) || lzx_br_fillup(strm, br))
+/*  True  : the cache buffer has some bits as much as we need.
+ *  False : there are no enough bits in the cache buffer to be used,
+ *          we have to get following bytes if we could. */
+#define lzx_br_read_ahead(strm, br, n)	\
+	(lzx_br_read_ahead_0((strm), (br), (n)) || lzx_br_has((br), (n)))
+
+/* Notify how many bits we consumed. */
+#define lzx_br_consume(br, n)	((br)->cache_avail -= (n))
+#define lzx_br_consume_unaligned_bits(br) ((br)->cache_avail &= ~0x0f)
+
+#define lzx_br_is_unaligned(br)	((br)->cache_avail & 0x0f)
+
+static const uint32_t cache_masks[] = {
+	0x00000000, 0x00000001, 0x00000003, 0x00000007,
+	0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
+	0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
+	0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
+	0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
+	0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
+	0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
+	0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
+	0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+lzx_br_fillup(struct lzx_stream *strm, struct lzx_br *br)
+{
+/*
+ * x86 processor family can read misaligned data without an access error.
+ */
+	int n = CACHE_BITS - br->cache_avail;
+
+	for (;;) {
+		switch (n >> 4) {
+		case 4:
+			if (strm->avail_in >= 8) {
+				br->cache_buffer =
+				    ((uint64_t)strm->next_in[1]) << 56 |
+				    ((uint64_t)strm->next_in[0]) << 48 |
+				    ((uint64_t)strm->next_in[3]) << 40 |
+				    ((uint64_t)strm->next_in[2]) << 32 |
+				    ((uint32_t)strm->next_in[5]) << 24 |
+				    ((uint32_t)strm->next_in[4]) << 16 |
+				    ((uint32_t)strm->next_in[7]) << 8 |
+				     (uint32_t)strm->next_in[6];
+				strm->next_in += 8;
+				strm->avail_in -= 8;
+				br->cache_avail += 8 * 8;
+				return (1);
+			}
+			break;
+		case 3:
+			if (strm->avail_in >= 6) {
+				br->cache_buffer =
+		 		   (br->cache_buffer << 48) |
+				    ((uint64_t)strm->next_in[1]) << 40 |
+				    ((uint64_t)strm->next_in[0]) << 32 |
+				    ((uint32_t)strm->next_in[3]) << 24 |
+				    ((uint32_t)strm->next_in[2]) << 16 |
+				    ((uint32_t)strm->next_in[5]) << 8 |
+				     (uint32_t)strm->next_in[4];
+				strm->next_in += 6;
+				strm->avail_in -= 6;
+				br->cache_avail += 6 * 8;
+				return (1);
+			}
+			break;
+		case 0:
+			/* We have enough compressed data in
+			 * the cache buffer.*/
+			return (1);
+		default:
+			break;
+		}
+		if (strm->avail_in < 2) {
+			/* There is not enough compressed data to
+			 * fill up the cache buffer. */
+			if (strm->avail_in == 1) {
+				br->odd = *strm->next_in++;
+				strm->avail_in--;
+				br->have_odd = 1;
+			}
+			return (0);
+		}
+		br->cache_buffer =
+		   (br->cache_buffer << 16) |
+		    archive_le16dec(strm->next_in);
+		strm->next_in += 2;
+		strm->avail_in -= 2;
+		br->cache_avail += 16;
+		n -= 16;
+	}
+}
+
+static void
+lzx_br_fixup(struct lzx_stream *strm, struct lzx_br *br)
+{
+	int n = CACHE_BITS - br->cache_avail;
+
+	if (br->have_odd && n >= 16 && strm->avail_in > 0) {
+		br->cache_buffer =
+		   (br->cache_buffer << 16) |
+		   ((uint16_t)(*strm->next_in)) << 8 | br->odd;
+		strm->next_in++;
+		strm->avail_in--;
+		br->cache_avail += 16;
+		br->have_odd = 0;
+	}
+}
+
+static void
+lzx_cleanup_bitstream(struct lzx_stream *strm)
+{
+	strm->ds->br.cache_avail = 0;
+	strm->ds->br.have_odd = 0;
+}
+
+/*
+ * Decode LZX.
+ *
+ * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.
+ *    Please set available buffer and call this function again.
+ * 2. Returns ARCHIVE_EOF if decompression has been completed.
+ * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
+ *    is broken or you do not set 'last' flag properly.
+ */
+#define ST_RD_TRANSLATION	0
+#define ST_RD_TRANSLATION_SIZE	1
+#define ST_RD_BLOCK_TYPE	2
+#define ST_RD_BLOCK_SIZE	3
+#define ST_RD_ALIGNMENT		4
+#define ST_RD_R0		5
+#define ST_RD_R1		6
+#define ST_RD_R2		7
+#define ST_COPY_UNCOMP1		8
+#define ST_COPY_UNCOMP2		9
+#define ST_RD_ALIGNED_OFFSET	10
+#define ST_RD_VERBATIM		11
+#define ST_RD_PRE_MAIN_TREE_256	12
+#define ST_MAIN_TREE_256	13
+#define ST_RD_PRE_MAIN_TREE_REM	14
+#define ST_MAIN_TREE_REM	15
+#define ST_RD_PRE_LENGTH_TREE	16
+#define ST_LENGTH_TREE		17
+#define ST_MAIN			18
+#define ST_LENGTH		19
+#define ST_OFFSET		20
+#define ST_REAL_POS		21
+#define ST_COPY			22
+
+static int
+lzx_decode(struct lzx_stream *strm, int last)
+{
+	struct lzx_dec *ds = strm->ds;
+	int64_t avail_in;
+	int r;
+
+	if (ds->error)
+		return (ds->error);
+
+	avail_in = strm->avail_in;
+	lzx_br_fixup(strm, &(ds->br));
+	do {
+		if (ds->state < ST_MAIN)
+			r = lzx_read_blocks(strm, last);
+		else {
+			int64_t bytes_written = strm->avail_out;
+			r = lzx_decode_blocks(strm, last);
+			bytes_written -= strm->avail_out;
+			strm->next_out += bytes_written;
+			strm->total_out += bytes_written;
+		}
+	} while (r == 100);
+	strm->total_in += avail_in - strm->avail_in;
+	return (r);
+}
+
+static int
+lzx_read_blocks(struct lzx_stream *strm, int last)
+{
+	struct lzx_dec *ds = strm->ds;
+	struct lzx_br *br = &(ds->br);
+	int i, r;
+
+	for (;;) {
+		switch (ds->state) {
+		case ST_RD_TRANSLATION:
+			if (!lzx_br_read_ahead(strm, br, 1)) {
+				ds->state = ST_RD_TRANSLATION;
+				if (last)
+					goto failed;
+				return (ARCHIVE_OK);
+			}
+			ds->translation = lzx_br_bits(br, 1);
+			lzx_br_consume(br, 1);
+			/* FALL THROUGH */
+		case ST_RD_TRANSLATION_SIZE:
+			if (ds->translation) {
+				if (!lzx_br_read_ahead(strm, br, 32)) {
+					ds->state = ST_RD_TRANSLATION_SIZE;
+					if (last)
+						goto failed;
+					return (ARCHIVE_OK);
+				}
+				ds->translation_size = lzx_br_bits(br, 16);
+				lzx_br_consume(br, 16);
+				ds->translation_size <<= 16;
+				ds->translation_size |= lzx_br_bits(br, 16);
+				lzx_br_consume(br, 16);
+			}
+			/* FALL THROUGH */
+		case ST_RD_BLOCK_TYPE:
+			if (!lzx_br_read_ahead(strm, br, 3)) {
+				ds->state = ST_RD_BLOCK_TYPE;
+				if (last)
+					goto failed;
+				return (ARCHIVE_OK);
+			}
+			ds->block_type = lzx_br_bits(br, 3);
+			lzx_br_consume(br, 3);
+			/* Check a block type. */
+			switch (ds->block_type) {
+			case VERBATIM_BLOCK:
+			case ALIGNED_OFFSET_BLOCK:
+			case UNCOMPRESSED_BLOCK:
+				break;
+			default:
+				goto failed;/* Invalid */
+			}
+			/* FALL THROUGH */
+		case ST_RD_BLOCK_SIZE:
+			if (!lzx_br_read_ahead(strm, br, 24)) {
+				ds->state = ST_RD_BLOCK_SIZE;
+				if (last)
+					goto failed;
+				return (ARCHIVE_OK);
+			}
+			ds->block_size = lzx_br_bits(br, 8);
+			lzx_br_consume(br, 8);
+			ds->block_size <<= 16;
+			ds->block_size |= lzx_br_bits(br, 16);
+			lzx_br_consume(br, 16);
+			if (ds->block_size == 0)
+				goto failed;
+			ds->block_bytes_avail = ds->block_size;
+			if (ds->block_type != UNCOMPRESSED_BLOCK) {
+				if (ds->block_type == VERBATIM_BLOCK)
+					ds->state = ST_RD_VERBATIM;
+				else
+					ds->state = ST_RD_ALIGNED_OFFSET;
+				break;
+			}
+			/* FALL THROUGH */
+		case ST_RD_ALIGNMENT:
+			/*
+			 * Handle an Uncompressed Block.
+			 */
+			/* Skip padding to align following field on
+			 * 16-bit boundary. */
+			if (lzx_br_is_unaligned(br))
+				lzx_br_consume_unaligned_bits(br);
+			else {
+				if (lzx_br_read_ahead(strm, br, 16))
+					lzx_br_consume(br, 16);
+				else {
+					ds->state = ST_RD_ALIGNMENT;
+					if (last)
+						goto failed;
+					return (ARCHIVE_OK);
+				}
+			}
+			/* Preparation to read repeated offsets R0,R1 and R2. */
+			ds->rbytes_avail = 0;
+			ds->state = ST_RD_R0;
+			/* FALL THROUGH */
+		case ST_RD_R0:
+		case ST_RD_R1:
+		case ST_RD_R2:
+			do {
+				uint16_t u16;
+				/* Drain bits in the cache buffer of
+				 * bit-stream. */
+				if (lzx_br_has(br, 32)) {
+					u16 = lzx_br_bits(br, 16);
+					lzx_br_consume(br, 16);
+					archive_le16enc(ds->rbytes, u16);
+					u16 = lzx_br_bits(br, 16);
+					lzx_br_consume(br, 16);
+					archive_le16enc(ds->rbytes+2, u16);
+					ds->rbytes_avail = 4;
+				} else if (lzx_br_has(br, 16)) {
+					u16 = lzx_br_bits(br, 16);
+					lzx_br_consume(br, 16);
+					archive_le16enc(ds->rbytes, u16);
+					ds->rbytes_avail = 2;
+				}
+				if (ds->rbytes_avail < 4 && ds->br.have_odd) {
+					ds->rbytes[ds->rbytes_avail++] =
+					    ds->br.odd;
+					ds->br.have_odd = 0;
+				}
+				while (ds->rbytes_avail < 4) {
+					if (strm->avail_in <= 0) {
+						if (last)
+							goto failed;
+						return (ARCHIVE_OK);
+					}
+					ds->rbytes[ds->rbytes_avail++] =
+					    *strm->next_in++;
+					strm->avail_in--;
+				}
+				ds->rbytes_avail = 0;
+				if (ds->state == ST_RD_R0) {
+					ds->r0 = archive_le32dec(ds->rbytes);
+					if (ds->r0 < 0)
+						goto failed;
+					ds->state = ST_RD_R1;
+				} else if (ds->state == ST_RD_R1) {
+					ds->r1 = archive_le32dec(ds->rbytes);
+					if (ds->r1 < 0)
+						goto failed;
+					ds->state = ST_RD_R2;
+				} else if (ds->state == ST_RD_R2) {
+					ds->r2 = archive_le32dec(ds->rbytes);
+					if (ds->r2 < 0)
+						goto failed;
+					/* We've gotten all repeated offsets. */
+					ds->state = ST_COPY_UNCOMP1;
+				}
+			} while (ds->state != ST_COPY_UNCOMP1);
+			/* FALL THROUGH */
+		case ST_COPY_UNCOMP1:
+			/*
+			 * Copy bytes form next_in to next_out directly.
+			 */
+			while (ds->block_bytes_avail) {
+				int l;
+
+				if (strm->avail_out <= 0)
+					/* Output buffer is empty. */
+					return (ARCHIVE_OK);
+				if (strm->avail_in <= 0) {
+					/* Input buffer is empty. */
+					if (last)
+						goto failed;
+					return (ARCHIVE_OK);
+				}
+				l = (int)ds->block_bytes_avail;
+				if (l > ds->w_size - ds->w_pos)
+					l = ds->w_size - ds->w_pos;
+				if (l > strm->avail_out)
+					l = (int)strm->avail_out;
+				if (l > strm->avail_in)
+					l = (int)strm->avail_in;
+				memcpy(strm->next_out, strm->next_in, l);
+				memcpy(&(ds->w_buff[ds->w_pos]),
+				    strm->next_in, l);
+				strm->next_in += l;
+				strm->avail_in -= l;
+				strm->next_out += l;
+				strm->avail_out -= l;
+				strm->total_out += l;
+				ds->w_pos = (ds->w_pos + l) & ds->w_mask;
+				ds->block_bytes_avail -= l;
+			}
+			/* FALL THROUGH */
+		case ST_COPY_UNCOMP2:
+			/* Re-align; skip padding byte. */
+			if (ds->block_size & 1) {
+				if (strm->avail_in <= 0) {
+					/* Input buffer is empty. */
+					ds->state = ST_COPY_UNCOMP2;
+					if (last)
+						goto failed;
+					return (ARCHIVE_OK);
+				}
+				strm->next_in++;
+				strm->avail_in --;
+			}
+			/* This block ended. */
+			ds->state = ST_RD_BLOCK_TYPE;
+			return (ARCHIVE_EOF);
+			/********************/
+		case ST_RD_ALIGNED_OFFSET:
+			/*
+			 * Read Aligned offset tree.
+			 */
+			if (!lzx_br_read_ahead(strm, br, 3 * ds->at.len_size)) {
+				ds->state = ST_RD_ALIGNED_OFFSET;
+				if (last)
+					goto failed;
+				return (ARCHIVE_OK);
+			}
+			memset(ds->at.freq, 0, sizeof(ds->at.freq));
+			for (i = 0; i < ds->at.len_size; i++) {
+				ds->at.bitlen[i] = lzx_br_bits(br, 3);
+				ds->at.freq[ds->at.bitlen[i]]++;
+				lzx_br_consume(br, 3);
+			}
+			if (!lzx_make_huffman_table(&ds->at))
+				goto failed;
+			/* FALL THROUGH */
+		case ST_RD_VERBATIM:
+			ds->loop = 0;
+			/* FALL THROUGH */
+		case ST_RD_PRE_MAIN_TREE_256:
+			/*
+			 * Read Pre-tree for first 256 elements of main tree.
+			 */
+			if (!lzx_read_pre_tree(strm)) {
+				ds->state = ST_RD_PRE_MAIN_TREE_256;
+				if (last)
+					goto failed;
+				return (ARCHIVE_OK);
+			}
+			if (!lzx_make_huffman_table(&ds->pt))
+				goto failed;
+			ds->loop = 0;
+			/* FALL THROUGH */
+		case ST_MAIN_TREE_256:
+			/*
+			 * Get path lengths of first 256 elements of main tree.
+			 */
+			r = lzx_read_bitlen(strm, &ds->mt, 256);
+			if (r < 0)
+				goto failed;
+			else if (!r) {
+				ds->state = ST_MAIN_TREE_256;
+				if (last)
+					goto failed;
+				return (ARCHIVE_OK);
+			}
+			ds->loop = 0;
+			/* FALL THROUGH */
+		case ST_RD_PRE_MAIN_TREE_REM:
+			/*
+			 * Read Pre-tree for remaining elements of main tree.
+			 */
+			if (!lzx_read_pre_tree(strm)) {
+				ds->state = ST_RD_PRE_MAIN_TREE_REM;
+				if (last)
+					goto failed;
+				return (ARCHIVE_OK);
+			}
+			if (!lzx_make_huffman_table(&ds->pt))
+				goto failed;
+			ds->loop = 256;
+			/* FALL THROUGH */
+		case ST_MAIN_TREE_REM:
+			/*
+			 * Get path lengths of remaining elements of main tree.
+			 */
+			r = lzx_read_bitlen(strm, &ds->mt, -1);
+			if (r < 0)
+				goto failed;
+			else if (!r) {
+				ds->state = ST_MAIN_TREE_REM;
+				if (last)
+					goto failed;
+				return (ARCHIVE_OK);
+			}
+			if (!lzx_make_huffman_table(&ds->mt))
+				goto failed;
+			ds->loop = 0;
+			/* FALL THROUGH */
+		case ST_RD_PRE_LENGTH_TREE:
+			/*
+			 * Read Pre-tree for remaining elements of main tree.
+			 */
+			if (!lzx_read_pre_tree(strm)) {
+				ds->state = ST_RD_PRE_LENGTH_TREE;
+				if (last)
+					goto failed;
+				return (ARCHIVE_OK);
+			}
+			if (!lzx_make_huffman_table(&ds->pt))
+				goto failed;
+			ds->loop = 0;
+			/* FALL THROUGH */
+		case ST_LENGTH_TREE:
+			/*
+			 * Get path lengths of remaining elements of main tree.
+			 */
+			r = lzx_read_bitlen(strm, &ds->lt, -1);
+			if (r < 0)
+				goto failed;
+			else if (!r) {
+				ds->state = ST_LENGTH_TREE;
+				if (last)
+					goto failed;
+				return (ARCHIVE_OK);
+			}
+			if (!lzx_make_huffman_table(&ds->lt))
+				goto failed;
+			ds->state = ST_MAIN;
+			return (100);
+		}
+	}
+failed:
+	return (ds->error = ARCHIVE_FAILED);
+}
+
+static int
+lzx_decode_blocks(struct lzx_stream *strm, int last)
+{
+	struct lzx_dec *ds = strm->ds;
+	struct lzx_br bre = ds->br;
+	struct huffman *at = &(ds->at), *lt = &(ds->lt), *mt = &(ds->mt);
+	const struct lzx_pos_tbl *pos_tbl = ds->pos_tbl;
+	unsigned char *noutp = strm->next_out;
+	unsigned char *endp = noutp + strm->avail_out;
+	unsigned char *w_buff = ds->w_buff;
+	unsigned char *at_bitlen = at->bitlen;
+	unsigned char *lt_bitlen = lt->bitlen;
+	unsigned char *mt_bitlen = mt->bitlen;
+	size_t block_bytes_avail = ds->block_bytes_avail;
+	int at_max_bits = at->max_bits;
+	int lt_max_bits = lt->max_bits;
+	int mt_max_bits = mt->max_bits;
+	int c, copy_len = ds->copy_len, copy_pos = ds->copy_pos;
+	int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
+	int length_header = ds->length_header;
+	int offset_bits = ds->offset_bits;
+	int position_slot = ds->position_slot;
+	int r0 = ds->r0, r1 = ds->r1, r2 = ds->r2;
+	int state = ds->state;
+	char block_type = ds->block_type;
+
+	for (;;) {
+		switch (state) {
+		case ST_MAIN:
+			for (;;) {
+				if (block_bytes_avail == 0) {
+					/* This block ended. */
+					ds->state = ST_RD_BLOCK_TYPE;
+					ds->br = bre;
+					ds->block_bytes_avail =
+					    block_bytes_avail;
+					ds->copy_len = copy_len;
+					ds->copy_pos = copy_pos;
+					ds->length_header = length_header;
+					ds->position_slot = position_slot;
+					ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;
+					ds->w_pos = w_pos;
+					strm->avail_out = endp - noutp;
+					return (ARCHIVE_EOF);
+				}
+				if (noutp >= endp)
+					/* Output buffer is empty. */
+					goto next_data;
+
+				if (!lzx_br_read_ahead(strm, &bre,
+				    mt_max_bits)) {
+					if (!last)
+						goto next_data;
+					/* Remaining bits are less than
+					 * maximum bits(mt.max_bits) but maybe
+					 * it still remains as much as we need,
+					 * so we should try to use it with
+					 * dummy bits. */
+					c = lzx_decode_huffman(mt,
+					      lzx_br_bits_forced(
+				 	        &bre, mt_max_bits));
+					lzx_br_consume(&bre, mt_bitlen[c]);
+					if (!lzx_br_has(&bre, 0))
+						goto failed;/* Over read. */
+				} else {
+					c = lzx_decode_huffman(mt,
+					      lzx_br_bits(&bre, mt_max_bits));
+					lzx_br_consume(&bre, mt_bitlen[c]);
+				}
+				if (c > UCHAR_MAX)
+					break;
+				/*
+				 * 'c' is exactly literal code.
+				 */
+				/* Save a decoded code to reference it
+				 * afterward. */
+				w_buff[w_pos] = c;
+				w_pos = (w_pos + 1) & w_mask;
+				/* Store the decoded code to output buffer. */
+				*noutp++ = c;
+				block_bytes_avail--;
+			}
+			/*
+			 * Get a match code, its length and offset.
+			 */
+			c -= UCHAR_MAX + 1;
+			length_header = c & 7;
+			position_slot = c >> 3;
+			/* FALL THROUGH */
+		case ST_LENGTH:
+			/*
+			 * Get a length.
+			 */
+			if (length_header == 7) {
+				if (!lzx_br_read_ahead(strm, &bre,
+				    lt_max_bits)) {
+					if (!last) {
+						state = ST_LENGTH;
+						goto next_data;
+					}
+					c = lzx_decode_huffman(lt,
+					      lzx_br_bits_forced(
+					        &bre, lt_max_bits));
+					lzx_br_consume(&bre, lt_bitlen[c]);
+					if (!lzx_br_has(&bre, 0))
+						goto failed;/* Over read. */
+				} else {
+					c = lzx_decode_huffman(lt,
+					    lzx_br_bits(&bre, lt_max_bits));
+					lzx_br_consume(&bre, lt_bitlen[c]);
+				}
+				copy_len = c + 7 + 2;
+			} else
+				copy_len = length_header + 2;
+			if ((size_t)copy_len > block_bytes_avail)
+				goto failed;
+			/*
+			 * Get an offset.
+			 */
+			switch (position_slot) {
+			case 0: /* Use repeated offset 0. */
+				copy_pos = r0;
+				state = ST_REAL_POS;
+				continue;
+			case 1: /* Use repeated offset 1. */
+				copy_pos = r1;
+				/* Swap repeated offset. */
+				r1 = r0;
+				r0 = copy_pos;
+				state = ST_REAL_POS;
+				continue;
+			case 2: /* Use repeated offset 2. */
+				copy_pos = r2;
+				/* Swap repeated offset. */
+				r2 = r0;
+				r0 = copy_pos;
+				state = ST_REAL_POS;
+				continue;
+			default:
+				offset_bits =
+				    pos_tbl[position_slot].footer_bits;
+				break;
+			}
+			/* FALL THROUGH */
+		case ST_OFFSET:
+			/*
+			 * Get the offset, which is a distance from
+			 * current window position.
+			 */
+			if (block_type == ALIGNED_OFFSET_BLOCK &&
+			    offset_bits >= 3) {
+				int offbits = offset_bits - 3;
+
+				if (!lzx_br_read_ahead(strm, &bre, offbits)) {
+					state = ST_OFFSET;
+					if (last)
+						goto failed;
+					goto next_data;
+				}
+				copy_pos = lzx_br_bits(&bre, offbits) << 3;
+
+				/* Get an aligned number. */
+				if (!lzx_br_read_ahead(strm, &bre,
+				    offbits + at_max_bits)) {
+					if (!last) {
+						state = ST_OFFSET;
+						goto next_data;
+					}
+					lzx_br_consume(&bre, offbits);
+					c = lzx_decode_huffman(at,
+					      lzx_br_bits_forced(&bre,
+					        at_max_bits));
+					lzx_br_consume(&bre, at_bitlen[c]);
+					if (!lzx_br_has(&bre, 0))
+						goto failed;/* Over read. */
+				} else {
+					lzx_br_consume(&bre, offbits);
+					c = lzx_decode_huffman(at,
+					      lzx_br_bits(&bre, at_max_bits));
+					lzx_br_consume(&bre, at_bitlen[c]);
+				}
+				/* Add an aligned number. */
+				copy_pos += c;
+			} else {
+				if (!lzx_br_read_ahead(strm, &bre,
+				    offset_bits)) {
+					state = ST_OFFSET;
+					if (last)
+						goto failed;
+					goto next_data;
+				}
+				copy_pos = lzx_br_bits(&bre, offset_bits);
+				lzx_br_consume(&bre, offset_bits);
+			}
+			copy_pos += pos_tbl[position_slot].base -2;
+
+			/* Update repeated offset LRU queue. */
+			r2 = r1;
+			r1 = r0;
+			r0 = copy_pos;
+			/* FALL THROUGH */
+		case ST_REAL_POS:
+			/*
+			 * Compute a real position in window.
+			 */
+			copy_pos = (w_pos - copy_pos) & w_mask;
+			/* FALL THROUGH */
+		case ST_COPY:
+			/*
+			 * Copy several bytes as extracted data from the window
+			 * into the output buffer.
+			 */
+			for (;;) {
+				const unsigned char *s;
+				int l;
+
+				l = copy_len;
+				if (copy_pos > w_pos) {
+					if (l > w_size - copy_pos)
+						l = w_size - copy_pos;
+				} else {
+					if (l > w_size - w_pos)
+						l = w_size - w_pos;
+				}
+				if (noutp + l >= endp)
+					l = (int)(endp - noutp);
+				s = w_buff + copy_pos;
+				if (l >= 8 && ((copy_pos + l < w_pos)
+				  || (w_pos + l < copy_pos))) {
+					memcpy(w_buff + w_pos, s, l);
+					memcpy(noutp, s, l);
+				} else {
+					unsigned char *d;
+					int li;
+
+					d = w_buff + w_pos;
+					for (li = 0; li < l; li++)
+						noutp[li] = d[li] = s[li];
+				}
+				noutp += l;
+				copy_pos = (copy_pos + l) & w_mask;
+				w_pos = (w_pos + l) & w_mask;
+				block_bytes_avail -= l;
+				if (copy_len <= l)
+					/* A copy of current pattern ended. */
+					break;
+				copy_len -= l;
+				if (noutp >= endp) {
+					/* Output buffer is empty. */
+					state = ST_COPY;
+					goto next_data;
+				}
+			}
+			state = ST_MAIN;
+			break;
+		}
+	}
+failed:
+	return (ds->error = ARCHIVE_FAILED);
+next_data:
+	ds->br = bre;
+	ds->block_bytes_avail = block_bytes_avail;
+	ds->copy_len = copy_len;
+	ds->copy_pos = copy_pos;
+	ds->length_header = length_header;
+	ds->offset_bits = offset_bits;
+	ds->position_slot = position_slot;
+	ds->r0 = r0; ds->r1 = r1; ds->r2 = r2;
+	ds->state = state;
+	ds->w_pos = w_pos;
+	strm->avail_out = endp - noutp;
+	return (ARCHIVE_OK);
+}
+
+static int
+lzx_read_pre_tree(struct lzx_stream *strm)
+{
+	struct lzx_dec *ds = strm->ds;
+	struct lzx_br *br = &(ds->br);
+	int i;
+
+	if (ds->loop == 0)
+		memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
+	for (i = ds->loop; i < ds->pt.len_size; i++) {
+		if (!lzx_br_read_ahead(strm, br, 4)) {
+			ds->loop = i;
+			return (0);
+		}
+		ds->pt.bitlen[i] = lzx_br_bits(br, 4);
+		ds->pt.freq[ds->pt.bitlen[i]]++;
+		lzx_br_consume(br, 4);
+	}
+	ds->loop = i;
+	return (1);
+}
+
+/*
+ * Read a bunch of bit-lengths from pre-tree.
+ */
+static int
+lzx_read_bitlen(struct lzx_stream *strm, struct huffman *d, int end)
+{
+	struct lzx_dec *ds = strm->ds;
+	struct lzx_br *br = &(ds->br);
+	int c, i, j, ret, same;
+	unsigned rbits;
+
+	i = ds->loop;
+	if (i == 0)
+		memset(d->freq, 0, sizeof(d->freq));
+	ret = 0;
+	if (end < 0)
+		end = d->len_size;
+	while (i < end) {
+		ds->loop = i;
+		if (!lzx_br_read_ahead(strm, br, ds->pt.max_bits))
+			goto getdata;
+		rbits = lzx_br_bits(br, ds->pt.max_bits);
+		c = lzx_decode_huffman(&(ds->pt), rbits);
+		switch (c) {
+		case 17:/* several zero lengths, from 4 to 19. */
+			if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+4))
+				goto getdata;
+			lzx_br_consume(br, ds->pt.bitlen[c]);
+			same = lzx_br_bits(br, 4) + 4;
+			if (i + same > end)
+				return (-1);/* Invalid */
+			lzx_br_consume(br, 4);
+			for (j = 0; j < same; j++)
+				d->bitlen[i++] = 0;
+			break;
+		case 18:/* many zero lengths, from 20 to 51. */
+			if (!lzx_br_read_ahead(strm, br, ds->pt.bitlen[c]+5))
+				goto getdata;
+			lzx_br_consume(br, ds->pt.bitlen[c]);
+			same = lzx_br_bits(br, 5) + 20;
+			if (i + same > end)
+				return (-1);/* Invalid */
+			lzx_br_consume(br, 5);
+			memset(d->bitlen + i, 0, same);
+			i += same;
+			break;
+		case 19:/* a few same lengths. */
+			if (!lzx_br_read_ahead(strm, br,
+			    ds->pt.bitlen[c]+1+ds->pt.max_bits))
+				goto getdata;
+			lzx_br_consume(br, ds->pt.bitlen[c]);
+			same = lzx_br_bits(br, 1) + 4;
+			if (i + same > end)
+				return (-1);
+			lzx_br_consume(br, 1);
+			rbits = lzx_br_bits(br, ds->pt.max_bits);
+			c = lzx_decode_huffman(&(ds->pt), rbits);
+			lzx_br_consume(br, ds->pt.bitlen[c]);
+			c = (d->bitlen[i] - c + 17) % 17;
+			if (c < 0)
+				return (-1);/* Invalid */
+			for (j = 0; j < same; j++)
+				d->bitlen[i++] = c;
+			d->freq[c] += same;
+			break;
+		default:
+			lzx_br_consume(br, ds->pt.bitlen[c]);
+			c = (d->bitlen[i] - c + 17) % 17;
+			if (c < 0)
+				return (-1);/* Invalid */
+			d->freq[c]++;
+			d->bitlen[i++] = c;
+			break;
+		}
+	}
+	ret = 1;
+getdata:
+	ds->loop = i;
+	return (ret);
+}
+
+static int
+lzx_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
+{
+
+	if (hf->bitlen == NULL || hf->len_size != (int)len_size) {
+		free(hf->bitlen);
+		hf->bitlen = calloc(len_size,  sizeof(hf->bitlen[0]));
+		if (hf->bitlen == NULL)
+			return (ARCHIVE_FATAL);
+		hf->len_size = (int)len_size;
+	} else
+		memset(hf->bitlen, 0, len_size *  sizeof(hf->bitlen[0]));
+	if (hf->tbl == NULL) {
+		hf->tbl = malloc(((size_t)1 << tbl_bits) * sizeof(hf->tbl[0]));
+		if (hf->tbl == NULL)
+			return (ARCHIVE_FATAL);
+		hf->tbl_bits = tbl_bits;
+	}
+	return (ARCHIVE_OK);
+}
+
+static void
+lzx_huffman_free(struct huffman *hf)
+{
+	free(hf->bitlen);
+	free(hf->tbl);
+}
+
+/*
+ * Make a huffman coding table.
+ */
+static int
+lzx_make_huffman_table(struct huffman *hf)
+{
+	uint16_t *tbl;
+	const unsigned char *bitlen;
+	int bitptn[17], weight[17];
+	int i, maxbits = 0, ptn, tbl_size, w;
+	int len_avail;
+
+	/*
+	 * Initialize bit patterns.
+	 */
+	ptn = 0;
+	for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {
+		bitptn[i] = ptn;
+		weight[i] = w;
+		if (hf->freq[i]) {
+			ptn += hf->freq[i] * w;
+			maxbits = i;
+		}
+	}
+	if ((ptn & 0xffff) != 0 || maxbits > hf->tbl_bits)
+		return (0);/* Invalid */
+
+	hf->max_bits = maxbits;
+
+	/*
+	 * Cut out extra bits which we won't house in the table.
+	 * This preparation reduces the same calculation in the for-loop
+	 * making the table.
+	 */
+	if (maxbits < 16) {
+		int ebits = 16 - maxbits;
+		for (i = 1; i <= maxbits; i++) {
+			bitptn[i] >>= ebits;
+			weight[i] >>= ebits;
+		}
+	}
+
+	/*
+	 * Make the table.
+	 */
+	tbl_size = 1 << hf->tbl_bits;
+	tbl = hf->tbl;
+	bitlen = hf->bitlen;
+	len_avail = hf->len_size;
+	hf->tree_used = 0;
+	for (i = 0; i < len_avail; i++) {
+		uint16_t *p;
+		int len, cnt;
+
+		if (bitlen[i] == 0)
+			continue;
+		/* Get a bit pattern */
+		len = bitlen[i];
+		if (len > tbl_size)
+			return (0);
+		ptn = bitptn[len];
+		cnt = weight[len];
+		/* Calculate next bit pattern */
+		if ((bitptn[len] = ptn + cnt) > tbl_size)
+			return (0);/* Invalid */
+		/* Update the table */
+		p = &(tbl[ptn]);
+		while (--cnt >= 0)
+			p[cnt] = (uint16_t)i;
+	}
+	return (1);
+}
+
+static inline int
+lzx_decode_huffman(struct huffman *hf, unsigned rbits)
+{
+	int c;
+	c = hf->tbl[rbits];
+	if (c < hf->len_size)
+		return (c);
+	return (0);
+}
diff --git a/c/archive_read_support_format_cpio.c b/c/archive_read_support_format_cpio.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_cpio.c
@@ -0,0 +1,1086 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_read_support_format_cpio.c 201163 2009-12-29 05:50:34Z kientzle $");
+
+#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
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#define	bin_magic_offset 0
+#define	bin_magic_size 2
+#define	bin_dev_offset 2
+#define	bin_dev_size 2
+#define	bin_ino_offset 4
+#define	bin_ino_size 2
+#define	bin_mode_offset 6
+#define	bin_mode_size 2
+#define	bin_uid_offset 8
+#define	bin_uid_size 2
+#define	bin_gid_offset 10
+#define	bin_gid_size 2
+#define	bin_nlink_offset 12
+#define	bin_nlink_size 2
+#define	bin_rdev_offset 14
+#define	bin_rdev_size 2
+#define	bin_mtime_offset 16
+#define	bin_mtime_size 4
+#define	bin_namesize_offset 20
+#define	bin_namesize_size 2
+#define	bin_filesize_offset 22
+#define	bin_filesize_size 4
+#define	bin_header_size 26
+
+#define	odc_magic_offset 0
+#define	odc_magic_size 6
+#define	odc_dev_offset 6
+#define	odc_dev_size 6
+#define	odc_ino_offset 12
+#define	odc_ino_size 6
+#define	odc_mode_offset 18
+#define	odc_mode_size 6
+#define	odc_uid_offset 24
+#define	odc_uid_size 6
+#define	odc_gid_offset 30
+#define	odc_gid_size 6
+#define	odc_nlink_offset 36
+#define	odc_nlink_size 6
+#define	odc_rdev_offset 42
+#define	odc_rdev_size 6
+#define	odc_mtime_offset 48
+#define	odc_mtime_size 11
+#define	odc_namesize_offset 59
+#define	odc_namesize_size 6
+#define	odc_filesize_offset 65
+#define	odc_filesize_size 11
+#define	odc_header_size 76
+
+#define	newc_magic_offset 0
+#define	newc_magic_size 6
+#define	newc_ino_offset 6
+#define	newc_ino_size 8
+#define	newc_mode_offset 14
+#define	newc_mode_size 8
+#define	newc_uid_offset 22
+#define	newc_uid_size 8
+#define	newc_gid_offset 30
+#define	newc_gid_size 8
+#define	newc_nlink_offset 38
+#define	newc_nlink_size 8
+#define	newc_mtime_offset 46
+#define	newc_mtime_size 8
+#define	newc_filesize_offset 54
+#define	newc_filesize_size 8
+#define	newc_devmajor_offset 62
+#define	newc_devmajor_size 8
+#define	newc_devminor_offset 70
+#define	newc_devminor_size 8
+#define	newc_rdevmajor_offset 78
+#define	newc_rdevmajor_size 8
+#define	newc_rdevminor_offset 86
+#define	newc_rdevminor_size 8
+#define	newc_namesize_offset 94
+#define	newc_namesize_size 8
+#define	newc_checksum_offset 102
+#define	newc_checksum_size 8
+#define	newc_header_size 110
+
+/*
+ * An afio large ASCII header, which they named itself.
+ * afio utility uses this header, if a file size is larger than 2G bytes
+ * or inode/uid/gid is bigger than 65535(0xFFFF) or mtime is bigger than
+ * 0x7fffffff, which we cannot record to odc header because of its limit.
+ * If not, uses odc header.
+ */
+#define	afiol_magic_offset 0
+#define	afiol_magic_size 6
+#define	afiol_dev_offset 6
+#define	afiol_dev_size 8	/* hex */
+#define	afiol_ino_offset 14
+#define	afiol_ino_size 16	/* hex */
+#define	afiol_ino_m_offset 30	/* 'm' */
+#define	afiol_mode_offset 31
+#define	afiol_mode_size 6	/* oct */
+#define	afiol_uid_offset 37
+#define	afiol_uid_size 8	/* hex */
+#define	afiol_gid_offset 45
+#define	afiol_gid_size 8	/* hex */
+#define	afiol_nlink_offset 53
+#define	afiol_nlink_size 8	/* hex */
+#define	afiol_rdev_offset 61
+#define	afiol_rdev_size 8	/* hex */
+#define	afiol_mtime_offset 69
+#define	afiol_mtime_size 16	/* hex */
+#define	afiol_mtime_n_offset 85	/* 'n' */
+#define	afiol_namesize_offset 86
+#define	afiol_namesize_size 4	/* hex */
+#define	afiol_flag_offset 90
+#define	afiol_flag_size 4	/* hex */
+#define	afiol_xsize_offset 94
+#define	afiol_xsize_size 4	/* hex */
+#define	afiol_xsize_s_offset 98	/* 's' */
+#define	afiol_filesize_offset 99
+#define	afiol_filesize_size 16	/* hex */
+#define	afiol_filesize_c_offset 115	/* ':' */
+#define afiol_header_size 116
+
+
+struct links_entry {
+        struct links_entry      *next;
+        struct links_entry      *previous;
+        unsigned int             links;
+        dev_t                    dev;
+        int64_t                  ino;
+        char                    *name;
+};
+
+#define	CPIO_MAGIC   0x13141516
+struct cpio {
+	int			  magic;
+	int			(*read_header)(struct archive_read *, struct cpio *,
+				     struct archive_entry *, size_t *, size_t *);
+	struct links_entry	 *links_head;
+	int64_t			  entry_bytes_remaining;
+	int64_t			  entry_bytes_unconsumed;
+	int64_t			  entry_offset;
+	int64_t			  entry_padding;
+
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv_default;
+	int			  init_default_conversion;
+};
+
+static int64_t	atol16(const char *, unsigned);
+static int64_t	atol8(const char *, unsigned);
+static int	archive_read_format_cpio_bid(struct archive_read *, int);
+static int	archive_read_format_cpio_options(struct archive_read *,
+		    const char *, const char *);
+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 *, int64_t *);
+static int	archive_read_format_cpio_read_header(struct archive_read *,
+		    struct archive_entry *);
+static int	archive_read_format_cpio_skip(struct archive_read *);
+static int64_t	be4(const unsigned char *);
+static int	find_odc_header(struct archive_read *);
+static int	find_newc_header(struct archive_read *);
+static int	header_bin_be(struct archive_read *, struct cpio *,
+		    struct archive_entry *, size_t *, size_t *);
+static int	header_bin_le(struct archive_read *, struct cpio *,
+		    struct archive_entry *, size_t *, size_t *);
+static int	header_newc(struct archive_read *, struct cpio *,
+		    struct archive_entry *, size_t *, size_t *);
+static int	header_odc(struct archive_read *, struct cpio *,
+		    struct archive_entry *, size_t *, size_t *);
+static int	header_afiol(struct archive_read *, struct cpio *,
+		    struct archive_entry *, size_t *, size_t *);
+static int	is_octal(const char *, size_t);
+static int	is_hex(const char *, size_t);
+static int64_t	le4(const unsigned char *);
+static int	record_hardlink(struct archive_read *a,
+		    struct cpio *cpio, struct archive_entry *entry);
+
+int
+archive_read_support_format_cpio(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct cpio *cpio;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_cpio");
+
+	cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+	if (cpio == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+		return (ARCHIVE_FATAL);
+	}
+	cpio->magic = CPIO_MAGIC;
+
+	r = __archive_read_register_format(a,
+	    cpio,
+	    "cpio",
+	    archive_read_format_cpio_bid,
+	    archive_read_format_cpio_options,
+	    archive_read_format_cpio_read_header,
+	    archive_read_format_cpio_read_data,
+	    archive_read_format_cpio_skip,
+	    NULL,
+	    archive_read_format_cpio_cleanup,
+	    NULL,
+	    NULL);
+
+	if (r != ARCHIVE_OK)
+		free(cpio);
+	return (ARCHIVE_OK);
+}
+
+
+static int
+archive_read_format_cpio_bid(struct archive_read *a, int best_bid)
+{
+	const unsigned char *p;
+	struct cpio *cpio;
+	int bid;
+
+	(void)best_bid; /* UNUSED */
+
+	cpio = (struct cpio *)(a->format->data);
+
+	if ((p = __archive_read_ahead(a, 6, NULL)) == NULL)
+		return (-1);
+
+	bid = 0;
+	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, "070727", 6) == 0) {
+		/* afio large ASCII cpio archive */
+		cpio->read_header = header_odc;
+		bid += 48;
+		/*
+		 * XXX TODO:  More verification; Could check that almost hex
+		 * 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_options(struct archive_read *a,
+    const char *key, const char *val)
+{
+	struct cpio *cpio;
+	int ret = ARCHIVE_FAILED;
+
+	cpio = (struct cpio *)(a->format->data);
+	if (strcmp(key, "compat-2x")  == 0) {
+		/* Handle filenames as libarchive 2.x */
+		cpio->init_default_conversion = (val != NULL)?1:0;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "cpio: hdrcharset option needs a character-set name");
+		else {
+			cpio->opt_sconv =
+			    archive_string_conversion_from_charset(
+				&a->archive, val, 0);
+			if (cpio->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+archive_read_format_cpio_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct cpio *cpio;
+	const void *h, *hl;
+	struct archive_string_conv *sconv;
+	size_t namelength;
+	size_t name_pad;
+	int r;
+
+	cpio = (struct cpio *)(a->format->data);
+	sconv = cpio->opt_sconv;
+	if (sconv == NULL) {
+		if (!cpio->init_default_conversion) {
+			cpio->sconv_default =
+			    archive_string_default_conversion_for_read(
+			      &(a->archive));
+			cpio->init_default_conversion = 1;
+		}
+		sconv = cpio->sconv_default;
+	}
+	
+	r = (cpio->read_header(a, cpio, entry, &namelength, &name_pad));
+
+	if (r < ARCHIVE_WARN)
+		return (r);
+
+	/* Read name from buffer. */
+	h = __archive_read_ahead(a, namelength + name_pad, NULL);
+	if (h == NULL)
+	    return (ARCHIVE_FATAL);
+	if (archive_entry_copy_pathname_l(entry,
+	    (const char *)h, namelength, sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Pathname can't be converted from %s to current locale.",
+		    archive_string_conversion_charset_name(sconv));
+		r = ARCHIVE_WARN;
+	}
+	cpio->entry_offset = 0;
+
+	__archive_read_consume(a, namelength + name_pad);
+
+	/* If this is a symlink, read the link contents. */
+	if (archive_entry_filetype(entry) == AE_IFLNK) {
+		if (cpio->entry_bytes_remaining > 1024 * 1024) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Rejecting malformed cpio archive: symlink contents exceed 1 megabyte");
+			return (ARCHIVE_FATAL);
+		}
+		hl = __archive_read_ahead(a,
+			(size_t)cpio->entry_bytes_remaining, NULL);
+		if (hl == NULL)
+			return (ARCHIVE_FATAL);
+		if (archive_entry_copy_symlink_l(entry, (const char *)hl,
+		    (size_t)cpio->entry_bytes_remaining, sconv) != 0) {
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Linkname");
+				return (ARCHIVE_FATAL);
+			}
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Linkname can't be converted from %s to "
+			    "current locale.",
+			    archive_string_conversion_charset_name(sconv));
+			r = ARCHIVE_WARN;
+		}
+		__archive_read_consume(a, cpio->entry_bytes_remaining);
+		cpio->entry_bytes_remaining = 0;
+	}
+
+	/* XXX TODO: If the full mode is 0160200, then this is a Solaris
+	 * ACL description for the following entry.  Read this body
+	 * and parse it as a Solaris-style ACL, then read the next
+	 * header.  XXX */
+
+	/* Compare name to "TRAILER!!!" to test for end-of-archive. */
+	if (namelength == 11 && strncmp((const char *)h, "TRAILER!!!",
+	    11) == 0) {
+		/* TODO: Store file location of start of block. */
+		archive_clear_error(&a->archive);
+		return (ARCHIVE_EOF);
+	}
+
+	/* Detect and record hardlinks to previously-extracted entries. */
+	if (record_hardlink(a, cpio, entry) != ARCHIVE_OK) {
+		return (ARCHIVE_FATAL);
+	}
+
+	return (r);
+}
+
+static int
+archive_read_format_cpio_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	ssize_t bytes_read;
+	struct cpio *cpio;
+
+	cpio = (struct cpio *)(a->format->data);
+
+	if (cpio->entry_bytes_unconsumed) {
+		__archive_read_consume(a, cpio->entry_bytes_unconsumed);
+		cpio->entry_bytes_unconsumed = 0;
+	}
+
+	if (cpio->entry_bytes_remaining > 0) {
+		*buff = __archive_read_ahead(a, 1, &bytes_read);
+		if (bytes_read <= 0)
+			return (ARCHIVE_FATAL);
+		if (bytes_read > cpio->entry_bytes_remaining)
+			bytes_read = (ssize_t)cpio->entry_bytes_remaining;
+		*size = bytes_read;
+		cpio->entry_bytes_unconsumed = bytes_read;
+		*offset = cpio->entry_offset;
+		cpio->entry_offset += bytes_read;
+		cpio->entry_bytes_remaining -= bytes_read;
+		return (ARCHIVE_OK);
+	} else {
+		if (cpio->entry_padding !=
+			__archive_read_consume(a, cpio->entry_padding)) {
+			return (ARCHIVE_FATAL);
+		}
+		cpio->entry_padding = 0;
+		*buff = NULL;
+		*size = 0;
+		*offset = cpio->entry_offset;
+		return (ARCHIVE_EOF);
+	}
+}
+
+static int
+archive_read_format_cpio_skip(struct archive_read *a)
+{
+	struct cpio *cpio = (struct cpio *)(a->format->data);
+	int64_t to_skip = cpio->entry_bytes_remaining + cpio->entry_padding +
+		cpio->entry_bytes_unconsumed;
+
+	if (to_skip != __archive_read_consume(a, to_skip)) {
+		return (ARCHIVE_FATAL);
+	}
+	cpio->entry_bytes_remaining = 0;
+	cpio->entry_padding = 0;
+	cpio->entry_bytes_unconsumed = 0;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Skip forward to the next cpio newc header by searching for the
+ * 07070[12] string.  This should be generalized and merged with
+ * find_odc_header below.
+ */
+static int
+is_hex(const char *p, size_t len)
+{
+	while (len-- > 0) {
+		if ((*p >= '0' && *p <= '9')
+		    || (*p >= 'a' && *p <= 'f')
+		    || (*p >= 'A' && *p <= 'F'))
+			++p;
+		else
+			return (0);
+	}
+	return (1);
+}
+
+static int
+find_newc_header(struct archive_read *a)
+{
+	const void *h;
+	const char *p, *q;
+	size_t skip, skipped = 0;
+	ssize_t bytes;
+
+	for (;;) {
+		h = __archive_read_ahead(a, newc_header_size, &bytes);
+		if (h == NULL)
+			return (ARCHIVE_FATAL);
+		p = h;
+		q = p + bytes;
+
+		/* Try the typical case first, then go into the slow search.*/
+		if (memcmp("07070", p, 5) == 0
+		    && (p[5] == '1' || p[5] == '2')
+		    && is_hex(p, newc_header_size))
+			return (ARCHIVE_OK);
+
+		/*
+		 * Scan ahead until we find something that looks
+		 * like a newc header.
+		 */
+		while (p + newc_header_size <= q) {
+			switch (p[5]) {
+			case '1':
+			case '2':
+				if (memcmp("07070", p, 5) == 0
+				    && is_hex(p, newc_header_size)) {
+					skip = p - (const char *)h;
+					__archive_read_consume(a, skip);
+					skipped += skip;
+					if (skipped > 0) {
+						archive_set_error(&a->archive,
+						    0,
+						    "Skipped %d bytes before "
+						    "finding valid header",
+						    (int)skipped);
+						return (ARCHIVE_WARN);
+					}
+					return (ARCHIVE_OK);
+				}
+				p += 2;
+				break;
+			case '0':
+				p++;
+				break;
+			default:
+				p += 6;
+				break;
+			}
+		}
+		skip = p - (const char *)h;
+		__archive_read_consume(a, skip);
+		skipped += skip;
+	}
+}
+
+static int
+header_newc(struct archive_read *a, struct cpio *cpio,
+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+	const void *h;
+	const char *header;
+	int r;
+
+	r = find_newc_header(a);
+	if (r < ARCHIVE_WARN)
+		return (r);
+
+	/* Read fixed-size portion of header. */
+	h = __archive_read_ahead(a, newc_header_size, NULL);
+	if (h == NULL)
+	    return (ARCHIVE_FATAL);
+
+	/* Parse out hex fields. */
+	header = (const char *)h;
+
+	if (memcmp(header + newc_magic_offset, "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 + newc_magic_offset, "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? */
+	}
+
+	archive_entry_set_devmajor(entry,
+		(dev_t)atol16(header + newc_devmajor_offset, newc_devmajor_size));
+	archive_entry_set_devminor(entry, 
+		(dev_t)atol16(header + newc_devminor_offset, newc_devminor_size));
+	archive_entry_set_ino(entry, atol16(header + newc_ino_offset, newc_ino_size));
+	archive_entry_set_mode(entry, 
+		(mode_t)atol16(header + newc_mode_offset, newc_mode_size));
+	archive_entry_set_uid(entry, atol16(header + newc_uid_offset, newc_uid_size));
+	archive_entry_set_gid(entry, atol16(header + newc_gid_offset, newc_gid_size));
+	archive_entry_set_nlink(entry,
+		(unsigned int)atol16(header + newc_nlink_offset, newc_nlink_size));
+	archive_entry_set_rdevmajor(entry,
+		(dev_t)atol16(header + newc_rdevmajor_offset, newc_rdevmajor_size));
+	archive_entry_set_rdevminor(entry,
+		(dev_t)atol16(header + newc_rdevminor_offset, newc_rdevminor_size));
+	archive_entry_set_mtime(entry, atol16(header + newc_mtime_offset, newc_mtime_size), 0);
+	*namelength = (size_t)atol16(header + newc_namesize_offset, newc_namesize_size);
+	/* Pad name to 2 more than a multiple of 4. */
+	*name_pad = (2 - *namelength) & 3;
+
+	/* Make sure that the padded name length fits into size_t. */
+	if (*name_pad > SIZE_MAX - *namelength) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "cpio archive has invalid namelength");
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Note: entry_bytes_remaining is at least 64 bits and
+	 * therefore guaranteed to be big enough for a 33-bit file
+	 * size.
+	 */
+	cpio->entry_bytes_remaining =
+	    atol16(header + newc_filesize_offset, newc_filesize_size);
+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+	/* Pad file contents to a multiple of 4. */
+	cpio->entry_padding = 3 & -cpio->entry_bytes_remaining;
+	__archive_read_consume(a, newc_header_size);
+	return (r);
+}
+
+/*
+ * Skip forward to the next cpio odc header by searching for the
+ * 070707 string.  This is a hand-optimized search that could
+ * probably be easily generalized to handle all character-based
+ * cpio variants.
+ */
+static int
+is_octal(const char *p, size_t len)
+{
+	while (len-- > 0) {
+		if (*p < '0' || *p > '7')
+			return (0);
+	        ++p;
+	}
+	return (1);
+}
+
+static int
+is_afio_large(const char *h, size_t len)
+{
+	if (len < afiol_header_size)
+		return (0);
+	if (h[afiol_ino_m_offset] != 'm'
+	    || h[afiol_mtime_n_offset] != 'n'
+	    || h[afiol_xsize_s_offset] != 's'
+	    || h[afiol_filesize_c_offset] != ':')
+		return (0);
+	if (!is_hex(h + afiol_dev_offset, afiol_ino_m_offset - afiol_dev_offset))
+		return (0);
+	if (!is_hex(h + afiol_mode_offset, afiol_mtime_n_offset - afiol_mode_offset))
+		return (0);
+	if (!is_hex(h + afiol_namesize_offset, afiol_xsize_s_offset - afiol_namesize_offset))
+		return (0);
+	if (!is_hex(h + afiol_filesize_offset, afiol_filesize_size))
+		return (0);
+	return (1);
+}
+
+static int
+find_odc_header(struct archive_read *a)
+{
+	const void *h;
+	const char *p, *q;
+	size_t skip, skipped = 0;
+	ssize_t bytes;
+
+	for (;;) {
+		h = __archive_read_ahead(a, odc_header_size, &bytes);
+		if (h == NULL)
+			return (ARCHIVE_FATAL);
+		p = h;
+		q = p + bytes;
+
+		/* Try the typical case first, then go into the slow search.*/
+		if (memcmp("070707", p, 6) == 0 && is_octal(p, odc_header_size))
+			return (ARCHIVE_OK);
+		if (memcmp("070727", p, 6) == 0 && is_afio_large(p, bytes)) {
+			a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+			return (ARCHIVE_OK);
+		}
+
+		/*
+		 * Scan ahead until we find something that looks
+		 * like an odc header.
+		 */
+		while (p + odc_header_size <= q) {
+			switch (p[5]) {
+			case '7':
+				if ((memcmp("070707", p, 6) == 0
+				    && is_octal(p, odc_header_size))
+				    || (memcmp("070727", p, 6) == 0
+				        && is_afio_large(p, q - p))) {
+					skip = p - (const char *)h;
+					__archive_read_consume(a, skip);
+					skipped += skip;
+					if (p[4] == '2')
+						a->archive.archive_format =
+						    ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+					if (skipped > 0) {
+						archive_set_error(&a->archive,
+						    0,
+						    "Skipped %d bytes before "
+						    "finding valid header",
+						    (int)skipped);
+						return (ARCHIVE_WARN);
+					}
+					return (ARCHIVE_OK);
+				}
+				p += 2;
+				break;
+			case '0':
+				p++;
+				break;
+			default:
+				p += 6;
+				break;
+			}
+		}
+		skip = p - (const char *)h;
+		__archive_read_consume(a, skip);
+		skipped += skip;
+	}
+}
+
+static int
+header_odc(struct archive_read *a, struct cpio *cpio,
+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+	const void *h;
+	int r;
+	const char *header;
+
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
+	a->archive.archive_format_name = "POSIX octet-oriented cpio";
+
+	/* Find the start of the next header. */
+	r = find_odc_header(a);
+	if (r < ARCHIVE_WARN)
+		return (r);
+
+	if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_AFIO_LARGE) {
+		int r2 = (header_afiol(a, cpio, entry, namelength, name_pad));
+		if (r2 == ARCHIVE_OK)
+			return (r);
+		else
+			return (r2);
+	}
+
+	/* Read fixed-size portion of header. */
+	h = __archive_read_ahead(a, odc_header_size, NULL);
+	if (h == NULL)
+	    return (ARCHIVE_FATAL);
+
+	/* Parse out octal fields. */
+	header = (const char *)h;
+
+	archive_entry_set_dev(entry, 
+		(dev_t)atol8(header + odc_dev_offset, odc_dev_size));
+	archive_entry_set_ino(entry, atol8(header + odc_ino_offset, odc_ino_size));
+	archive_entry_set_mode(entry, 
+		(mode_t)atol8(header + odc_mode_offset, odc_mode_size));
+	archive_entry_set_uid(entry, atol8(header + odc_uid_offset, odc_uid_size));
+	archive_entry_set_gid(entry, atol8(header + odc_gid_offset, odc_gid_size));
+	archive_entry_set_nlink(entry, 
+		(unsigned int)atol8(header + odc_nlink_offset, odc_nlink_size));
+	archive_entry_set_rdev(entry,
+		(dev_t)atol8(header + odc_rdev_offset, odc_rdev_size));
+	archive_entry_set_mtime(entry, atol8(header + odc_mtime_offset, odc_mtime_size), 0);
+	*namelength = (size_t)atol8(header + odc_namesize_offset, odc_namesize_size);
+	*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.
+	 */
+	cpio->entry_bytes_remaining =
+	    atol8(header + odc_filesize_offset, odc_filesize_size);
+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+	cpio->entry_padding = 0;
+	__archive_read_consume(a, odc_header_size);
+	return (r);
+}
+
+/*
+ * NOTE: if a filename suffix is ".z", it is the file gziped by afio.
+ * it would be nice that we can show uncompressed file size and we can
+ * uncompressed file contents automatically, unfortunately we have nothing
+ * to get a uncompressed file size while reading each header. It means
+ * we also cannot uncompress file contents under our framework.
+ */
+static int
+header_afiol(struct archive_read *a, struct cpio *cpio,
+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+	const void *h;
+	const char *header;
+
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_AFIO_LARGE;
+	a->archive.archive_format_name = "afio large ASCII";
+
+	/* Read fixed-size portion of header. */
+	h = __archive_read_ahead(a, afiol_header_size, NULL);
+	if (h == NULL)
+	    return (ARCHIVE_FATAL);
+
+	/* Parse out octal fields. */
+	header = (const char *)h;
+
+	archive_entry_set_dev(entry, 
+		(dev_t)atol16(header + afiol_dev_offset, afiol_dev_size));
+	archive_entry_set_ino(entry, atol16(header + afiol_ino_offset, afiol_ino_size));
+	archive_entry_set_mode(entry,
+		(mode_t)atol8(header + afiol_mode_offset, afiol_mode_size));
+	archive_entry_set_uid(entry, atol16(header + afiol_uid_offset, afiol_uid_size));
+	archive_entry_set_gid(entry, atol16(header + afiol_gid_offset, afiol_gid_size));
+	archive_entry_set_nlink(entry,
+		(unsigned int)atol16(header + afiol_nlink_offset, afiol_nlink_size));
+	archive_entry_set_rdev(entry,
+		(dev_t)atol16(header + afiol_rdev_offset, afiol_rdev_size));
+	archive_entry_set_mtime(entry, atol16(header + afiol_mtime_offset, afiol_mtime_size), 0);
+	*namelength = (size_t)atol16(header + afiol_namesize_offset, afiol_namesize_size);
+	*name_pad = 0; /* No padding of filename. */
+
+	cpio->entry_bytes_remaining =
+	    atol16(header + afiol_filesize_offset, afiol_filesize_size);
+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+	cpio->entry_padding = 0;
+	__archive_read_consume(a, afiol_header_size);
+	return (ARCHIVE_OK);
+}
+
+
+static int
+header_bin_le(struct archive_read *a, struct cpio *cpio,
+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+	const void *h;
+	const unsigned char *header;
+
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_LE;
+	a->archive.archive_format_name = "cpio (little-endian binary)";
+
+	/* Read fixed-size portion of header. */
+	h = __archive_read_ahead(a, bin_header_size, NULL);
+	if (h == NULL) {
+	    archive_set_error(&a->archive, 0,
+		"End of file trying to read next cpio header");
+	    return (ARCHIVE_FATAL);
+	}
+
+	/* Parse out binary fields. */
+	header = (const unsigned char *)h;
+
+	archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256);
+	archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256);
+	archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256);
+	archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256);
+	archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256);
+	archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256);
+	archive_entry_set_rdev(entry, header[bin_rdev_offset] + header[bin_rdev_offset + 1] * 256);
+	archive_entry_set_mtime(entry, le4(header + bin_mtime_offset), 0);
+	*namelength = header[bin_namesize_offset] + header[bin_namesize_offset + 1] * 256;
+	*name_pad = *namelength & 1; /* Pad to even. */
+
+	cpio->entry_bytes_remaining = le4(header + bin_filesize_offset);
+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+	cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
+	__archive_read_consume(a, bin_header_size);
+	return (ARCHIVE_OK);
+}
+
+static int
+header_bin_be(struct archive_read *a, struct cpio *cpio,
+    struct archive_entry *entry, size_t *namelength, size_t *name_pad)
+{
+	const void *h;
+	const unsigned char *header;
+
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_BIN_BE;
+	a->archive.archive_format_name = "cpio (big-endian binary)";
+
+	/* Read fixed-size portion of header. */
+	h = __archive_read_ahead(a, bin_header_size, NULL);
+	if (h == NULL) {
+	    archive_set_error(&a->archive, 0,
+		"End of file trying to read next cpio header");
+	    return (ARCHIVE_FATAL);
+	}
+
+	/* Parse out binary fields. */
+	header = (const unsigned char *)h;
+
+	archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]);
+	archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]);
+	archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]);
+	archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]);
+	archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]);
+	archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]);
+	archive_entry_set_rdev(entry, header[bin_rdev_offset] * 256 + header[bin_rdev_offset + 1]);
+	archive_entry_set_mtime(entry, be4(header + bin_mtime_offset), 0);
+	*namelength = header[bin_namesize_offset] * 256 + header[bin_namesize_offset + 1];
+	*name_pad = *namelength & 1; /* Pad to even. */
+
+	cpio->entry_bytes_remaining = be4(header + bin_filesize_offset);
+	archive_entry_set_size(entry, cpio->entry_bytes_remaining);
+	cpio->entry_padding = cpio->entry_bytes_remaining & 1; /* Pad to even. */
+	    __archive_read_consume(a, bin_header_size);
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_cpio_cleanup(struct archive_read *a)
+{
+	struct cpio *cpio;
+
+	cpio = (struct cpio *)(a->format->data);
+        /* Free inode->name map */
+        while (cpio->links_head != NULL) {
+                struct links_entry *lp = cpio->links_head->next;
+
+                free(cpio->links_head->name);
+                free(cpio->links_head);
+                cpio->links_head = lp;
+        }
+	free(cpio);
+	(a->format->data) = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int64_t
+le4(const unsigned char *p)
+{
+	return ((p[0] << 16) + (((int64_t)p[1]) << 24) + (p[2] << 0) + (p[3] << 8));
+}
+
+
+static int64_t
+be4(const unsigned char *p)
+{
+	return ((((int64_t)p[0]) << 24) + (p[1] << 16) + (p[2] << 8) + (p[3]));
+}
+
+/*
+ * 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 int
+record_hardlink(struct archive_read *a,
+    struct cpio *cpio, struct archive_entry *entry)
+{
+	struct links_entry      *le;
+	dev_t dev;
+	int64_t ino;
+
+	if (archive_entry_nlink(entry) <= 1)
+		return (ARCHIVE_OK);
+
+	dev = archive_entry_dev(entry);
+	ino = archive_entry_ino64(entry);
+
+	/*
+	 * 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 == dev && le->ino == ino) {
+			archive_entry_copy_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->name);
+				free(le);
+			}
+
+			return (ARCHIVE_OK);
+		}
+	}
+
+	le = (struct links_entry *)malloc(sizeof(struct links_entry));
+	if (le == NULL) {
+		archive_set_error(&a->archive,
+		    ENOMEM, "Out of memory adding file to list");
+		return (ARCHIVE_FATAL);
+	}
+	if (cpio->links_head != NULL)
+		cpio->links_head->previous = le;
+	le->next = cpio->links_head;
+	le->previous = NULL;
+	cpio->links_head = le;
+	le->dev = dev;
+	le->ino = ino;
+	le->links = archive_entry_nlink(entry) - 1;
+	le->name = strdup(archive_entry_pathname(entry));
+	if (le->name == NULL) {
+		archive_set_error(&a->archive,
+		    ENOMEM, "Out of memory adding file to list");
+		return (ARCHIVE_FATAL);
+	}
+
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_read_support_format_empty.c b/c/archive_read_support_format_empty.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_empty.c
@@ -0,0 +1,96 @@
+/*-
+ * 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: head/lib/libarchive/archive_read_support_format_empty.c 191524 2009-04-26 18:24:14Z kientzle $");
+
+#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 *, int);
+static int	archive_read_format_empty_read_data(struct archive_read *,
+		    const void **, size_t *, int64_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;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_empty");
+
+	r = __archive_read_register_format(a,
+	    NULL,
+	    NULL,
+	    archive_read_format_empty_bid,
+	    NULL,
+	    archive_read_format_empty_read_header,
+	    archive_read_format_empty_read_data,
+	    NULL,
+	    NULL,
+	    NULL,
+	    NULL,
+	    NULL);
+
+	return (r);
+}
+
+
+static int
+archive_read_format_empty_bid(struct archive_read *a, int best_bid)
+{
+	if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) == NULL)
+		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, int64_t *offset)
+{
+	(void)a; /* UNUSED */
+	(void)buff; /* UNUSED */
+	(void)size; /* UNUSED */
+	(void)offset; /* UNUSED */
+
+	return (ARCHIVE_EOF);
+}
diff --git a/c/archive_read_support_format_iso9660.c b/c/archive_read_support_format_iso9660.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_iso9660.c
@@ -0,0 +1,3278 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2009 Andreas Henriksson <andreas@fatal.se>
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_read_support_format_iso9660.c 201246 2009-12-30 05:30:35Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+/* #include <stdint.h> */ /* See archive_platform.h */
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_string.h"
+
+/*
+ * An overview of ISO 9660 format:
+ *
+ * Each disk is laid out as follows:
+ *   * 32k reserved for private use
+ *   * Volume descriptor table.  Each volume descriptor
+ *     is 2k and specifies basic format information.
+ *     The "Primary Volume Descriptor" (PVD) is defined by the
+ *     standard and should always be present; other volume
+ *     descriptors include various vendor-specific extensions.
+ *   * Files and directories.  Each file/dir is specified by
+ *     an "extent" (starting sector and length in bytes).
+ *     Dirs are just files with directory records packed one
+ *     after another.  The PVD contains a single dir entry
+ *     specifying the location of the root directory.  Everything
+ *     else follows from there.
+ *
+ * This module works by first reading the volume descriptors, then
+ * building a list of directory entries, sorted by starting
+ * sector.  At each step, I look for the earliest dir entry that
+ * hasn't yet been read, seek forward to that location and read
+ * that entry.  If it's a dir, I slurp in the new dir entries and
+ * add them to the heap; if it's a regular file, I return the
+ * corresponding archive_entry and wait for the client to request
+ * the file body.  This strategy allows us to read most compliant
+ * CDs with a single pass through the data, as required by libarchive.
+ */
+#define	LOGICAL_BLOCK_SIZE	2048
+#define	SYSTEM_AREA_BLOCK	16
+
+/* Structure of on-disk primary volume descriptor. */
+#define PVD_type_offset 0
+#define PVD_type_size 1
+#define PVD_id_offset (PVD_type_offset + PVD_type_size)
+#define PVD_id_size 5
+#define PVD_version_offset (PVD_id_offset + PVD_id_size)
+#define PVD_version_size 1
+#define PVD_reserved1_offset (PVD_version_offset + PVD_version_size)
+#define PVD_reserved1_size 1
+#define PVD_system_id_offset (PVD_reserved1_offset + PVD_reserved1_size)
+#define PVD_system_id_size 32
+#define PVD_volume_id_offset (PVD_system_id_offset + PVD_system_id_size)
+#define PVD_volume_id_size 32
+#define PVD_reserved2_offset (PVD_volume_id_offset + PVD_volume_id_size)
+#define PVD_reserved2_size 8
+#define PVD_volume_space_size_offset (PVD_reserved2_offset + PVD_reserved2_size)
+#define PVD_volume_space_size_size 8
+#define PVD_reserved3_offset (PVD_volume_space_size_offset + PVD_volume_space_size_size)
+#define PVD_reserved3_size 32
+#define PVD_volume_set_size_offset (PVD_reserved3_offset + PVD_reserved3_size)
+#define PVD_volume_set_size_size 4
+#define PVD_volume_sequence_number_offset (PVD_volume_set_size_offset + PVD_volume_set_size_size)
+#define PVD_volume_sequence_number_size 4
+#define PVD_logical_block_size_offset (PVD_volume_sequence_number_offset + PVD_volume_sequence_number_size)
+#define PVD_logical_block_size_size 4
+#define PVD_path_table_size_offset (PVD_logical_block_size_offset + PVD_logical_block_size_size)
+#define PVD_path_table_size_size 8
+#define PVD_type_1_path_table_offset (PVD_path_table_size_offset + PVD_path_table_size_size)
+#define PVD_type_1_path_table_size 4
+#define PVD_opt_type_1_path_table_offset (PVD_type_1_path_table_offset + PVD_type_1_path_table_size)
+#define PVD_opt_type_1_path_table_size 4
+#define PVD_type_m_path_table_offset (PVD_opt_type_1_path_table_offset + PVD_opt_type_1_path_table_size)
+#define PVD_type_m_path_table_size 4
+#define PVD_opt_type_m_path_table_offset (PVD_type_m_path_table_offset + PVD_type_m_path_table_size)
+#define PVD_opt_type_m_path_table_size 4
+#define PVD_root_directory_record_offset (PVD_opt_type_m_path_table_offset + PVD_opt_type_m_path_table_size)
+#define PVD_root_directory_record_size 34
+#define PVD_volume_set_id_offset (PVD_root_directory_record_offset + PVD_root_directory_record_size)
+#define PVD_volume_set_id_size 128
+#define PVD_publisher_id_offset (PVD_volume_set_id_offset + PVD_volume_set_id_size)
+#define PVD_publisher_id_size 128
+#define PVD_preparer_id_offset (PVD_publisher_id_offset + PVD_publisher_id_size)
+#define PVD_preparer_id_size 128
+#define PVD_application_id_offset (PVD_preparer_id_offset + PVD_preparer_id_size)
+#define PVD_application_id_size 128
+#define PVD_copyright_file_id_offset (PVD_application_id_offset + PVD_application_id_size)
+#define PVD_copyright_file_id_size 37
+#define PVD_abstract_file_id_offset (PVD_copyright_file_id_offset + PVD_copyright_file_id_size)
+#define PVD_abstract_file_id_size 37
+#define PVD_bibliographic_file_id_offset (PVD_abstract_file_id_offset + PVD_abstract_file_id_size)
+#define PVD_bibliographic_file_id_size 37
+#define PVD_creation_date_offset (PVD_bibliographic_file_id_offset + PVD_bibliographic_file_id_size)
+#define PVD_creation_date_size 17
+#define PVD_modification_date_offset (PVD_creation_date_offset + PVD_creation_date_size)
+#define PVD_modification_date_size 17
+#define PVD_expiration_date_offset (PVD_modification_date_offset + PVD_modification_date_size)
+#define PVD_expiration_date_size 17
+#define PVD_effective_date_offset (PVD_expiration_date_offset + PVD_expiration_date_size)
+#define PVD_effective_date_size 17
+#define PVD_file_structure_version_offset (PVD_effective_date_offset + PVD_effective_date_size)
+#define PVD_file_structure_version_size 1
+#define PVD_reserved4_offset (PVD_file_structure_version_offset + PVD_file_structure_version_size)
+#define PVD_reserved4_size 1
+#define PVD_application_data_offset (PVD_reserved4_offset + PVD_reserved4_size)
+#define PVD_application_data_size 512
+#define PVD_reserved5_offset (PVD_application_data_offset + PVD_application_data_size)
+#define PVD_reserved5_size (2048 - PVD_reserved5_offset)
+
+/* TODO: It would make future maintenance easier to just hardcode the
+ * above values.  In particular, ECMA119 states the offsets as part of
+ * the standard.  That would eliminate the need for the following check.*/
+#if PVD_reserved5_offset != 1395
+#error PVD offset and size definitions are wrong.
+#endif
+
+
+/* Structure of optional on-disk supplementary volume descriptor. */
+#define SVD_type_offset 0
+#define SVD_type_size 1
+#define SVD_id_offset (SVD_type_offset + SVD_type_size)
+#define SVD_id_size 5
+#define SVD_version_offset (SVD_id_offset + SVD_id_size)
+#define SVD_version_size 1
+/* ... */
+#define SVD_reserved1_offset	72
+#define SVD_reserved1_size	8
+#define SVD_volume_space_size_offset 80
+#define SVD_volume_space_size_size 8
+#define SVD_escape_sequences_offset (SVD_volume_space_size_offset + SVD_volume_space_size_size)
+#define SVD_escape_sequences_size 32
+/* ... */
+#define SVD_logical_block_size_offset 128
+#define SVD_logical_block_size_size 4
+#define SVD_type_L_path_table_offset 140
+#define SVD_type_M_path_table_offset 148
+/* ... */
+#define SVD_root_directory_record_offset 156
+#define SVD_root_directory_record_size 34
+#define SVD_file_structure_version_offset 881
+#define SVD_reserved2_offset	882
+#define SVD_reserved2_size	1
+#define SVD_reserved3_offset	1395
+#define SVD_reserved3_size	653
+/* ... */
+/* FIXME: validate correctness of last SVD entry offset. */
+
+/* Structure of an on-disk directory record. */
+/* Note:  ISO9660 stores each multi-byte integer twice, once in
+ * each byte order.  The sizes here are the size of just one
+ * of the two integers.  (This is why the offset of a field isn't
+ * the same as the offset+size of the previous field.) */
+#define DR_length_offset 0
+#define DR_length_size 1
+#define DR_ext_attr_length_offset 1
+#define DR_ext_attr_length_size 1
+#define DR_extent_offset 2
+#define DR_extent_size 4
+#define DR_size_offset 10
+#define DR_size_size 4
+#define DR_date_offset 18
+#define DR_date_size 7
+#define DR_flags_offset 25
+#define DR_flags_size 1
+#define DR_file_unit_size_offset 26
+#define DR_file_unit_size_size 1
+#define DR_interleave_offset 27
+#define DR_interleave_size 1
+#define DR_volume_sequence_number_offset 28
+#define DR_volume_sequence_number_size 2
+#define DR_name_len_offset 32
+#define DR_name_len_size 1
+#define DR_name_offset 33
+
+#ifdef HAVE_ZLIB_H
+static const unsigned char zisofs_magic[8] = {
+	0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07
+};
+
+struct zisofs {
+	/* Set 1 if this file compressed by paged zlib */
+	int		 pz;
+	int		 pz_log2_bs; /* Log2 of block size */
+	uint64_t	 pz_uncompressed_size;
+
+	int		 initialized;
+	unsigned char	*uncompressed_buffer;
+	size_t		 uncompressed_buffer_size;
+
+	uint32_t	 pz_offset;
+	unsigned char	 header[16];
+	size_t		 header_avail;
+	int		 header_passed;
+	unsigned char	*block_pointers;
+	size_t		 block_pointers_alloc;
+	size_t		 block_pointers_size;
+	size_t		 block_pointers_avail;
+	size_t		 block_off;
+	uint32_t	 block_avail;
+
+	z_stream	 stream;
+	int		 stream_valid;
+};
+#else
+struct zisofs {
+	/* Set 1 if this file compressed by paged zlib */
+	int		 pz;
+};
+#endif
+
+struct content {
+	uint64_t	 offset;/* Offset on disk.		*/
+	uint64_t	 size;	/* File size in bytes.		*/
+	struct content	*next;
+};
+
+/* In-memory storage for a directory record. */
+struct file_info {
+	struct file_info	*use_next;
+	struct file_info	*parent;
+	struct file_info	*next;
+	struct file_info	*re_next;
+	int		 subdirs;
+	uint64_t	 key;		/* Heap Key.			*/
+	uint64_t	 offset;	/* Offset on disk.		*/
+	uint64_t	 size;		/* File size in bytes.		*/
+	uint32_t	 ce_offset;	/* Offset of CE.		*/
+	uint32_t	 ce_size;	/* Size of CE.			*/
+	char		 rr_moved;	/* Flag to rr_moved.		*/
+	char		 rr_moved_has_re_only;
+	char		 re;		/* Having RRIP "RE" extension.	*/
+	char		 re_descendant;
+	uint64_t	 cl_offset;	/* Having RRIP "CL" extension.	*/
+	int		 birthtime_is_set;
+	time_t		 birthtime;	/* File created time.		*/
+	time_t		 mtime;		/* File last modified time.	*/
+	time_t		 atime;		/* File last accessed time.	*/
+	time_t		 ctime;		/* File attribute change time.	*/
+	uint64_t	 rdev;		/* Device number.		*/
+	mode_t		 mode;
+	uid_t		 uid;
+	gid_t		 gid;
+	int64_t		 number;
+	int		 nlinks;
+	struct archive_string name; /* Pathname */
+	unsigned char	*utf16be_name;
+	size_t		 utf16be_bytes;
+	char		 name_continues; /* Non-zero if name continues */
+	struct archive_string symlink;
+	char		 symlink_continues; /* Non-zero if link continues */
+	/* Set 1 if this file compressed by paged zlib(zisofs) */
+	int		 pz;
+	int		 pz_log2_bs; /* Log2 of block size */
+	uint64_t	 pz_uncompressed_size;
+	/* Set 1 if this file is multi extent. */
+	int		 multi_extent;
+	struct {
+		struct content	*first;
+		struct content	**last;
+	} contents;
+	struct {
+		struct file_info	*first;
+		struct file_info	**last;
+	} rede_files;
+};
+
+struct heap_queue {
+	struct file_info **files;
+	int		 allocated;
+	int		 used;
+};
+
+struct iso9660 {
+	int	magic;
+#define ISO9660_MAGIC   0x96609660
+
+	int opt_support_joliet;
+	int opt_support_rockridge;
+
+	struct archive_string pathname;
+	char	seenRockridge;	/* Set true if RR extensions are used. */
+	char	seenSUSP;	/* Set true if SUSP is being used. */
+	char	seenJoliet;
+
+	unsigned char	suspOffset;
+	struct file_info *rr_moved;
+	struct read_ce_queue {
+		struct read_ce_req {
+			uint64_t	 offset;/* Offset of CE on disk. */
+			struct file_info *file;
+		}		*reqs;
+		int		 cnt;
+		int		 allocated;
+	}	read_ce_req;
+
+	int64_t		previous_number;
+	struct archive_string previous_pathname;
+
+	struct file_info		*use_files;
+	struct heap_queue		 pending_files;
+	struct {
+		struct file_info	*first;
+		struct file_info	**last;
+	}	cache_files;
+	struct {
+		struct file_info	*first;
+		struct file_info	**last;
+	}	re_files;
+
+	uint64_t current_position;
+	ssize_t	logical_block_size;
+	uint64_t volume_size; /* Total size of volume in bytes. */
+	int32_t  volume_block;/* Total size of volume in logical blocks. */
+
+	struct vd {
+		int		location;	/* Location of Extent.	*/
+		uint32_t	size;
+	} primary, joliet;
+
+	int64_t	entry_sparse_offset;
+	int64_t	entry_bytes_remaining;
+	size_t  entry_bytes_unconsumed;
+	struct zisofs	 entry_zisofs;
+	struct content	*entry_content;
+	struct archive_string_conv *sconv_utf16be;
+	/*
+	 * Buffers for a full pathname in UTF-16BE in Joliet extensions.
+	 */
+#define UTF16_NAME_MAX	1024
+	unsigned char *utf16be_path;
+	size_t		 utf16be_path_len;
+	unsigned char *utf16be_previous_path;
+	size_t		 utf16be_previous_path_len;
+	/* Null buffer used in bidder to improve its performance. */
+	unsigned char	 null[2048];
+};
+
+static int	archive_read_format_iso9660_bid(struct archive_read *, int);
+static int	archive_read_format_iso9660_options(struct archive_read *,
+		    const char *, const char *);
+static int	archive_read_format_iso9660_cleanup(struct archive_read *);
+static int	archive_read_format_iso9660_read_data(struct archive_read *,
+		    const void **, size_t *, int64_t *);
+static int	archive_read_format_iso9660_read_data_skip(struct archive_read *);
+static int	archive_read_format_iso9660_read_header(struct archive_read *,
+		    struct archive_entry *);
+static const char *build_pathname(struct archive_string *, struct file_info *, int);
+static int	build_pathname_utf16be(unsigned char *, size_t, size_t *,
+		    struct file_info *);
+#if DEBUG
+static void	dump_isodirrec(FILE *, const unsigned char *isodirrec);
+#endif
+static time_t	time_from_tm(struct tm *);
+static time_t	isodate17(const unsigned char *);
+static time_t	isodate7(const unsigned char *);
+static int	isBootRecord(struct iso9660 *, const unsigned char *);
+static int	isVolumePartition(struct iso9660 *, const unsigned char *);
+static int	isVDSetTerminator(struct iso9660 *, const unsigned char *);
+static int	isJolietSVD(struct iso9660 *, const unsigned char *);
+static int	isSVD(struct iso9660 *, const unsigned char *);
+static int	isEVD(struct iso9660 *, const unsigned char *);
+static int	isPVD(struct iso9660 *, const unsigned char *);
+static int	next_cache_entry(struct archive_read *, struct iso9660 *,
+		    struct file_info **);
+static int	next_entry_seek(struct archive_read *, struct iso9660 *,
+		    struct file_info **);
+static struct file_info *
+		parse_file_info(struct archive_read *a,
+		    struct file_info *parent, const unsigned char *isodirrec,
+		    size_t reclen);
+static int	parse_rockridge(struct archive_read *a,
+		    struct file_info *file, const unsigned char *start,
+		    const unsigned char *end);
+static int	register_CE(struct archive_read *a, int32_t location,
+		    struct file_info *file);
+static int	read_CE(struct archive_read *a, struct iso9660 *iso9660);
+static void	parse_rockridge_NM1(struct file_info *,
+		    const unsigned char *, int);
+static void	parse_rockridge_SL1(struct file_info *,
+		    const unsigned char *, int);
+static void	parse_rockridge_TF1(struct file_info *,
+		    const unsigned char *, int);
+static void	parse_rockridge_ZF1(struct file_info *,
+		    const unsigned char *, int);
+static void	register_file(struct iso9660 *, struct file_info *);
+static void	release_files(struct iso9660 *);
+static unsigned	toi(const void *p, int n);
+static inline void re_add_entry(struct iso9660 *, struct file_info *);
+static inline struct file_info * re_get_entry(struct iso9660 *);
+static inline int rede_add_entry(struct file_info *);
+static inline struct file_info * rede_get_entry(struct file_info *);
+static inline void cache_add_entry(struct iso9660 *iso9660,
+		    struct file_info *file);
+static inline struct file_info *cache_get_entry(struct iso9660 *iso9660);
+static int	heap_add_entry(struct archive_read *a, struct heap_queue *heap,
+		    struct file_info *file, uint64_t key);
+static struct file_info *heap_get_entry(struct heap_queue *heap);
+
+#define add_entry(arch, iso9660, file)	\
+	heap_add_entry(arch, &((iso9660)->pending_files), file, file->offset)
+#define next_entry(iso9660)		\
+	heap_get_entry(&((iso9660)->pending_files))
+
+int
+archive_read_support_format_iso9660(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct iso9660 *iso9660;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_iso9660");
+
+	iso9660 = (struct iso9660 *)calloc(1, sizeof(*iso9660));
+	if (iso9660 == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate iso9660 data");
+		return (ARCHIVE_FATAL);
+	}
+	iso9660->magic = ISO9660_MAGIC;
+	iso9660->cache_files.first = NULL;
+	iso9660->cache_files.last = &(iso9660->cache_files.first);
+	iso9660->re_files.first = NULL;
+	iso9660->re_files.last = &(iso9660->re_files.first);
+	/* Enable to support Joliet extensions by default.	*/
+	iso9660->opt_support_joliet = 1;
+	/* Enable to support Rock Ridge extensions by default.	*/
+	iso9660->opt_support_rockridge = 1;
+
+	r = __archive_read_register_format(a,
+	    iso9660,
+	    "iso9660",
+	    archive_read_format_iso9660_bid,
+	    archive_read_format_iso9660_options,
+	    archive_read_format_iso9660_read_header,
+	    archive_read_format_iso9660_read_data,
+	    archive_read_format_iso9660_read_data_skip,
+	    NULL,
+	    archive_read_format_iso9660_cleanup,
+	    NULL,
+	    NULL);
+
+	if (r != ARCHIVE_OK) {
+		free(iso9660);
+		return (r);
+	}
+	return (ARCHIVE_OK);
+}
+
+
+static int
+archive_read_format_iso9660_bid(struct archive_read *a, int best_bid)
+{
+	struct iso9660 *iso9660;
+	ssize_t bytes_read;
+	const unsigned char *p;
+	int seenTerminator;
+
+	/* If there's already a better bid than we can ever
+	   make, don't bother testing. */
+	if (best_bid > 48)
+		return (-1);
+
+	iso9660 = (struct iso9660 *)(a->format->data);
+
+	/*
+	 * Skip the first 32k (reserved area) and get the first
+	 * 8 sectors of the volume descriptor table.  Of course,
+	 * if the I/O layer gives us more, we'll take it.
+	 */
+#define RESERVED_AREA	(SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE)
+	p = __archive_read_ahead(a,
+	    RESERVED_AREA + 8 * LOGICAL_BLOCK_SIZE,
+	    &bytes_read);
+	if (p == NULL)
+	    return (-1);
+
+	/* Skip the reserved area. */
+	bytes_read -= RESERVED_AREA;
+	p += RESERVED_AREA;
+
+	/* Check each volume descriptor. */
+	seenTerminator = 0;
+	for (; bytes_read > LOGICAL_BLOCK_SIZE;
+	    bytes_read -= LOGICAL_BLOCK_SIZE, p += LOGICAL_BLOCK_SIZE) {
+		/* Do not handle undefined Volume Descriptor Type. */
+		if (p[0] >= 4 && p[0] <= 254)
+			return (0);
+		/* Standard Identifier must be "CD001" */
+		if (memcmp(p + 1, "CD001", 5) != 0)
+			return (0);
+		if (isPVD(iso9660, p))
+			continue;
+		if (!iso9660->joliet.location) {
+			if (isJolietSVD(iso9660, p))
+				continue;
+		}
+		if (isBootRecord(iso9660, p))
+			continue;
+		if (isEVD(iso9660, p))
+			continue;
+		if (isSVD(iso9660, p))
+			continue;
+		if (isVolumePartition(iso9660, p))
+			continue;
+		if (isVDSetTerminator(iso9660, p)) {
+			seenTerminator = 1;
+			break;
+		}
+		return (0);
+	}
+	/*
+	 * ISO 9660 format must have Primary Volume Descriptor and
+	 * Volume Descriptor Set Terminator.
+	 */
+	if (seenTerminator && iso9660->primary.location > 16)
+		return (48);
+
+	/* We didn't find a valid PVD; return a bid of zero. */
+	return (0);
+}
+
+static int
+archive_read_format_iso9660_options(struct archive_read *a,
+		const char *key, const char *val)
+{
+	struct iso9660 *iso9660;
+
+	iso9660 = (struct iso9660 *)(a->format->data);
+
+	if (strcmp(key, "joliet") == 0) {
+		if (val == NULL || strcmp(val, "off") == 0 ||
+				strcmp(val, "ignore") == 0 ||
+				strcmp(val, "disable") == 0 ||
+				strcmp(val, "0") == 0)
+			iso9660->opt_support_joliet = 0;
+		else
+			iso9660->opt_support_joliet = 1;
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "rockridge") == 0 ||
+	    strcmp(key, "Rockridge") == 0) {
+		iso9660->opt_support_rockridge = val != NULL;
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+isNull(struct iso9660 *iso9660, const unsigned char *h, unsigned offset,
+unsigned bytes)
+{
+
+	while (bytes >= sizeof(iso9660->null)) {
+		if (!memcmp(iso9660->null, h + offset, sizeof(iso9660->null)))
+			return (0);
+		offset += sizeof(iso9660->null);
+		bytes -= sizeof(iso9660->null);
+	}
+	if (bytes)
+		return memcmp(iso9660->null, h + offset, bytes) == 0;
+	else
+		return (1);
+}
+
+static int
+isBootRecord(struct iso9660 *iso9660, const unsigned char *h)
+{
+	(void)iso9660; /* UNUSED */
+
+	/* Type of the Volume Descriptor Boot Record must be 0. */
+	if (h[0] != 0)
+		return (0);
+
+	/* Volume Descriptor Version must be 1. */
+	if (h[6] != 1)
+		return (0);
+
+	return (1);
+}
+
+static int
+isVolumePartition(struct iso9660 *iso9660, const unsigned char *h)
+{
+	int32_t location;
+
+	/* Type of the Volume Partition Descriptor must be 3. */
+	if (h[0] != 3)
+		return (0);
+
+	/* Volume Descriptor Version must be 1. */
+	if (h[6] != 1)
+		return (0);
+	/* Unused Field */
+	if (h[7] != 0)
+		return (0);
+
+	location = archive_le32dec(h + 72);
+	if (location <= SYSTEM_AREA_BLOCK ||
+	    location >= iso9660->volume_block)
+		return (0);
+	if ((uint32_t)location != archive_be32dec(h + 76))
+		return (0);
+
+	return (1);
+}
+
+static int
+isVDSetTerminator(struct iso9660 *iso9660, const unsigned char *h)
+{
+	(void)iso9660; /* UNUSED */
+
+	/* Type of the Volume Descriptor Set Terminator must be 255. */
+	if (h[0] != 255)
+		return (0);
+
+	/* Volume Descriptor Version must be 1. */
+	if (h[6] != 1)
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (!isNull(iso9660, h, 7, 2048-7))
+		return (0);
+
+	return (1);
+}
+
+static int
+isJolietSVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+	const unsigned char *p;
+	ssize_t logical_block_size;
+	int32_t volume_block;
+
+	/* Check if current sector is a kind of Supplementary Volume
+	 * Descriptor. */
+	if (!isSVD(iso9660, h))
+		return (0);
+
+	/* FIXME: do more validations according to joliet spec. */
+
+	/* check if this SVD contains joliet extension! */
+	p = h + SVD_escape_sequences_offset;
+	/* N.B. Joliet spec says p[1] == '\\', but.... */
+	if (p[0] == '%' && p[1] == '/') {
+		int level = 0;
+
+		if (p[2] == '@')
+			level = 1;
+		else if (p[2] == 'C')
+			level = 2;
+		else if (p[2] == 'E')
+			level = 3;
+		else /* not joliet */
+			return (0);
+
+		iso9660->seenJoliet = level;
+
+	} else /* not joliet */
+		return (0);
+
+	logical_block_size =
+	    archive_le16dec(h + SVD_logical_block_size_offset);
+	volume_block = archive_le32dec(h + SVD_volume_space_size_offset);
+
+	iso9660->logical_block_size = logical_block_size;
+	iso9660->volume_block = volume_block;
+	iso9660->volume_size = logical_block_size * (uint64_t)volume_block;
+	/* Read Root Directory Record in Volume Descriptor. */
+	p = h + SVD_root_directory_record_offset;
+	iso9660->joliet.location = archive_le32dec(p + DR_extent_offset);
+	iso9660->joliet.size = archive_le32dec(p + DR_size_offset);
+
+	return (48);
+}
+
+static int
+isSVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+	const unsigned char *p;
+	ssize_t logical_block_size;
+	int32_t volume_block;
+	int32_t location;
+
+	(void)iso9660; /* UNUSED */
+
+	/* Type 2 means it's a SVD. */
+	if (h[SVD_type_offset] != 2)
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (!isNull(iso9660, h, SVD_reserved1_offset, SVD_reserved1_size))
+		return (0);
+	if (!isNull(iso9660, h, SVD_reserved2_offset, SVD_reserved2_size))
+		return (0);
+	if (!isNull(iso9660, h, SVD_reserved3_offset, SVD_reserved3_size))
+		return (0);
+
+	/* File structure version must be 1 for ISO9660/ECMA119. */
+	if (h[SVD_file_structure_version_offset] != 1)
+		return (0);
+
+	logical_block_size =
+	    archive_le16dec(h + SVD_logical_block_size_offset);
+	if (logical_block_size <= 0)
+		return (0);
+
+	volume_block = archive_le32dec(h + SVD_volume_space_size_offset);
+	if (volume_block <= SYSTEM_AREA_BLOCK+4)
+		return (0);
+
+	/* Location of Occurrence of Type L Path Table must be
+	 * available location,
+	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+	location = archive_le32dec(h+SVD_type_L_path_table_offset);
+	if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+		return (0);
+
+	/* The Type M Path Table must be at a valid location (WinISO
+	 * and probably other programs omit this, so we allow zero)
+	 *
+	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+	location = archive_be32dec(h+SVD_type_M_path_table_offset);
+	if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
+	    || location >= volume_block)
+		return (0);
+
+	/* Read Root Directory Record in Volume Descriptor. */
+	p = h + SVD_root_directory_record_offset;
+	if (p[DR_length_offset] != 34)
+		return (0);
+
+	return (48);
+}
+
+static int
+isEVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+	const unsigned char *p;
+	ssize_t logical_block_size;
+	int32_t volume_block;
+	int32_t location;
+
+	(void)iso9660; /* UNUSED */
+
+	/* Type of the Enhanced Volume Descriptor must be 2. */
+	if (h[PVD_type_offset] != 2)
+		return (0);
+
+	/* EVD version must be 2. */
+	if (h[PVD_version_offset] != 2)
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (h[PVD_reserved1_offset] != 0)
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+		return (0);
+
+	/* Logical block size must be > 0. */
+	/* I've looked at Ecma 119 and can't find any stronger
+	 * restriction on this field. */
+	logical_block_size =
+	    archive_le16dec(h + PVD_logical_block_size_offset);
+	if (logical_block_size <= 0)
+		return (0);
+
+	volume_block =
+	    archive_le32dec(h + PVD_volume_space_size_offset);
+	if (volume_block <= SYSTEM_AREA_BLOCK+4)
+		return (0);
+
+	/* File structure version must be 2 for ISO9660:1999. */
+	if (h[PVD_file_structure_version_offset] != 2)
+		return (0);
+
+	/* Location of Occurrence of Type L Path Table must be
+	 * available location,
+	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+	location = archive_le32dec(h+PVD_type_1_path_table_offset);
+	if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+		return (0);
+
+	/* Location of Occurrence of Type M Path Table must be
+	 * available location,
+	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+	location = archive_be32dec(h+PVD_type_m_path_table_offset);
+	if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
+	    || location >= volume_block)
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (!isNull(iso9660, h, PVD_reserved4_offset, PVD_reserved4_size))
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
+		return (0);
+
+	/* Read Root Directory Record in Volume Descriptor. */
+	p = h + PVD_root_directory_record_offset;
+	if (p[DR_length_offset] != 34)
+		return (0);
+
+	return (48);
+}
+
+static int
+isPVD(struct iso9660 *iso9660, const unsigned char *h)
+{
+	const unsigned char *p;
+	ssize_t logical_block_size;
+	int32_t volume_block;
+	int32_t location;
+	int i;
+
+	/* Type of the Primary Volume Descriptor must be 1. */
+	if (h[PVD_type_offset] != 1)
+		return (0);
+
+	/* PVD version must be 1. */
+	if (h[PVD_version_offset] != 1)
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (h[PVD_reserved1_offset] != 0)
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (!isNull(iso9660, h, PVD_reserved2_offset, PVD_reserved2_size))
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+		return (0);
+
+	/* Logical block size must be > 0. */
+	/* I've looked at Ecma 119 and can't find any stronger
+	 * restriction on this field. */
+	logical_block_size =
+	    archive_le16dec(h + PVD_logical_block_size_offset);
+	if (logical_block_size <= 0)
+		return (0);
+
+	volume_block = archive_le32dec(h + PVD_volume_space_size_offset);
+	if (volume_block <= SYSTEM_AREA_BLOCK+4)
+		return (0);
+
+	/* File structure version must be 1 for ISO9660/ECMA119. */
+	if (h[PVD_file_structure_version_offset] != 1)
+		return (0);
+
+	/* Location of Occurrence of Type L Path Table must be
+	 * available location,
+	 * > SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+	location = archive_le32dec(h+PVD_type_1_path_table_offset);
+	if (location < SYSTEM_AREA_BLOCK+2 || location >= volume_block)
+		return (0);
+
+	/* The Type M Path Table must also be at a valid location
+	 * (although ECMA 119 requires a Type M Path Table, WinISO and
+	 * probably other programs omit it, so we permit a zero here)
+	 *
+	 * >= SYSTEM_AREA_BLOCK(16) + 2 and < Volume Space Size. */
+	location = archive_be32dec(h+PVD_type_m_path_table_offset);
+	if ((location > 0 && location < SYSTEM_AREA_BLOCK+2)
+	    || location >= volume_block)
+		return (0);
+
+	/* Reserved field must be 0. */
+	/* But accept NetBSD/FreeBSD "makefs" images with 0x20 here. */
+	for (i = 0; i < PVD_reserved4_size; ++i)
+		if (h[PVD_reserved4_offset + i] != 0
+		    && h[PVD_reserved4_offset + i] != 0x20)
+			return (0);
+
+	/* Reserved field must be 0. */
+	if (!isNull(iso9660, h, PVD_reserved5_offset, PVD_reserved5_size))
+		return (0);
+
+	/* XXX TODO: Check other values for sanity; reject more
+	 * malformed PVDs. XXX */
+
+	/* Read Root Directory Record in Volume Descriptor. */
+	p = h + PVD_root_directory_record_offset;
+	if (p[DR_length_offset] != 34)
+		return (0);
+
+	if (!iso9660->primary.location) {
+		iso9660->logical_block_size = logical_block_size;
+		iso9660->volume_block = volume_block;
+		iso9660->volume_size =
+		    logical_block_size * (uint64_t)volume_block;
+		iso9660->primary.location =
+		    archive_le32dec(p + DR_extent_offset);
+		iso9660->primary.size = archive_le32dec(p + DR_size_offset);
+	}
+
+	return (48);
+}
+
+static int
+read_children(struct archive_read *a, struct file_info *parent)
+{
+	struct iso9660 *iso9660;
+	const unsigned char *b, *p;
+	struct file_info *multi;
+	size_t step, skip_size;
+
+	iso9660 = (struct iso9660 *)(a->format->data);
+	/* flush any remaining bytes from the last round to ensure
+	 * we're positioned */
+	if (iso9660->entry_bytes_unconsumed) {
+		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+		iso9660->entry_bytes_unconsumed = 0;
+	}
+	if (iso9660->current_position > parent->offset) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Ignoring out-of-order directory (%s) %jd > %jd",
+		    parent->name.s,
+		    (intmax_t)iso9660->current_position,
+		    (intmax_t)parent->offset);
+		return (ARCHIVE_WARN);
+	}
+	if (parent->offset + parent->size > iso9660->volume_size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Directory is beyond end-of-media: %s",
+		    parent->name.s);
+		return (ARCHIVE_WARN);
+	}
+	if (iso9660->current_position < parent->offset) {
+		int64_t skipsize;
+
+		skipsize = parent->offset - iso9660->current_position;
+		skipsize = __archive_read_consume(a, skipsize);
+		if (skipsize < 0)
+			return ((int)skipsize);
+		iso9660->current_position = parent->offset;
+	}
+
+	step = (size_t)(((parent->size + iso9660->logical_block_size -1) /
+	    iso9660->logical_block_size) * iso9660->logical_block_size);
+	b = __archive_read_ahead(a, step, NULL);
+	if (b == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to read full block when scanning "
+		    "ISO9660 directory list");
+		return (ARCHIVE_FATAL);
+	}
+	iso9660->current_position += step;
+	multi = NULL;
+	skip_size = step;
+	while (step) {
+		p = b;
+		b += iso9660->logical_block_size;
+		step -= iso9660->logical_block_size;
+		for (; *p != 0 && p < b && p + *p <= b; p += *p) {
+			struct file_info *child;
+
+			/* N.B.: these special directory identifiers
+			 * are 8 bit "values" even on a
+			 * Joliet CD with UCS-2 (16bit) encoding.
+			 */
+
+			/* Skip '.' entry. */
+			if (*(p + DR_name_len_offset) == 1
+			    && *(p + DR_name_offset) == '\0')
+				continue;
+			/* Skip '..' entry. */
+			if (*(p + DR_name_len_offset) == 1
+			    && *(p + DR_name_offset) == '\001')
+				continue;
+			child = parse_file_info(a, parent, p, b - p);
+			if (child == NULL) {
+				__archive_read_consume(a, skip_size);
+				return (ARCHIVE_FATAL);
+			}
+			if (child->cl_offset == 0 &&
+			    (child->multi_extent || multi != NULL)) {
+				struct content *con;
+
+				if (multi == NULL) {
+					multi = child;
+					multi->contents.first = NULL;
+					multi->contents.last =
+					    &(multi->contents.first);
+				}
+				con = malloc(sizeof(struct content));
+				if (con == NULL) {
+					archive_set_error(
+					    &a->archive, ENOMEM,
+					    "No memory for multi extent");
+					__archive_read_consume(a, skip_size);
+					return (ARCHIVE_FATAL);
+				}
+				con->offset = child->offset;
+				con->size = child->size;
+				con->next = NULL;
+				*multi->contents.last = con;
+				multi->contents.last = &(con->next);
+				if (multi == child) {
+					if (add_entry(a, iso9660, child)
+					    != ARCHIVE_OK)
+						return (ARCHIVE_FATAL);
+				} else {
+					multi->size += child->size;
+					if (!child->multi_extent)
+						multi = NULL;
+				}
+			} else
+				if (add_entry(a, iso9660, child) != ARCHIVE_OK)
+					return (ARCHIVE_FATAL);
+		}
+	}
+
+	__archive_read_consume(a, skip_size);
+
+	/* Read data which recorded by RRIP "CE" extension. */
+	if (read_CE(a, iso9660) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+choose_volume(struct archive_read *a, struct iso9660 *iso9660)
+{
+	struct file_info *file;
+	int64_t skipsize;
+	struct vd *vd;
+	const void *block;
+	char seenJoliet;
+
+	vd = &(iso9660->primary);
+	if (!iso9660->opt_support_joliet)
+		iso9660->seenJoliet = 0;
+	if (iso9660->seenJoliet &&
+		vd->location > iso9660->joliet.location)
+		/* This condition is unlikely; by way of caution. */
+		vd = &(iso9660->joliet);
+
+	skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location;
+	skipsize = __archive_read_consume(a, skipsize);
+	if (skipsize < 0)
+		return ((int)skipsize);
+	iso9660->current_position = skipsize;
+
+	block = __archive_read_ahead(a, vd->size, NULL);
+	if (block == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to read full block when scanning "
+		    "ISO9660 directory list");
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * While reading Root Directory, flag seenJoliet must be zero to
+	 * avoid converting special name 0x00(Current Directory) and
+	 * next byte to UCS2.
+	 */
+	seenJoliet = iso9660->seenJoliet;/* Save flag. */
+	iso9660->seenJoliet = 0;
+	file = parse_file_info(a, NULL, block, vd->size);
+	if (file == NULL)
+		return (ARCHIVE_FATAL);
+	iso9660->seenJoliet = seenJoliet;
+
+	/*
+	 * If the iso image has both RockRidge and Joliet, we preferentially
+	 * use RockRidge Extensions rather than Joliet ones.
+	 */
+	if (vd == &(iso9660->primary) && iso9660->seenRockridge
+	    && iso9660->seenJoliet)
+		iso9660->seenJoliet = 0;
+
+	if (vd == &(iso9660->primary) && !iso9660->seenRockridge
+	    && iso9660->seenJoliet) {
+		/* Switch reading data from primary to joliet. */
+		vd = &(iso9660->joliet);
+		skipsize = LOGICAL_BLOCK_SIZE * (int64_t)vd->location;
+		skipsize -= iso9660->current_position;
+		skipsize = __archive_read_consume(a, skipsize);
+		if (skipsize < 0)
+			return ((int)skipsize);
+		iso9660->current_position += skipsize;
+
+		block = __archive_read_ahead(a, vd->size, NULL);
+		if (block == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Failed to read full block when scanning "
+			    "ISO9660 directory list");
+			return (ARCHIVE_FATAL);
+		}
+		iso9660->seenJoliet = 0;
+		file = parse_file_info(a, NULL, block, vd->size);
+		if (file == NULL)
+			return (ARCHIVE_FATAL);
+		iso9660->seenJoliet = seenJoliet;
+	}
+
+	/* Store the root directory in the pending list. */
+	if (add_entry(a, iso9660, file) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	if (iso9660->seenRockridge) {
+		a->archive.archive_format = ARCHIVE_FORMAT_ISO9660_ROCKRIDGE;
+		a->archive.archive_format_name =
+		    "ISO9660 with Rockridge extensions";
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_iso9660_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct iso9660 *iso9660;
+	struct file_info *file;
+	int r, rd_r = ARCHIVE_OK;
+
+	iso9660 = (struct iso9660 *)(a->format->data);
+
+	if (!a->archive.archive_format) {
+		a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
+		a->archive.archive_format_name = "ISO9660";
+	}
+
+	if (iso9660->current_position == 0) {
+		r = choose_volume(a, iso9660);
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	file = NULL;/* Eliminate a warning. */
+	/* Get the next entry that appears after the current offset. */
+	r = next_entry_seek(a, iso9660, &file);
+	if (r != ARCHIVE_OK)
+		return (r);
+
+	if (iso9660->seenJoliet) {
+		/*
+		 * Convert UTF-16BE of a filename to local locale MBS
+		 * and store the result into a filename field.
+		 */
+		if (iso9660->sconv_utf16be == NULL) {
+			iso9660->sconv_utf16be =
+			    archive_string_conversion_from_charset(
+				&(a->archive), "UTF-16BE", 1);
+			if (iso9660->sconv_utf16be == NULL)
+				/* Couldn't allocate memory */
+				return (ARCHIVE_FATAL);
+		}
+		if (iso9660->utf16be_path == NULL) {
+			iso9660->utf16be_path = malloc(UTF16_NAME_MAX);
+			if (iso9660->utf16be_path == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory");
+				return (ARCHIVE_FATAL);
+			}
+		}
+		if (iso9660->utf16be_previous_path == NULL) {
+			iso9660->utf16be_previous_path = malloc(UTF16_NAME_MAX);
+			if (iso9660->utf16be_previous_path == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory");
+				return (ARCHIVE_FATAL);
+			}
+		}
+
+		iso9660->utf16be_path_len = 0;
+		if (build_pathname_utf16be(iso9660->utf16be_path,
+		    UTF16_NAME_MAX, &(iso9660->utf16be_path_len), file) != 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Pathname is too long");
+			return (ARCHIVE_FATAL);
+		}
+
+		r = archive_entry_copy_pathname_l(entry,
+		    (const char *)iso9660->utf16be_path,
+		    iso9660->utf16be_path_len,
+		    iso9660->sconv_utf16be);
+		if (r != 0) {
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory for Pathname");
+				return (ARCHIVE_FATAL);
+			}
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Pathname cannot be converted "
+			    "from %s to current locale.",
+			    archive_string_conversion_charset_name(
+			      iso9660->sconv_utf16be));
+
+			rd_r = ARCHIVE_WARN;
+		}
+	} else {
+		const char *path = build_pathname(&iso9660->pathname, file, 0);
+		if (path == NULL) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Pathname is too long");
+			return (ARCHIVE_FATAL);
+		} else {
+			archive_string_empty(&iso9660->pathname);
+			archive_entry_set_pathname(entry, path);
+		}
+	}
+
+	iso9660->entry_bytes_remaining = file->size;
+	/* Offset for sparse-file-aware clients. */
+	iso9660->entry_sparse_offset = 0;
+
+	if (file->offset + file->size > iso9660->volume_size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "File is beyond end-of-media: %s",
+		    archive_entry_pathname(entry));
+		iso9660->entry_bytes_remaining = 0;
+		return (ARCHIVE_WARN);
+	}
+
+	/* Set up the entry structure with information about this entry. */
+	archive_entry_set_mode(entry, file->mode);
+	archive_entry_set_uid(entry, file->uid);
+	archive_entry_set_gid(entry, file->gid);
+	archive_entry_set_nlink(entry, file->nlinks);
+	if (file->birthtime_is_set)
+		archive_entry_set_birthtime(entry, file->birthtime, 0);
+	else
+		archive_entry_unset_birthtime(entry);
+	archive_entry_set_mtime(entry, file->mtime, 0);
+	archive_entry_set_ctime(entry, file->ctime, 0);
+	archive_entry_set_atime(entry, file->atime, 0);
+	/* N.B.: Rock Ridge supports 64-bit device numbers. */
+	archive_entry_set_rdev(entry, (dev_t)file->rdev);
+	archive_entry_set_size(entry, iso9660->entry_bytes_remaining);
+	if (file->symlink.s != NULL)
+		archive_entry_copy_symlink(entry, file->symlink.s);
+
+	/* Note: If the input isn't seekable, we can't rewind to
+	 * return the same body again, so if the next entry refers to
+	 * the same data, we have to return it as a hardlink to the
+	 * original entry. */
+	if (file->number != -1 &&
+	    file->number == iso9660->previous_number) {
+		if (iso9660->seenJoliet) {
+			r = archive_entry_copy_hardlink_l(entry,
+			    (const char *)iso9660->utf16be_previous_path,
+			    iso9660->utf16be_previous_path_len,
+			    iso9660->sconv_utf16be);
+			if (r != 0) {
+				if (errno == ENOMEM) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "No memory for Linkname");
+					return (ARCHIVE_FATAL);
+				}
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Linkname cannot be converted "
+				    "from %s to current locale.",
+				    archive_string_conversion_charset_name(
+				      iso9660->sconv_utf16be));
+				rd_r = ARCHIVE_WARN;
+			}
+		} else
+			archive_entry_set_hardlink(entry,
+			    iso9660->previous_pathname.s);
+		archive_entry_unset_size(entry);
+		iso9660->entry_bytes_remaining = 0;
+		return (rd_r);
+	}
+
+	if ((file->mode & AE_IFMT) != AE_IFDIR &&
+	    file->offset < iso9660->current_position) {
+		int64_t r64;
+
+		r64 = __archive_read_seek(a, file->offset, SEEK_SET);
+		if (r64 != (int64_t)file->offset) {
+			/* We can't seek backwards to extract it, so issue
+			 * a warning.  Note that this can only happen if
+			 * this entry was added to the heap after we passed
+			 * this offset, that is, only if the directory
+			 * mentioning this entry is later than the body of
+			 * the entry. Such layouts are very unusual; most
+			 * ISO9660 writers lay out and record all directory
+			 * information first, then store all file bodies. */
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Ignoring out-of-order file @%jx (%s) %jd < %jd",
+			    (intmax_t)file->number,
+			    iso9660->pathname.s,
+			    (intmax_t)file->offset,
+			    (intmax_t)iso9660->current_position);
+			iso9660->entry_bytes_remaining = 0;
+			return (ARCHIVE_WARN);
+		}
+		iso9660->current_position = (uint64_t)r64;
+	}
+
+	/* Initialize zisofs variables. */
+	iso9660->entry_zisofs.pz = file->pz;
+	if (file->pz) {
+#ifdef HAVE_ZLIB_H
+		struct zisofs  *zisofs;
+
+		zisofs = &iso9660->entry_zisofs;
+		zisofs->initialized = 0;
+		zisofs->pz_log2_bs = file->pz_log2_bs;
+		zisofs->pz_uncompressed_size = file->pz_uncompressed_size;
+		zisofs->pz_offset = 0;
+		zisofs->header_avail = 0;
+		zisofs->header_passed = 0;
+		zisofs->block_pointers_avail = 0;
+#endif
+		archive_entry_set_size(entry, file->pz_uncompressed_size);
+	}
+
+	iso9660->previous_number = file->number;
+	if (iso9660->seenJoliet) {
+		memcpy(iso9660->utf16be_previous_path, iso9660->utf16be_path,
+		    iso9660->utf16be_path_len);
+		iso9660->utf16be_previous_path_len = iso9660->utf16be_path_len;
+	} else
+		archive_strcpy(
+		    &iso9660->previous_pathname, iso9660->pathname.s);
+
+	/* Reset entry_bytes_remaining if the file is multi extent. */
+	iso9660->entry_content = file->contents.first;
+	if (iso9660->entry_content != NULL)
+		iso9660->entry_bytes_remaining = iso9660->entry_content->size;
+
+	if (archive_entry_filetype(entry) == AE_IFDIR) {
+		/* Overwrite nlinks by proper link number which is
+		 * calculated from number of sub directories. */
+		archive_entry_set_nlink(entry, 2 + file->subdirs);
+		/* Directory data has been read completely. */
+		iso9660->entry_bytes_remaining = 0;
+	}
+
+	if (rd_r != ARCHIVE_OK)
+		return (rd_r);
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_iso9660_read_data_skip(struct archive_read *a)
+{
+	/* Because read_next_header always does an explicit skip
+	 * to the next entry, we don't need to do anything here. */
+	(void)a; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+#ifdef HAVE_ZLIB_H
+
+static int
+zisofs_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	struct iso9660 *iso9660;
+	struct zisofs  *zisofs;
+	const unsigned char *p;
+	size_t avail;
+	ssize_t bytes_read;
+	size_t uncompressed_size;
+	int r;
+
+	iso9660 = (struct iso9660 *)(a->format->data);
+	zisofs = &iso9660->entry_zisofs;
+
+	p = __archive_read_ahead(a, 1, &bytes_read);
+	if (bytes_read <= 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated zisofs file body");
+		return (ARCHIVE_FATAL);
+	}
+	if (bytes_read > iso9660->entry_bytes_remaining)
+		bytes_read = (ssize_t)iso9660->entry_bytes_remaining;
+	avail = bytes_read;
+	uncompressed_size = 0;
+
+	if (!zisofs->initialized) {
+		size_t ceil, xsize;
+
+		/* Allocate block pointers buffer. */
+		ceil = (size_t)((zisofs->pz_uncompressed_size +
+			(((int64_t)1) << zisofs->pz_log2_bs) - 1)
+			>> zisofs->pz_log2_bs);
+		xsize = (ceil + 1) * 4;
+		if (zisofs->block_pointers_alloc < xsize) {
+			size_t alloc;
+
+			if (zisofs->block_pointers != NULL)
+				free(zisofs->block_pointers);
+			alloc = ((xsize >> 10) + 1) << 10;
+			zisofs->block_pointers = malloc(alloc);
+			if (zisofs->block_pointers == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory for zisofs decompression");
+				return (ARCHIVE_FATAL);
+			}
+			zisofs->block_pointers_alloc = alloc;
+		}
+		zisofs->block_pointers_size = xsize;
+
+		/* Allocate uncompressed data buffer. */
+		xsize = (size_t)1UL << zisofs->pz_log2_bs;
+		if (zisofs->uncompressed_buffer_size < xsize) {
+			if (zisofs->uncompressed_buffer != NULL)
+				free(zisofs->uncompressed_buffer);
+			zisofs->uncompressed_buffer = malloc(xsize);
+			if (zisofs->uncompressed_buffer == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "No memory for zisofs decompression");
+				return (ARCHIVE_FATAL);
+			}
+		}
+		zisofs->uncompressed_buffer_size = xsize;
+
+		/*
+		 * Read the file header, and check the magic code of zisofs.
+		 */
+		if (zisofs->header_avail < sizeof(zisofs->header)) {
+			xsize = sizeof(zisofs->header) - zisofs->header_avail;
+			if (avail < xsize)
+				xsize = avail;
+			memcpy(zisofs->header + zisofs->header_avail, p, xsize);
+			zisofs->header_avail += xsize;
+			avail -= xsize;
+			p += xsize;
+		}
+		if (!zisofs->header_passed &&
+		    zisofs->header_avail == sizeof(zisofs->header)) {
+			int err = 0;
+
+			if (memcmp(zisofs->header, zisofs_magic,
+			    sizeof(zisofs_magic)) != 0)
+				err = 1;
+			if (archive_le32dec(zisofs->header + 8)
+			    != zisofs->pz_uncompressed_size)
+				err = 1;
+			if (zisofs->header[12] != 4)
+				err = 1;
+			if (zisofs->header[13] != zisofs->pz_log2_bs)
+				err = 1;
+			if (err) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Illegal zisofs file body");
+				return (ARCHIVE_FATAL);
+			}
+			zisofs->header_passed = 1;
+		}
+		/*
+		 * Read block pointers.
+		 */
+		if (zisofs->header_passed &&
+		    zisofs->block_pointers_avail < zisofs->block_pointers_size) {
+			xsize = zisofs->block_pointers_size
+			    - zisofs->block_pointers_avail;
+			if (avail < xsize)
+				xsize = avail;
+			memcpy(zisofs->block_pointers
+			    + zisofs->block_pointers_avail, p, xsize);
+			zisofs->block_pointers_avail += xsize;
+			avail -= xsize;
+			p += xsize;
+		    	if (zisofs->block_pointers_avail
+			    == zisofs->block_pointers_size) {
+				/* We've got all block pointers and initialize
+				 * related variables.	*/
+				zisofs->block_off = 0;
+				zisofs->block_avail = 0;
+				/* Complete a initialization */
+				zisofs->initialized = 1;
+			}
+		}
+
+		if (!zisofs->initialized)
+			goto next_data; /* We need more data. */
+	}
+
+	/*
+	 * Get block offsets from block pointers.
+	 */
+	if (zisofs->block_avail == 0) {
+		uint32_t bst, bed;
+
+		if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {
+			/* There isn't a pair of offsets. */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Illegal zisofs block pointers");
+			return (ARCHIVE_FATAL);
+		}
+		bst = archive_le32dec(
+		    zisofs->block_pointers + zisofs->block_off);
+		if (bst != zisofs->pz_offset + (bytes_read - avail)) {
+			/* TODO: Should we seek offset of current file
+			 * by bst ? */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Illegal zisofs block pointers(cannot seek)");
+			return (ARCHIVE_FATAL);
+		}
+		bed = archive_le32dec(
+		    zisofs->block_pointers + zisofs->block_off + 4);
+		if (bed < bst) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Illegal zisofs block pointers");
+			return (ARCHIVE_FATAL);
+		}
+		zisofs->block_avail = bed - bst;
+		zisofs->block_off += 4;
+
+		/* Initialize compression library for new block. */
+		if (zisofs->stream_valid)
+			r = inflateReset(&zisofs->stream);
+		else
+			r = inflateInit(&zisofs->stream);
+		if (r != Z_OK) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Can't initialize zisofs decompression.");
+			return (ARCHIVE_FATAL);
+		}
+		zisofs->stream_valid = 1;
+		zisofs->stream.total_in = 0;
+		zisofs->stream.total_out = 0;
+	}
+
+	/*
+	 * Make uncompressed data.
+	 */
+	if (zisofs->block_avail == 0) {
+		memset(zisofs->uncompressed_buffer, 0,
+		    zisofs->uncompressed_buffer_size);
+		uncompressed_size = zisofs->uncompressed_buffer_size;
+	} else {
+		zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p;
+		if (avail > zisofs->block_avail)
+			zisofs->stream.avail_in = zisofs->block_avail;
+		else
+			zisofs->stream.avail_in = (uInt)avail;
+		zisofs->stream.next_out = zisofs->uncompressed_buffer;
+		zisofs->stream.avail_out =
+		    (uInt)zisofs->uncompressed_buffer_size;
+
+		r = inflate(&zisofs->stream, 0);
+		switch (r) {
+		case Z_OK: /* Decompressor made some progress.*/
+		case Z_STREAM_END: /* Found end of stream. */
+			break;
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "zisofs decompression failed (%d)", r);
+			return (ARCHIVE_FATAL);
+		}
+		uncompressed_size =
+		    zisofs->uncompressed_buffer_size - zisofs->stream.avail_out;
+		avail -= zisofs->stream.next_in - p;
+		zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p);
+	}
+next_data:
+	bytes_read -= avail;
+	*buff = zisofs->uncompressed_buffer;
+	*size = uncompressed_size;
+	*offset = iso9660->entry_sparse_offset;
+	iso9660->entry_sparse_offset += uncompressed_size;
+	iso9660->entry_bytes_remaining -= bytes_read;
+	iso9660->current_position += bytes_read;
+	zisofs->pz_offset += (uint32_t)bytes_read;
+	iso9660->entry_bytes_unconsumed += bytes_read;
+
+	return (ARCHIVE_OK);
+}
+
+#else /* HAVE_ZLIB_H */
+
+static int
+zisofs_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+
+	(void)buff;/* UNUSED */
+	(void)size;/* UNUSED */
+	(void)offset;/* UNUSED */
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "zisofs is not supported on this platform.");
+	return (ARCHIVE_FAILED);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static int
+archive_read_format_iso9660_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	ssize_t bytes_read;
+	struct iso9660 *iso9660;
+
+	iso9660 = (struct iso9660 *)(a->format->data);
+
+	if (iso9660->entry_bytes_unconsumed) {
+		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+		iso9660->entry_bytes_unconsumed = 0;
+	}
+
+	if (iso9660->entry_bytes_remaining <= 0) {
+		if (iso9660->entry_content != NULL)
+			iso9660->entry_content = iso9660->entry_content->next;
+		if (iso9660->entry_content == NULL) {
+			*buff = NULL;
+			*size = 0;
+			*offset = iso9660->entry_sparse_offset;
+			return (ARCHIVE_EOF);
+		}
+		/* Seek forward to the start of the entry. */
+		if (iso9660->current_position < iso9660->entry_content->offset) {
+			int64_t step;
+
+			step = iso9660->entry_content->offset -
+			    iso9660->current_position;
+			step = __archive_read_consume(a, step);
+			if (step < 0)
+				return ((int)step);
+			iso9660->current_position =
+			    iso9660->entry_content->offset;
+		}
+		if (iso9660->entry_content->offset < iso9660->current_position) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Ignoring out-of-order file (%s) %jd < %jd",
+			    iso9660->pathname.s,
+			    (intmax_t)iso9660->entry_content->offset,
+			    (intmax_t)iso9660->current_position);
+			*buff = NULL;
+			*size = 0;
+			*offset = iso9660->entry_sparse_offset;
+			return (ARCHIVE_WARN);
+		}
+		iso9660->entry_bytes_remaining = iso9660->entry_content->size;
+	}
+	if (iso9660->entry_zisofs.pz)
+		return (zisofs_read_data(a, buff, size, offset));
+
+	*buff = __archive_read_ahead(a, 1, &bytes_read);
+	if (bytes_read == 0)
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Truncated input file");
+	if (*buff == NULL)
+		return (ARCHIVE_FATAL);
+	if (bytes_read > iso9660->entry_bytes_remaining)
+		bytes_read = (ssize_t)iso9660->entry_bytes_remaining;
+	*size = bytes_read;
+	*offset = iso9660->entry_sparse_offset;
+	iso9660->entry_sparse_offset += bytes_read;
+	iso9660->entry_bytes_remaining -= bytes_read;
+	iso9660->entry_bytes_unconsumed = bytes_read;
+	iso9660->current_position += bytes_read;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_iso9660_cleanup(struct archive_read *a)
+{
+	struct iso9660 *iso9660;
+	int r = ARCHIVE_OK;
+
+	iso9660 = (struct iso9660 *)(a->format->data);
+	release_files(iso9660);
+	free(iso9660->read_ce_req.reqs);
+	archive_string_free(&iso9660->pathname);
+	archive_string_free(&iso9660->previous_pathname);
+	free(iso9660->pending_files.files);
+#ifdef HAVE_ZLIB_H
+	free(iso9660->entry_zisofs.uncompressed_buffer);
+	free(iso9660->entry_zisofs.block_pointers);
+	if (iso9660->entry_zisofs.stream_valid) {
+		if (inflateEnd(&iso9660->entry_zisofs.stream) != Z_OK) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Failed to clean up zlib decompressor");
+			r = ARCHIVE_FATAL;
+		}
+	}
+#endif
+	free(iso9660->utf16be_path);
+	free(iso9660->utf16be_previous_path);
+	free(iso9660);
+	(a->format->data) = NULL;
+	return (r);
+}
+
+/*
+ * This routine parses a single ISO directory record, makes sense
+ * of any extensions, and stores the result in memory.
+ */
+static struct file_info *
+parse_file_info(struct archive_read *a, struct file_info *parent,
+    const unsigned char *isodirrec, size_t reclen)
+{
+	struct iso9660 *iso9660;
+	struct file_info *file, *filep;
+	size_t name_len;
+	const unsigned char *rr_start, *rr_end;
+	const unsigned char *p;
+	size_t dr_len;
+	uint64_t fsize, offset;
+	int32_t location;
+	int flags;
+
+	iso9660 = (struct iso9660 *)(a->format->data);
+
+	if (reclen != 0)
+		dr_len = (size_t)isodirrec[DR_length_offset];
+	/*
+	 * Sanity check that reclen is not zero and dr_len is greater than
+	 * reclen but at least 34
+	 */
+	if (reclen == 0 || reclen < dr_len || dr_len < 34) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"Invalid length of directory record");
+		return (NULL);
+	}
+	name_len = (size_t)isodirrec[DR_name_len_offset];
+	location = archive_le32dec(isodirrec + DR_extent_offset);
+	fsize = toi(isodirrec + DR_size_offset, DR_size_size);
+	/* Sanity check that name_len doesn't exceed dr_len. */
+	if (dr_len - 33 < name_len || name_len == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid length of file identifier");
+		return (NULL);
+	}
+	/* Sanity check that location doesn't exceed volume block.
+	 * Don't check lower limit of location; it's possibility
+	 * the location has negative value when file type is symbolic
+	 * link or file size is zero. As far as I know latest mkisofs
+	 * do that.
+	 */
+	if (location > 0 &&
+	    (location + ((fsize + iso9660->logical_block_size -1)
+	       / iso9660->logical_block_size))
+			> (uint32_t)iso9660->volume_block) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid location of extent of file");
+		return (NULL);
+	}
+	/* Sanity check that location doesn't have a negative value
+	 * when the file is not empty. it's too large. */
+	if (fsize != 0 && location < 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid location of extent of file");
+		return (NULL);
+	}
+
+	/* Sanity check that this entry does not create a cycle. */
+	offset = iso9660->logical_block_size * (uint64_t)location;
+	for (filep = parent; filep != NULL; filep = filep->parent) {
+		if (filep->offset == offset) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Directory structure contains loop");
+			return (NULL);
+		}
+	}
+
+	/* Create a new file entry and copy data from the ISO dir record. */
+	file = (struct file_info *)calloc(1, sizeof(*file));
+	if (file == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "No memory for file entry");
+		return (NULL);
+	}
+	file->parent = parent;
+	file->offset = offset;
+	file->size = fsize;
+	file->mtime = isodate7(isodirrec + DR_date_offset);
+	file->ctime = file->atime = file->mtime;
+	file->rede_files.first = NULL;
+	file->rede_files.last = &(file->rede_files.first);
+
+	p = isodirrec + DR_name_offset;
+	/* Rockridge extensions (if any) follow name.  Compute this
+	 * before fidgeting the name_len below. */
+	rr_start = p + name_len + (name_len & 1 ? 0 : 1);
+	rr_end = isodirrec + dr_len;
+
+	if (iso9660->seenJoliet) {
+		/* Joliet names are max 64 chars (128 bytes) according to spec,
+		 * but genisoimage/mkisofs allows recording longer Joliet
+		 * names which are 103 UCS2 characters(206 bytes) by their
+		 * option '-joliet-long'.
+		 */
+		if (name_len > 206)
+			name_len = 206;
+		name_len &= ~1;
+
+		/* trim trailing first version and dot from filename.
+		 *
+		 * Remember we were in UTF-16BE land!
+		 * SEPARATOR 1 (.) and SEPARATOR 2 (;) are both
+		 * 16 bits big endian characters on Joliet.
+		 *
+		 * TODO: sanitize filename?
+		 *       Joliet allows any UCS-2 char except:
+		 *       *, /, :, ;, ? and \.
+		 */
+		/* Chop off trailing ';1' from files. */
+		if (name_len > 4 && p[name_len-4] == 0 && p[name_len-3] == ';'
+		    && p[name_len-2] == 0 && p[name_len-1] == '1')
+			name_len -= 4;
+#if 0 /* XXX: this somehow manages to strip of single-character file extensions, like '.c'. */
+		/* Chop off trailing '.' from filenames. */
+		if (name_len > 2 && p[name_len-2] == 0 && p[name_len-1] == '.')
+			name_len -= 2;
+#endif
+		if ((file->utf16be_name = malloc(name_len)) == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "No memory for file name");
+			goto fail;
+		}
+		memcpy(file->utf16be_name, p, name_len);
+		file->utf16be_bytes = name_len;
+	} else {
+		/* Chop off trailing ';1' from files. */
+		if (name_len > 2 && p[name_len - 2] == ';' &&
+				p[name_len - 1] == '1')
+			name_len -= 2;
+		/* Chop off trailing '.' from filenames. */
+		if (name_len > 1 && p[name_len - 1] == '.')
+			--name_len;
+
+		archive_strncpy(&file->name, (const char *)p, name_len);
+	}
+
+	flags = isodirrec[DR_flags_offset];
+	if (flags & 0x02)
+		file->mode = AE_IFDIR | 0700;
+	else
+		file->mode = AE_IFREG | 0400;
+	if (flags & 0x80)
+		file->multi_extent = 1;
+	else
+		file->multi_extent = 0;
+	/*
+	 * Use a location for the file number, which is treated as an inode
+	 * number to find out hardlink target. If Rockridge extensions is
+	 * being used, the file number will be overwritten by FILE SERIAL
+	 * NUMBER of RRIP "PX" extension.
+	 * Note: Old mkisofs did not record that FILE SERIAL NUMBER
+	 * in ISO images.
+	 * Note2: xorriso set 0 to the location of a symlink file. 
+	 */
+	if (file->size == 0 && location >= 0) {
+		/* If file->size is zero, its location points wrong place,
+		 * and so we should not use it for the file number.
+		 * When the location has negative value, it can be used
+		 * for the file number.
+		 */
+		file->number = -1;
+		/* Do not appear before any directory entries. */
+		file->offset = -1;
+	} else
+		file->number = (int64_t)(uint32_t)location;
+
+	/* Rockridge extensions overwrite information from above. */
+	if (iso9660->opt_support_rockridge) {
+		if (parent == NULL && rr_end - rr_start >= 7) {
+			p = rr_start;
+			if (memcmp(p, "SP\x07\x01\xbe\xef", 6) == 0) {
+				/*
+				 * SP extension stores the suspOffset
+				 * (Number of bytes to skip between
+				 * filename and SUSP records.)
+				 * It is mandatory by the SUSP standard
+				 * (IEEE 1281).
+				 *
+				 * It allows SUSP to coexist with
+				 * non-SUSP uses of the System
+				 * Use Area by placing non-SUSP data
+				 * before SUSP data.
+				 *
+				 * SP extension must be in the root
+				 * directory entry, disable all SUSP
+				 * processing if not found.
+				 */
+				iso9660->suspOffset = p[6];
+				iso9660->seenSUSP = 1;
+				rr_start += 7;
+			}
+		}
+		if (iso9660->seenSUSP) {
+			int r;
+
+			file->name_continues = 0;
+			file->symlink_continues = 0;
+			rr_start += iso9660->suspOffset;
+			r = parse_rockridge(a, file, rr_start, rr_end);
+			if (r != ARCHIVE_OK)
+				goto fail;
+			/*
+			 * A file size of symbolic link files in ISO images
+			 * made by makefs is not zero and its location is
+			 * the same as those of next regular file. That is
+			 * the same as hard like file and it causes unexpected
+			 * error. 
+			 */
+			if (file->size > 0 &&
+			    (file->mode & AE_IFMT) == AE_IFLNK) {
+				file->size = 0;
+				file->number = -1;
+				file->offset = -1;
+			}
+		} else
+			/* If there isn't SUSP, disable parsing
+			 * rock ridge extensions. */
+			iso9660->opt_support_rockridge = 0;
+	}
+
+	file->nlinks = 1;/* Reset nlink. we'll calculate it later. */
+	/* Tell file's parent how many children that parent has. */
+	if (parent != NULL && (flags & 0x02))
+		parent->subdirs++;
+
+	if (iso9660->seenRockridge) {
+		if (parent != NULL && parent->parent == NULL &&
+		    (flags & 0x02) && iso9660->rr_moved == NULL &&
+		    file->name.s &&
+		    (strcmp(file->name.s, "rr_moved") == 0 ||
+		     strcmp(file->name.s, ".rr_moved") == 0)) {
+			iso9660->rr_moved = file;
+			file->rr_moved = 1;
+			file->rr_moved_has_re_only = 1;
+			file->re = 0;
+			parent->subdirs--;
+		} else if (file->re) {
+			/*
+			 * Sanity check: file's parent is rr_moved.
+			 */
+			if (parent == NULL || parent->rr_moved == 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Invalid Rockridge RE");
+				goto fail;
+			}
+			/*
+			 * Sanity check: file does not have "CL" extension.
+			 */
+			if (file->cl_offset) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Invalid Rockridge RE and CL");
+				goto fail;
+			}
+			/*
+			 * Sanity check: The file type must be a directory.
+			 */
+			if ((flags & 0x02) == 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Invalid Rockridge RE");
+				goto fail;
+			}
+		} else if (parent != NULL && parent->rr_moved)
+			file->rr_moved_has_re_only = 0;
+		else if (parent != NULL && (flags & 0x02) &&
+		    (parent->re || parent->re_descendant))
+			file->re_descendant = 1;
+		if (file->cl_offset) {
+			struct file_info *r;
+
+			if (parent == NULL || parent->parent == NULL) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Invalid Rockridge CL");
+				goto fail;
+			}
+			/*
+			 * Sanity check: The file type must be a regular file.
+			 */
+			if ((flags & 0x02) != 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Invalid Rockridge CL");
+				goto fail;
+			}
+			parent->subdirs++;
+			/* Overwrite an offset and a number of this "CL" entry
+			 * to appear before other dirs. "+1" to those is to
+			 * make sure to appear after "RE" entry which this
+			 * "CL" entry should be connected with. */
+			file->offset = file->number = file->cl_offset + 1;
+
+			/*
+			 * Sanity check: cl_offset does not point at its
+			 * the parents or itself.
+			 */
+			for (r = parent; r; r = r->parent) {
+				if (r->offset == file->cl_offset) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_MISC,
+					    "Invalid Rockridge CL");
+					goto fail;
+				}
+			}
+			if (file->cl_offset == file->offset ||
+			    parent->rr_moved) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Invalid Rockridge CL");
+				goto fail;
+			}
+		}
+	}
+
+#if DEBUG
+	/* DEBUGGING: Warn about attributes I don't yet fully support. */
+	if ((flags & ~0x02) != 0) {
+		fprintf(stderr, "\n ** Unrecognized flag: ");
+		dump_isodirrec(stderr, isodirrec);
+		fprintf(stderr, "\n");
+	} else if (toi(isodirrec + DR_volume_sequence_number_offset, 2) != 1) {
+		fprintf(stderr, "\n ** Unrecognized sequence number: ");
+		dump_isodirrec(stderr, isodirrec);
+		fprintf(stderr, "\n");
+	} else if (*(isodirrec + DR_file_unit_size_offset) != 0) {
+		fprintf(stderr, "\n ** Unexpected file unit size: ");
+		dump_isodirrec(stderr, isodirrec);
+		fprintf(stderr, "\n");
+	} else if (*(isodirrec + DR_interleave_offset) != 0) {
+		fprintf(stderr, "\n ** Unexpected interleave: ");
+		dump_isodirrec(stderr, isodirrec);
+		fprintf(stderr, "\n");
+	} else if (*(isodirrec + DR_ext_attr_length_offset) != 0) {
+		fprintf(stderr, "\n ** Unexpected extended attribute length: ");
+		dump_isodirrec(stderr, isodirrec);
+		fprintf(stderr, "\n");
+	}
+#endif
+	register_file(iso9660, file);
+	return (file);
+fail:
+	archive_string_free(&file->name);
+	free(file);
+	return (NULL);
+}
+
+static int
+parse_rockridge(struct archive_read *a, struct file_info *file,
+    const unsigned char *p, const unsigned char *end)
+{
+	struct iso9660 *iso9660;
+	int entry_seen = 0;
+
+	iso9660 = (struct iso9660 *)(a->format->data);
+
+	while (p + 4 <= end  /* Enough space for another entry. */
+	    && p[0] >= 'A' && p[0] <= 'Z' /* Sanity-check 1st char of name. */
+	    && p[1] >= 'A' && p[1] <= 'Z' /* Sanity-check 2nd char of name. */
+	    && p[2] >= 4 /* Sanity-check length. */
+	    && p + p[2] <= end) { /* Sanity-check length. */
+		const unsigned char *data = p + 4;
+		int data_length = p[2] - 4;
+		int version = p[3];
+
+		switch(p[0]) {
+		case 'C':
+			if (p[1] == 'E') {
+				if (version == 1 && data_length == 24) {
+					/*
+					 * CE extension comprises:
+					 *   8 byte sector containing extension
+					 *   8 byte offset w/in above sector
+					 *   8 byte length of continuation
+					 */
+					int32_t location =
+					    archive_le32dec(data);
+					file->ce_offset =
+					    archive_le32dec(data+8);
+					file->ce_size =
+					    archive_le32dec(data+16);
+					if (register_CE(a, location, file)
+					    != ARCHIVE_OK)
+						return (ARCHIVE_FATAL);
+				}
+			}
+			else if (p[1] == 'L') {
+				if (version == 1 && data_length == 8) {
+					file->cl_offset = (uint64_t)
+					    iso9660->logical_block_size *
+					    (uint64_t)archive_le32dec(data);
+					iso9660->seenRockridge = 1;
+				}
+			}
+			break;
+		case 'N':
+			if (p[1] == 'M') {
+				if (version == 1) {
+					parse_rockridge_NM1(file,
+					    data, data_length);
+					iso9660->seenRockridge = 1;
+				}
+			}
+			break;
+		case 'P':
+			/*
+			 * PD extension is padding;
+			 * contents are always ignored.
+			 *
+			 * PL extension won't appear;
+			 * contents are always ignored.
+			 */
+			if (p[1] == 'N') {
+				if (version == 1 && data_length == 16) {
+					file->rdev = toi(data,4);
+					file->rdev <<= 32;
+					file->rdev |= toi(data + 8, 4);
+					iso9660->seenRockridge = 1;
+				}
+			}
+			else if (p[1] == 'X') {
+				/*
+				 * PX extension comprises:
+				 *   8 bytes for mode,
+				 *   8 bytes for nlinks,
+				 *   8 bytes for uid,
+				 *   8 bytes for gid,
+				 *   8 bytes for inode.
+				 */
+				if (version == 1) {
+					if (data_length >= 8)
+						file->mode
+						    = toi(data, 4);
+					if (data_length >= 16)
+						file->nlinks
+						    = toi(data + 8, 4);
+					if (data_length >= 24)
+						file->uid
+						    = toi(data + 16, 4);
+					if (data_length >= 32)
+						file->gid
+						    = toi(data + 24, 4);
+					if (data_length >= 40)
+						file->number
+						    = toi(data + 32, 4);
+					iso9660->seenRockridge = 1;
+				}
+			}
+			break;
+		case 'R':
+			if (p[1] == 'E' && version == 1) {
+				file->re = 1;
+				iso9660->seenRockridge = 1;
+			}
+			else if (p[1] == 'R' && version == 1) {
+				/*
+				 * RR extension comprises:
+				 *    one byte flag value
+				 * This extension is obsolete,
+				 * so contents are always ignored.
+				 */
+			}
+			break;
+		case 'S':
+			if (p[1] == 'L') {
+				if (version == 1) {
+					parse_rockridge_SL1(file,
+					    data, data_length);
+					iso9660->seenRockridge = 1;
+				}
+			}
+			else if (p[1] == 'T'
+			    && data_length == 0 && version == 1) {
+				/*
+				 * ST extension marks end of this
+				 * block of SUSP entries.
+				 *
+				 * It allows SUSP to coexist with
+				 * non-SUSP uses of the System
+				 * Use Area by placing non-SUSP data
+				 * after SUSP data.
+				 */
+				iso9660->seenSUSP = 0;
+				iso9660->seenRockridge = 0;
+				return (ARCHIVE_OK);
+			}
+			break;
+		case 'T':
+			if (p[1] == 'F') {
+				if (version == 1) {
+					parse_rockridge_TF1(file,
+					    data, data_length);
+					iso9660->seenRockridge = 1;
+				}
+			}
+			break;
+		case 'Z':
+			if (p[1] == 'F') {
+				if (version == 1)
+					parse_rockridge_ZF1(file,
+					    data, data_length);
+			}
+			break;
+		default:
+			break;
+		}
+
+		p += p[2];
+		entry_seen = 1;
+	}
+
+	if (entry_seen)
+		return (ARCHIVE_OK);
+	else {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				  "Tried to parse Rockridge extensions, but none found");
+		return (ARCHIVE_WARN);
+	}
+}
+
+static int
+register_CE(struct archive_read *a, int32_t location,
+    struct file_info *file)
+{
+	struct iso9660 *iso9660;
+	struct read_ce_queue *heap;
+	struct read_ce_req *p;
+	uint64_t offset, parent_offset;
+	int hole, parent;
+
+	iso9660 = (struct iso9660 *)(a->format->data);
+	offset = ((uint64_t)location) * (uint64_t)iso9660->logical_block_size;
+	if (((file->mode & AE_IFMT) == AE_IFREG &&
+	    offset >= file->offset) ||
+	    offset < iso9660->current_position ||
+	    (((uint64_t)file->ce_offset) + file->ce_size)
+	      > (uint64_t)iso9660->logical_block_size ||
+	    offset + file->ce_offset + file->ce_size
+		  > iso9660->volume_size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid parameter in SUSP \"CE\" extension");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Expand our CE list as necessary. */
+	heap = &(iso9660->read_ce_req);
+	if (heap->cnt >= heap->allocated) {
+		int new_size;
+
+		if (heap->allocated < 16)
+			new_size = 16;
+		else
+			new_size = heap->allocated * 2;
+		/* Overflow might keep us from growing the list. */
+		if (new_size <= heap->allocated) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		p = calloc(new_size, sizeof(p[0]));
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		if (heap->reqs != NULL) {
+			memcpy(p, heap->reqs, heap->cnt * sizeof(*p));
+			free(heap->reqs);
+		}
+		heap->reqs = p;
+		heap->allocated = new_size;
+	}
+
+	/*
+	 * Start with hole at end, walk it up tree to find insertion point.
+	 */
+	hole = heap->cnt++;
+	while (hole > 0) {
+		parent = (hole - 1)/2;
+		parent_offset = heap->reqs[parent].offset;
+		if (offset >= parent_offset) {
+			heap->reqs[hole].offset = offset;
+			heap->reqs[hole].file = file;
+			return (ARCHIVE_OK);
+		}
+		/* Move parent into hole <==> move hole up tree. */
+		heap->reqs[hole] = heap->reqs[parent];
+		hole = parent;
+	}
+	heap->reqs[0].offset = offset;
+	heap->reqs[0].file = file;
+	return (ARCHIVE_OK);
+}
+
+static void
+next_CE(struct read_ce_queue *heap)
+{
+	uint64_t a_offset, b_offset, c_offset;
+	int a, b, c;
+	struct read_ce_req tmp;
+
+	if (heap->cnt < 1)
+		return;
+
+	/*
+	 * Move the last item in the heap to the root of the tree
+	 */
+	heap->reqs[0] = heap->reqs[--(heap->cnt)];
+
+	/*
+	 * Rebalance the heap.
+	 */
+	a = 0; /* Starting element and its offset */
+	a_offset = heap->reqs[a].offset;
+	for (;;) {
+		b = a + a + 1; /* First child */
+		if (b >= heap->cnt)
+			return;
+		b_offset = heap->reqs[b].offset;
+		c = b + 1; /* Use second child if it is smaller. */
+		if (c < heap->cnt) {
+			c_offset = heap->reqs[c].offset;
+			if (c_offset < b_offset) {
+				b = c;
+				b_offset = c_offset;
+			}
+		}
+		if (a_offset <= b_offset)
+			return;
+		tmp = heap->reqs[a];
+		heap->reqs[a] = heap->reqs[b];
+		heap->reqs[b] = tmp;
+		a = b;
+	}
+}
+
+
+static int
+read_CE(struct archive_read *a, struct iso9660 *iso9660)
+{
+	struct read_ce_queue *heap;
+	const unsigned char *b, *p, *end;
+	struct file_info *file;
+	size_t step;
+	int r;
+
+	/* Read data which RRIP "CE" extension points. */
+	heap = &(iso9660->read_ce_req);
+	step = iso9660->logical_block_size;
+	while (heap->cnt &&
+	    heap->reqs[0].offset == iso9660->current_position) {
+		b = __archive_read_ahead(a, step, NULL);
+		if (b == NULL) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "Failed to read full block when scanning "
+			    "ISO9660 directory list");
+			return (ARCHIVE_FATAL);
+		}
+		do {
+			file = heap->reqs[0].file;
+			if (file->ce_offset + file->ce_size > step) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Malformed CE information");
+				return (ARCHIVE_FATAL);
+			}
+			p = b + file->ce_offset;
+			end = p + file->ce_size;
+			next_CE(heap);
+			r = parse_rockridge(a, file, p, end);
+			if (r != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		} while (heap->cnt &&
+		    heap->reqs[0].offset == iso9660->current_position);
+		/* NOTE: Do not move this consume's code to front of
+		 * do-while loop. Registration of nested CE extension
+		 * might cause error because of current position. */
+		__archive_read_consume(a, step);
+		iso9660->current_position += step;
+	}
+	return (ARCHIVE_OK);
+}
+
+static void
+parse_rockridge_NM1(struct file_info *file,
+		    const unsigned char *data, int data_length)
+{
+	if (!file->name_continues)
+		archive_string_empty(&file->name);
+	file->name_continues = 0;
+	if (data_length < 1)
+		return;
+	/*
+	 * NM version 1 extension comprises:
+	 *   1 byte flag, value is one of:
+	 *     = 0: remainder is name
+	 *     = 1: remainder is name, next NM entry continues name
+	 *     = 2: "."
+	 *     = 4: ".."
+	 *     = 32: Implementation specific
+	 *     All other values are reserved.
+	 */
+	switch(data[0]) {
+	case 0:
+		if (data_length < 2)
+			return;
+		archive_strncat(&file->name,
+		    (const char *)data + 1, data_length - 1);
+		break;
+	case 1:
+		if (data_length < 2)
+			return;
+		archive_strncat(&file->name,
+		    (const char *)data + 1, data_length - 1);
+		file->name_continues = 1;
+		break;
+	case 2:
+		archive_strcat(&file->name, ".");
+		break;
+	case 4:
+		archive_strcat(&file->name, "..");
+		break;
+	default:
+		return;
+	}
+
+}
+
+static void
+parse_rockridge_TF1(struct file_info *file, const unsigned char *data,
+    int data_length)
+{
+	char flag;
+	/*
+	 * TF extension comprises:
+	 *   one byte flag
+	 *   create time (optional)
+	 *   modify time (optional)
+	 *   access time (optional)
+	 *   attribute time (optional)
+	 *  Time format and presence of fields
+	 *  is controlled by flag bits.
+	 */
+	if (data_length < 1)
+		return;
+	flag = data[0];
+	++data;
+	--data_length;
+	if (flag & 0x80) {
+		/* Use 17-byte time format. */
+		if ((flag & 1) && data_length >= 17) {
+			/* Create time. */
+			file->birthtime_is_set = 1;
+			file->birthtime = isodate17(data);
+			data += 17;
+			data_length -= 17;
+		}
+		if ((flag & 2) && data_length >= 17) {
+			/* Modify time. */
+			file->mtime = isodate17(data);
+			data += 17;
+			data_length -= 17;
+		}
+		if ((flag & 4) && data_length >= 17) {
+			/* Access time. */
+			file->atime = isodate17(data);
+			data += 17;
+			data_length -= 17;
+		}
+		if ((flag & 8) && data_length >= 17) {
+			/* Attribute change time. */
+			file->ctime = isodate17(data);
+		}
+	} else {
+		/* Use 7-byte time format. */
+		if ((flag & 1) && data_length >= 7) {
+			/* Create time. */
+			file->birthtime_is_set = 1;
+			file->birthtime = isodate7(data);
+			data += 7;
+			data_length -= 7;
+		}
+		if ((flag & 2) && data_length >= 7) {
+			/* Modify time. */
+			file->mtime = isodate7(data);
+			data += 7;
+			data_length -= 7;
+		}
+		if ((flag & 4) && data_length >= 7) {
+			/* Access time. */
+			file->atime = isodate7(data);
+			data += 7;
+			data_length -= 7;
+		}
+		if ((flag & 8) && data_length >= 7) {
+			/* Attribute change time. */
+			file->ctime = isodate7(data);
+		}
+	}
+}
+
+static void
+parse_rockridge_SL1(struct file_info *file, const unsigned char *data,
+    int data_length)
+{
+	const char *separator = "";
+
+	if (!file->symlink_continues || file->symlink.length < 1)
+		archive_string_empty(&file->symlink);
+	file->symlink_continues = 0;
+
+	/*
+	 * Defined flag values:
+	 *  0: This is the last SL record for this symbolic link
+	 *  1: this symbolic link field continues in next SL entry
+	 *  All other values are reserved.
+	 */
+	if (data_length < 1)
+		return;
+	switch(*data) {
+	case 0:
+		break;
+	case 1:
+		file->symlink_continues = 1;
+		break;
+	default:
+		return;
+	}
+	++data;  /* Skip flag byte. */
+	--data_length;
+
+	/*
+	 * SL extension body stores "components".
+	 * Basically, this is a complicated way of storing
+	 * a POSIX path.  It also interferes with using
+	 * symlinks for storing non-path data. <sigh>
+	 *
+	 * Each component is 2 bytes (flag and length)
+	 * possibly followed by name data.
+	 */
+	while (data_length >= 2) {
+		unsigned char flag = *data++;
+		unsigned char nlen = *data++;
+		data_length -= 2;
+
+		archive_strcat(&file->symlink, separator);
+		separator = "/";
+
+		switch(flag) {
+		case 0: /* Usual case, this is text. */
+			if (data_length < nlen)
+				return;
+			archive_strncat(&file->symlink,
+			    (const char *)data, nlen);
+			break;
+		case 0x01: /* Text continues in next component. */
+			if (data_length < nlen)
+				return;
+			archive_strncat(&file->symlink,
+			    (const char *)data, nlen);
+			separator = "";
+			break;
+		case 0x02: /* Current dir. */
+			archive_strcat(&file->symlink, ".");
+			break;
+		case 0x04: /* Parent dir. */
+			archive_strcat(&file->symlink, "..");
+			break;
+		case 0x08: /* Root of filesystem. */
+			archive_strcat(&file->symlink, "/");
+			separator = "";
+			break;
+		case 0x10: /* Undefined (historically "volume root" */
+			archive_string_empty(&file->symlink);
+			archive_strcat(&file->symlink, "ROOT");
+			break;
+		case 0x20: /* Undefined (historically "hostname") */
+			archive_strcat(&file->symlink, "hostname");
+			break;
+		default:
+			/* TODO: issue a warning ? */
+			return;
+		}
+		data += nlen;
+		data_length -= nlen;
+	}
+}
+
+static void
+parse_rockridge_ZF1(struct file_info *file, const unsigned char *data,
+    int data_length)
+{
+
+	if (data[0] == 0x70 && data[1] == 0x7a && data_length == 12) {
+		/* paged zlib */
+		file->pz = 1;
+		file->pz_log2_bs = data[3];
+		file->pz_uncompressed_size = archive_le32dec(&data[4]);
+	}
+}
+
+static void
+register_file(struct iso9660 *iso9660, struct file_info *file)
+{
+
+	file->use_next = iso9660->use_files;
+	iso9660->use_files = file;
+}
+
+static void
+release_files(struct iso9660 *iso9660)
+{
+	struct content *con, *connext;
+	struct file_info *file;
+
+	file = iso9660->use_files;
+	while (file != NULL) {
+		struct file_info *next = file->use_next;
+
+		archive_string_free(&file->name);
+		archive_string_free(&file->symlink);
+		free(file->utf16be_name);
+		con = file->contents.first;
+		while (con != NULL) {
+			connext = con->next;
+			free(con);
+			con = connext;
+		}
+		free(file);
+		file = next;
+	}
+}
+
+static int
+next_entry_seek(struct archive_read *a, struct iso9660 *iso9660,
+    struct file_info **pfile)
+{
+	struct file_info *file;
+	int r;
+
+	r = next_cache_entry(a, iso9660, pfile);
+	if (r != ARCHIVE_OK)
+		return (r);
+	file = *pfile;
+
+	/* Don't waste time seeking for zero-length bodies. */
+	if (file->size == 0)
+		file->offset = iso9660->current_position;
+
+	/* flush any remaining bytes from the last round to ensure
+	 * we're positioned */
+	if (iso9660->entry_bytes_unconsumed) {
+		__archive_read_consume(a, iso9660->entry_bytes_unconsumed);
+		iso9660->entry_bytes_unconsumed = 0;
+	}
+
+	/* Seek forward to the start of the entry. */
+	if (iso9660->current_position < file->offset) {
+		int64_t step;
+
+		step = file->offset - iso9660->current_position;
+		step = __archive_read_consume(a, step);
+		if (step < 0)
+			return ((int)step);
+		iso9660->current_position = file->offset;
+	}
+
+	/* We found body of file; handle it now. */
+	return (ARCHIVE_OK);
+}
+
+static int
+next_cache_entry(struct archive_read *a, struct iso9660 *iso9660,
+    struct file_info **pfile)
+{
+	struct file_info *file;
+	struct {
+		struct file_info	*first;
+		struct file_info	**last;
+	}	empty_files;
+	int64_t number;
+	int count;
+
+	file = cache_get_entry(iso9660);
+	if (file != NULL) {
+		*pfile = file;
+		return (ARCHIVE_OK);
+	}
+
+	for (;;) {
+		struct file_info *re, *d;
+
+		*pfile = file = next_entry(iso9660);
+		if (file == NULL) {
+			/*
+			 * If directory entries all which are descendant of
+			 * rr_moved are still remaining, expose their.
+			 */
+			if (iso9660->re_files.first != NULL && 
+			    iso9660->rr_moved != NULL &&
+			    iso9660->rr_moved->rr_moved_has_re_only)
+				/* Expose "rr_moved" entry. */
+				cache_add_entry(iso9660, iso9660->rr_moved);
+			while ((re = re_get_entry(iso9660)) != NULL) {
+				/* Expose its descendant dirs. */
+				while ((d = rede_get_entry(re)) != NULL)
+					cache_add_entry(iso9660, d);
+			}
+			if (iso9660->cache_files.first != NULL)
+				return (next_cache_entry(a, iso9660, pfile));
+			return (ARCHIVE_EOF);
+		}
+
+		if (file->cl_offset) {
+			struct file_info *first_re = NULL;
+			int nexted_re = 0;
+
+			/*
+			 * Find "RE" dir for the current file, which
+			 * has "CL" flag.
+			 */
+			while ((re = re_get_entry(iso9660))
+			    != first_re) {
+				if (first_re == NULL)
+					first_re = re;
+				if (re->offset == file->cl_offset) {
+					re->parent->subdirs--;
+					re->parent = file->parent;
+					re->re = 0;
+					if (re->parent->re_descendant) {
+						nexted_re = 1;
+						re->re_descendant = 1;
+						if (rede_add_entry(re) < 0)
+							goto fatal_rr;
+						/* Move a list of descendants
+						 * to a new ancestor. */
+						while ((d = rede_get_entry(
+						    re)) != NULL)
+							if (rede_add_entry(d)
+							    < 0)
+								goto fatal_rr;
+						break;
+					}
+					/* Replace the current file
+					 * with "RE" dir */
+					*pfile = file = re;
+					/* Expose its descendant */
+					while ((d = rede_get_entry(
+					    file)) != NULL)
+						cache_add_entry(
+						    iso9660, d);
+					break;
+				} else
+					re_add_entry(iso9660, re);
+			}
+			if (nexted_re) {
+				/*
+				 * Do not expose this at this time
+				 * because we have not gotten its full-path
+				 * name yet.
+				 */
+				continue;
+			}
+		} else if ((file->mode & AE_IFMT) == AE_IFDIR) {
+			int r;
+
+			/* Read file entries in this dir. */
+			r = read_children(a, file);
+			if (r != ARCHIVE_OK)
+				return (r);
+
+			/*
+			 * Handle a special dir of Rockridge extensions,
+			 * "rr_moved".
+			 */
+			if (file->rr_moved) {
+				/*
+				 * If this has only the subdirectories which
+				 * have "RE" flags, do not expose at this time.
+				 */
+				if (file->rr_moved_has_re_only)
+					continue;
+				/* Otherwise expose "rr_moved" entry. */
+			} else if (file->re) {
+				/*
+				 * Do not expose this at this time
+				 * because we have not gotten its full-path
+				 * name yet.
+				 */
+				re_add_entry(iso9660, file);
+				continue;
+			} else if (file->re_descendant) {
+				/*
+				 * If the top level "RE" entry of this entry
+				 * is not exposed, we, accordingly, should not
+				 * expose this entry at this time because
+				 * we cannot make its proper full-path name.
+				 */
+				if (rede_add_entry(file) == 0)
+					continue;
+				/* Otherwise we can expose this entry because
+				 * it seems its top level "RE" has already been
+				 * exposed. */
+			}
+		}
+		break;
+	}
+
+	if ((file->mode & AE_IFMT) != AE_IFREG || file->number == -1)
+		return (ARCHIVE_OK);
+
+	count = 0;
+	number = file->number;
+	iso9660->cache_files.first = NULL;
+	iso9660->cache_files.last = &(iso9660->cache_files.first);
+	empty_files.first = NULL;
+	empty_files.last = &empty_files.first;
+	/* Collect files which has the same file serial number.
+	 * Peek pending_files so that file which number is different
+	 * is not put back. */
+	while (iso9660->pending_files.used > 0 &&
+	    (iso9660->pending_files.files[0]->number == -1 ||
+	     iso9660->pending_files.files[0]->number == number)) {
+		if (file->number == -1) {
+			/* This file has the same offset
+			 * but it's wrong offset which empty files
+			 * and symlink files have.
+			 * NOTE: This wrong offset was recorded by
+			 * old mkisofs utility. If ISO images is
+			 * created by latest mkisofs, this does not
+			 * happen.
+			 */
+			file->next = NULL;
+			*empty_files.last = file;
+			empty_files.last = &(file->next);
+		} else {
+			count++;
+			cache_add_entry(iso9660, file);
+		}
+		file = next_entry(iso9660);
+	}
+
+	if (count == 0) {
+		*pfile = file;
+		return ((file == NULL)?ARCHIVE_EOF:ARCHIVE_OK);
+	}
+	if (file->number == -1) {
+		file->next = NULL;
+		*empty_files.last = file;
+		empty_files.last = &(file->next);
+	} else {
+		count++;
+		cache_add_entry(iso9660, file);
+	}
+
+	if (count > 1) {
+		/* The count is the same as number of hardlink,
+		 * so much so that each nlinks of files in cache_file
+		 * is overwritten by value of the count.
+		 */
+		for (file = iso9660->cache_files.first;
+		    file != NULL; file = file->next)
+			file->nlinks = count;
+	}
+	/* If there are empty files, that files are added
+	 * to the tail of the cache_files. */
+	if (empty_files.first != NULL) {
+		*iso9660->cache_files.last = empty_files.first;
+		iso9660->cache_files.last = empty_files.last;
+	}
+	*pfile = cache_get_entry(iso9660);
+	return ((*pfile == NULL)?ARCHIVE_EOF:ARCHIVE_OK);
+
+fatal_rr:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Failed to connect 'CL' pointer to 'RE' rr_moved pointer of "
+	    "Rockridge extensions: current position = %jd, CL offset = %jd",
+	    (intmax_t)iso9660->current_position, (intmax_t)file->cl_offset);
+	return (ARCHIVE_FATAL);
+}
+
+static inline void
+re_add_entry(struct iso9660 *iso9660, struct file_info *file)
+{
+	file->re_next = NULL;
+	*iso9660->re_files.last = file;
+	iso9660->re_files.last = &(file->re_next);
+}
+
+static inline struct file_info *
+re_get_entry(struct iso9660 *iso9660)
+{
+	struct file_info *file;
+
+	if ((file = iso9660->re_files.first) != NULL) {
+		iso9660->re_files.first = file->re_next;
+		if (iso9660->re_files.first == NULL)
+			iso9660->re_files.last =
+			    &(iso9660->re_files.first);
+	}
+	return (file);
+}
+
+static inline int
+rede_add_entry(struct file_info *file)
+{
+	struct file_info *re;
+
+	/*
+	 * Find "RE" entry.
+	 */
+	re = file->parent;
+	while (re != NULL && !re->re)
+		re = re->parent;
+	if (re == NULL)
+		return (-1);
+
+	file->re_next = NULL;
+	*re->rede_files.last = file;
+	re->rede_files.last = &(file->re_next);
+	return (0);
+}
+
+static inline struct file_info *
+rede_get_entry(struct file_info *re)
+{
+	struct file_info *file;
+
+	if ((file = re->rede_files.first) != NULL) {
+		re->rede_files.first = file->re_next;
+		if (re->rede_files.first == NULL)
+			re->rede_files.last =
+			    &(re->rede_files.first);
+	}
+	return (file);
+}
+
+static inline void
+cache_add_entry(struct iso9660 *iso9660, struct file_info *file)
+{
+	file->next = NULL;
+	*iso9660->cache_files.last = file;
+	iso9660->cache_files.last = &(file->next);
+}
+
+static inline struct file_info *
+cache_get_entry(struct iso9660 *iso9660)
+{
+	struct file_info *file;
+
+	if ((file = iso9660->cache_files.first) != NULL) {
+		iso9660->cache_files.first = file->next;
+		if (iso9660->cache_files.first == NULL)
+			iso9660->cache_files.last =
+			    &(iso9660->cache_files.first);
+	}
+	return (file);
+}
+
+static int
+heap_add_entry(struct archive_read *a, struct heap_queue *heap,
+    struct file_info *file, uint64_t key)
+{
+	uint64_t file_key, parent_key;
+	int hole, parent;
+
+	/* Expand our pending files list as necessary. */
+	if (heap->used >= heap->allocated) {
+		struct file_info **new_pending_files;
+		int new_size = heap->allocated * 2;
+
+		if (heap->allocated < 1024)
+			new_size = 1024;
+		/* Overflow might keep us from growing the list. */
+		if (new_size <= heap->allocated) {
+			archive_set_error(&a->archive,
+			    ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		new_pending_files = (struct file_info **)
+		    malloc(new_size * sizeof(new_pending_files[0]));
+		if (new_pending_files == NULL) {
+			archive_set_error(&a->archive,
+			    ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		if (heap->allocated)
+			memcpy(new_pending_files, heap->files,
+			    heap->allocated * sizeof(new_pending_files[0]));
+		free(heap->files);
+		heap->files = new_pending_files;
+		heap->allocated = new_size;
+	}
+
+	file_key = file->key = key;
+
+	/*
+	 * Start with hole at end, walk it up tree to find insertion point.
+	 */
+	hole = heap->used++;
+	while (hole > 0) {
+		parent = (hole - 1)/2;
+		parent_key = heap->files[parent]->key;
+		if (file_key >= parent_key) {
+			heap->files[hole] = file;
+			return (ARCHIVE_OK);
+		}
+		/* Move parent into hole <==> move hole up tree. */
+		heap->files[hole] = heap->files[parent];
+		hole = parent;
+	}
+	heap->files[0] = file;
+
+	return (ARCHIVE_OK);
+}
+
+static struct file_info *
+heap_get_entry(struct heap_queue *heap)
+{
+	uint64_t a_key, b_key, c_key;
+	int a, b, c;
+	struct file_info *r, *tmp;
+
+	if (heap->used < 1)
+		return (NULL);
+
+	/*
+	 * The first file in the list is the earliest; we'll return this.
+	 */
+	r = heap->files[0];
+
+	/*
+	 * Move the last item in the heap to the root of the tree
+	 */
+	heap->files[0] = heap->files[--(heap->used)];
+
+	/*
+	 * Rebalance the heap.
+	 */
+	a = 0; /* Starting element and its heap key */
+	a_key = heap->files[a]->key;
+	for (;;) {
+		b = a + a + 1; /* First child */
+		if (b >= heap->used)
+			return (r);
+		b_key = heap->files[b]->key;
+		c = b + 1; /* Use second child if it is smaller. */
+		if (c < heap->used) {
+			c_key = heap->files[c]->key;
+			if (c_key < b_key) {
+				b = c;
+				b_key = c_key;
+			}
+		}
+		if (a_key <= b_key)
+			return (r);
+		tmp = heap->files[a];
+		heap->files[a] = heap->files[b];
+		heap->files[b] = tmp;
+		a = b;
+	}
+}
+
+static unsigned int
+toi(const void *p, int n)
+{
+	const unsigned char *v = (const unsigned char *)p;
+	if (n > 1)
+		return v[0] + 256 * toi(v + 1, n - 1);
+	if (n == 1)
+		return v[0];
+	return (0);
+}
+
+static time_t
+isodate7(const unsigned char *v)
+{
+	struct tm tm;
+	int offset;
+	time_t t;
+
+	memset(&tm, 0, sizeof(tm));
+	tm.tm_year = v[0];
+	tm.tm_mon = v[1] - 1;
+	tm.tm_mday = v[2];
+	tm.tm_hour = v[3];
+	tm.tm_min = v[4];
+	tm.tm_sec = v[5];
+	/* v[6] is the signed timezone offset, in 1/4-hour increments. */
+	offset = ((const signed char *)v)[6];
+	if (offset > -48 && offset < 52) {
+		tm.tm_hour -= offset / 4;
+		tm.tm_min -= (offset % 4) * 15;
+	}
+	t = time_from_tm(&tm);
+	if (t == (time_t)-1)
+		return ((time_t)0);
+	return (t);
+}
+
+static time_t
+isodate17(const unsigned char *v)
+{
+	struct tm tm;
+	int offset;
+	time_t t;
+
+	memset(&tm, 0, sizeof(tm));
+	tm.tm_year = (v[0] - '0') * 1000 + (v[1] - '0') * 100
+	    + (v[2] - '0') * 10 + (v[3] - '0')
+	    - 1900;
+	tm.tm_mon = (v[4] - '0') * 10 + (v[5] - '0');
+	tm.tm_mday = (v[6] - '0') * 10 + (v[7] - '0');
+	tm.tm_hour = (v[8] - '0') * 10 + (v[9] - '0');
+	tm.tm_min = (v[10] - '0') * 10 + (v[11] - '0');
+	tm.tm_sec = (v[12] - '0') * 10 + (v[13] - '0');
+	/* v[16] is the signed timezone offset, in 1/4-hour increments. */
+	offset = ((const signed char *)v)[16];
+	if (offset > -48 && offset < 52) {
+		tm.tm_hour -= offset / 4;
+		tm.tm_min -= (offset % 4) * 15;
+	}
+	t = time_from_tm(&tm);
+	if (t == (time_t)-1)
+		return ((time_t)0);
+	return (t);
+}
+
+static time_t
+time_from_tm(struct tm *t)
+{
+#if HAVE_TIMEGM
+        /* Use platform timegm() if available. */
+        return (timegm(t));
+#elif HAVE__MKGMTIME64
+        return (_mkgmtime64(t));
+#else
+        /* Else use direct calculation using POSIX assumptions. */
+        /* First, fix up tm_yday based on the year/month/day. */
+        if (mktime(t) == (time_t)-1)
+                return ((time_t)-1);
+        /* Then we can compute timegm() from first principles. */
+        return (t->tm_sec
+            + t->tm_min * 60
+            + t->tm_hour * 3600
+            + t->tm_yday * 86400
+            + (t->tm_year - 70) * 31536000
+            + ((t->tm_year - 69) / 4) * 86400
+            - ((t->tm_year - 1) / 100) * 86400
+            + ((t->tm_year + 299) / 400) * 86400);
+#endif
+}
+
+static const char *
+build_pathname(struct archive_string *as, struct file_info *file, int depth)
+{
+	// Plain ISO9660 only allows 8 dir levels; if we get
+	// to 1000, then something is very, very wrong.
+	if (depth > 1000) {
+		return NULL;
+	}
+	if (file->parent != NULL && archive_strlen(&file->parent->name) > 0) {
+		if (build_pathname(as, file->parent, depth + 1) == NULL) {
+			return NULL;
+		}
+		archive_strcat(as, "/");
+	}
+	if (archive_strlen(&file->name) == 0)
+		archive_strcat(as, ".");
+	else
+		archive_string_concat(as, &file->name);
+	return (as->s);
+}
+
+static int
+build_pathname_utf16be(unsigned char *p, size_t max, size_t *len,
+    struct file_info *file)
+{
+	if (file->parent != NULL && file->parent->utf16be_bytes > 0) {
+		if (build_pathname_utf16be(p, max, len, file->parent) != 0)
+			return (-1);
+		p[*len] = 0;
+		p[*len + 1] = '/';
+		*len += 2;
+	}
+	if (file->utf16be_bytes == 0) {
+		if (*len + 2 > max)
+			return (-1);/* Path is too long! */
+		p[*len] = 0;
+		p[*len + 1] = '.';
+		*len += 2;
+	} else {
+		if (*len + file->utf16be_bytes > max)
+			return (-1);/* Path is too long! */
+		memcpy(p + *len, file->utf16be_name, file->utf16be_bytes);
+		*len += file->utf16be_bytes;
+	}
+	return (0);
+}
+
+#if DEBUG
+static void
+dump_isodirrec(FILE *out, const unsigned char *isodirrec)
+{
+	fprintf(out, " l %d,",
+	    toi(isodirrec + DR_length_offset, DR_length_size));
+	fprintf(out, " a %d,",
+	    toi(isodirrec + DR_ext_attr_length_offset, DR_ext_attr_length_size));
+	fprintf(out, " ext 0x%x,",
+	    toi(isodirrec + DR_extent_offset, DR_extent_size));
+	fprintf(out, " s %d,",
+	    toi(isodirrec + DR_size_offset, DR_extent_size));
+	fprintf(out, " f 0x%x,",
+	    toi(isodirrec + DR_flags_offset, DR_flags_size));
+	fprintf(out, " u %d,",
+	    toi(isodirrec + DR_file_unit_size_offset, DR_file_unit_size_size));
+	fprintf(out, " ilv %d,",
+	    toi(isodirrec + DR_interleave_offset, DR_interleave_size));
+	fprintf(out, " seq %d,",
+	    toi(isodirrec + DR_volume_sequence_number_offset,
+		DR_volume_sequence_number_size));
+	fprintf(out, " nl %d:",
+	    toi(isodirrec + DR_name_len_offset, DR_name_len_size));
+	fprintf(out, " `%.*s'",
+	    toi(isodirrec + DR_name_len_offset, DR_name_len_size),
+		isodirrec + DR_name_offset);
+}
+#endif
diff --git a/c/archive_read_support_format_lha.c b/c/archive_read_support_format_lha.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_lha.c
@@ -0,0 +1,2912 @@
+/*-
+ * Copyright (c) 2008-2014 Michihiro NAKAJIMA
+ * 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"
+
+#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
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+#include "archive_endian.h"
+
+
+#define MAXMATCH		256	/* Maximum match length. */
+#define MINMATCH		3	/* Minimum match length. */
+/*
+ * Literal table format:
+ * +0              +256                      +510
+ * +---------------+-------------------------+
+ * | literal code  |       match length      |
+ * |   0 ... 255   |  MINMATCH ... MAXMATCH  |
+ * +---------------+-------------------------+
+ *  <---          LT_BITLEN_SIZE         --->
+ */
+/* Literal table size. */
+#define LT_BITLEN_SIZE		(UCHAR_MAX + 1 + MAXMATCH - MINMATCH + 1)
+/* Position table size.
+ * Note: this used for both position table and pre literal table.*/
+#define PT_BITLEN_SIZE		(3 + 16)
+
+struct lzh_dec {
+	/* Decoding status. */
+	int     		 state;
+
+	/*
+	 * Window to see last 8Ki(lh5),32Ki(lh6),64Ki(lh7) bytes of decoded
+	 * data.
+	 */
+	int			 w_size;
+	int			 w_mask;
+	/* Window buffer, which is a loop buffer. */
+	unsigned char		*w_buff;
+	/* The insert position to the window. */
+	int			 w_pos;
+	/* The position where we can copy decoded code from the window. */
+	int     		 copy_pos;
+	/* The length how many bytes we can copy decoded code from
+	 * the window. */
+	int     		 copy_len;
+
+	/*
+	 * Bit stream reader.
+	 */
+	struct lzh_br {
+#define CACHE_TYPE		uint64_t
+#define CACHE_BITS		(8 * sizeof(CACHE_TYPE))
+	 	/* Cache buffer. */
+		CACHE_TYPE	 cache_buffer;
+		/* Indicates how many bits avail in cache_buffer. */
+		int		 cache_avail;
+	} br;
+
+	/*
+	 * Huffman coding.
+	 */
+	struct huffman {
+		int		 len_size;
+		int		 len_avail;
+		int		 len_bits;
+		int		 freq[17];
+		unsigned char	*bitlen;
+
+		/*
+		 * Use a index table. It's faster than searching a huffman
+		 * coding tree, which is a binary tree. But a use of a large
+		 * index table causes L1 cache read miss many times.
+		 */
+#define HTBL_BITS	10
+		int		 max_bits;
+		int		 shift_bits;
+		int		 tbl_bits;
+		int		 tree_used;
+		int		 tree_avail;
+		/* Direct access table. */
+		uint16_t	*tbl;
+		/* Binary tree table for extra bits over the direct access. */
+		struct htree_t {
+			uint16_t left;
+			uint16_t right;
+		}		*tree;
+	}			 lt, pt;
+
+	int			 blocks_avail;
+	int			 pos_pt_len_size;
+	int			 pos_pt_len_bits;
+	int			 literal_pt_len_size;
+	int			 literal_pt_len_bits;
+	int			 reading_position;
+	int			 loop;
+	int			 error;
+};
+
+struct lzh_stream {
+	const unsigned char	*next_in;
+	int			 avail_in;
+	int64_t			 total_in;
+	const unsigned char	*ref_ptr;
+	int			 avail_out;
+	int64_t			 total_out;
+	struct lzh_dec		*ds;
+};
+
+struct lha {
+	/* entry_bytes_remaining is the number of bytes we expect.	    */
+	int64_t                  entry_offset;
+	int64_t                  entry_bytes_remaining;
+	int64_t			 entry_unconsumed;
+	uint16_t		 entry_crc_calculated;
+ 
+	size_t			 header_size;	/* header size		    */
+	unsigned char		 level;		/* header level		    */
+	char			 method[3];	/* compress type	    */
+	int64_t			 compsize;	/* compressed data size	    */
+	int64_t			 origsize;	/* original file size	    */
+	int			 setflag;
+#define BIRTHTIME_IS_SET	1
+#define ATIME_IS_SET		2
+#define UNIX_MODE_IS_SET	4
+#define CRC_IS_SET		8
+	time_t			 birthtime;
+	long			 birthtime_tv_nsec;
+	time_t			 mtime;
+	long			 mtime_tv_nsec;
+	time_t			 atime;
+	long			 atime_tv_nsec;
+	mode_t			 mode;
+	int64_t			 uid;
+	int64_t			 gid;
+	struct archive_string 	 uname;
+	struct archive_string 	 gname;
+	uint16_t		 header_crc;
+	uint16_t		 crc;
+	/* dirname and filename could be in different codepages */
+	struct archive_string_conv *sconv_dir;
+	struct archive_string_conv *sconv_fname;
+	struct archive_string_conv *opt_sconv;
+
+	struct archive_string 	 dirname;
+	struct archive_string 	 filename;
+	struct archive_wstring	 ws;
+
+	unsigned char		 dos_attr;
+
+	/* Flag to mark progress that an archive was read their first header.*/
+	char			 found_first_header;
+	/* Flag to mark that indicates an empty directory. */
+	char			 directory;
+
+	/* Flags to mark progress of decompression. */
+	char			 decompress_init;
+	char			 end_of_entry;
+	char			 end_of_entry_cleanup;
+	char			 entry_is_compressed;
+
+	char			 format_name[64];
+
+	struct lzh_stream	 strm;
+};
+
+/*
+ * LHA header common member offset.
+ */
+#define H_METHOD_OFFSET	2	/* Compress type. */
+#define H_ATTR_OFFSET	19	/* DOS attribute. */
+#define H_LEVEL_OFFSET	20	/* Header Level.  */
+#define H_SIZE		22	/* Minimum header size. */
+
+static int      archive_read_format_lha_bid(struct archive_read *, int);
+static int      archive_read_format_lha_options(struct archive_read *,
+		    const char *, const char *);
+static int	archive_read_format_lha_read_header(struct archive_read *,
+		    struct archive_entry *);
+static int	archive_read_format_lha_read_data(struct archive_read *,
+		    const void **, size_t *, int64_t *);
+static int	archive_read_format_lha_read_data_skip(struct archive_read *);
+static int	archive_read_format_lha_cleanup(struct archive_read *);
+
+static void	lha_replace_path_separator(struct lha *,
+		    struct archive_entry *);
+static int	lha_read_file_header_0(struct archive_read *, struct lha *);
+static int	lha_read_file_header_1(struct archive_read *, struct lha *);
+static int	lha_read_file_header_2(struct archive_read *, struct lha *);
+static int	lha_read_file_header_3(struct archive_read *, struct lha *);
+static int	lha_read_file_extended_header(struct archive_read *,
+		    struct lha *, uint16_t *, int, size_t, size_t *);
+static size_t	lha_check_header_format(const void *);
+static int	lha_skip_sfx(struct archive_read *);
+static time_t	lha_dos_time(const unsigned char *);
+static time_t	lha_win_time(uint64_t, long *);
+static unsigned char	lha_calcsum(unsigned char, const void *,
+		    int, size_t);
+static int	lha_parse_linkname(struct archive_wstring *,
+		    struct archive_wstring *);
+static int	lha_read_data_none(struct archive_read *, const void **,
+		    size_t *, int64_t *);
+static int	lha_read_data_lzh(struct archive_read *, const void **,
+		    size_t *, int64_t *);
+static void	lha_crc16_init(void);
+static uint16_t lha_crc16(uint16_t, const void *, size_t);
+static int	lzh_decode_init(struct lzh_stream *, const char *);
+static void	lzh_decode_free(struct lzh_stream *);
+static int	lzh_decode(struct lzh_stream *, int);
+static int	lzh_br_fillup(struct lzh_stream *, struct lzh_br *);
+static int	lzh_huffman_init(struct huffman *, size_t, int);
+static void	lzh_huffman_free(struct huffman *);
+static int	lzh_read_pt_bitlen(struct lzh_stream *, int start, int end);
+static int	lzh_make_fake_table(struct huffman *, uint16_t);
+static int	lzh_make_huffman_table(struct huffman *);
+static inline int lzh_decode_huffman(struct huffman *, unsigned);
+static int	lzh_decode_huffman_tree(struct huffman *, unsigned, int);
+
+
+int
+archive_read_support_format_lha(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct lha *lha;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_lha");
+
+	lha = (struct lha *)calloc(1, sizeof(*lha));
+	if (lha == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate lha data");
+		return (ARCHIVE_FATAL);
+	}
+	archive_string_init(&lha->ws);
+
+	r = __archive_read_register_format(a,
+	    lha,
+	    "lha",
+	    archive_read_format_lha_bid,
+	    archive_read_format_lha_options,
+	    archive_read_format_lha_read_header,
+	    archive_read_format_lha_read_data,
+	    archive_read_format_lha_read_data_skip,
+	    NULL,
+	    archive_read_format_lha_cleanup,
+	    NULL,
+	    NULL);
+
+	if (r != ARCHIVE_OK)
+		free(lha);
+	return (ARCHIVE_OK);
+}
+
+static size_t
+lha_check_header_format(const void *h)
+{
+	const unsigned char *p = h;
+	size_t next_skip_bytes;
+
+	switch (p[H_METHOD_OFFSET+3]) {
+	/*
+	 * "-lh0-" ... "-lh7-" "-lhd-"
+	 * "-lzs-" "-lz5-"
+	 */
+	case '0': case '1': case '2': case '3':
+	case '4': case '5': case '6': case '7':
+	case 'd':
+	case 's':
+		next_skip_bytes = 4;
+
+		/* b0 == 0 means the end of an LHa archive file.	*/
+		if (p[0] == 0)
+			break;
+		if (p[H_METHOD_OFFSET] != '-' || p[H_METHOD_OFFSET+1] != 'l'
+		    ||  p[H_METHOD_OFFSET+4] != '-')
+			break;
+
+		if (p[H_METHOD_OFFSET+2] == 'h') {
+			/* "-lh?-" */
+			if (p[H_METHOD_OFFSET+3] == 's')
+				break;
+			if (p[H_LEVEL_OFFSET] == 0)
+				return (0);
+			if (p[H_LEVEL_OFFSET] <= 3 && p[H_ATTR_OFFSET] == 0x20)
+				return (0);
+		}
+		if (p[H_METHOD_OFFSET+2] == 'z') {
+			/* LArc extensions: -lzs-,-lz4- and -lz5- */
+			if (p[H_LEVEL_OFFSET] != 0)
+				break;
+			if (p[H_METHOD_OFFSET+3] == 's'
+			    || p[H_METHOD_OFFSET+3] == '4'
+			    || p[H_METHOD_OFFSET+3] == '5')
+				return (0);
+		}
+		break;
+	case 'h': next_skip_bytes = 1; break;
+	case 'z': next_skip_bytes = 1; break;
+	case 'l': next_skip_bytes = 2; break;
+	case '-': next_skip_bytes = 3; break;
+	default : next_skip_bytes = 4; break;
+	}
+
+	return (next_skip_bytes);
+}
+
+static int
+archive_read_format_lha_bid(struct archive_read *a, int best_bid)
+{
+	const char *p;
+	const void *buff;
+	ssize_t bytes_avail, offset, window;
+	size_t next;
+
+	/* If there's already a better bid than we can ever
+	   make, don't bother testing. */
+	if (best_bid > 30)
+		return (-1);
+
+	if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL)
+		return (-1);
+
+	if (lha_check_header_format(p) == 0)
+		return (30);
+
+	if (p[0] == 'M' && p[1] == 'Z') {
+		/* PE file */
+		offset = 0;
+		window = 4096;
+		while (offset < (1024 * 20)) {
+			buff = __archive_read_ahead(a, offset + window,
+			    &bytes_avail);
+			if (buff == NULL) {
+				/* Remaining bytes are less than window. */
+				window >>= 1;
+				if (window < (H_SIZE + 3))
+					return (0);
+				continue;
+			}
+			p = (const char *)buff + offset;
+			while (p + H_SIZE < (const char *)buff + bytes_avail) {
+				if ((next = lha_check_header_format(p)) == 0)
+					return (30);
+				p += next;
+			}
+			offset = p - (const char *)buff;
+		}
+	}
+	return (0);
+}
+
+static int
+archive_read_format_lha_options(struct archive_read *a,
+    const char *key, const char *val)
+{
+	struct lha *lha;
+	int ret = ARCHIVE_FAILED;
+
+	lha = (struct lha *)(a->format->data);
+	if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "lha: hdrcharset option needs a character-set name");
+		else {
+			lha->opt_sconv =
+			    archive_string_conversion_from_charset(
+				&a->archive, val, 0);
+			if (lha->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+lha_skip_sfx(struct archive_read *a)
+{
+	const void *h;
+	const char *p, *q;
+	size_t next, skip;
+	ssize_t bytes, window;
+
+	window = 4096;
+	for (;;) {
+		h = __archive_read_ahead(a, window, &bytes);
+		if (h == NULL) {
+			/* Remaining bytes are less than window. */
+			window >>= 1;
+			if (window < (H_SIZE + 3))
+				goto fatal;
+			continue;
+		}
+		if (bytes < H_SIZE)
+			goto fatal;
+		p = h;
+		q = p + bytes;
+
+		/*
+		 * Scan ahead until we find something that looks
+		 * like the lha header.
+		 */
+		while (p + H_SIZE < q) {
+			if ((next = lha_check_header_format(p)) == 0) {
+				skip = p - (const char *)h;
+				__archive_read_consume(a, skip);
+				return (ARCHIVE_OK);
+			}
+			p += next;
+		}
+		skip = p - (const char *)h;
+		__archive_read_consume(a, skip);
+	}
+fatal:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Couldn't find out LHa header");
+	return (ARCHIVE_FATAL);
+}
+
+static int
+truncated_error(struct archive_read *a)
+{
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Truncated LHa header");
+	return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_lha_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct archive_wstring linkname;
+	struct archive_wstring pathname;
+	struct lha *lha;
+	const unsigned char *p;
+	const char *signature;
+	int err;
+	struct archive_mstring conv_buffer;
+	const wchar_t *conv_buffer_p;
+
+	lha_crc16_init();
+
+	a->archive.archive_format = ARCHIVE_FORMAT_LHA;
+	if (a->archive.archive_format_name == NULL)
+		a->archive.archive_format_name = "lha";
+
+	lha = (struct lha *)(a->format->data);
+	lha->decompress_init = 0;
+	lha->end_of_entry = 0;
+	lha->end_of_entry_cleanup = 0;
+	lha->entry_unconsumed = 0;
+
+	if ((p = __archive_read_ahead(a, H_SIZE, NULL)) == NULL) {
+		/*
+		 * LHa archiver added 0 to the tail of its archive file as
+		 * the mark of the end of the archive.
+		 */
+		signature = __archive_read_ahead(a, sizeof(signature[0]), NULL);
+		if (signature == NULL || signature[0] == 0)
+			return (ARCHIVE_EOF);
+		return (truncated_error(a));
+	}
+
+	signature = (const char *)p;
+	if (lha->found_first_header == 0 &&
+	    signature[0] == 'M' && signature[1] == 'Z') {
+                /* This is an executable?  Must be self-extracting... 	*/
+		err = lha_skip_sfx(a);
+		if (err < ARCHIVE_WARN)
+			return (err);
+
+		if ((p = __archive_read_ahead(a, sizeof(*p), NULL)) == NULL)
+			return (truncated_error(a));
+		signature = (const char *)p;
+	}
+	/* signature[0] == 0 means the end of an LHa archive file. */
+	if (signature[0] == 0)
+		return (ARCHIVE_EOF);
+
+	/*
+	 * Check the header format and method type.
+	 */
+	if (lha_check_header_format(p) != 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Bad LHa file");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* We've found the first header. */
+	lha->found_first_header = 1;
+	/* Set a default value and common data */
+	lha->header_size = 0;
+	lha->level = p[H_LEVEL_OFFSET];
+	lha->method[0] = p[H_METHOD_OFFSET+1];
+	lha->method[1] = p[H_METHOD_OFFSET+2];
+	lha->method[2] = p[H_METHOD_OFFSET+3];
+	if (memcmp(lha->method, "lhd", 3) == 0)
+		lha->directory = 1;
+	else
+		lha->directory = 0;
+	if (memcmp(lha->method, "lh0", 3) == 0 ||
+	    memcmp(lha->method, "lz4", 3) == 0)
+		lha->entry_is_compressed = 0;
+	else
+		lha->entry_is_compressed = 1;
+
+	lha->compsize = 0;
+	lha->origsize = 0;
+	lha->setflag = 0;
+	lha->birthtime = 0;
+	lha->birthtime_tv_nsec = 0;
+	lha->mtime = 0;
+	lha->mtime_tv_nsec = 0;
+	lha->atime = 0;
+	lha->atime_tv_nsec = 0;
+	lha->mode = (lha->directory)? 0777 : 0666;
+	lha->uid = 0;
+	lha->gid = 0;
+	archive_string_empty(&lha->dirname);
+	archive_string_empty(&lha->filename);
+	lha->dos_attr = 0;
+	if (lha->opt_sconv != NULL) {
+		lha->sconv_dir = lha->opt_sconv;
+		lha->sconv_fname = lha->opt_sconv;
+	} else {
+		lha->sconv_dir = NULL;
+		lha->sconv_fname = NULL;
+	}
+
+	switch (p[H_LEVEL_OFFSET]) {
+	case 0:
+		err = lha_read_file_header_0(a, lha);
+		break;
+	case 1:
+		err = lha_read_file_header_1(a, lha);
+		break;
+	case 2:
+		err = lha_read_file_header_2(a, lha);
+		break;
+	case 3:
+		err = lha_read_file_header_3(a, lha);
+		break;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unsupported LHa header level %d", p[H_LEVEL_OFFSET]);
+		err = ARCHIVE_FATAL;
+		break;
+	}
+	if (err < ARCHIVE_WARN)
+		return (err);
+
+
+	if (!lha->directory && archive_strlen(&lha->filename) == 0)
+		/* The filename has not been set */
+		return (truncated_error(a));
+
+	/*
+	 * Make a pathname from a dirname and a filename, after converting to Unicode.
+	 * This is because codepages might differ between dirname and filename.
+	*/
+	archive_string_init(&pathname);
+	archive_string_init(&linkname);
+	archive_string_init(&conv_buffer.aes_mbs);
+	archive_string_init(&conv_buffer.aes_mbs_in_locale);
+	archive_string_init(&conv_buffer.aes_utf8);
+	archive_string_init(&conv_buffer.aes_wcs);
+	if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->dirname.s, lha->dirname.length, lha->sconv_dir)) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_FILE_FORMAT,
+			"Pathname cannot be converted "
+			"from %s to Unicode.",
+			archive_string_conversion_charset_name(lha->sconv_dir));
+		err = ARCHIVE_FATAL;
+	} else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p))
+		err = ARCHIVE_FATAL;
+	if (err == ARCHIVE_FATAL) {
+		archive_mstring_clean(&conv_buffer);
+		archive_wstring_free(&pathname);
+		archive_wstring_free(&linkname);
+		return (err);
+	}
+	archive_wstring_copy(&pathname, &conv_buffer.aes_wcs);
+
+	archive_string_empty(&conv_buffer.aes_mbs);
+	archive_string_empty(&conv_buffer.aes_mbs_in_locale);
+	archive_string_empty(&conv_buffer.aes_utf8);
+	archive_wstring_empty(&conv_buffer.aes_wcs);
+	if (0 != archive_mstring_copy_mbs_len_l(&conv_buffer, lha->filename.s, lha->filename.length, lha->sconv_fname)) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_FILE_FORMAT,
+			"Pathname cannot be converted "
+			"from %s to Unicode.",
+			archive_string_conversion_charset_name(lha->sconv_fname));
+		err = ARCHIVE_FATAL;
+	}
+	else if (0 != archive_mstring_get_wcs(&a->archive, &conv_buffer, &conv_buffer_p))
+		err = ARCHIVE_FATAL;
+	if (err == ARCHIVE_FATAL) {
+		archive_mstring_clean(&conv_buffer);
+		archive_wstring_free(&pathname);
+		archive_wstring_free(&linkname);
+		return (err);
+	}
+	archive_wstring_concat(&pathname, &conv_buffer.aes_wcs);
+	archive_mstring_clean(&conv_buffer);
+
+	if ((lha->mode & AE_IFMT) == AE_IFLNK) {
+		/*
+	 	 * Extract the symlink-name if it's included in the pathname.
+	 	 */
+		if (!lha_parse_linkname(&linkname, &pathname)) {
+			/* We couldn't get the symlink-name. */
+			archive_set_error(&a->archive,
+		    	    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Unknown symlink-name");
+			archive_wstring_free(&pathname);
+			archive_wstring_free(&linkname);
+			return (ARCHIVE_FAILED);
+		}
+	} else {
+		/*
+		 * Make sure a file-type is set.
+		 * The mode has been overridden if it is in the extended data.
+		 */
+		lha->mode = (lha->mode & ~AE_IFMT) |
+		    ((lha->directory)? AE_IFDIR: AE_IFREG);
+	}
+	if ((lha->setflag & UNIX_MODE_IS_SET) == 0 &&
+	    (lha->dos_attr & 1) != 0)
+		lha->mode &= ~(0222);/* read only. */
+
+	/*
+	 * Set basic file parameters.
+	 */
+	archive_entry_copy_pathname_w(entry, pathname.s);
+	archive_wstring_free(&pathname);
+	if (archive_strlen(&linkname) > 0) {
+		archive_entry_copy_symlink_w(entry, linkname.s);
+	} else
+		archive_entry_set_symlink(entry, NULL);
+	archive_wstring_free(&linkname);
+	/*
+	 * When a header level is 0, there is a possibility that
+	 * a pathname and a symlink has '\' character, a directory
+	 * separator in DOS/Windows. So we should convert it to '/'.
+	 */
+	if (p[H_LEVEL_OFFSET] == 0)
+		lha_replace_path_separator(lha, entry);
+
+	archive_entry_set_mode(entry, lha->mode);
+	archive_entry_set_uid(entry, lha->uid);
+	archive_entry_set_gid(entry, lha->gid);
+	if (archive_strlen(&lha->uname) > 0)
+		archive_entry_set_uname(entry, lha->uname.s);
+	if (archive_strlen(&lha->gname) > 0)
+		archive_entry_set_gname(entry, lha->gname.s);
+	if (lha->setflag & BIRTHTIME_IS_SET) {
+		archive_entry_set_birthtime(entry, lha->birthtime,
+		    lha->birthtime_tv_nsec);
+		archive_entry_set_ctime(entry, lha->birthtime,
+		    lha->birthtime_tv_nsec);
+	} else {
+		archive_entry_unset_birthtime(entry);
+		archive_entry_unset_ctime(entry);
+	}
+	archive_entry_set_mtime(entry, lha->mtime, lha->mtime_tv_nsec);
+	if (lha->setflag & ATIME_IS_SET)
+		archive_entry_set_atime(entry, lha->atime,
+		    lha->atime_tv_nsec);
+	else
+		archive_entry_unset_atime(entry);
+	if (lha->directory || archive_entry_symlink(entry) != NULL)
+		archive_entry_unset_size(entry);
+	else
+		archive_entry_set_size(entry, lha->origsize);
+
+	/*
+	 * Prepare variables used to read a file content.
+	 */
+	lha->entry_bytes_remaining = lha->compsize;
+	if (lha->entry_bytes_remaining < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Invalid LHa entry size");
+		return (ARCHIVE_FATAL);
+	}
+	lha->entry_offset = 0;
+	lha->entry_crc_calculated = 0;
+
+	/*
+	 * This file does not have a content.
+	 */
+	if (lha->directory || lha->compsize == 0)
+		lha->end_of_entry = 1;
+
+	sprintf(lha->format_name, "lha -%c%c%c-",
+	    lha->method[0], lha->method[1], lha->method[2]);
+	a->archive.archive_format_name = lha->format_name;
+
+	return (err);
+}
+
+/*
+ * Replace a DOS path separator '\' by a character '/'.
+ * Some multi-byte character set have  a character '\' in its second byte.
+ */
+static void
+lha_replace_path_separator(struct lha *lha, struct archive_entry *entry)
+{
+	const wchar_t *wp;
+	size_t i;
+
+	if ((wp = archive_entry_pathname_w(entry)) != NULL) {
+		archive_wstrcpy(&(lha->ws), wp);
+		for (i = 0; i < archive_strlen(&(lha->ws)); i++) {
+			if (lha->ws.s[i] == L'\\')
+				lha->ws.s[i] = L'/';
+		}
+		archive_entry_copy_pathname_w(entry, lha->ws.s);
+	}
+
+	if ((wp = archive_entry_symlink_w(entry)) != NULL) {
+		archive_wstrcpy(&(lha->ws), wp);
+		for (i = 0; i < archive_strlen(&(lha->ws)); i++) {
+			if (lha->ws.s[i] == L'\\')
+				lha->ws.s[i] = L'/';
+		}
+		archive_entry_copy_symlink_w(entry, lha->ws.s);
+	}
+}
+
+/*
+ * Header 0 format
+ *
+ * +0              +1         +2               +7                  +11
+ * +---------------+----------+----------------+-------------------+
+ * |header size(*1)|header sum|compression type|compressed size(*2)|
+ * +---------------+----------+----------------+-------------------+
+ *                             <---------------------(*1)----------*
+ *
+ * +11               +15       +17       +19            +20              +21
+ * +-----------------+---------+---------+--------------+----------------+
+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=0)|
+ * +-----------------+---------+---------+--------------+----------------+
+ * *--------------------------------(*1)---------------------------------*
+ *
+ * +21             +22       +22+(*3)   +22+(*3)+2       +22+(*3)+2+(*4)
+ * +---------------+---------+----------+----------------+------------------+
+ * |name length(*3)|file name|file CRC16|extra header(*4)|  compressed data |
+ * +---------------+---------+----------+----------------+------------------+
+ *                  <--(*3)->                             <------(*2)------>
+ * *----------------------(*1)-------------------------->
+ *
+ */
+#define H0_HEADER_SIZE_OFFSET	0
+#define H0_HEADER_SUM_OFFSET	1
+#define H0_COMP_SIZE_OFFSET	7
+#define H0_ORIG_SIZE_OFFSET	11
+#define H0_DOS_TIME_OFFSET	15
+#define H0_NAME_LEN_OFFSET	21
+#define H0_FILE_NAME_OFFSET	22
+#define H0_FIXED_SIZE		24
+static int
+lha_read_file_header_0(struct archive_read *a, struct lha *lha)
+{
+	const unsigned char *p;
+	int extdsize, namelen;
+	unsigned char headersum, sum_calculated;
+
+	if ((p = __archive_read_ahead(a, H0_FIXED_SIZE, NULL)) == NULL)
+		return (truncated_error(a));
+	lha->header_size = p[H0_HEADER_SIZE_OFFSET] + 2;
+	headersum = p[H0_HEADER_SUM_OFFSET];
+	lha->compsize = archive_le32dec(p + H0_COMP_SIZE_OFFSET);
+	lha->origsize = archive_le32dec(p + H0_ORIG_SIZE_OFFSET);
+	lha->mtime = lha_dos_time(p + H0_DOS_TIME_OFFSET);
+	namelen = p[H0_NAME_LEN_OFFSET];
+	extdsize = (int)lha->header_size - H0_FIXED_SIZE - namelen;
+	if ((namelen > 221 || extdsize < 0) && extdsize != -2) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Invalid LHa header");
+		return (ARCHIVE_FATAL);
+	}
+	if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)
+		return (truncated_error(a));
+
+	archive_strncpy(&lha->filename, p + H0_FILE_NAME_OFFSET, namelen);
+	/* When extdsize == -2, A CRC16 value is not present in the header. */
+	if (extdsize >= 0) {
+		lha->crc = archive_le16dec(p + H0_FILE_NAME_OFFSET + namelen);
+		lha->setflag |= CRC_IS_SET;
+	}
+	sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);
+
+	/* Read an extended header */
+	if (extdsize > 0) {
+		/* This extended data is set by 'LHa for UNIX' only.
+		 * Maybe fixed size.
+		 */
+		p += H0_FILE_NAME_OFFSET + namelen + 2;
+		if (p[0] == 'U' && extdsize == 12) {
+			/* p[1] is a minor version. */
+			lha->mtime = archive_le32dec(&p[2]);
+			lha->mode = archive_le16dec(&p[6]);
+			lha->uid = archive_le16dec(&p[8]);
+			lha->gid = archive_le16dec(&p[10]);
+			lha->setflag |= UNIX_MODE_IS_SET;
+		}
+	}
+	__archive_read_consume(a, lha->header_size);
+
+	if (sum_calculated != headersum) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "LHa header sum error");
+		return (ARCHIVE_FATAL);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Header 1 format
+ *
+ * +0              +1         +2               +7            +11
+ * +---------------+----------+----------------+-------------+
+ * |header size(*1)|header sum|compression type|skip size(*2)|
+ * +---------------+----------+----------------+-------------+
+ *                             <---------------(*1)----------*
+ *
+ * +11               +15       +17       +19            +20              +21
+ * +-----------------+---------+---------+--------------+----------------+
+ * |uncompressed size|time(DOS)|date(DOS)|attribute(DOS)|header level(=1)|
+ * +-----------------+---------+---------+--------------+----------------+
+ * *-------------------------------(*1)----------------------------------*
+ *
+ * +21             +22       +22+(*3)   +22+(*3)+2  +22+(*3)+3  +22+(*3)+3+(*4)
+ * +---------------+---------+----------+-----------+-----------+
+ * |name length(*3)|file name|file CRC16|  creator  |padding(*4)|
+ * +---------------+---------+----------+-----------+-----------+
+ *                  <--(*3)->
+ * *----------------------------(*1)----------------------------*
+ *
+ * +22+(*3)+3+(*4)  +22+(*3)+3+(*4)+2     +22+(*3)+3+(*4)+2+(*5)
+ * +----------------+---------------------+------------------------+
+ * |next header size| extended header(*5) |     compressed data    |
+ * +----------------+---------------------+------------------------+
+ * *------(*1)-----> <--------------------(*2)-------------------->
+ */
+#define H1_HEADER_SIZE_OFFSET	0
+#define H1_HEADER_SUM_OFFSET	1
+#define H1_COMP_SIZE_OFFSET	7
+#define H1_ORIG_SIZE_OFFSET	11
+#define H1_DOS_TIME_OFFSET	15
+#define H1_NAME_LEN_OFFSET	21
+#define H1_FILE_NAME_OFFSET	22
+#define H1_FIXED_SIZE		27
+static int
+lha_read_file_header_1(struct archive_read *a, struct lha *lha)
+{
+	const unsigned char *p;
+	size_t extdsize;
+	int i, err, err2;
+	int namelen, padding;
+	unsigned char headersum, sum_calculated;
+
+	err = ARCHIVE_OK;
+
+	if ((p = __archive_read_ahead(a, H1_FIXED_SIZE, NULL)) == NULL)
+		return (truncated_error(a));
+
+	lha->header_size = p[H1_HEADER_SIZE_OFFSET] + 2;
+	headersum = p[H1_HEADER_SUM_OFFSET];
+	/* Note: An extended header size is included in a compsize. */
+	lha->compsize = archive_le32dec(p + H1_COMP_SIZE_OFFSET);
+	lha->origsize = archive_le32dec(p + H1_ORIG_SIZE_OFFSET);
+	lha->mtime = lha_dos_time(p + H1_DOS_TIME_OFFSET);
+	namelen = p[H1_NAME_LEN_OFFSET];
+	/* Calculate a padding size. The result will be normally 0 only(?) */
+	padding = ((int)lha->header_size) - H1_FIXED_SIZE - namelen;
+
+	if (namelen > 230 || padding < 0)
+		goto invalid;
+
+	if ((p = __archive_read_ahead(a, lha->header_size, NULL)) == NULL)
+		return (truncated_error(a));
+
+	for (i = 0; i < namelen; i++) {
+		if (p[i + H1_FILE_NAME_OFFSET] == 0xff)
+			goto invalid;/* Invalid filename. */
+	}
+	archive_strncpy(&lha->filename, p + H1_FILE_NAME_OFFSET, namelen);
+	lha->crc = archive_le16dec(p + H1_FILE_NAME_OFFSET + namelen);
+	lha->setflag |= CRC_IS_SET;
+
+	sum_calculated = lha_calcsum(0, p, 2, lha->header_size - 2);
+	/* Consume used bytes but not include `next header size' data
+	 * since it will be consumed in lha_read_file_extended_header(). */
+	__archive_read_consume(a, lha->header_size - 2);
+
+	/* Read extended headers */
+	err2 = lha_read_file_extended_header(a, lha, NULL, 2,
+	    (size_t)(lha->compsize + 2), &extdsize);
+	if (err2 < ARCHIVE_WARN)
+		return (err2);
+	if (err2 < err)
+		err = err2;
+	/* Get a real compressed file size. */
+	lha->compsize -= extdsize - 2;
+
+	if (lha->compsize < 0)
+		goto invalid;	/* Invalid compressed file size */
+
+	if (sum_calculated != headersum) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "LHa header sum error");
+		return (ARCHIVE_FATAL);
+	}
+	return (err);
+invalid:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Invalid LHa header");
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Header 2 format
+ *
+ * +0              +2               +7                  +11               +15
+ * +---------------+----------------+-------------------+-----------------+
+ * |header size(*1)|compression type|compressed size(*2)|uncompressed size|
+ * +---------------+----------------+-------------------+-----------------+
+ *  <--------------------------------(*1)---------------------------------*
+ *
+ * +15               +19          +20              +21        +23         +24
+ * +-----------------+------------+----------------+----------+-----------+
+ * |data/time(time_t)| 0x20 fixed |header level(=2)|file CRC16|  creator  |
+ * +-----------------+------------+----------------+----------+-----------+
+ * *---------------------------------(*1)---------------------------------*
+ *
+ * +24              +26                 +26+(*3)      +26+(*3)+(*4)
+ * +----------------+-------------------+-------------+-------------------+
+ * |next header size|extended header(*3)| padding(*4) |  compressed data  |
+ * +----------------+-------------------+-------------+-------------------+
+ * *--------------------------(*1)-------------------> <------(*2)------->
+ *
+ */
+#define H2_HEADER_SIZE_OFFSET	0
+#define H2_COMP_SIZE_OFFSET	7
+#define H2_ORIG_SIZE_OFFSET	11
+#define H2_TIME_OFFSET		15
+#define H2_CRC_OFFSET		21
+#define H2_FIXED_SIZE		24
+static int
+lha_read_file_header_2(struct archive_read *a, struct lha *lha)
+{
+	const unsigned char *p;
+	size_t extdsize;
+	int err, padding;
+	uint16_t header_crc;
+
+	if ((p = __archive_read_ahead(a, H2_FIXED_SIZE, NULL)) == NULL)
+		return (truncated_error(a));
+
+	lha->header_size =archive_le16dec(p + H2_HEADER_SIZE_OFFSET);
+	lha->compsize = archive_le32dec(p + H2_COMP_SIZE_OFFSET);
+	lha->origsize = archive_le32dec(p + H2_ORIG_SIZE_OFFSET);
+	lha->mtime = archive_le32dec(p + H2_TIME_OFFSET);
+	lha->crc = archive_le16dec(p + H2_CRC_OFFSET);
+	lha->setflag |= CRC_IS_SET;
+
+	if (lha->header_size < H2_FIXED_SIZE) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Invalid LHa header size");
+		return (ARCHIVE_FATAL);
+	}
+
+	header_crc = lha_crc16(0, p, H2_FIXED_SIZE);
+	__archive_read_consume(a, H2_FIXED_SIZE);
+
+	/* Read extended headers */
+	err = lha_read_file_extended_header(a, lha, &header_crc, 2,
+		  lha->header_size - H2_FIXED_SIZE, &extdsize);
+	if (err < ARCHIVE_WARN)
+		return (err);
+
+	/* Calculate a padding size. The result will be normally 0 or 1. */
+	padding = (int)lha->header_size - (int)(H2_FIXED_SIZE + extdsize);
+	if (padding > 0) {
+		if ((p = __archive_read_ahead(a, padding, NULL)) == NULL)
+			return (truncated_error(a));
+		header_crc = lha_crc16(header_crc, p, padding);
+		__archive_read_consume(a, padding);
+	}
+
+	if (header_crc != lha->header_crc) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "LHa header CRC error");
+		return (ARCHIVE_FATAL);
+	}
+	return (err);
+}
+
+/*
+ * Header 3 format
+ *
+ * +0           +2               +7                  +11               +15
+ * +------------+----------------+-------------------+-----------------+
+ * | 0x04 fixed |compression type|compressed size(*2)|uncompressed size|
+ * +------------+----------------+-------------------+-----------------+
+ *  <-------------------------------(*1)-------------------------------*
+ *
+ * +15               +19          +20              +21        +23         +24
+ * +-----------------+------------+----------------+----------+-----------+
+ * |date/time(time_t)| 0x20 fixed |header level(=3)|file CRC16|  creator  |
+ * +-----------------+------------+----------------+----------+-----------+
+ * *--------------------------------(*1)----------------------------------*
+ *
+ * +24             +28              +32                 +32+(*3)
+ * +---------------+----------------+-------------------+-----------------+
+ * |header size(*1)|next header size|extended header(*3)| compressed data |
+ * +---------------+----------------+-------------------+-----------------+
+ * *------------------------(*1)-----------------------> <------(*2)----->
+ *
+ */
+#define H3_FIELD_LEN_OFFSET	0
+#define H3_COMP_SIZE_OFFSET	7
+#define H3_ORIG_SIZE_OFFSET	11
+#define H3_TIME_OFFSET		15
+#define H3_CRC_OFFSET		21
+#define H3_HEADER_SIZE_OFFSET	24
+#define H3_FIXED_SIZE		28
+static int
+lha_read_file_header_3(struct archive_read *a, struct lha *lha)
+{
+	const unsigned char *p;
+	size_t extdsize;
+	int err;
+	uint16_t header_crc;
+
+	if ((p = __archive_read_ahead(a, H3_FIXED_SIZE, NULL)) == NULL)
+		return (truncated_error(a));
+
+	if (archive_le16dec(p + H3_FIELD_LEN_OFFSET) != 4)
+		goto invalid;
+	lha->header_size =archive_le32dec(p + H3_HEADER_SIZE_OFFSET);
+	lha->compsize = archive_le32dec(p + H3_COMP_SIZE_OFFSET);
+	lha->origsize = archive_le32dec(p + H3_ORIG_SIZE_OFFSET);
+	lha->mtime = archive_le32dec(p + H3_TIME_OFFSET);
+	lha->crc = archive_le16dec(p + H3_CRC_OFFSET);
+	lha->setflag |= CRC_IS_SET;
+
+	if (lha->header_size < H3_FIXED_SIZE + 4)
+		goto invalid;
+	header_crc = lha_crc16(0, p, H3_FIXED_SIZE);
+	__archive_read_consume(a, H3_FIXED_SIZE);
+
+	/* Read extended headers */
+	err = lha_read_file_extended_header(a, lha, &header_crc, 4,
+		  lha->header_size - H3_FIXED_SIZE, &extdsize);
+	if (err < ARCHIVE_WARN)
+		return (err);
+
+	if (header_crc != lha->header_crc) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "LHa header CRC error");
+		return (ARCHIVE_FATAL);
+	}
+	return (err);
+invalid:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Invalid LHa header");
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Extended header format
+ *
+ * +0             +2        +3  -- used in header 1 and 2
+ * +0             +4        +5  -- used in header 3
+ * +--------------+---------+-------------------+--------------+--
+ * |ex-header size|header id|        data       |ex-header size| .......
+ * +--------------+---------+-------------------+--------------+--
+ *  <-------------( ex-header size)------------> <-- next extended header --*
+ *
+ * If the ex-header size is zero, it is the make of the end of extended
+ * headers.
+ *
+ */
+static int
+lha_read_file_extended_header(struct archive_read *a, struct lha *lha,
+    uint16_t *crc, int sizefield_length, size_t limitsize, size_t *total_size)
+{
+	const void *h;
+	const unsigned char *extdheader;
+	size_t	extdsize;
+	size_t	datasize;
+	unsigned int i;
+	unsigned char extdtype;
+
+#define EXT_HEADER_CRC		0x00		/* Header CRC and information*/
+#define EXT_FILENAME		0x01		/* Filename 		    */
+#define EXT_DIRECTORY		0x02		/* Directory name	    */
+#define EXT_DOS_ATTR		0x40		/* MS-DOS attribute	    */
+#define EXT_TIMESTAMP		0x41		/* Windows time stamp	    */
+#define EXT_FILESIZE		0x42		/* Large file size	    */
+#define EXT_TIMEZONE		0x43		/* Time zone		    */
+#define EXT_UTF16_FILENAME	0x44		/* UTF-16 filename 	    */
+#define EXT_UTF16_DIRECTORY	0x45		/* UTF-16 directory name    */
+#define EXT_CODEPAGE		0x46		/* Codepage		    */
+#define EXT_UNIX_MODE		0x50		/* File permission	    */
+#define EXT_UNIX_GID_UID	0x51		/* gid,uid		    */
+#define EXT_UNIX_GNAME		0x52		/* Group name		    */
+#define EXT_UNIX_UNAME		0x53		/* User name		    */
+#define EXT_UNIX_MTIME		0x54		/* Modified time	    */
+#define EXT_OS2_NEW_ATTR	0x7f		/* new attribute(OS/2 only) */
+#define EXT_NEW_ATTR		0xff		/* new attribute	    */
+
+	*total_size = sizefield_length;
+
+	for (;;) {
+		/* Read an extended header size. */
+		if ((h =
+		    __archive_read_ahead(a, sizefield_length, NULL)) == NULL)
+			return (truncated_error(a));
+		/* Check if the size is the zero indicates the end of the
+		 * extended header. */
+		if (sizefield_length == sizeof(uint16_t))
+			extdsize = archive_le16dec(h);
+		else
+			extdsize = archive_le32dec(h);
+		if (extdsize == 0) {
+			/* End of extended header */
+			if (crc != NULL)
+				*crc = lha_crc16(*crc, h, sizefield_length);
+			__archive_read_consume(a, sizefield_length);
+			return (ARCHIVE_OK);
+		}
+
+		/* Sanity check to the extended header size. */
+		if (((uint64_t)*total_size + extdsize) >
+				    (uint64_t)limitsize ||
+		    extdsize <= (size_t)sizefield_length)
+			goto invalid;
+
+		/* Read the extended header. */
+		if ((h = __archive_read_ahead(a, extdsize, NULL)) == NULL)
+			return (truncated_error(a));
+		*total_size += extdsize;
+
+		extdheader = (const unsigned char *)h;
+		/* Get the extended header type. */
+		extdtype = extdheader[sizefield_length];
+		/* Calculate an extended data size. */
+		datasize = extdsize - (1 + sizefield_length);
+		/* Skip an extended header size field and type field. */
+		extdheader += sizefield_length + 1;
+
+		if (crc != NULL && extdtype != EXT_HEADER_CRC)
+			*crc = lha_crc16(*crc, h, extdsize);
+		switch (extdtype) {
+		case EXT_HEADER_CRC:
+			/* We only use a header CRC. Following data will not
+			 * be used. */
+			if (datasize >= 2) {
+				lha->header_crc = archive_le16dec(extdheader);
+				if (crc != NULL) {
+					static const char zeros[2] = {0, 0};
+					*crc = lha_crc16(*crc, h,
+					    extdsize - datasize);
+					/* CRC value itself as zero */
+					*crc = lha_crc16(*crc, zeros, 2);
+					*crc = lha_crc16(*crc,
+					    extdheader+2, datasize - 2);
+				}
+			}
+			break;
+		case EXT_FILENAME:
+			if (datasize == 0) {
+				/* maybe directory header */
+				archive_string_empty(&lha->filename);
+				break;
+			}
+			if (extdheader[0] == '\0')
+				goto invalid;
+			archive_strncpy(&lha->filename,
+			    (const char *)extdheader, datasize);
+			break;
+		case EXT_UTF16_FILENAME:
+			if (datasize == 0) {
+				/* maybe directory header */
+				archive_string_empty(&lha->filename);
+				break;
+			} else if (datasize & 1) {
+				/* UTF-16 characters take always 2 or 4 bytes */
+				goto invalid;
+			}
+			if (extdheader[0] == '\0')
+				goto invalid;
+			archive_string_empty(&lha->filename);
+			archive_array_append(&lha->filename,
+				(const char *)extdheader, datasize);
+			/* Setup a string conversion for a filename. */
+			lha->sconv_fname =
+			    archive_string_conversion_from_charset(&a->archive,
+			        "UTF-16LE", 1);
+			if (lha->sconv_fname == NULL)
+				return (ARCHIVE_FATAL);
+			break;
+		case EXT_DIRECTORY:
+			if (datasize == 0 || extdheader[0] == '\0')
+				/* no directory name data. exit this case. */
+				goto invalid;
+
+			archive_strncpy(&lha->dirname,
+		  	    (const char *)extdheader, datasize);
+			/*
+			 * Convert directory delimiter from 0xFF
+			 * to '/' for local system.
+	 		 */
+			for (i = 0; i < lha->dirname.length; i++) {
+				if ((unsigned char)lha->dirname.s[i] == 0xFF)
+					lha->dirname.s[i] = '/';
+			}
+			/* Is last character directory separator? */
+			if (lha->dirname.s[lha->dirname.length-1] != '/')
+				/* invalid directory data */
+				goto invalid;
+			break;
+		case EXT_UTF16_DIRECTORY:
+			/* UTF-16 characters take always 2 or 4 bytes */
+			if (datasize == 0 || (datasize & 1) ||
+			    extdheader[0] == '\0') {
+				/* no directory name data. exit this case. */
+				goto invalid;
+			}
+
+			archive_string_empty(&lha->dirname);
+			archive_array_append(&lha->dirname,
+				(const char *)extdheader, datasize);
+			lha->sconv_dir =
+			    archive_string_conversion_from_charset(&a->archive,
+			        "UTF-16LE", 1);
+			if (lha->sconv_dir == NULL)
+				return (ARCHIVE_FATAL);
+			else {
+				/*
+				 * Convert directory delimiter from 0xFFFF
+				 * to '/' for local system.
+				 */
+				uint16_t dirSep;
+				uint16_t d = 1;
+				if (archive_be16dec(&d) == 1)
+					dirSep = 0x2F00;
+				else
+					dirSep = 0x002F;
+
+				/* UTF-16LE character */
+				uint16_t *utf16name =
+				    (uint16_t *)lha->dirname.s;
+				for (i = 0; i < lha->dirname.length / 2; i++) {
+					if (utf16name[i] == 0xFFFF) {
+						utf16name[i] = dirSep;
+					}
+				}
+				/* Is last character directory separator? */
+				if (utf16name[lha->dirname.length / 2 - 1] !=
+				    dirSep) {
+					/* invalid directory data */
+					goto invalid;
+				}
+			}
+			break;
+		case EXT_DOS_ATTR:
+			if (datasize == 2)
+				lha->dos_attr = (unsigned char)
+				    (archive_le16dec(extdheader) & 0xff);
+			break;
+		case EXT_TIMESTAMP:
+			if (datasize == (sizeof(uint64_t) * 3)) {
+				lha->birthtime = lha_win_time(
+				    archive_le64dec(extdheader),
+				    &lha->birthtime_tv_nsec);
+				extdheader += sizeof(uint64_t);
+				lha->mtime = lha_win_time(
+				    archive_le64dec(extdheader),
+				    &lha->mtime_tv_nsec);
+				extdheader += sizeof(uint64_t);
+				lha->atime = lha_win_time(
+				    archive_le64dec(extdheader),
+				    &lha->atime_tv_nsec);
+				lha->setflag |= BIRTHTIME_IS_SET |
+				    ATIME_IS_SET;
+			}
+			break;
+		case EXT_FILESIZE:
+			if (datasize == sizeof(uint64_t) * 2) {
+				lha->compsize = archive_le64dec(extdheader);
+				extdheader += sizeof(uint64_t);
+				lha->origsize = archive_le64dec(extdheader);
+			}
+			break;
+		case EXT_CODEPAGE:
+			/* Get an archived filename charset from codepage.
+			 * This overwrites the charset specified by
+			 * hdrcharset option. */
+			if (datasize == sizeof(uint32_t)) {
+				struct archive_string cp;
+				const char *charset;
+
+				archive_string_init(&cp);
+				switch (archive_le32dec(extdheader)) {
+				case 65001: /* UTF-8 */
+					charset = "UTF-8";
+					break;
+				default:
+					archive_string_sprintf(&cp, "CP%d",
+					    (int)archive_le32dec(extdheader));
+					charset = cp.s;
+					break;
+				}
+				lha->sconv_dir =
+				    archive_string_conversion_from_charset(
+					&(a->archive), charset, 1);
+				lha->sconv_fname =
+				    archive_string_conversion_from_charset(
+					&(a->archive), charset, 1);
+				archive_string_free(&cp);
+				if (lha->sconv_dir == NULL)
+					return (ARCHIVE_FATAL);
+				if (lha->sconv_fname == NULL)
+					return (ARCHIVE_FATAL);
+			}
+			break;
+		case EXT_UNIX_MODE:
+			if (datasize == sizeof(uint16_t)) {
+				lha->mode = archive_le16dec(extdheader);
+				lha->setflag |= UNIX_MODE_IS_SET;
+			}
+			break;
+		case EXT_UNIX_GID_UID:
+			if (datasize == (sizeof(uint16_t) * 2)) {
+				lha->gid = archive_le16dec(extdheader);
+				lha->uid = archive_le16dec(extdheader+2);
+			}
+			break;
+		case EXT_UNIX_GNAME:
+			if (datasize > 0)
+				archive_strncpy(&lha->gname,
+				    (const char *)extdheader, datasize);
+			break;
+		case EXT_UNIX_UNAME:
+			if (datasize > 0)
+				archive_strncpy(&lha->uname,
+				    (const char *)extdheader, datasize);
+			break;
+		case EXT_UNIX_MTIME:
+			if (datasize == sizeof(uint32_t))
+				lha->mtime = archive_le32dec(extdheader);
+			break;
+		case EXT_OS2_NEW_ATTR:
+			/* This extended header is OS/2 depend. */
+			if (datasize == 16) {
+				lha->dos_attr = (unsigned char)
+				    (archive_le16dec(extdheader) & 0xff);
+				lha->mode = archive_le16dec(extdheader+2);
+				lha->gid = archive_le16dec(extdheader+4);
+				lha->uid = archive_le16dec(extdheader+6);
+				lha->birthtime = archive_le32dec(extdheader+8);
+				lha->atime = archive_le32dec(extdheader+12);
+				lha->setflag |= UNIX_MODE_IS_SET
+				    | BIRTHTIME_IS_SET | ATIME_IS_SET;
+			}
+			break;
+		case EXT_NEW_ATTR:
+			if (datasize == 20) {
+				lha->mode = (mode_t)archive_le32dec(extdheader);
+				lha->gid = archive_le32dec(extdheader+4);
+				lha->uid = archive_le32dec(extdheader+8);
+				lha->birthtime = archive_le32dec(extdheader+12);
+				lha->atime = archive_le32dec(extdheader+16);
+				lha->setflag |= UNIX_MODE_IS_SET
+				    | BIRTHTIME_IS_SET | ATIME_IS_SET;
+			}
+			break;
+		case EXT_TIMEZONE:		/* Not supported */
+			break;
+		default:
+			break;
+		}
+
+		__archive_read_consume(a, extdsize);
+	}
+invalid:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Invalid extended LHa header");
+	return (ARCHIVE_FATAL);
+}
+
+static int
+lha_end_of_entry(struct archive_read *a)
+{
+	struct lha *lha = (struct lha *)(a->format->data);
+	int r = ARCHIVE_EOF;
+
+	if (!lha->end_of_entry_cleanup) {
+		if ((lha->setflag & CRC_IS_SET) &&
+		    lha->crc != lha->entry_crc_calculated) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "LHa data CRC error");
+			r = ARCHIVE_WARN;
+		}
+
+		/* End-of-entry cleanup done. */
+		lha->end_of_entry_cleanup = 1;
+	}
+	return (r);
+}
+
+static int
+archive_read_format_lha_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	struct lha *lha = (struct lha *)(a->format->data);
+	int r;
+
+	if (lha->entry_unconsumed) {
+		/* Consume as much as the decompressor actually used. */
+		__archive_read_consume(a, lha->entry_unconsumed);
+		lha->entry_unconsumed = 0;
+	}
+	if (lha->end_of_entry) {
+		*offset = lha->entry_offset;
+		*size = 0;
+		*buff = NULL;
+		return (lha_end_of_entry(a));
+	}
+
+	if (lha->entry_is_compressed)
+		r =  lha_read_data_lzh(a, buff, size, offset);
+	else
+		/* No compression. */
+		r =  lha_read_data_none(a, buff, size, offset);
+	return (r);
+}
+
+/*
+ * Read a file content in no compression.
+ *
+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
+ * lha->end_of_entry if it consumes all of the data.
+ */
+static int
+lha_read_data_none(struct archive_read *a, const void **buff,
+    size_t *size, int64_t *offset)
+{
+	struct lha *lha = (struct lha *)(a->format->data);
+	ssize_t bytes_avail;
+
+	if (lha->entry_bytes_remaining == 0) {
+		*buff = NULL;
+		*size = 0;
+		*offset = lha->entry_offset;
+		lha->end_of_entry = 1;
+		return (ARCHIVE_OK);
+	}
+	/*
+	 * Note: '1' here is a performance optimization.
+	 * Recall that the decompression layer returns a count of
+	 * available bytes; asking for more than that forces the
+	 * decompressor to combine reads by copying data.
+	 */
+	*buff = __archive_read_ahead(a, 1, &bytes_avail);
+	if (bytes_avail <= 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated LHa file data");
+		return (ARCHIVE_FATAL);
+	}
+	if (bytes_avail > lha->entry_bytes_remaining)
+		bytes_avail = (ssize_t)lha->entry_bytes_remaining;
+	lha->entry_crc_calculated =
+	    lha_crc16(lha->entry_crc_calculated, *buff, bytes_avail);
+	*size = bytes_avail;
+	*offset = lha->entry_offset;
+	lha->entry_offset += bytes_avail;
+	lha->entry_bytes_remaining -= bytes_avail;
+	if (lha->entry_bytes_remaining == 0)
+		lha->end_of_entry = 1;
+	lha->entry_unconsumed = bytes_avail;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Read a file content in LZHUFF encoding.
+ *
+ * Returns ARCHIVE_OK if successful, returns ARCHIVE_WARN if compression is
+ * unsupported, ARCHIVE_FATAL otherwise, sets lha->end_of_entry if it consumes
+ * all of the data.
+ */
+static int
+lha_read_data_lzh(struct archive_read *a, const void **buff,
+    size_t *size, int64_t *offset)
+{
+	struct lha *lha = (struct lha *)(a->format->data);
+	ssize_t bytes_avail;
+	int r;
+
+	/* If we haven't yet read any data, initialize the decompressor. */
+	if (!lha->decompress_init) {
+		r = lzh_decode_init(&(lha->strm), lha->method);
+		switch (r) {
+		case ARCHIVE_OK:
+			break;
+		case ARCHIVE_FAILED:
+        		/* Unsupported compression. */
+			*buff = NULL;
+			*size = 0;
+			*offset = 0;
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Unsupported lzh compression method -%c%c%c-",
+			    lha->method[0], lha->method[1], lha->method[2]);
+			/* We know compressed size; just skip it. */
+			archive_read_format_lha_read_data_skip(a);
+			return (ARCHIVE_WARN);
+		default:
+			archive_set_error(&a->archive, ENOMEM,
+			    "Couldn't allocate memory "
+			    "for lzh decompression");
+			return (ARCHIVE_FATAL);
+		}
+		/* We've initialized decompression for this stream. */
+		lha->decompress_init = 1;
+		lha->strm.avail_out = 0;
+		lha->strm.total_out = 0;
+	}
+
+	/*
+	 * Note: '1' here is a performance optimization.
+	 * Recall that the decompression layer returns a count of
+	 * available bytes; asking for more than that forces the
+	 * decompressor to combine reads by copying data.
+	 */
+	lha->strm.next_in = __archive_read_ahead(a, 1, &bytes_avail);
+	if (bytes_avail <= 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated LHa file body");
+		return (ARCHIVE_FATAL);
+	}
+	if (bytes_avail > lha->entry_bytes_remaining)
+		bytes_avail = (ssize_t)lha->entry_bytes_remaining;
+
+	lha->strm.avail_in = (int)bytes_avail;
+	lha->strm.total_in = 0;
+	lha->strm.avail_out = 0;
+
+	r = lzh_decode(&(lha->strm), bytes_avail == lha->entry_bytes_remaining);
+	switch (r) {
+	case ARCHIVE_OK:
+		break;
+	case ARCHIVE_EOF:
+		lha->end_of_entry = 1;
+		break;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Bad lzh data");
+		return (ARCHIVE_FAILED);
+	}
+	lha->entry_unconsumed = lha->strm.total_in;
+	lha->entry_bytes_remaining -= lha->strm.total_in;
+
+	if (lha->strm.avail_out) {
+		*offset = lha->entry_offset;
+		*size = lha->strm.avail_out;
+		*buff = lha->strm.ref_ptr;
+		lha->entry_crc_calculated =
+		    lha_crc16(lha->entry_crc_calculated, *buff, *size);
+		lha->entry_offset += *size;
+	} else {
+		*offset = lha->entry_offset;
+		*size = 0;
+		*buff = NULL;
+		if (lha->end_of_entry)
+			return (lha_end_of_entry(a));
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Skip a file content.
+ */
+static int
+archive_read_format_lha_read_data_skip(struct archive_read *a)
+{
+	struct lha *lha;
+	int64_t bytes_skipped;
+
+	lha = (struct lha *)(a->format->data);
+
+	if (lha->entry_unconsumed) {
+		/* Consume as much as the decompressor actually used. */
+		__archive_read_consume(a, lha->entry_unconsumed);
+		lha->entry_unconsumed = 0;
+	}
+
+	/* if we've already read to end of data, we're done. */
+	if (lha->end_of_entry_cleanup)
+		return (ARCHIVE_OK);
+
+	/*
+	 * If the length is at the beginning, we can skip the
+	 * compressed data much more quickly.
+	 */
+	bytes_skipped = __archive_read_consume(a, lha->entry_bytes_remaining);
+	if (bytes_skipped < 0)
+		return (ARCHIVE_FATAL);
+
+	/* This entry is finished and done. */
+	lha->end_of_entry_cleanup = lha->end_of_entry = 1;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_lha_cleanup(struct archive_read *a)
+{
+	struct lha *lha = (struct lha *)(a->format->data);
+
+	lzh_decode_free(&(lha->strm));
+	archive_string_free(&(lha->dirname));
+	archive_string_free(&(lha->filename));
+	archive_string_free(&(lha->uname));
+	archive_string_free(&(lha->gname));
+	archive_wstring_free(&(lha->ws));
+	free(lha);
+	(a->format->data) = NULL;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * 'LHa for UNIX' utility has archived a symbolic-link name after
+ * a pathname with '|' character.
+ * This function extracts the symbolic-link name from the pathname.
+ *
+ * example.
+ *   1. a symbolic-name is 'aaa/bb/cc'
+ *   2. a filename is 'xxx/bbb'
+ *  then a archived pathname is 'xxx/bbb|aaa/bb/cc'
+ */
+static int
+lha_parse_linkname(struct archive_wstring *linkname,
+    struct archive_wstring *pathname)
+{
+	wchar_t *	linkptr;
+	size_t 	symlen;
+
+	linkptr = wcschr(pathname->s, L'|');
+	if (linkptr != NULL) {
+		symlen = wcslen(linkptr + 1);
+		archive_wstrncpy(linkname, linkptr+1, symlen);
+
+		*linkptr = 0;
+		pathname->length = wcslen(pathname->s);
+
+		return (1);
+	}
+	return (0);
+}
+
+/* Convert an MSDOS-style date/time into Unix-style time. */
+static time_t
+lha_dos_time(const unsigned char *p)
+{
+	int msTime, msDate;
+	struct tm ts;
+
+	msTime = archive_le16dec(p);
+	msDate = archive_le16dec(p+2);
+
+	memset(&ts, 0, sizeof(ts));
+	ts.tm_year = ((msDate >> 9) & 0x7f) + 80;   /* Years since 1900. */
+	ts.tm_mon = ((msDate >> 5) & 0x0f) - 1;     /* Month number.     */
+	ts.tm_mday = msDate & 0x1f;		    /* Day of month.     */
+	ts.tm_hour = (msTime >> 11) & 0x1f;
+	ts.tm_min = (msTime >> 5) & 0x3f;
+	ts.tm_sec = (msTime << 1) & 0x3e;
+	ts.tm_isdst = -1;
+	return (mktime(&ts));
+}
+
+/* Convert an MS-Windows-style date/time into Unix-style time. */
+static time_t
+lha_win_time(uint64_t wintime, long *ns)
+{
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+
+	if (wintime >= EPOC_TIME) {
+		wintime -= EPOC_TIME;	/* 1970-01-01 00:00:00 (UTC) */
+		if (ns != NULL)
+			*ns = (long)(wintime % 10000000) * 100;
+		return (wintime / 10000000);
+	} else {
+		if (ns != NULL)
+			*ns = 0;
+		return (0);
+	}
+}
+
+static unsigned char
+lha_calcsum(unsigned char sum, const void *pp, int offset, size_t size)
+{
+	unsigned char const *p = (unsigned char const *)pp;
+
+	p += offset;
+	for (;size > 0; --size)
+		sum += *p++;
+	return (sum);
+}
+
+static uint16_t crc16tbl[2][256];
+static void
+lha_crc16_init(void)
+{
+	unsigned int i;
+	static int crc16init = 0;
+
+	if (crc16init)
+		return;
+	crc16init = 1;
+
+	for (i = 0; i < 256; i++) {
+		unsigned int j;
+		uint16_t crc = (uint16_t)i;
+		for (j = 8; j; j--)
+			crc = (crc >> 1) ^ ((crc & 1) * 0xA001);
+		crc16tbl[0][i] = crc;
+	}
+
+	for (i = 0; i < 256; i++) {
+		crc16tbl[1][i] = (crc16tbl[0][i] >> 8)
+			^ crc16tbl[0][crc16tbl[0][i] & 0xff];
+	}
+}
+
+static uint16_t
+lha_crc16(uint16_t crc, const void *pp, size_t len)
+{
+	const unsigned char *p = (const unsigned char *)pp;
+	const uint16_t *buff;
+	const union {
+		uint32_t i;
+		char c[4];
+	} u = { 0x01020304 };
+
+	if (len == 0)
+		return crc;
+
+	/* Process unaligned address. */
+	if (((uintptr_t)p) & (uintptr_t)0x1) {
+		crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff];
+		len--;
+	}
+	buff = (const uint16_t *)p;
+	/*
+	 * Modern C compiler such as GCC does not unroll automatically yet
+	 * without unrolling pragma, and Clang is so. So we should
+	 * unroll this loop for its performance.
+	 */
+	for (;len >= 8; len -= 8) {
+		/* This if statement expects compiler optimization will
+		 * remove the statement which will not be executed. */
+#undef bswap16
+#if defined(_MSC_VER) && _MSC_VER >= 1400  /* Visual Studio */
+#  define bswap16(x) _byteswap_ushort(x)
+#elif defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ > 4)
+/* GCC 4.8 and later has __builtin_bswap16() */
+#  define bswap16(x) __builtin_bswap16(x)
+#elif defined(__clang__)
+/* All clang versions have __builtin_bswap16() */
+#  define bswap16(x) __builtin_bswap16(x)
+#else
+#  define bswap16(x) ((((x) >> 8) & 0xff) | ((x) << 8))
+#endif
+#define CRC16W	do { 	\
+		if(u.c[0] == 1) { /* Big endian */		\
+			crc ^= bswap16(*buff); buff++;		\
+		} else						\
+			crc ^= *buff++;				\
+		crc = crc16tbl[1][crc & 0xff] ^ crc16tbl[0][crc >> 8];\
+} while (0)
+		CRC16W;
+		CRC16W;
+		CRC16W;
+		CRC16W;
+#undef CRC16W
+#undef bswap16
+	}
+
+	p = (const unsigned char *)buff;
+	for (;len; len--) {
+		crc = (crc >> 8) ^ crc16tbl[0][(crc ^ *p++) & 0xff];
+	}
+	return crc;
+}
+
+/*
+ * Initialize LZHUF decoder.
+ *
+ * Returns ARCHIVE_OK if initialization was successful.
+ * Returns ARCHIVE_FAILED if method is unsupported.
+ * Returns ARCHIVE_FATAL if initialization failed; memory allocation
+ * error occurred.
+ */
+static int
+lzh_decode_init(struct lzh_stream *strm, const char *method)
+{
+	struct lzh_dec *ds;
+	int w_bits, w_size;
+
+	if (strm->ds == NULL) {
+		strm->ds = calloc(1, sizeof(*strm->ds));
+		if (strm->ds == NULL)
+			return (ARCHIVE_FATAL);
+	}
+	ds = strm->ds;
+	ds->error = ARCHIVE_FAILED;
+	if (method == NULL || method[0] != 'l' || method[1] != 'h')
+		return (ARCHIVE_FAILED);
+	switch (method[2]) {
+	case '5':
+		w_bits = 13;/* 8KiB for window */
+		break;
+	case '6':
+		w_bits = 15;/* 32KiB for window */
+		break;
+	case '7':
+		w_bits = 16;/* 64KiB for window */
+		break;
+	default:
+		return (ARCHIVE_FAILED);/* Not supported. */
+	}
+	ds->error = ARCHIVE_FATAL;
+	/* Expand a window size up to 128 KiB for decompressing process
+	 * performance whatever its original window size is. */
+	ds->w_size = 1U << 17;
+	ds->w_mask = ds->w_size -1;
+	if (ds->w_buff == NULL) {
+		ds->w_buff = malloc(ds->w_size);
+		if (ds->w_buff == NULL)
+			return (ARCHIVE_FATAL);
+	}
+	w_size = 1U << w_bits;
+	memset(ds->w_buff + ds->w_size - w_size, 0x20, w_size);
+	ds->w_pos = 0;
+	ds->state = 0;
+	ds->pos_pt_len_size = w_bits + 1;
+	ds->pos_pt_len_bits = (w_bits == 15 || w_bits == 16)? 5: 4;
+	ds->literal_pt_len_size = PT_BITLEN_SIZE;
+	ds->literal_pt_len_bits = 5;
+	ds->br.cache_buffer = 0;
+	ds->br.cache_avail = 0;
+
+	if (lzh_huffman_init(&(ds->lt), LT_BITLEN_SIZE, 16)
+	    != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	ds->lt.len_bits = 9;
+	if (lzh_huffman_init(&(ds->pt), PT_BITLEN_SIZE, 16)
+	    != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	ds->error = 0;
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Release LZHUF decoder.
+ */
+static void
+lzh_decode_free(struct lzh_stream *strm)
+{
+
+	if (strm->ds == NULL)
+		return;
+	free(strm->ds->w_buff);
+	lzh_huffman_free(&(strm->ds->lt));
+	lzh_huffman_free(&(strm->ds->pt));
+	free(strm->ds);
+	strm->ds = NULL;
+}
+
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define lzh_br_has(br, n)	((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define lzh_br_bits(br, n)				\
+	(((uint16_t)((br)->cache_buffer >>		\
+		((br)->cache_avail - (n)))) & cache_masks[n])
+#define lzh_br_bits_forced(br, n)			\
+	(((uint16_t)((br)->cache_buffer <<		\
+		((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ *  True  : completed, there is enough data in the cache buffer.
+ *  False : we met that strm->next_in is empty, we have to get following
+ *          bytes. */
+#define lzh_br_read_ahead_0(strm, br, n)	\
+	(lzh_br_has(br, (n)) || lzh_br_fillup(strm, br))
+/*  True  : the cache buffer has some bits as much as we need.
+ *  False : there are no enough bits in the cache buffer to be used,
+ *          we have to get following bytes if we could. */
+#define lzh_br_read_ahead(strm, br, n)	\
+	(lzh_br_read_ahead_0((strm), (br), (n)) || lzh_br_has((br), (n)))
+
+/* Notify how many bits we consumed. */
+#define lzh_br_consume(br, n)	((br)->cache_avail -= (n))
+#define lzh_br_unconsume(br, n)	((br)->cache_avail += (n))
+
+static const uint16_t cache_masks[] = {
+	0x0000, 0x0001, 0x0003, 0x0007,
+	0x000F, 0x001F, 0x003F, 0x007F,
+	0x00FF, 0x01FF, 0x03FF, 0x07FF,
+	0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF,
+	0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+lzh_br_fillup(struct lzh_stream *strm, struct lzh_br *br)
+{
+	int n = CACHE_BITS - br->cache_avail;
+
+	for (;;) {
+		const int x = n >> 3;
+		if (strm->avail_in >= x) {
+			switch (x) {
+			case 8:
+				br->cache_buffer =
+				    ((uint64_t)strm->next_in[0]) << 56 |
+				    ((uint64_t)strm->next_in[1]) << 48 |
+				    ((uint64_t)strm->next_in[2]) << 40 |
+				    ((uint64_t)strm->next_in[3]) << 32 |
+				    ((uint32_t)strm->next_in[4]) << 24 |
+				    ((uint32_t)strm->next_in[5]) << 16 |
+				    ((uint32_t)strm->next_in[6]) << 8 |
+				     (uint32_t)strm->next_in[7];
+				strm->next_in += 8;
+				strm->avail_in -= 8;
+				br->cache_avail += 8 * 8;
+				return (1);
+			case 7:
+				br->cache_buffer =
+		 		   (br->cache_buffer << 56) |
+				    ((uint64_t)strm->next_in[0]) << 48 |
+				    ((uint64_t)strm->next_in[1]) << 40 |
+				    ((uint64_t)strm->next_in[2]) << 32 |
+				    ((uint32_t)strm->next_in[3]) << 24 |
+				    ((uint32_t)strm->next_in[4]) << 16 |
+				    ((uint32_t)strm->next_in[5]) << 8 |
+				     (uint32_t)strm->next_in[6];
+				strm->next_in += 7;
+				strm->avail_in -= 7;
+				br->cache_avail += 7 * 8;
+				return (1);
+			case 6:
+				br->cache_buffer =
+		 		   (br->cache_buffer << 48) |
+				    ((uint64_t)strm->next_in[0]) << 40 |
+				    ((uint64_t)strm->next_in[1]) << 32 |
+				    ((uint32_t)strm->next_in[2]) << 24 |
+				    ((uint32_t)strm->next_in[3]) << 16 |
+				    ((uint32_t)strm->next_in[4]) << 8 |
+				     (uint32_t)strm->next_in[5];
+				strm->next_in += 6;
+				strm->avail_in -= 6;
+				br->cache_avail += 6 * 8;
+				return (1);
+			case 0:
+				/* We have enough compressed data in
+				 * the cache buffer.*/
+				return (1);
+			default:
+				break;
+			}
+		}
+		if (strm->avail_in == 0) {
+			/* There is not enough compressed data to fill up the
+			 * cache buffer. */
+			return (0);
+		}
+		br->cache_buffer =
+		   (br->cache_buffer << 8) | *strm->next_in++;
+		strm->avail_in--;
+		br->cache_avail += 8;
+		n -= 8;
+	}
+}
+
+/*
+ * Decode LZHUF.
+ *
+ * 1. Returns ARCHIVE_OK if output buffer or input buffer are empty.
+ *    Please set available buffer and call this function again.
+ * 2. Returns ARCHIVE_EOF if decompression has been completed.
+ * 3. Returns ARCHIVE_FAILED if an error occurred; compressed data
+ *    is broken or you do not set 'last' flag properly.
+ * 4. 'last' flag is very important, you must set 1 to the flag if there
+ *    is no input data. The lha compressed data format does not provide how
+ *    to know the compressed data is really finished.
+ *    Note: lha command utility check if the total size of output bytes is
+ *    reached the uncompressed size recorded in its header. it does not mind
+ *    that the decoding process is properly finished.
+ *    GNU ZIP can decompress another compressed file made by SCO LZH compress.
+ *    it handles EOF as null to fill read buffer with zero until the decoding
+ *    process meet 2 bytes of zeros at reading a size of a next chunk, so the
+ *    zeros are treated as the mark of the end of the data although the zeros
+ *    is dummy, not the file data.
+ */
+static int	lzh_read_blocks(struct lzh_stream *, int);
+static int	lzh_decode_blocks(struct lzh_stream *, int);
+#define ST_RD_BLOCK		0
+#define ST_RD_PT_1		1
+#define ST_RD_PT_2		2
+#define ST_RD_PT_3		3
+#define ST_RD_PT_4		4
+#define ST_RD_LITERAL_1		5
+#define ST_RD_LITERAL_2		6
+#define ST_RD_LITERAL_3		7
+#define ST_RD_POS_DATA_1	8
+#define ST_GET_LITERAL		9
+#define ST_GET_POS_1		10
+#define ST_GET_POS_2		11
+#define ST_COPY_DATA		12
+
+static int
+lzh_decode(struct lzh_stream *strm, int last)
+{
+	struct lzh_dec *ds = strm->ds;
+	int avail_in;
+	int r;
+
+	if (ds->error)
+		return (ds->error);
+
+	avail_in = strm->avail_in;
+	do {
+		if (ds->state < ST_GET_LITERAL)
+			r = lzh_read_blocks(strm, last);
+		else
+			r = lzh_decode_blocks(strm, last);
+	} while (r == 100);
+	strm->total_in += avail_in - strm->avail_in;
+	return (r);
+}
+
+static void
+lzh_emit_window(struct lzh_stream *strm, size_t s)
+{
+	strm->ref_ptr = strm->ds->w_buff;
+	strm->avail_out = (int)s;
+	strm->total_out += s;
+}
+
+static int
+lzh_read_blocks(struct lzh_stream *strm, int last)
+{
+	struct lzh_dec *ds = strm->ds;
+	struct lzh_br *br = &(ds->br);
+	int c = 0, i;
+	unsigned rbits;
+
+	for (;;) {
+		switch (ds->state) {
+		case ST_RD_BLOCK:
+			/*
+			 * Read a block number indicates how many blocks
+			 * we will handle. The block is composed of a
+			 * literal and a match, sometimes a literal only
+			 * in particular, there are no reference data at
+			 * the beginning of the decompression.
+			 */
+			if (!lzh_br_read_ahead_0(strm, br, 16)) {
+				if (!last)
+					/* We need following data. */
+					return (ARCHIVE_OK);
+				if (lzh_br_has(br, 8)) {
+					/*
+					 * It seems there are extra bits.
+					 *  1. Compressed data is broken.
+					 *  2. `last' flag does not properly
+					 *     set.
+					 */
+					goto failed;
+				}
+				if (ds->w_pos > 0) {
+					lzh_emit_window(strm, ds->w_pos);
+					ds->w_pos = 0;
+					return (ARCHIVE_OK);
+				}
+				/* End of compressed data; we have completely
+				 * handled all compressed data. */
+				return (ARCHIVE_EOF);
+			}
+			ds->blocks_avail = lzh_br_bits(br, 16);
+			if (ds->blocks_avail == 0)
+				goto failed;
+			lzh_br_consume(br, 16);
+			/*
+			 * Read a literal table compressed in huffman
+			 * coding.
+			 */
+			ds->pt.len_size = ds->literal_pt_len_size;
+			ds->pt.len_bits = ds->literal_pt_len_bits;
+			ds->reading_position = 0;
+			/* FALL THROUGH */
+		case ST_RD_PT_1:
+			/* Note: ST_RD_PT_1, ST_RD_PT_2 and ST_RD_PT_4 are
+			 * used in reading both a literal table and a
+			 * position table. */
+			if (!lzh_br_read_ahead(strm, br, ds->pt.len_bits)) {
+				if (last)
+					goto failed;/* Truncated data. */
+				ds->state = ST_RD_PT_1;
+				return (ARCHIVE_OK);
+			}
+			ds->pt.len_avail = lzh_br_bits(br, ds->pt.len_bits);
+			lzh_br_consume(br, ds->pt.len_bits);
+			/* FALL THROUGH */
+		case ST_RD_PT_2:
+			if (ds->pt.len_avail == 0) {
+				/* There is no bitlen. */
+				if (!lzh_br_read_ahead(strm, br,
+				    ds->pt.len_bits)) {
+					if (last)
+						goto failed;/* Truncated data.*/
+					ds->state = ST_RD_PT_2;
+					return (ARCHIVE_OK);
+				}
+				if (!lzh_make_fake_table(&(ds->pt),
+				    lzh_br_bits(br, ds->pt.len_bits)))
+					goto failed;/* Invalid data. */
+				lzh_br_consume(br, ds->pt.len_bits);
+				if (ds->reading_position)
+					ds->state = ST_GET_LITERAL;
+				else
+					ds->state = ST_RD_LITERAL_1;
+				break;
+			} else if (ds->pt.len_avail > ds->pt.len_size)
+				goto failed;/* Invalid data. */
+			ds->loop = 0;
+			memset(ds->pt.freq, 0, sizeof(ds->pt.freq));
+			if (ds->pt.len_avail < 3 ||
+			    ds->pt.len_size == ds->pos_pt_len_size) {
+				ds->state = ST_RD_PT_4;
+				break;
+			}
+			/* FALL THROUGH */
+		case ST_RD_PT_3:
+			ds->loop = lzh_read_pt_bitlen(strm, ds->loop, 3);
+			if (ds->loop < 3) {
+				if (ds->loop < 0 || last)
+					goto failed;/* Invalid data. */
+				/* Not completed, get following data. */
+				ds->state = ST_RD_PT_3;
+				return (ARCHIVE_OK);
+			}
+			/* There are some null in bitlen of the literal. */
+			if (!lzh_br_read_ahead(strm, br, 2)) {
+				if (last)
+					goto failed;/* Truncated data. */
+				ds->state = ST_RD_PT_3;
+				return (ARCHIVE_OK);
+			}
+			c = lzh_br_bits(br, 2);
+			lzh_br_consume(br, 2);
+			if (c > ds->pt.len_avail - 3)
+				goto failed;/* Invalid data. */
+			for (i = 3; c-- > 0 ;)
+				ds->pt.bitlen[i++] = 0;
+			ds->loop = i;
+			/* FALL THROUGH */
+		case ST_RD_PT_4:
+			ds->loop = lzh_read_pt_bitlen(strm, ds->loop,
+			    ds->pt.len_avail);
+			if (ds->loop < ds->pt.len_avail) {
+				if (ds->loop < 0 || last)
+					goto failed;/* Invalid data. */
+				/* Not completed, get following data. */
+				ds->state = ST_RD_PT_4;
+				return (ARCHIVE_OK);
+			}
+			if (!lzh_make_huffman_table(&(ds->pt)))
+				goto failed;/* Invalid data */
+			if (ds->reading_position) {
+				ds->state = ST_GET_LITERAL;
+				break;
+			}
+			/* FALL THROUGH */
+		case ST_RD_LITERAL_1:
+			if (!lzh_br_read_ahead(strm, br, ds->lt.len_bits)) {
+				if (last)
+					goto failed;/* Truncated data. */
+				ds->state = ST_RD_LITERAL_1;
+				return (ARCHIVE_OK);
+			}
+			ds->lt.len_avail = lzh_br_bits(br, ds->lt.len_bits);
+			lzh_br_consume(br, ds->lt.len_bits);
+			/* FALL THROUGH */
+		case ST_RD_LITERAL_2:
+			if (ds->lt.len_avail == 0) {
+				/* There is no bitlen. */
+				if (!lzh_br_read_ahead(strm, br,
+				    ds->lt.len_bits)) {
+					if (last)
+						goto failed;/* Truncated data.*/
+					ds->state = ST_RD_LITERAL_2;
+					return (ARCHIVE_OK);
+				}
+				if (!lzh_make_fake_table(&(ds->lt),
+				    lzh_br_bits(br, ds->lt.len_bits)))
+					goto failed;/* Invalid data */
+				lzh_br_consume(br, ds->lt.len_bits);
+				ds->state = ST_RD_POS_DATA_1;
+				break;
+			} else if (ds->lt.len_avail > ds->lt.len_size)
+				goto failed;/* Invalid data */
+			ds->loop = 0;
+			memset(ds->lt.freq, 0, sizeof(ds->lt.freq));
+			/* FALL THROUGH */
+		case ST_RD_LITERAL_3:
+			i = ds->loop;
+			while (i < ds->lt.len_avail) {
+				if (!lzh_br_read_ahead(strm, br,
+				    ds->pt.max_bits)) {
+					if (last)
+						goto failed;/* Truncated data.*/
+					ds->loop = i;
+					ds->state = ST_RD_LITERAL_3;
+					return (ARCHIVE_OK);
+				}
+				rbits = lzh_br_bits(br, ds->pt.max_bits);
+				c = lzh_decode_huffman(&(ds->pt), rbits);
+				if (c > 2) {
+					/* Note: 'c' will never be more than
+					 * eighteen since it's limited by
+					 * PT_BITLEN_SIZE, which is being set
+					 * to ds->pt.len_size through
+					 * ds->literal_pt_len_size. */
+					lzh_br_consume(br, ds->pt.bitlen[c]);
+					c -= 2;
+					ds->lt.freq[c]++;
+					ds->lt.bitlen[i++] = c;
+				} else if (c == 0) {
+					lzh_br_consume(br, ds->pt.bitlen[c]);
+					ds->lt.bitlen[i++] = 0;
+				} else {
+					/* c == 1 or c == 2 */
+					int n = (c == 1)?4:9;
+					if (!lzh_br_read_ahead(strm, br,
+					     ds->pt.bitlen[c] + n)) {
+						if (last) /* Truncated data. */
+							goto failed;
+						ds->loop = i;
+						ds->state = ST_RD_LITERAL_3;
+						return (ARCHIVE_OK);
+					}
+					lzh_br_consume(br, ds->pt.bitlen[c]);
+					c = lzh_br_bits(br, n);
+					lzh_br_consume(br, n);
+					c += (n == 4)?3:20;
+					if (i + c > ds->lt.len_avail)
+						goto failed;/* Invalid data */
+					memset(&(ds->lt.bitlen[i]), 0, c);
+					i += c;
+				}
+			}
+			if (i > ds->lt.len_avail ||
+			    !lzh_make_huffman_table(&(ds->lt)))
+				goto failed;/* Invalid data */
+			/* FALL THROUGH */
+		case ST_RD_POS_DATA_1:
+			/*
+			 * Read a position table compressed in huffman
+			 * coding.
+			 */
+			ds->pt.len_size = ds->pos_pt_len_size;
+			ds->pt.len_bits = ds->pos_pt_len_bits;
+			ds->reading_position = 1;
+			ds->state = ST_RD_PT_1;
+			break;
+		case ST_GET_LITERAL:
+			return (100);
+		}
+	}
+failed:
+	return (ds->error = ARCHIVE_FAILED);
+}
+
+static int
+lzh_decode_blocks(struct lzh_stream *strm, int last)
+{
+	struct lzh_dec *ds = strm->ds;
+	struct lzh_br bre = ds->br;
+	struct huffman *lt = &(ds->lt);
+	struct huffman *pt = &(ds->pt);
+	unsigned char *w_buff = ds->w_buff;
+	unsigned char *lt_bitlen = lt->bitlen;
+	unsigned char *pt_bitlen = pt->bitlen;
+	int blocks_avail = ds->blocks_avail, c = 0;
+	int copy_len = ds->copy_len, copy_pos = ds->copy_pos;
+	int w_pos = ds->w_pos, w_mask = ds->w_mask, w_size = ds->w_size;
+	int lt_max_bits = lt->max_bits, pt_max_bits = pt->max_bits;
+	int state = ds->state;
+
+	for (;;) {
+		switch (state) {
+		case ST_GET_LITERAL:
+			for (;;) {
+				if (blocks_avail == 0) {
+					/* We have decoded all blocks.
+					 * Let's handle next blocks. */
+					ds->state = ST_RD_BLOCK;
+					ds->br = bre;
+					ds->blocks_avail = 0;
+					ds->w_pos = w_pos;
+					ds->copy_pos = 0;
+					return (100);
+				}
+
+				/* lzh_br_read_ahead() always try to fill the
+				 * cache buffer up. In specific situation we
+				 * are close to the end of the data, the cache
+				 * buffer will not be full and thus we have to
+				 * determine if the cache buffer has some bits
+				 * as much as we need after lzh_br_read_ahead()
+				 * failed. */
+				if (!lzh_br_read_ahead(strm, &bre,
+				    lt_max_bits)) {
+					if (!last)
+						goto next_data;
+					/* Remaining bits are less than
+					 * maximum bits(lt.max_bits) but maybe
+					 * it still remains as much as we need,
+					 * so we should try to use it with
+					 * dummy bits. */
+					c = lzh_decode_huffman(lt,
+					      lzh_br_bits_forced(&bre,
+					        lt_max_bits));
+					lzh_br_consume(&bre, lt_bitlen[c]);
+					if (!lzh_br_has(&bre, 0))
+						goto failed;/* Over read. */
+				} else {
+					c = lzh_decode_huffman(lt,
+					      lzh_br_bits(&bre, lt_max_bits));
+					lzh_br_consume(&bre, lt_bitlen[c]);
+				}
+				blocks_avail--;
+				if (c > UCHAR_MAX)
+					/* Current block is a match data. */
+					break;
+				/*
+				 * 'c' is exactly a literal code.
+				 */
+				/* Save a decoded code to reference it
+				 * afterward. */
+				w_buff[w_pos] = c;
+				if (++w_pos >= w_size) {
+					w_pos = 0;
+					lzh_emit_window(strm, w_size);
+					goto next_data;
+				}
+			}
+			/* 'c' is the length of a match pattern we have
+			 * already extracted, which has be stored in
+			 * window(ds->w_buff). */
+			copy_len = c - (UCHAR_MAX + 1) + MINMATCH;
+			/* FALL THROUGH */
+		case ST_GET_POS_1:
+			/*
+			 * Get a reference position. 
+			 */
+			if (!lzh_br_read_ahead(strm, &bre, pt_max_bits)) {
+				if (!last) {
+					state = ST_GET_POS_1;
+					ds->copy_len = copy_len;
+					goto next_data;
+				}
+				copy_pos = lzh_decode_huffman(pt,
+				    lzh_br_bits_forced(&bre, pt_max_bits));
+				lzh_br_consume(&bre, pt_bitlen[copy_pos]);
+				if (!lzh_br_has(&bre, 0))
+					goto failed;/* Over read. */
+			} else {
+				copy_pos = lzh_decode_huffman(pt,
+				    lzh_br_bits(&bre, pt_max_bits));
+				lzh_br_consume(&bre, pt_bitlen[copy_pos]);
+			}
+			/* FALL THROUGH */
+		case ST_GET_POS_2:
+			if (copy_pos > 1) {
+				/* We need an additional adjustment number to
+				 * the position. */
+				int p = copy_pos - 1;
+				if (!lzh_br_read_ahead(strm, &bre, p)) {
+					if (last)
+						goto failed;/* Truncated data.*/
+					state = ST_GET_POS_2;
+					ds->copy_len = copy_len;
+					ds->copy_pos = copy_pos;
+					goto next_data;
+				}
+				copy_pos = (1 << p) + lzh_br_bits(&bre, p);
+				lzh_br_consume(&bre, p);
+			}
+			/* The position is actually a distance from the last
+			 * code we had extracted and thus we have to convert
+			 * it to a position of the window. */
+			copy_pos = (w_pos - copy_pos - 1) & w_mask;
+			/* FALL THROUGH */
+		case ST_COPY_DATA:
+			/*
+			 * Copy `copy_len' bytes as extracted data from
+			 * the window into the output buffer.
+			 */
+			for (;;) {
+				int l;
+
+				l = copy_len;
+				if (copy_pos > w_pos) {
+					if (l > w_size - copy_pos)
+						l = w_size - copy_pos;
+				} else {
+					if (l > w_size - w_pos)
+						l = w_size - w_pos;
+				}
+				if ((copy_pos + l < w_pos)
+				    || (w_pos + l < copy_pos)) {
+					/* No overlap. */
+					memcpy(w_buff + w_pos,
+					    w_buff + copy_pos, l);
+				} else {
+					const unsigned char *s;
+					unsigned char *d;
+					int li;
+
+					d = w_buff + w_pos;
+					s = w_buff + copy_pos;
+					for (li = 0; li < l-1;) {
+						d[li] = s[li];li++;
+						d[li] = s[li];li++;
+					}
+					if (li < l)
+						d[li] = s[li];
+				}
+				w_pos += l;
+				if (w_pos == w_size) {
+					w_pos = 0;
+					lzh_emit_window(strm, w_size);
+					if (copy_len <= l)
+						state = ST_GET_LITERAL;
+					else {
+						state = ST_COPY_DATA;
+						ds->copy_len = copy_len - l;
+						ds->copy_pos =
+						    (copy_pos + l) & w_mask;
+					}
+					goto next_data;
+				}
+				if (copy_len <= l)
+					/* A copy of current pattern ended. */
+					break;
+				copy_len -= l;
+				copy_pos = (copy_pos + l) & w_mask;
+			}
+			state = ST_GET_LITERAL;
+			break;
+		}
+	}
+failed:
+	return (ds->error = ARCHIVE_FAILED);
+next_data:
+	ds->br = bre;
+	ds->blocks_avail = blocks_avail;
+	ds->state = state;
+	ds->w_pos = w_pos;
+	return (ARCHIVE_OK);
+}
+
+static int
+lzh_huffman_init(struct huffman *hf, size_t len_size, int tbl_bits)
+{
+	int bits;
+
+	if (hf->bitlen == NULL) {
+		hf->bitlen = malloc(len_size * sizeof(hf->bitlen[0]));
+		if (hf->bitlen == NULL)
+			return (ARCHIVE_FATAL);
+	}
+	if (hf->tbl == NULL) {
+		if (tbl_bits < HTBL_BITS)
+			bits = tbl_bits;
+		else
+			bits = HTBL_BITS;
+		hf->tbl = malloc(((size_t)1 << bits) * sizeof(hf->tbl[0]));
+		if (hf->tbl == NULL)
+			return (ARCHIVE_FATAL);
+	}
+	if (hf->tree == NULL && tbl_bits > HTBL_BITS) {
+		hf->tree_avail = 1 << (tbl_bits - HTBL_BITS + 4);
+		hf->tree = malloc(hf->tree_avail * sizeof(hf->tree[0]));
+		if (hf->tree == NULL)
+			return (ARCHIVE_FATAL);
+	}
+	hf->len_size = (int)len_size;
+	hf->tbl_bits = tbl_bits;
+	return (ARCHIVE_OK);
+}
+
+static void
+lzh_huffman_free(struct huffman *hf)
+{
+	free(hf->bitlen);
+	free(hf->tbl);
+	free(hf->tree);
+}
+
+static const char bitlen_tbl[0x400] = {
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,  7,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
+	 9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,  9,
+	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+	10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+	11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+	11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+	12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+	13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 16,  0
+};
+static int
+lzh_read_pt_bitlen(struct lzh_stream *strm, int start, int end)
+{
+	struct lzh_dec *ds = strm->ds;
+	struct lzh_br *br = &(ds->br);
+	int c, i;
+
+	for (i = start; i < end; ) {
+		/*
+		 *  bit pattern     the number we need
+		 *     000           ->  0
+		 *     001           ->  1
+		 *     010           ->  2
+		 *     ...
+		 *     110           ->  6
+		 *     1110          ->  7
+		 *     11110         ->  8
+		 *     ...
+		 *     1111111111110 ->  16
+		 */
+		if (!lzh_br_read_ahead(strm, br, 3))
+			return (i);
+		if ((c = lzh_br_bits(br, 3)) == 7) {
+			if (!lzh_br_read_ahead(strm, br, 13))
+				return (i);
+			c = bitlen_tbl[lzh_br_bits(br, 13) & 0x3FF];
+			if (c)
+				lzh_br_consume(br, c - 3);
+			else
+				return (-1);/* Invalid data. */
+		} else
+			lzh_br_consume(br, 3);
+		ds->pt.bitlen[i++] = c;
+		ds->pt.freq[c]++;
+	}
+	return (i);
+}
+
+static int
+lzh_make_fake_table(struct huffman *hf, uint16_t c)
+{
+	if (c >= hf->len_size)
+		return (0);
+	hf->tbl[0] = c;
+	hf->max_bits = 0;
+	hf->shift_bits = 0;
+	hf->bitlen[hf->tbl[0]] = 0;
+	return (1);
+}
+
+/*
+ * Make a huffman coding table.
+ */
+static int
+lzh_make_huffman_table(struct huffman *hf)
+{
+	uint16_t *tbl;
+	const unsigned char *bitlen;
+	int bitptn[17], weight[17];
+	int i, maxbits = 0, ptn, tbl_size, w;
+	int diffbits, len_avail;
+
+	/*
+	 * Initialize bit patterns.
+	 */
+	ptn = 0;
+	for (i = 1, w = 1 << 15; i <= 16; i++, w >>= 1) {
+		bitptn[i] = ptn;
+		weight[i] = w;
+		if (hf->freq[i]) {
+			ptn += hf->freq[i] * w;
+			maxbits = i;
+		}
+	}
+	if (ptn != 0x10000 || maxbits > hf->tbl_bits)
+		return (0);/* Invalid */
+
+	hf->max_bits = maxbits;
+
+	/*
+	 * Cut out extra bits which we won't house in the table.
+	 * This preparation reduces the same calculation in the for-loop
+	 * making the table.
+	 */
+	if (maxbits < 16) {
+		int ebits = 16 - maxbits;
+		for (i = 1; i <= maxbits; i++) {
+			bitptn[i] >>= ebits;
+			weight[i] >>= ebits;
+		}
+	}
+	if (maxbits > HTBL_BITS) {
+		unsigned htbl_max;
+		uint16_t *p;
+
+		diffbits = maxbits - HTBL_BITS;
+		for (i = 1; i <= HTBL_BITS; i++) {
+			bitptn[i] >>= diffbits;
+			weight[i] >>= diffbits;
+		}
+		htbl_max = bitptn[HTBL_BITS] +
+		    weight[HTBL_BITS] * hf->freq[HTBL_BITS];
+		p = &(hf->tbl[htbl_max]);
+		while (p < &hf->tbl[1U<<HTBL_BITS])
+			*p++ = 0;
+	} else
+		diffbits = 0;
+	hf->shift_bits = diffbits;
+
+	/*
+	 * Make the table.
+	 */
+	tbl_size = 1 << HTBL_BITS;
+	tbl = hf->tbl;
+	bitlen = hf->bitlen;
+	len_avail = hf->len_avail;
+	hf->tree_used = 0;
+	for (i = 0; i < len_avail; i++) {
+		uint16_t *p;
+		int len, cnt;
+		uint16_t bit;
+		int extlen;
+		struct htree_t *ht;
+
+		if (bitlen[i] == 0)
+			continue;
+		/* Get a bit pattern */
+		len = bitlen[i];
+		ptn = bitptn[len];
+		cnt = weight[len];
+		if (len <= HTBL_BITS) {
+			/* Calculate next bit pattern */
+			if ((bitptn[len] = ptn + cnt) > tbl_size)
+				return (0);/* Invalid */
+			/* Update the table */
+			p = &(tbl[ptn]);
+			if (cnt > 7) {
+				uint16_t *pc;
+
+				cnt -= 8;
+				pc = &p[cnt];
+				pc[0] = (uint16_t)i;
+				pc[1] = (uint16_t)i;
+				pc[2] = (uint16_t)i;
+				pc[3] = (uint16_t)i;
+				pc[4] = (uint16_t)i;
+				pc[5] = (uint16_t)i;
+				pc[6] = (uint16_t)i;
+				pc[7] = (uint16_t)i;
+				if (cnt > 7) {
+					cnt -= 8;
+					memcpy(&p[cnt], pc,
+						8 * sizeof(uint16_t));
+					pc = &p[cnt];
+					while (cnt > 15) {
+						cnt -= 16;
+						memcpy(&p[cnt], pc,
+							16 * sizeof(uint16_t));
+					}
+				}
+				if (cnt)
+					memcpy(p, pc, cnt * sizeof(uint16_t));
+			} else {
+				while (cnt > 1) {
+					p[--cnt] = (uint16_t)i;
+					p[--cnt] = (uint16_t)i;
+				}
+				if (cnt)
+					p[--cnt] = (uint16_t)i;
+			}
+			continue;
+		}
+
+		/*
+		 * A bit length is too big to be housed to a direct table,
+		 * so we use a tree model for its extra bits.
+		 */
+		bitptn[len] = ptn + cnt;
+		bit = 1U << (diffbits -1);
+		extlen = len - HTBL_BITS;
+		
+		p = &(tbl[ptn >> diffbits]);
+		if (*p == 0) {
+			*p = len_avail + hf->tree_used;
+			ht = &(hf->tree[hf->tree_used++]);
+			if (hf->tree_used > hf->tree_avail)
+				return (0);/* Invalid */
+			ht->left = 0;
+			ht->right = 0;
+		} else {
+			if (*p < len_avail ||
+			    *p >= (len_avail + hf->tree_used))
+				return (0);/* Invalid */
+			ht = &(hf->tree[*p - len_avail]);
+		}
+		while (--extlen > 0) {
+			if (ptn & bit) {
+				if (ht->left < len_avail) {
+					ht->left = len_avail + hf->tree_used;
+					ht = &(hf->tree[hf->tree_used++]);
+					if (hf->tree_used > hf->tree_avail)
+						return (0);/* Invalid */
+					ht->left = 0;
+					ht->right = 0;
+				} else {
+					ht = &(hf->tree[ht->left - len_avail]);
+				}
+			} else {
+				if (ht->right < len_avail) {
+					ht->right = len_avail + hf->tree_used;
+					ht = &(hf->tree[hf->tree_used++]);
+					if (hf->tree_used > hf->tree_avail)
+						return (0);/* Invalid */
+					ht->left = 0;
+					ht->right = 0;
+				} else {
+					ht = &(hf->tree[ht->right - len_avail]);
+				}
+			}
+			bit >>= 1;
+		}
+		if (ptn & bit) {
+			if (ht->left != 0)
+				return (0);/* Invalid */
+			ht->left = (uint16_t)i;
+		} else {
+			if (ht->right != 0)
+				return (0);/* Invalid */
+			ht->right = (uint16_t)i;
+		}
+	}
+	return (1);
+}
+
+static int
+lzh_decode_huffman_tree(struct huffman *hf, unsigned rbits, int c)
+{
+	struct htree_t *ht;
+	int extlen;
+
+	ht = hf->tree;
+	extlen = hf->shift_bits;
+	while (c >= hf->len_avail) {
+		c -= hf->len_avail;
+		if (extlen-- <= 0 || c >= hf->tree_used)
+			return (0);
+		if (rbits & (1U << extlen))
+			c = ht[c].left;
+		else
+			c = ht[c].right;
+	}
+	return (c);
+}
+
+static inline int
+lzh_decode_huffman(struct huffman *hf, unsigned rbits)
+{
+	int c;
+	/*
+	 * At first search an index table for a bit pattern.
+	 * If it fails, search a huffman tree for.
+	 */
+	c = hf->tbl[rbits >> hf->shift_bits];
+	if (c < hf->len_avail || hf->len_avail == 0)
+		return (c);
+	/* This bit pattern needs to be found out at a huffman tree. */
+	return (lzh_decode_huffman_tree(hf, rbits, c));
+}
+
diff --git a/c/archive_read_support_format_mtree.c b/c/archive_read_support_format_mtree.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_mtree.c
@@ -0,0 +1,2025 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_read_support_format_mtree.c 201165 2009-12-29 05:52:13Z kientzle $");
+
+#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
+#include <stddef.h>
+/* #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_CTYPE_H
+#include <ctype.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_read_private.h"
+#include "archive_string.h"
+#include "archive_pack_dev.h"
+
+#ifndef O_BINARY
+#define	O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC	0
+#endif
+
+#define	MTREE_HAS_DEVICE	0x0001
+#define	MTREE_HAS_FFLAGS	0x0002
+#define	MTREE_HAS_GID		0x0004
+#define	MTREE_HAS_GNAME		0x0008
+#define	MTREE_HAS_MTIME		0x0010
+#define	MTREE_HAS_NLINK		0x0020
+#define	MTREE_HAS_PERM		0x0040
+#define	MTREE_HAS_SIZE		0x0080
+#define	MTREE_HAS_TYPE		0x0100
+#define	MTREE_HAS_UID		0x0200
+#define	MTREE_HAS_UNAME		0x0400
+
+#define	MTREE_HAS_OPTIONAL	0x0800
+#define	MTREE_HAS_NOCHANGE	0x1000 /* FreeBSD specific */
+
+#define	MAX_LINE_LEN		(1024 * 1024)
+
+struct mtree_option {
+	struct mtree_option *next;
+	char *value;
+};
+
+struct mtree_entry {
+	struct archive_rb_node rbnode;
+	struct mtree_entry *next_dup;
+	struct mtree_entry *next;
+	struct mtree_option *options;
+	char *name;
+	char full;
+	char used;
+};
+
+struct mtree {
+	struct archive_string	 line;
+	size_t			 buffsize;
+	char			*buff;
+	int64_t			 offset;
+	int			 fd;
+	int			 archive_format;
+	const char		*archive_format_name;
+	struct mtree_entry	*entries;
+	struct mtree_entry	*this_entry;
+	struct archive_rb_tree	 entry_rbtree;
+	struct archive_string	 current_dir;
+	struct archive_string	 contents_name;
+
+	struct archive_entry_linkresolver *resolver;
+	struct archive_rb_tree rbtree;
+
+	int64_t			 cur_size;
+	char checkfs;
+};
+
+static int	bid_keycmp(const char *, const char *, ssize_t);
+static int	cleanup(struct archive_read *);
+static int	detect_form(struct archive_read *, int *);
+static int	mtree_bid(struct archive_read *, int);
+static int	parse_file(struct archive_read *, struct archive_entry *,
+		    struct mtree *, struct mtree_entry *, int *);
+static void	parse_escapes(char *, struct mtree_entry *);
+static int	parse_line(struct archive_read *, struct archive_entry *,
+		    struct mtree *, struct mtree_entry *, int *);
+static int	parse_keyword(struct archive_read *, struct mtree *,
+		    struct archive_entry *, struct mtree_option *, int *);
+static int	read_data(struct archive_read *a,
+		    const void **buff, size_t *size, int64_t *offset);
+static ssize_t	readline(struct archive_read *, struct mtree *, char **, ssize_t);
+static int	skip(struct archive_read *a);
+static int	read_header(struct archive_read *,
+		    struct archive_entry *);
+static int64_t	mtree_atol(char **, int base);
+
+/*
+ * There's no standard for TIME_T_MAX/TIME_T_MIN.  So we compute them
+ * here.  TODO: Move this to configure time, but be careful
+ * about cross-compile environments.
+ */
+static int64_t
+get_time_t_max(void)
+{
+#if defined(TIME_T_MAX)
+	return TIME_T_MAX;
+#else
+	/* ISO C allows time_t to be a floating-point type,
+	   but POSIX requires an integer type.  The following
+	   should work on any system that follows the POSIX
+	   conventions. */
+	if (((time_t)0) < ((time_t)-1)) {
+		/* Time_t is unsigned */
+		return (~(time_t)0);
+	} else {
+		/* Time_t is signed. */
+		/* Assume it's the same as int64_t or int32_t */
+		if (sizeof(time_t) == sizeof(int64_t)) {
+			return (time_t)INT64_MAX;
+		} else {
+			return (time_t)INT32_MAX;
+		}
+	}
+#endif
+}
+
+static int64_t
+get_time_t_min(void)
+{
+#if defined(TIME_T_MIN)
+	return TIME_T_MIN;
+#else
+	if (((time_t)0) < ((time_t)-1)) {
+		/* Time_t is unsigned */
+		return (time_t)0;
+	} else {
+		/* Time_t is signed. */
+		if (sizeof(time_t) == sizeof(int64_t)) {
+			return (time_t)INT64_MIN;
+		} else {
+			return (time_t)INT32_MIN;
+		}
+	}
+#endif
+}
+
+static int
+archive_read_format_mtree_options(struct archive_read *a,
+    const char *key, const char *val)
+{
+	struct mtree *mtree;
+
+	mtree = (struct mtree *)(a->format->data);
+	if (strcmp(key, "checkfs")  == 0) {
+		/* Allows to read information missing from the mtree from the file system */
+		if (val == NULL || val[0] == 0) {
+			mtree->checkfs = 0;
+		} else {
+			mtree->checkfs = 1;
+		}
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static void
+free_options(struct mtree_option *head)
+{
+	struct mtree_option *next;
+
+	for (; head != NULL; head = next) {
+		next = head->next;
+		free(head->value);
+		free(head);
+	}
+}
+
+static int
+mtree_cmp_node(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	const struct mtree_entry *e1 = (const struct mtree_entry *)n1;
+	const struct mtree_entry *e2 = (const struct mtree_entry *)n2;
+
+	return (strcmp(e1->name, e2->name));
+}
+
+static int
+mtree_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+	const struct mtree_entry *e = (const struct mtree_entry *)n;
+
+	return (strcmp(e->name, key));
+}
+
+int
+archive_read_support_format_mtree(struct archive *_a)
+{
+	static const struct archive_rb_tree_ops rb_ops = {
+		mtree_cmp_node, mtree_cmp_key,
+	};
+	struct archive_read *a = (struct archive_read *)_a;
+	struct mtree *mtree;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_mtree");
+
+	mtree = (struct mtree *)calloc(1, sizeof(*mtree));
+	if (mtree == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate mtree data");
+		return (ARCHIVE_FATAL);
+	}
+	mtree->checkfs = 0;
+	mtree->fd = -1;
+
+	__archive_rb_tree_init(&mtree->rbtree, &rb_ops);
+
+	r = __archive_read_register_format(a, mtree, "mtree",
+           mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL);
+
+	if (r != ARCHIVE_OK)
+		free(mtree);
+	return (ARCHIVE_OK);
+}
+
+static int
+cleanup(struct archive_read *a)
+{
+	struct mtree *mtree;
+	struct mtree_entry *p, *q;
+
+	mtree = (struct mtree *)(a->format->data);
+
+	p = mtree->entries;
+	while (p != NULL) {
+		q = p->next;
+		free(p->name);
+		free_options(p->options);
+		free(p);
+		p = q;
+	}
+	archive_string_free(&mtree->line);
+	archive_string_free(&mtree->current_dir);
+	archive_string_free(&mtree->contents_name);
+	archive_entry_linkresolver_free(mtree->resolver);
+
+	free(mtree->buff);
+	free(mtree);
+	(a->format->data) = NULL;
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+get_line_size(const char *b, ssize_t avail, ssize_t *nlsize)
+{
+	ssize_t len;
+
+	len = 0;
+	while (len < avail) {
+		switch (*b) {
+		case '\0':/* Non-ascii character or control character. */
+			if (nlsize != NULL)
+				*nlsize = 0;
+			return (-1);
+		case '\r':
+			if (avail-len > 1 && b[1] == '\n') {
+				if (nlsize != NULL)
+					*nlsize = 2;
+				return (len+2);
+			}
+			/* FALL THROUGH */
+		case '\n':
+			if (nlsize != NULL)
+				*nlsize = 1;
+			return (len+1);
+		default:
+			b++;
+			len++;
+			break;
+		}
+	}
+	if (nlsize != NULL)
+		*nlsize = 0;
+	return (avail);
+}
+
+/*
+ *  <---------------- ravail --------------------->
+ *  <-- diff ------> <---  avail ----------------->
+ *                   <---- len ----------->
+ * | Previous lines | line being parsed  nl extra |
+ *                  ^
+ *                  b
+ *
+ */
+static ssize_t
+next_line(struct archive_read *a,
+    const char **b, ssize_t *avail, ssize_t *ravail, ssize_t *nl)
+{
+	ssize_t len;
+	int quit;
+	
+	quit = 0;
+	if (*avail == 0) {
+		*nl = 0;
+		len = 0;
+	} else
+		len = get_line_size(*b, *avail, nl);
+	/*
+	 * Read bytes more while it does not reach the end of line.
+	 */
+	while (*nl == 0 && len == *avail && !quit) {
+		ssize_t diff = *ravail - *avail;
+		size_t nbytes_req = (*ravail+1023) & ~1023U;
+		ssize_t tested;
+
+		/*
+		 * Place an arbitrary limit on the line length.
+		 * mtree is almost free-form input and without line length limits,
+		 * it can consume a lot of memory.
+		 */
+		if (len >= MAX_LINE_LEN)
+			return (-1);
+
+		/* Increase reading bytes if it is not enough to at least
+		 * new two lines. */
+		if (nbytes_req < (size_t)*ravail + 160)
+			nbytes_req <<= 1;
+
+		*b = __archive_read_ahead(a, nbytes_req, avail);
+		if (*b == NULL) {
+			if (*ravail >= *avail)
+				return (0);
+			/* Reading bytes reaches the end of file. */
+			*b = __archive_read_ahead(a, *avail, avail);
+			quit = 1;
+		}
+		*ravail = *avail;
+		*b += diff;
+		*avail -= diff;
+		tested = len;/* Skip some bytes we already determinated. */
+		len = get_line_size(*b + len, *avail - len, nl);
+		if (len >= 0)
+			len += tested;
+	}
+	return (len);
+}
+
+/*
+ * Compare characters with a mtree keyword.
+ * Returns the length of a mtree keyword if matched.
+ * Returns 0 if not matched.
+ */
+static int
+bid_keycmp(const char *p, const char *key, ssize_t len)
+{
+	int match_len = 0;
+
+	while (len > 0 && *p && *key) {
+		if (*p == *key) {
+			--len;
+			++p;
+			++key;
+			++match_len;
+			continue;
+		}
+		return (0);/* Not match */
+	}
+	if (*key != '\0')
+		return (0);/* Not match */
+
+	/* A following character should be specified characters */
+	if (p[0] == '=' || p[0] == ' ' || p[0] == '\t' ||
+	    p[0] == '\n' || p[0] == '\r' ||
+	   (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r')))
+		return (match_len);
+	return (0);/* Not match */
+}
+
+/*
+ * Test whether the characters 'p' has is mtree keyword.
+ * Returns the length of a detected keyword.
+ * Returns 0 if any keywords were not found.
+ */
+static int
+bid_keyword(const char *p,  ssize_t len)
+{
+	static const char * const keys_c[] = {
+		"content", "contents", "cksum", NULL
+	};
+	static const char * const keys_df[] = {
+		"device", "flags", NULL
+	};
+	static const char * const keys_g[] = {
+		"gid", "gname", NULL
+	};
+	static const char * const keys_il[] = {
+		"ignore", "inode", "link", NULL
+	};
+	static const char * const keys_m[] = {
+		"md5", "md5digest", "mode", NULL
+	};
+	static const char * const keys_no[] = {
+		"nlink", "nochange", "optional", NULL
+	};
+	static const char * const keys_r[] = {
+		"resdevice", "rmd160", "rmd160digest", NULL
+	};
+	static const char * const keys_s[] = {
+		"sha1", "sha1digest",
+		"sha256", "sha256digest",
+		"sha384", "sha384digest",
+		"sha512", "sha512digest",
+		"size", NULL
+	};
+	static const char * const keys_t[] = {
+		"tags", "time", "type", NULL
+	};
+	static const char * const keys_u[] = {
+		"uid", "uname",	NULL
+	};
+	const char * const *keys;
+	int i;
+
+	switch (*p) {
+	case 'c': keys = keys_c; break;
+	case 'd': case 'f': keys = keys_df; break;
+	case 'g': keys = keys_g; break;
+	case 'i': case 'l': keys = keys_il; break;
+	case 'm': keys = keys_m; break;
+	case 'n': case 'o': keys = keys_no; break;
+	case 'r': keys = keys_r; break;
+	case 's': keys = keys_s; break;
+	case 't': keys = keys_t; break;
+	case 'u': keys = keys_u; break;
+	default: return (0);/* Unknown key */
+	}
+
+	for (i = 0; keys[i] != NULL; i++) {
+		int l = bid_keycmp(p, keys[i], len);
+		if (l > 0)
+			return (l);
+	}
+	return (0);/* Unknown key */
+}
+
+/*
+ * Test whether there is a set of mtree keywords.
+ * Returns the number of keyword.
+ * Returns -1 if we got incorrect sequence.
+ * This function expects a set of "<space characters>keyword=value".
+ * When "unset" is specified, expects a set of "<space characters>keyword".
+ */
+static int
+bid_keyword_list(const char *p,  ssize_t len, int unset, int last_is_path)
+{
+	int l;
+	int keycnt = 0;
+
+	while (len > 0 && *p) {
+		int blank = 0;
+
+		/* Test whether there are blank characters in the line. */
+		while (len >0 && (*p == ' ' || *p == '\t')) {
+			++p;
+			--len;
+			blank = 1;
+		}
+		if (*p == '\n' || *p == '\r')
+			break;
+		if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))
+			break;
+		if (!blank && !last_is_path) /* No blank character. */
+			return (-1);
+		if (last_is_path && len == 0)
+				return (keycnt);
+
+		if (unset) {
+			l = bid_keycmp(p, "all", len);
+			if (l > 0)
+				return (1);
+		}
+		/* Test whether there is a correct key in the line. */
+		l = bid_keyword(p, len);
+		if (l == 0)
+			return (-1);/* Unknown keyword was found. */
+		p += l;
+		len -= l;
+		keycnt++;
+
+		/* Skip value */
+		if (*p == '=') {
+			int value = 0;
+			++p;
+			--len;
+			while (len > 0 && *p != ' ' && *p != '\t') {
+				++p;
+				--len;
+				value = 1;
+			}
+			/* A keyword should have a its value unless
+			 * "/unset" operation. */ 
+			if (!unset && value == 0)
+				return (-1);
+		}
+	}
+	return (keycnt);
+}
+
+static int
+bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path)
+{
+	int f = 0;
+	static const unsigned char safe_char[256] = {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+		/* !"$%&'()*+,-./  EXCLUSION:( )(#) */
+		0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+		/* 0123456789:;<>?  EXCLUSION:(=) */
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */
+		/* @ABCDEFGHIJKLMNO */
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+		/* PQRSTUVWXYZ[\]^_  */
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */
+		/* `abcdefghijklmno */
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+		/* pqrstuvwxyz{|}~ */
+		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+	};
+	ssize_t ll;
+	const char *pp = p;
+	const char * const pp_end = pp + len;
+
+	*last_is_path = 0;
+	/*
+	 * Skip the path-name which is quoted.
+	 */
+	for (;pp < pp_end; ++pp) {
+		if (!safe_char[*(const unsigned char *)pp]) {
+			if (*pp != ' ' && *pp != '\t' && *pp != '\r'
+			    && *pp != '\n')
+				f = 0;
+			break;
+		}
+		f = 1;
+	}
+	ll = pp_end - pp;
+
+	/* If a path-name was not found at the first, try to check
+	 * a mtree format(a.k.a form D) ``NetBSD's mtree -D'' creates,
+	 * which places the path-name at the last. */
+	if (f == 0) {
+		const char *pb = p + len - nl;
+		int name_len = 0;
+		int slash;
+
+		/* The form D accepts only a single line for an entry. */
+		if (pb-2 >= p &&
+		    pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t'))
+			return (-1);
+		if (pb-1 >= p && pb[-1] == '\\')
+			return (-1);
+
+		slash = 0;
+		while (p <= --pb && *pb != ' ' && *pb != '\t') {
+			if (!safe_char[*(const unsigned char *)pb])
+				return (-1);
+			name_len++;
+			/* The pathname should have a slash in this
+			 * format. */
+			if (*pb == '/')
+				slash = 1;
+		}
+		if (name_len == 0 || slash == 0)
+			return (-1);
+		/* If '/' is placed at the first in this field, this is not
+		 * a valid filename. */
+		if (pb[1] == '/')
+			return (-1);
+		ll = len - nl - name_len;
+		pp = p;
+		*last_is_path = 1;
+	}
+
+	return (bid_keyword_list(pp, ll, 0, *last_is_path));
+}
+
+#define MAX_BID_ENTRY	3
+
+static int
+mtree_bid(struct archive_read *a, int best_bid)
+{
+	const char *signature = "#mtree";
+	const char *p;
+
+	(void)best_bid; /* UNUSED */
+
+	/* Now let's look at the actual header and see if it matches. */
+	p = __archive_read_ahead(a, strlen(signature), NULL);
+	if (p == NULL)
+		return (-1);
+
+	if (memcmp(p, signature, strlen(signature)) == 0)
+		return (8 * (int)strlen(signature));
+
+	/*
+	 * There is not a mtree signature. Let's try to detect mtree format.
+	 */
+	return (detect_form(a, NULL));
+}
+
+static int
+detect_form(struct archive_read *a, int *is_form_d)
+{
+	const char *p;
+	ssize_t avail, ravail;
+	ssize_t detected_bytes = 0, len, nl;
+	int entry_cnt = 0, multiline = 0;
+	int form_D = 0;/* The archive is generated by `NetBSD mtree -D'
+			* (In this source we call it `form D') . */
+
+	if (is_form_d != NULL)
+		*is_form_d = 0;
+	p = __archive_read_ahead(a, 1, &avail);
+	if (p == NULL)
+		return (-1);
+	ravail = avail;
+	for (;;) {
+		len = next_line(a, &p, &avail, &ravail, &nl);
+		/* The terminal character of the line should be
+		 * a new line character, '\r\n' or '\n'. */
+		if (len <= 0 || nl == 0)
+			break;
+		if (!multiline) {
+			/* Leading whitespace is never significant,
+			 * ignore it. */
+			while (len > 0 && (*p == ' ' || *p == '\t')) {
+				++p;
+				--avail;
+				--len;
+			}
+			/* Skip comment or empty line. */ 
+			if (p[0] == '#' || p[0] == '\n' || p[0] == '\r') {
+				p += len;
+				avail -= len;
+				continue;
+			}
+		} else {
+			/* A continuance line; the terminal
+			 * character of previous line was '\' character. */
+			if (bid_keyword_list(p, len, 0, 0) <= 0)
+				break;
+			if (multiline == 1)
+				detected_bytes += len;
+			if (p[len-nl-1] != '\\') {
+				if (multiline == 1 &&
+				    ++entry_cnt >= MAX_BID_ENTRY)
+					break;
+				multiline = 0;
+			}
+			p += len;
+			avail -= len;
+			continue;
+		}
+		if (p[0] != '/') {
+			int last_is_path, keywords;
+
+			keywords = bid_entry(p, len, nl, &last_is_path);
+			if (keywords >= 0) {
+				detected_bytes += len;
+				if (form_D == 0) {
+					if (last_is_path)
+						form_D = 1;
+					else if (keywords > 0)
+						/* This line is not `form D'. */
+						form_D = -1;
+				} else if (form_D == 1) {
+					if (!last_is_path && keywords > 0)
+						/* This this is not `form D'
+						 * and We cannot accept mixed
+						 * format. */
+						break;
+				}
+				if (!last_is_path && p[len-nl-1] == '\\')
+					/* This line continues. */
+					multiline = 1;
+				else {
+					/* We've got plenty of correct lines
+					 * to assume that this file is a mtree
+					 * format. */
+					if (++entry_cnt >= MAX_BID_ENTRY)
+						break;
+				}
+			} else
+				break;
+		} else if (len > 4 && strncmp(p, "/set", 4) == 0) {
+			if (bid_keyword_list(p+4, len-4, 0, 0) <= 0)
+				break;
+			/* This line continues. */
+			if (p[len-nl-1] == '\\')
+				multiline = 2;
+		} else if (len > 6 && strncmp(p, "/unset", 6) == 0) {
+			if (bid_keyword_list(p+6, len-6, 1, 0) <= 0)
+				break;
+			/* This line continues. */
+			if (p[len-nl-1] == '\\')
+				multiline = 2;
+		} else
+			break;
+
+		/* Test next line. */
+		p += len;
+		avail -= len;
+	}
+	if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) {
+		if (is_form_d != NULL) {
+			if (form_D == 1)
+				*is_form_d = 1;
+		}
+		return (32);
+	}
+
+	return (0);
+}
+
+/*
+ * The extended mtree format permits multiple lines specifying
+ * attributes for each file.  For those entries, only the last line
+ * is actually used.  Practically speaking, that means we have
+ * to read the entire mtree file into memory up front.
+ *
+ * The parsing is done in two steps.  First, it is decided if a line
+ * changes the global defaults and if it is, processed accordingly.
+ * Otherwise, the options of the line are merged with the current
+ * global options.
+ */
+static int
+add_option(struct archive_read *a, struct mtree_option **global,
+    const char *value, size_t len)
+{
+	struct mtree_option *opt;
+
+	if ((opt = malloc(sizeof(*opt))) == NULL) {
+		archive_set_error(&a->archive, errno, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	if ((opt->value = malloc(len + 1)) == NULL) {
+		free(opt);
+		archive_set_error(&a->archive, errno, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	memcpy(opt->value, value, len);
+	opt->value[len] = '\0';
+	opt->next = *global;
+	*global = opt;
+	return (ARCHIVE_OK);
+}
+
+static void
+remove_option(struct mtree_option **global, const char *value, size_t len)
+{
+	struct mtree_option *iter, *last;
+
+	last = NULL;
+	for (iter = *global; iter != NULL; last = iter, iter = iter->next) {
+		if (strncmp(iter->value, value, len) == 0 &&
+		    (iter->value[len] == '\0' ||
+		     iter->value[len] == '='))
+			break;
+	}
+	if (iter == NULL)
+		return;
+	if (last == NULL)
+		*global = iter->next;
+	else
+		last->next = iter->next;
+
+	free(iter->value);
+	free(iter);
+}
+
+static int
+process_global_set(struct archive_read *a,
+    struct mtree_option **global, const char *line)
+{
+	const char *next, *eq;
+	size_t len;
+	int r;
+
+	line += 4;
+	for (;;) {
+		next = line + strspn(line, " \t\r\n");
+		if (*next == '\0')
+			return (ARCHIVE_OK);
+		line = next;
+		next = line + strcspn(line, " \t\r\n");
+		eq = strchr(line, '=');
+		if (eq > next)
+			len = next - line;
+		else
+			len = eq - line;
+
+		remove_option(global, line, len);
+		r = add_option(a, global, line, next - line);
+		if (r != ARCHIVE_OK)
+			return (r);
+		line = next;
+	}
+}
+
+static int
+process_global_unset(struct archive_read *a,
+    struct mtree_option **global, const char *line)
+{
+	const char *next;
+	size_t len;
+
+	line += 6;
+	if (strchr(line, '=') != NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "/unset shall not contain `='");
+		return ARCHIVE_FATAL;
+	}
+
+	for (;;) {
+		next = line + strspn(line, " \t\r\n");
+		if (*next == '\0')
+			return (ARCHIVE_OK);
+		line = next;
+		len = strcspn(line, " \t\r\n");
+
+		if (len == 3 && strncmp(line, "all", 3) == 0) {
+			free_options(*global);
+			*global = NULL;
+		} else {
+			remove_option(global, line, len);
+		}
+
+		line += len;
+	}
+}
+
+static int
+process_add_entry(struct archive_read *a, struct mtree *mtree,
+    struct mtree_option **global, const char *line, ssize_t line_len,
+    struct mtree_entry **last_entry, int is_form_d)
+{
+	struct mtree_entry *entry;
+	struct mtree_option *iter;
+	const char *next, *eq, *name, *end;
+	size_t name_len, len;
+	int r, i;
+
+	if ((entry = malloc(sizeof(*entry))) == NULL) {
+		archive_set_error(&a->archive, errno, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	entry->next = NULL;
+	entry->options = NULL;
+	entry->name = NULL;
+	entry->used = 0;
+	entry->full = 0;
+
+	/* Add this entry to list. */
+	if (*last_entry == NULL)
+		mtree->entries = entry;
+	else
+		(*last_entry)->next = entry;
+	*last_entry = entry;
+
+	if (is_form_d) {
+		/* Filename is last item on line. */
+		/* Adjust line_len to trim trailing whitespace */
+		while (line_len > 0) {
+			char last_character = line[line_len - 1];
+			if (last_character == '\r'
+			    || last_character == '\n'
+			    || last_character == '\t'
+			    || last_character == ' ') {
+				line_len--;
+			} else {
+				break;
+			}
+		}
+		/* Name starts after the last whitespace separator */
+		name = line;
+		for (i = 0; i < line_len; i++) {
+			if (line[i] == '\r'
+			    || line[i] == '\n'
+			    || line[i] == '\t'
+			    || line[i] == ' ') {
+				name = line + i + 1;
+			}
+		}
+		name_len = line + line_len - name;
+		end = name;
+	} else {
+		/* Filename is first item on line */
+		name_len = strcspn(line, " \t\r\n");
+		name = line;
+		line += name_len;
+		end = line + line_len;
+	}
+	/* name/name_len is the name within the line. */
+	/* line..end brackets the entire line except the name */
+
+	if ((entry->name = malloc(name_len + 1)) == NULL) {
+		archive_set_error(&a->archive, errno, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	memcpy(entry->name, name, name_len);
+	entry->name[name_len] = '\0';
+	parse_escapes(entry->name, entry);
+
+	entry->next_dup = NULL;
+	if (entry->full) {
+		if (!__archive_rb_tree_insert_node(&mtree->rbtree, &entry->rbnode)) {
+			struct mtree_entry *alt;
+			alt = (struct mtree_entry *)__archive_rb_tree_find_node(
+			    &mtree->rbtree, entry->name);
+			while (alt->next_dup)
+				alt = alt->next_dup;
+			alt->next_dup = entry;
+		}
+	}
+
+	for (iter = *global; iter != NULL; iter = iter->next) {
+		r = add_option(a, &entry->options, iter->value,
+		    strlen(iter->value));
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	for (;;) {
+		next = line + strspn(line, " \t\r\n");
+		if (*next == '\0')
+			return (ARCHIVE_OK);
+		if (next >= end)
+			return (ARCHIVE_OK);
+		line = next;
+		next = line + strcspn(line, " \t\r\n");
+		eq = strchr(line, '=');
+		if (eq == NULL || eq > next)
+			len = next - line;
+		else
+			len = eq - line;
+
+		remove_option(&entry->options, line, len);
+		r = add_option(a, &entry->options, line, next - line);
+		if (r != ARCHIVE_OK)
+			return (r);
+		line = next;
+	}
+}
+
+static int
+read_mtree(struct archive_read *a, struct mtree *mtree)
+{
+	ssize_t len;
+	uintmax_t counter;
+	char *p, *s;
+	struct mtree_option *global;
+	struct mtree_entry *last_entry;
+	int r, is_form_d;
+
+	mtree->archive_format = ARCHIVE_FORMAT_MTREE;
+	mtree->archive_format_name = "mtree";
+
+	global = NULL;
+	last_entry = NULL;
+
+	(void)detect_form(a, &is_form_d);
+
+	for (counter = 1; ; ++counter) {
+		r = ARCHIVE_OK;
+		len = readline(a, mtree, &p, 65536);
+		if (len == 0) {
+			mtree->this_entry = mtree->entries;
+			free_options(global);
+			return (ARCHIVE_OK);
+		}
+		if (len < 0) {
+			free_options(global);
+			return ((int)len);
+		}
+		/* Leading whitespace is never significant, ignore it. */
+		while (*p == ' ' || *p == '\t') {
+			++p;
+			--len;
+		}
+		/* Skip content lines and blank lines. */
+		if (*p == '#')
+			continue;
+		if (*p == '\r' || *p == '\n' || *p == '\0')
+			continue;
+		/* Non-printable characters are not allowed */
+		for (s = p;s < p + len - 1; s++) {
+			if (!isprint(*s)) {
+				r = ARCHIVE_FATAL;
+				break;
+			}
+		}
+		if (r != ARCHIVE_OK)
+			break;
+		if (*p != '/') {
+			r = process_add_entry(a, mtree, &global, p, len,
+			    &last_entry, is_form_d);
+		} else if (len > 4 && strncmp(p, "/set", 4) == 0) {
+			if (p[4] != ' ' && p[4] != '\t')
+				break;
+			r = process_global_set(a, &global, p);
+		} else if (len > 6 && strncmp(p, "/unset", 6) == 0) {
+			if (p[6] != ' ' && p[6] != '\t')
+				break;
+			r = process_global_unset(a, &global, p);
+		} else
+			break;
+
+		if (r != ARCHIVE_OK) {
+			free_options(global);
+			return r;
+		}
+	}
+
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Can't parse line %ju", counter);
+	free_options(global);
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Read in the entire mtree file into memory on the first request.
+ * Then use the next unused file to satisfy each header request.
+ */
+static int
+read_header(struct archive_read *a, struct archive_entry *entry)
+{
+	struct mtree *mtree;
+	char *p;
+	int r, use_next;
+
+	mtree = (struct mtree *)(a->format->data);
+
+	if (mtree->fd >= 0) {
+		close(mtree->fd);
+		mtree->fd = -1;
+	}
+
+	if (mtree->entries == NULL) {
+		mtree->resolver = archive_entry_linkresolver_new();
+		if (mtree->resolver == NULL)
+			return ARCHIVE_FATAL;
+		archive_entry_linkresolver_set_strategy(mtree->resolver,
+		    ARCHIVE_FORMAT_MTREE);
+		r = read_mtree(a, mtree);
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	a->archive.archive_format = mtree->archive_format;
+	a->archive.archive_format_name = mtree->archive_format_name;
+
+	for (;;) {
+		if (mtree->this_entry == NULL)
+			return (ARCHIVE_EOF);
+		if (strcmp(mtree->this_entry->name, "..") == 0) {
+			mtree->this_entry->used = 1;
+			if (archive_strlen(&mtree->current_dir) > 0) {
+				/* Roll back current path. */
+				p = mtree->current_dir.s
+				    + mtree->current_dir.length - 1;
+				while (p >= mtree->current_dir.s && *p != '/')
+					--p;
+				if (p >= mtree->current_dir.s)
+					--p;
+				mtree->current_dir.length
+				    = p - mtree->current_dir.s + 1;
+			}
+		}
+		if (!mtree->this_entry->used) {
+			use_next = 0;
+			r = parse_file(a, entry, mtree, mtree->this_entry,
+				&use_next);
+			if (use_next == 0)
+				return (r);
+		}
+		mtree->this_entry = mtree->this_entry->next;
+	}
+}
+
+/*
+ * A single file can have multiple lines contribute specifications.
+ * Parse as many lines as necessary, then pull additional information
+ * from a backing file on disk as necessary.
+ */
+static int
+parse_file(struct archive_read *a, struct archive_entry *entry,
+    struct mtree *mtree, struct mtree_entry *mentry, int *use_next)
+{
+	const char *path;
+	struct stat st_storage, *st;
+	struct mtree_entry *mp;
+	struct archive_entry *sparse_entry;
+	int r = ARCHIVE_OK, r1, parsed_kws;
+
+	mentry->used = 1;
+
+	/* Initialize reasonable defaults. */
+	archive_entry_set_filetype(entry, AE_IFREG);
+	archive_entry_set_size(entry, 0);
+	archive_string_empty(&mtree->contents_name);
+
+	/* Parse options from this line. */
+	parsed_kws = 0;
+	r = parse_line(a, entry, mtree, mentry, &parsed_kws);
+
+	if (mentry->full) {
+		archive_entry_copy_pathname(entry, mentry->name);
+		/*
+		 * "Full" entries are allowed to have multiple lines
+		 * and those lines aren't required to be adjacent.  We
+		 * don't support multiple lines for "relative" entries
+		 * nor do we make any attempt to merge data from
+		 * separate "relative" and "full" entries.  (Merging
+		 * "relative" and "full" entries would require dealing
+		 * with pathname canonicalization, which is a very
+		 * tricky subject.)
+		 */
+		mp = (struct mtree_entry *)__archive_rb_tree_find_node(
+		    &mtree->rbtree, mentry->name);
+		for (; mp; mp = mp->next_dup) {
+			if (mp->full && !mp->used) {
+				/* Later lines override earlier ones. */
+				mp->used = 1;
+				r1 = parse_line(a, entry, mtree, mp, &parsed_kws);
+				if (r1 < r)
+					r = r1;
+			}
+		}
+	} else {
+		/*
+		 * Relative entries require us to construct
+		 * the full path and possibly update the
+		 * current directory.
+		 */
+		size_t n = archive_strlen(&mtree->current_dir);
+		if (n > 0)
+			archive_strcat(&mtree->current_dir, "/");
+		archive_strcat(&mtree->current_dir, mentry->name);
+		archive_entry_copy_pathname(entry, mtree->current_dir.s);
+		if (archive_entry_filetype(entry) != AE_IFDIR)
+			mtree->current_dir.length = n;
+	}
+
+	if (mtree->checkfs) {
+		/*
+		 * Try to open and stat the file to get the real size
+		 * and other file info.  It would be nice to avoid
+		 * this here so that getting a listing of an mtree
+		 * wouldn't require opening every referenced contents
+		 * file.  But then we wouldn't know the actual
+		 * contents size, so I don't see a really viable way
+		 * around this.  (Also, we may want to someday pull
+		 * other unspecified info from the contents file on
+		 * disk.)
+		 */
+		mtree->fd = -1;
+		if (archive_strlen(&mtree->contents_name) > 0)
+			path = mtree->contents_name.s;
+		else
+			path = archive_entry_pathname(entry);
+
+		if (archive_entry_filetype(entry) == AE_IFREG ||
+				archive_entry_filetype(entry) == AE_IFDIR) {
+			mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC);
+			__archive_ensure_cloexec_flag(mtree->fd);
+			if (mtree->fd == -1 &&
+				(errno != ENOENT ||
+				 archive_strlen(&mtree->contents_name) > 0)) {
+				archive_set_error(&a->archive, errno,
+						"Can't open %s", path);
+				r = ARCHIVE_WARN;
+			}
+		}
+
+		st = &st_storage;
+		if (mtree->fd >= 0) {
+			if (fstat(mtree->fd, st) == -1) {
+				archive_set_error(&a->archive, errno,
+						"Could not fstat %s", path);
+				r = ARCHIVE_WARN;
+				/* If we can't stat it, don't keep it open. */
+				close(mtree->fd);
+				mtree->fd = -1;
+				st = NULL;
+			}
+		} else if (lstat(path, st) == -1) {
+			st = NULL;
+		}
+
+		/*
+		 * Check for a mismatch between the type in the specification
+		 * and the type of the contents object on disk.
+		 */
+		if (st != NULL) {
+			if (((st->st_mode & S_IFMT) == S_IFREG &&
+			      archive_entry_filetype(entry) == AE_IFREG)
+#ifdef S_IFLNK
+			  ||((st->st_mode & S_IFMT) == S_IFLNK &&
+			      archive_entry_filetype(entry) == AE_IFLNK)
+#endif
+#ifdef S_IFSOCK
+			  ||((st->st_mode & S_IFSOCK) == S_IFSOCK &&
+			      archive_entry_filetype(entry) == AE_IFSOCK)
+#endif
+#ifdef S_IFCHR
+			  ||((st->st_mode & S_IFMT) == S_IFCHR &&
+			      archive_entry_filetype(entry) == AE_IFCHR)
+#endif
+#ifdef S_IFBLK
+			  ||((st->st_mode & S_IFMT) == S_IFBLK &&
+			      archive_entry_filetype(entry) == AE_IFBLK)
+#endif
+			  ||((st->st_mode & S_IFMT) == S_IFDIR &&
+			      archive_entry_filetype(entry) == AE_IFDIR)
+#ifdef S_IFIFO
+			  ||((st->st_mode & S_IFMT) == S_IFIFO &&
+			      archive_entry_filetype(entry) == AE_IFIFO)
+#endif
+			) {
+				/* Types match. */
+			} else {
+				/* Types don't match; bail out gracefully. */
+				if (mtree->fd >= 0)
+					close(mtree->fd);
+				mtree->fd = -1;
+				if (parsed_kws & MTREE_HAS_OPTIONAL) {
+					/* It's not an error for an optional
+					 * entry to not match disk. */
+					*use_next = 1;
+				} else if (r == ARCHIVE_OK) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_MISC,
+					    "mtree specification has different"
+					    " type for %s",
+					    archive_entry_pathname(entry));
+					r = ARCHIVE_WARN;
+				}
+				return (r);
+			}
+		}
+
+		/*
+		 * If there is a contents file on disk, pick some of the
+		 * metadata from that file.  For most of these, we only
+		 * set it from the contents if it wasn't already parsed
+		 * from the specification.
+		 */
+		if (st != NULL) {
+			if (((parsed_kws & MTREE_HAS_DEVICE) == 0 ||
+				(parsed_kws & MTREE_HAS_NOCHANGE) != 0) &&
+				(archive_entry_filetype(entry) == AE_IFCHR ||
+				 archive_entry_filetype(entry) == AE_IFBLK))
+				archive_entry_set_rdev(entry, st->st_rdev);
+			if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME))
+				== 0 ||
+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+				archive_entry_set_gid(entry, st->st_gid);
+			if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME))
+				== 0 ||
+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+				archive_entry_set_uid(entry, st->st_uid);
+			if ((parsed_kws & MTREE_HAS_MTIME) == 0 ||
+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0) {
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+				archive_entry_set_mtime(entry, st->st_mtime,
+						st->st_mtimespec.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+				archive_entry_set_mtime(entry, st->st_mtime,
+						st->st_mtim.tv_nsec);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+				archive_entry_set_mtime(entry, st->st_mtime,
+						st->st_mtime_n);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+				archive_entry_set_mtime(entry, st->st_mtime,
+						st->st_umtime*1000);
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+				archive_entry_set_mtime(entry, st->st_mtime,
+						st->st_mtime_usec*1000);
+#else
+				archive_entry_set_mtime(entry, st->st_mtime, 0);
+#endif
+			}
+			if ((parsed_kws & MTREE_HAS_NLINK) == 0 ||
+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+				archive_entry_set_nlink(entry, st->st_nlink);
+			if ((parsed_kws & MTREE_HAS_PERM) == 0 ||
+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+				archive_entry_set_perm(entry, st->st_mode);
+			if ((parsed_kws & MTREE_HAS_SIZE) == 0 ||
+			    (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+				archive_entry_set_size(entry, st->st_size);
+			archive_entry_set_ino(entry, st->st_ino);
+			archive_entry_set_dev(entry, st->st_dev);
+
+			archive_entry_linkify(mtree->resolver, &entry,
+				&sparse_entry);
+		} else if (parsed_kws & MTREE_HAS_OPTIONAL) {
+			/*
+			 * Couldn't open the entry, stat it or the on-disk type
+			 * didn't match.  If this entry is optional, just
+			 * ignore it and read the next header entry.
+			 */
+			*use_next = 1;
+			return ARCHIVE_OK;
+		}
+	}
+
+	mtree->cur_size = archive_entry_size(entry);
+	mtree->offset = 0;
+
+	return r;
+}
+
+/*
+ * Each line contains a sequence of keywords.
+ */
+static int
+parse_line(struct archive_read *a, struct archive_entry *entry,
+    struct mtree *mtree, struct mtree_entry *mp, int *parsed_kws)
+{
+	struct mtree_option *iter;
+	int r = ARCHIVE_OK, r1;
+
+	for (iter = mp->options; iter != NULL; iter = iter->next) {
+		r1 = parse_keyword(a, mtree, entry, iter, parsed_kws);
+		if (r1 < r)
+			r = r1;
+	}
+	if (r == ARCHIVE_OK && (*parsed_kws & MTREE_HAS_TYPE) == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Missing type keyword in mtree specification");
+		return (ARCHIVE_WARN);
+	}
+	return (r);
+}
+
+/*
+ * Device entries have one of the following forms:
+ *  - raw dev_t
+ *  - format,major,minor[,subdevice]
+ * When parsing succeeded, `pdev' will contain the appropriate dev_t value.
+ */
+
+/* strsep() is not in C90, but strcspn() is. */
+/* Taken from http://unixpapa.com/incnote/string.html */
+static char *
+la_strsep(char **sp, const char *sep)
+{
+	char *p, *s;
+	if (sp == NULL || *sp == NULL || **sp == '\0')
+		return(NULL);
+	s = *sp;
+	p = s + strcspn(s, sep);
+	if (*p != '\0')
+		*p++ = '\0';
+	*sp = p;
+	return(s);
+}
+
+static int
+parse_device(dev_t *pdev, struct archive *a, char *val)
+{
+#define MAX_PACK_ARGS 3
+	unsigned long numbers[MAX_PACK_ARGS];
+	char *p, *dev;
+	int argc;
+	pack_t *pack;
+	dev_t result;
+	const char *error = NULL;
+
+	memset(pdev, 0, sizeof(*pdev));
+	if ((dev = strchr(val, ',')) != NULL) {
+		/*
+		 * Device's major/minor are given in a specified format.
+		 * Decode and pack it accordingly.
+		 */
+		*dev++ = '\0';
+		if ((pack = pack_find(val)) == NULL) {
+			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Unknown format `%s'", val);
+			return ARCHIVE_WARN;
+		}
+		argc = 0;
+		while ((p = la_strsep(&dev, ",")) != NULL) {
+			if (*p == '\0') {
+				archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Missing number");
+				return ARCHIVE_WARN;
+			}
+			if (argc >= MAX_PACK_ARGS) {
+				archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Too many arguments");
+				return ARCHIVE_WARN;
+			}
+			numbers[argc++] = (unsigned long)mtree_atol(&p, 0);
+		}
+		if (argc < 2) {
+			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Not enough arguments");
+			return ARCHIVE_WARN;
+		}
+		result = (*pack)(argc, numbers, &error);
+		if (error != NULL) {
+			archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "%s", error);
+			return ARCHIVE_WARN;
+		}
+	} else {
+		/* file system raw value. */
+		result = (dev_t)mtree_atol(&val, 0);
+	}
+	*pdev = result;
+	return ARCHIVE_OK;
+#undef MAX_PACK_ARGS
+}
+
+/*
+ * Parse a single keyword and its value.
+ */
+static int
+parse_keyword(struct archive_read *a, struct mtree *mtree,
+    struct archive_entry *entry, struct mtree_option *opt, int *parsed_kws)
+{
+	char *val, *key;
+
+	key = opt->value;
+
+	if (*key == '\0')
+		return (ARCHIVE_OK);
+
+	if (strcmp(key, "nochange") == 0) {
+		*parsed_kws |= MTREE_HAS_NOCHANGE;
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "optional") == 0) {
+		*parsed_kws |= MTREE_HAS_OPTIONAL;
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "ignore") == 0) {
+		/*
+		 * The mtree processing is not recursive, so
+		 * recursion will only happen for explicitly listed
+		 * entries.
+		 */
+		return (ARCHIVE_OK);
+	}
+
+	val = strchr(key, '=');
+	if (val == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Malformed attribute \"%s\" (%d)", key, key[0]);
+		return (ARCHIVE_WARN);
+	}
+
+	*val = '\0';
+	++val;
+
+	switch (key[0]) {
+	case 'c':
+		if (strcmp(key, "content") == 0
+		    || strcmp(key, "contents") == 0) {
+			parse_escapes(val, NULL);
+			archive_strcpy(&mtree->contents_name, val);
+			break;
+		}
+		if (strcmp(key, "cksum") == 0)
+			break;
+		__LA_FALLTHROUGH;
+	case 'd':
+		if (strcmp(key, "device") == 0) {
+			/* stat(2) st_rdev field, e.g. the major/minor IDs
+			 * of a char/block special file */
+			int r;
+			dev_t dev;
+
+			*parsed_kws |= MTREE_HAS_DEVICE;
+			r = parse_device(&dev, &a->archive, val);
+			if (r == ARCHIVE_OK)
+				archive_entry_set_rdev(entry, dev);
+			return r;
+		}
+		__LA_FALLTHROUGH;
+	case 'f':
+		if (strcmp(key, "flags") == 0) {
+			*parsed_kws |= MTREE_HAS_FFLAGS;
+			archive_entry_copy_fflags_text(entry, val);
+			break;
+		}
+		__LA_FALLTHROUGH;
+	case 'g':
+		if (strcmp(key, "gid") == 0) {
+			*parsed_kws |= MTREE_HAS_GID;
+			archive_entry_set_gid(entry, mtree_atol(&val, 10));
+			break;
+		}
+		if (strcmp(key, "gname") == 0) {
+			*parsed_kws |= MTREE_HAS_GNAME;
+			archive_entry_copy_gname(entry, val);
+			break;
+		}
+		__LA_FALLTHROUGH;
+	case 'i':
+		if (strcmp(key, "inode") == 0) {
+			archive_entry_set_ino(entry, mtree_atol(&val, 10));
+			break;
+		}
+		__LA_FALLTHROUGH;
+	case 'l':
+		if (strcmp(key, "link") == 0) {
+			archive_entry_copy_symlink(entry, val);
+			break;
+		}
+		__LA_FALLTHROUGH;
+	case 'm':
+		if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0)
+			break;
+		if (strcmp(key, "mode") == 0) {
+			if (val[0] >= '0' && val[0] <= '7') {
+				*parsed_kws |= MTREE_HAS_PERM;
+				archive_entry_set_perm(entry,
+				    (mode_t)mtree_atol(&val, 8));
+			} else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Symbolic or non-octal mode \"%s\" unsupported", val);
+				return ARCHIVE_WARN;
+			}
+			break;
+		}
+		__LA_FALLTHROUGH;
+	case 'n':
+		if (strcmp(key, "nlink") == 0) {
+			*parsed_kws |= MTREE_HAS_NLINK;
+			archive_entry_set_nlink(entry,
+				(unsigned int)mtree_atol(&val, 10));
+			break;
+		}
+		__LA_FALLTHROUGH;
+	case 'r':
+		if (strcmp(key, "resdevice") == 0) {
+			/* stat(2) st_dev field, e.g. the device ID where the
+			 * inode resides */
+			int r;
+			dev_t dev;
+
+			r = parse_device(&dev, &a->archive, val);
+			if (r == ARCHIVE_OK)
+				archive_entry_set_dev(entry, dev);
+			return r;
+		}
+		if (strcmp(key, "rmd160") == 0 ||
+		    strcmp(key, "rmd160digest") == 0)
+			break;
+		__LA_FALLTHROUGH;
+	case 's':
+		if (strcmp(key, "sha1") == 0 || strcmp(key, "sha1digest") == 0)
+			break;
+		if (strcmp(key, "sha256") == 0 ||
+		    strcmp(key, "sha256digest") == 0)
+			break;
+		if (strcmp(key, "sha384") == 0 ||
+		    strcmp(key, "sha384digest") == 0)
+			break;
+		if (strcmp(key, "sha512") == 0 ||
+		    strcmp(key, "sha512digest") == 0)
+			break;
+		if (strcmp(key, "size") == 0) {
+			archive_entry_set_size(entry, mtree_atol(&val, 10));
+			break;
+		}
+		__LA_FALLTHROUGH;
+	case 't':
+		if (strcmp(key, "tags") == 0) {
+			/*
+			 * Comma delimited list of tags.
+			 * Ignore the tags for now, but the interface
+			 * should be extended to allow inclusion/exclusion.
+			 */
+			break;
+		}
+		if (strcmp(key, "time") == 0) {
+			int64_t m;
+			int64_t my_time_t_max = get_time_t_max();
+			int64_t my_time_t_min = get_time_t_min();
+			long ns = 0;
+
+			*parsed_kws |= MTREE_HAS_MTIME;
+			m = mtree_atol(&val, 10);
+			/* Replicate an old mtree bug:
+			 * 123456789.1 represents 123456789
+			 * seconds and 1 nanosecond. */
+			if (*val == '.') {
+				++val;
+				ns = (long)mtree_atol(&val, 10);
+				if (ns < 0)
+					ns = 0;
+				else if (ns > 999999999)
+					ns = 999999999;
+			}
+			if (m > my_time_t_max)
+				m = my_time_t_max;
+			else if (m < my_time_t_min)
+				m = my_time_t_min;
+			archive_entry_set_mtime(entry, (time_t)m, ns);
+			break;
+		}
+		if (strcmp(key, "type") == 0) {
+			switch (val[0]) {
+			case 'b':
+				if (strcmp(val, "block") == 0) {
+					archive_entry_set_filetype(entry, AE_IFBLK);
+					break;
+				}
+				__LA_FALLTHROUGH;
+			case 'c':
+				if (strcmp(val, "char") == 0) {
+					archive_entry_set_filetype(entry,
+						AE_IFCHR);
+					break;
+				}
+				__LA_FALLTHROUGH;
+			case 'd':
+				if (strcmp(val, "dir") == 0) {
+					archive_entry_set_filetype(entry,
+						AE_IFDIR);
+					break;
+				}
+				__LA_FALLTHROUGH;
+			case 'f':
+				if (strcmp(val, "fifo") == 0) {
+					archive_entry_set_filetype(entry,
+						AE_IFIFO);
+					break;
+				}
+				if (strcmp(val, "file") == 0) {
+					archive_entry_set_filetype(entry,
+						AE_IFREG);
+					break;
+				}
+				__LA_FALLTHROUGH;
+			case 'l':
+				if (strcmp(val, "link") == 0) {
+					archive_entry_set_filetype(entry,
+						AE_IFLNK);
+					break;
+				}
+				__LA_FALLTHROUGH;
+			default:
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Unrecognized file type \"%s\"; "
+				    "assuming \"file\"", val);
+				archive_entry_set_filetype(entry, AE_IFREG);
+				return (ARCHIVE_WARN);
+			}
+			*parsed_kws |= MTREE_HAS_TYPE;
+			break;
+		}
+		__LA_FALLTHROUGH;
+	case 'u':
+		if (strcmp(key, "uid") == 0) {
+			*parsed_kws |= MTREE_HAS_UID;
+			archive_entry_set_uid(entry, mtree_atol(&val, 10));
+			break;
+		}
+		if (strcmp(key, "uname") == 0) {
+			*parsed_kws |= MTREE_HAS_UNAME;
+			archive_entry_copy_uname(entry, val);
+			break;
+		}
+		__LA_FALLTHROUGH;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unrecognized key %s=%s", key, val);
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+read_data(struct archive_read *a, const void **buff, size_t *size,
+    int64_t *offset)
+{
+	size_t bytes_to_read;
+	ssize_t bytes_read;
+	struct mtree *mtree;
+
+	mtree = (struct mtree *)(a->format->data);
+	if (mtree->fd < 0) {
+		*buff = NULL;
+		*offset = 0;
+		*size = 0;
+		return (ARCHIVE_EOF);
+	}
+	if (mtree->buff == NULL) {
+		mtree->buffsize = 64 * 1024;
+		mtree->buff = malloc(mtree->buffsize);
+		if (mtree->buff == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	*buff = mtree->buff;
+	*offset = mtree->offset;
+	if ((int64_t)mtree->buffsize > mtree->cur_size - mtree->offset)
+		bytes_to_read = (size_t)(mtree->cur_size - mtree->offset);
+	else
+		bytes_to_read = mtree->buffsize;
+	bytes_read = read(mtree->fd, mtree->buff, bytes_to_read);
+	if (bytes_read < 0) {
+		archive_set_error(&a->archive, errno, "Can't read");
+		return (ARCHIVE_WARN);
+	}
+	if (bytes_read == 0) {
+		*size = 0;
+		return (ARCHIVE_EOF);
+	}
+	mtree->offset += bytes_read;
+	*size = bytes_read;
+	return (ARCHIVE_OK);
+}
+
+/* Skip does nothing except possibly close the contents file. */
+static int
+skip(struct archive_read *a)
+{
+	struct mtree *mtree;
+
+	mtree = (struct mtree *)(a->format->data);
+	if (mtree->fd >= 0) {
+		close(mtree->fd);
+		mtree->fd = -1;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Since parsing backslash sequences always makes strings shorter,
+ * we can always do this conversion in-place.
+ */
+static void
+parse_escapes(char *src, struct mtree_entry *mentry)
+{
+	char *dest = src;
+	char c;
+
+	if (mentry != NULL && strcmp(src, ".") == 0)
+		mentry->full = 1;
+
+	while (*src != '\0') {
+		c = *src++;
+		if (c == '/' && mentry != NULL)
+			mentry->full = 1;
+		if (c == '\\') {
+			switch (src[0]) {
+			case '0':
+				if (src[1] < '0' || src[1] > '7') {
+					c = 0;
+					++src;
+					break;
+				}
+				/* FALLTHROUGH */
+			case '1':
+			case '2':
+			case '3':
+				if (src[1] >= '0' && src[1] <= '7' &&
+				    src[2] >= '0' && src[2] <= '7') {
+					c = (src[0] - '0') << 6;
+					c |= (src[1] - '0') << 3;
+					c |= (src[2] - '0');
+					src += 3;
+				}
+				break;
+			case 'a':
+				c = '\a';
+				++src;
+				break;
+			case 'b':
+				c = '\b';
+				++src;
+				break;
+			case 'f':
+				c = '\f';
+				++src;
+				break;
+			case 'n':
+				c = '\n';
+				++src;
+				break;
+			case 'r':
+				c = '\r';
+				++src;
+				break;
+			case 's':
+				c = ' ';
+				++src;
+				break;
+			case 't':
+				c = '\t';
+				++src;
+				break;
+			case 'v':
+				c = '\v';
+				++src;
+				break;
+			case '\\':
+				c = '\\';
+				++src;
+				break;
+			}
+		}
+		*dest++ = c;
+	}
+	*dest = '\0';
+}
+
+/* Parse a hex digit. */
+static int
+parsedigit(char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	else if (c >= 'a' && c <= 'f')
+		return c - 'a';
+	else if (c >= 'A' && c <= 'F')
+		return c - 'A';
+	else
+		return -1;
+}
+
+/*
+ * 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
+mtree_atol(char **p, int base)
+{
+	int64_t l, limit;
+	int digit, last_digit_limit;
+
+	if (base == 0) {
+		if (**p != '0')
+			base = 10;
+		else if ((*p)[1] == 'x' || (*p)[1] == 'X') {
+			*p += 2;
+			base = 16;
+		} else {
+			base = 8;
+		}
+	}
+
+	if (**p == '-') {
+		limit = INT64_MIN / base;
+		last_digit_limit = INT64_MIN % base;
+		++(*p);
+
+		l = 0;
+		digit = parsedigit(**p);
+		while (digit >= 0 && digit < base) {
+			if (l < limit || (l == limit && digit > last_digit_limit))
+				return INT64_MIN;
+			l = (l * base) - digit;
+			digit = parsedigit(*++(*p));
+		}
+		return l;
+	} else {
+		limit = INT64_MAX / base;
+		last_digit_limit = INT64_MAX % base;
+
+		l = 0;
+		digit = parsedigit(**p);
+		while (digit >= 0 && digit < base) {
+			if (l > limit || (l == limit && digit > last_digit_limit))
+				return INT64_MAX;
+			l = (l * base) + digit;
+			digit = parsedigit(*++(*p));
+		}
+		return l;
+	}
+}
+
+/*
+ * Returns length of line (including trailing newline)
+ * or negative on error.  'start' argument is updated to
+ * point to first character of line.
+ */
+static ssize_t
+readline(struct archive_read *a, struct mtree *mtree, char **start,
+    ssize_t limit)
+{
+	ssize_t bytes_read;
+	ssize_t total_size = 0;
+	ssize_t find_off = 0;
+	const void *t;
+	void *nl;
+	char *u;
+
+	/* Accumulate line in a line buffer. */
+	for (;;) {
+		/* Read some more. */
+		t = __archive_read_ahead(a, 1, &bytes_read);
+		if (t == NULL)
+			return (0);
+		if (bytes_read < 0)
+			return (ARCHIVE_FATAL);
+		nl = memchr(t, '\n', bytes_read);
+		/* If we found '\n', trim the read to end exactly there. */
+		if (nl != NULL) {
+			bytes_read = ((const char *)nl) - ((const char *)t) + 1;
+		}
+		if (total_size + bytes_read + 1 > limit) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Line too long");
+			return (ARCHIVE_FATAL);
+		}
+		if (archive_string_ensure(&mtree->line,
+			total_size + bytes_read + 1) == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate working buffer");
+			return (ARCHIVE_FATAL);
+		}
+		/* Append new bytes to string. */
+		memcpy(mtree->line.s + total_size, t, bytes_read);
+		__archive_read_consume(a, bytes_read);
+		total_size += bytes_read;
+		mtree->line.s[total_size] = '\0';
+
+		for (u = mtree->line.s + find_off; *u; ++u) {
+			if (u[0] == '\n') {
+				/* Ends with unescaped newline. */
+				*start = mtree->line.s;
+				return total_size;
+			} else if (u[0] == '#') {
+				/* Ends with comment sequence #...\n */
+				if (nl == NULL) {
+					/* But we've not found the \n yet */
+					break;
+				}
+			} else if (u[0] == '\\') {
+				if (u[1] == '\n') {
+					/* Trim escaped newline. */
+					total_size -= 2;
+					mtree->line.s[total_size] = '\0';
+					break;
+				} else if (u[1] != '\0') {
+					/* Skip the two-char escape sequence */
+					++u;
+				}
+			}
+		}
+		find_off = u - mtree->line.s;
+	}
+}
diff --git a/c/archive_read_support_format_rar.c b/c/archive_read_support_format_rar.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_rar.c
@@ -0,0 +1,2999 @@
+/*-
+* Copyright (c) 2003-2007 Tim Kientzle
+* Copyright (c) 2011 Andres Mejia
+* 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"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+#include <limits.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h> /* crc32 */
+#endif
+
+#include "archive.h"
+#ifndef HAVE_ZLIB_H
+#include "archive_crc32.h"
+#endif
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+/* RAR signature, also known as the mark header */
+#define RAR_SIGNATURE "\x52\x61\x72\x21\x1A\x07\x00"
+
+/* Header types */
+#define MARK_HEAD    0x72
+#define MAIN_HEAD    0x73
+#define FILE_HEAD    0x74
+#define COMM_HEAD    0x75
+#define AV_HEAD      0x76
+#define SUB_HEAD     0x77
+#define PROTECT_HEAD 0x78
+#define SIGN_HEAD    0x79
+#define NEWSUB_HEAD  0x7a
+#define ENDARC_HEAD  0x7b
+
+/* Main Header Flags */
+#define MHD_VOLUME       0x0001
+#define MHD_COMMENT      0x0002
+#define MHD_LOCK         0x0004
+#define MHD_SOLID        0x0008
+#define MHD_NEWNUMBERING 0x0010
+#define MHD_AV           0x0020
+#define MHD_PROTECT      0x0040
+#define MHD_PASSWORD     0x0080
+#define MHD_FIRSTVOLUME  0x0100
+#define MHD_ENCRYPTVER   0x0200
+
+/* Flags common to all headers */
+#define HD_MARKDELETION     0x4000
+#define HD_ADD_SIZE_PRESENT 0x8000
+
+/* File Header Flags */
+#define FHD_SPLIT_BEFORE 0x0001
+#define FHD_SPLIT_AFTER  0x0002
+#define FHD_PASSWORD     0x0004
+#define FHD_COMMENT      0x0008
+#define FHD_SOLID        0x0010
+#define FHD_LARGE        0x0100
+#define FHD_UNICODE      0x0200
+#define FHD_SALT         0x0400
+#define FHD_VERSION      0x0800
+#define FHD_EXTTIME      0x1000
+#define FHD_EXTFLAGS     0x2000
+
+/* File dictionary sizes */
+#define DICTIONARY_SIZE_64   0x00
+#define DICTIONARY_SIZE_128  0x20
+#define DICTIONARY_SIZE_256  0x40
+#define DICTIONARY_SIZE_512  0x60
+#define DICTIONARY_SIZE_1024 0x80
+#define DICTIONARY_SIZE_2048 0xA0
+#define DICTIONARY_SIZE_4096 0xC0
+#define FILE_IS_DIRECTORY    0xE0
+#define DICTIONARY_MASK      FILE_IS_DIRECTORY
+
+/* OS Flags */
+#define OS_MSDOS  0
+#define OS_OS2    1
+#define OS_WIN32  2
+#define OS_UNIX   3
+#define OS_MAC_OS 4
+#define OS_BEOS   5
+
+/* Compression Methods */
+#define COMPRESS_METHOD_STORE   0x30
+/* LZSS */
+#define COMPRESS_METHOD_FASTEST 0x31
+#define COMPRESS_METHOD_FAST    0x32
+#define COMPRESS_METHOD_NORMAL  0x33
+/* PPMd Variant H */
+#define COMPRESS_METHOD_GOOD    0x34
+#define COMPRESS_METHOD_BEST    0x35
+
+#define CRC_POLYNOMIAL 0xEDB88320
+
+#define NS_UNIT 10000000
+
+#define DICTIONARY_MAX_SIZE 0x400000
+
+#define MAINCODE_SIZE      299
+#define OFFSETCODE_SIZE    60
+#define LOWOFFSETCODE_SIZE 17
+#define LENGTHCODE_SIZE    28
+#define HUFFMAN_TABLE_SIZE \
+  MAINCODE_SIZE + OFFSETCODE_SIZE + LOWOFFSETCODE_SIZE + LENGTHCODE_SIZE
+
+#define MAX_SYMBOL_LENGTH 0xF
+#define MAX_SYMBOLS       20
+
+/*
+ * Considering L1,L2 cache miss and a calling of write system-call,
+ * the best size of the output buffer(uncompressed buffer) is 128K.
+ * If the structure of extracting process is changed, this value
+ * might be researched again.
+ */
+#define UNP_BUFFER_SIZE   (128 * 1024)
+
+/* Define this here for non-Windows platforms */
+#if !((defined(__WIN32__) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__))
+#define FILE_ATTRIBUTE_DIRECTORY 0x10
+#endif
+
+#undef minimum
+#define minimum(a, b)	((a)<(b)?(a):(b))
+
+/* Fields common to all headers */
+struct rar_header
+{
+  char crc[2];
+  char type;
+  char flags[2];
+  char size[2];
+};
+
+/* Fields common to all file headers */
+struct rar_file_header
+{
+  char pack_size[4];
+  char unp_size[4];
+  char host_os;
+  char file_crc[4];
+  char file_time[4];
+  char unp_ver;
+  char method;
+  char name_size[2];
+  char file_attr[4];
+};
+
+struct huffman_tree_node
+{
+  int branches[2];
+};
+
+struct huffman_table_entry
+{
+  unsigned int length;
+  int value;
+};
+
+struct huffman_code
+{
+  struct huffman_tree_node *tree;
+  int numentries;
+  int numallocatedentries;
+  int minlength;
+  int maxlength;
+  int tablesize;
+  struct huffman_table_entry *table;
+};
+
+struct lzss
+{
+  unsigned char *window;
+  int mask;
+  int64_t position;
+};
+
+struct data_block_offsets
+{
+  int64_t header_size;
+  int64_t start_offset;
+  int64_t end_offset;
+};
+
+struct rar
+{
+  /* Entries from main RAR header */
+  unsigned main_flags;
+  unsigned long file_crc;
+  char reserved1[2];
+  char reserved2[4];
+  char encryptver;
+
+  /* File header entries */
+  char compression_method;
+  unsigned file_flags;
+  int64_t packed_size;
+  int64_t unp_size;
+  time_t mtime;
+  long mnsec;
+  mode_t mode;
+  char *filename;
+  char *filename_save;
+  size_t filename_save_size;
+  size_t filename_allocated;
+
+  /* File header optional entries */
+  char salt[8];
+  time_t atime;
+  long ansec;
+  time_t ctime;
+  long cnsec;
+  time_t arctime;
+  long arcnsec;
+
+  /* Fields to help with tracking decompression of files. */
+  int64_t bytes_unconsumed;
+  int64_t bytes_remaining;
+  int64_t bytes_uncopied;
+  int64_t offset;
+  int64_t offset_outgoing;
+  int64_t offset_seek;
+  char valid;
+  unsigned int unp_offset;
+  unsigned int unp_buffer_size;
+  unsigned char *unp_buffer;
+  unsigned int dictionary_size;
+  char start_new_block;
+  char entry_eof;
+  unsigned long crc_calculated;
+  int found_first_header;
+  char has_endarc_header;
+  struct data_block_offsets *dbo;
+  unsigned int cursor;
+  unsigned int nodes;
+  char filename_must_match;
+
+  /* LZSS members */
+  struct huffman_code maincode;
+  struct huffman_code offsetcode;
+  struct huffman_code lowoffsetcode;
+  struct huffman_code lengthcode;
+  unsigned char lengthtable[HUFFMAN_TABLE_SIZE];
+  struct lzss lzss;
+  char output_last_match;
+  unsigned int lastlength;
+  unsigned int lastoffset;
+  unsigned int oldoffset[4];
+  unsigned int lastlowoffset;
+  unsigned int numlowoffsetrepeats;
+  int64_t filterstart;
+  char start_new_table;
+
+  /* PPMd Variant H members */
+  char ppmd_valid;
+  char ppmd_eod;
+  char is_ppmd_block;
+  int ppmd_escape;
+  CPpmd7 ppmd7_context;
+  CPpmd7z_RangeDec range_dec;
+  IByteIn bytein;
+
+  /*
+   * String conversion object.
+   */
+  int init_default_conversion;
+  struct archive_string_conv *sconv_default;
+  struct archive_string_conv *opt_sconv;
+  struct archive_string_conv *sconv_utf8;
+  struct archive_string_conv *sconv_utf16be;
+
+  /*
+   * Bit stream reader.
+   */
+  struct rar_br {
+#define CACHE_TYPE	uint64_t
+#define CACHE_BITS	(8 * sizeof(CACHE_TYPE))
+    /* Cache buffer. */
+    CACHE_TYPE		 cache_buffer;
+    /* Indicates how many bits avail in cache_buffer. */
+    int			 cache_avail;
+    ssize_t		 avail_in;
+    const unsigned char *next_in;
+  } br;
+
+  /*
+   * Custom field to denote that this archive contains encrypted entries
+   */
+  int has_encrypted_entries;
+};
+
+static int archive_read_support_format_rar_capabilities(struct archive_read *);
+static int archive_read_format_rar_has_encrypted_entries(struct archive_read *);
+static int archive_read_format_rar_bid(struct archive_read *, int);
+static int archive_read_format_rar_options(struct archive_read *,
+    const char *, const char *);
+static int archive_read_format_rar_read_header(struct archive_read *,
+    struct archive_entry *);
+static int archive_read_format_rar_read_data(struct archive_read *,
+    const void **, size_t *, int64_t *);
+static int archive_read_format_rar_read_data_skip(struct archive_read *a);
+static int64_t archive_read_format_rar_seek_data(struct archive_read *, int64_t,
+    int);
+static int archive_read_format_rar_cleanup(struct archive_read *);
+
+/* Support functions */
+static int read_header(struct archive_read *, struct archive_entry *, char);
+static time_t get_time(int);
+static int read_exttime(const char *, struct rar *, const char *);
+static int read_symlink_stored(struct archive_read *, struct archive_entry *,
+                               struct archive_string_conv *);
+static int read_data_stored(struct archive_read *, const void **, size_t *,
+                            int64_t *);
+static int read_data_compressed(struct archive_read *, const void **, size_t *,
+                          int64_t *);
+static int rar_br_preparation(struct archive_read *, struct rar_br *);
+static int parse_codes(struct archive_read *);
+static void free_codes(struct archive_read *);
+static int read_next_symbol(struct archive_read *, struct huffman_code *);
+static int create_code(struct archive_read *, struct huffman_code *,
+                        unsigned char *, int, char);
+static int add_value(struct archive_read *, struct huffman_code *, int, int,
+                     int);
+static int new_node(struct huffman_code *);
+static int make_table(struct archive_read *, struct huffman_code *);
+static int make_table_recurse(struct archive_read *, struct huffman_code *, int,
+                              struct huffman_table_entry *, int, int);
+static int64_t expand(struct archive_read *, int64_t);
+static int copy_from_lzss_window(struct archive_read *, const void **,
+                                   int64_t, int);
+static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *);
+
+/*
+ * Bit stream reader.
+ */
+/* Check that the cache buffer has enough bits. */
+#define rar_br_has(br, n) ((br)->cache_avail >= n)
+/* Get compressed data by bit. */
+#define rar_br_bits(br, n)        \
+  (((uint32_t)((br)->cache_buffer >>    \
+    ((br)->cache_avail - (n)))) & cache_masks[n])
+#define rar_br_bits_forced(br, n)     \
+  (((uint32_t)((br)->cache_buffer <<    \
+    ((n) - (br)->cache_avail))) & cache_masks[n])
+/* Read ahead to make sure the cache buffer has enough compressed data we
+ * will use.
+ *  True  : completed, there is enough data in the cache buffer.
+ *  False : there is no data in the stream. */
+#define rar_br_read_ahead(a, br, n) \
+  ((rar_br_has(br, (n)) || rar_br_fillup(a, br)) || rar_br_has(br, (n)))
+/* Notify how many bits we consumed. */
+#define rar_br_consume(br, n) ((br)->cache_avail -= (n))
+#define rar_br_consume_unalined_bits(br) ((br)->cache_avail &= ~7)
+
+static const uint32_t cache_masks[] = {
+  0x00000000, 0x00000001, 0x00000003, 0x00000007,
+  0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
+  0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF,
+  0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
+  0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF,
+  0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
+  0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF,
+  0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
+  0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/*
+ * Shift away used bits in the cache data and fill it up with following bits.
+ * Call this when cache buffer does not have enough bits you need.
+ *
+ * Returns 1 if the cache buffer is full.
+ * Returns 0 if the cache buffer is not full; input buffer is empty.
+ */
+static int
+rar_br_fillup(struct archive_read *a, struct rar_br *br)
+{
+  struct rar *rar = (struct rar *)(a->format->data);
+  int n = CACHE_BITS - br->cache_avail;
+
+  for (;;) {
+    switch (n >> 3) {
+    case 8:
+      if (br->avail_in >= 8) {
+        br->cache_buffer =
+            ((uint64_t)br->next_in[0]) << 56 |
+            ((uint64_t)br->next_in[1]) << 48 |
+            ((uint64_t)br->next_in[2]) << 40 |
+            ((uint64_t)br->next_in[3]) << 32 |
+            ((uint32_t)br->next_in[4]) << 24 |
+            ((uint32_t)br->next_in[5]) << 16 |
+            ((uint32_t)br->next_in[6]) << 8 |
+             (uint32_t)br->next_in[7];
+        br->next_in += 8;
+        br->avail_in -= 8;
+        br->cache_avail += 8 * 8;
+        rar->bytes_unconsumed += 8;
+        rar->bytes_remaining -= 8;
+        return (1);
+      }
+      break;
+    case 7:
+      if (br->avail_in >= 7) {
+        br->cache_buffer =
+           (br->cache_buffer << 56) |
+            ((uint64_t)br->next_in[0]) << 48 |
+            ((uint64_t)br->next_in[1]) << 40 |
+            ((uint64_t)br->next_in[2]) << 32 |
+            ((uint32_t)br->next_in[3]) << 24 |
+            ((uint32_t)br->next_in[4]) << 16 |
+            ((uint32_t)br->next_in[5]) << 8 |
+             (uint32_t)br->next_in[6];
+        br->next_in += 7;
+        br->avail_in -= 7;
+        br->cache_avail += 7 * 8;
+        rar->bytes_unconsumed += 7;
+        rar->bytes_remaining -= 7;
+        return (1);
+      }
+      break;
+    case 6:
+      if (br->avail_in >= 6) {
+        br->cache_buffer =
+           (br->cache_buffer << 48) |
+            ((uint64_t)br->next_in[0]) << 40 |
+            ((uint64_t)br->next_in[1]) << 32 |
+            ((uint32_t)br->next_in[2]) << 24 |
+            ((uint32_t)br->next_in[3]) << 16 |
+            ((uint32_t)br->next_in[4]) << 8 |
+             (uint32_t)br->next_in[5];
+        br->next_in += 6;
+        br->avail_in -= 6;
+        br->cache_avail += 6 * 8;
+        rar->bytes_unconsumed += 6;
+        rar->bytes_remaining -= 6;
+        return (1);
+      }
+      break;
+    case 0:
+      /* We have enough compressed data in
+       * the cache buffer.*/
+      return (1);
+    default:
+      break;
+    }
+    if (br->avail_in <= 0) {
+
+      if (rar->bytes_unconsumed > 0) {
+        /* Consume as much as the decompressor
+         * actually used. */
+        __archive_read_consume(a, rar->bytes_unconsumed);
+        rar->bytes_unconsumed = 0;
+      }
+      br->next_in = rar_read_ahead(a, 1, &(br->avail_in));
+      if (br->next_in == NULL)
+        return (0);
+      if (br->avail_in == 0)
+        return (0);
+    }
+    br->cache_buffer =
+       (br->cache_buffer << 8) | *br->next_in++;
+    br->avail_in--;
+    br->cache_avail += 8;
+    n -= 8;
+    rar->bytes_unconsumed++;
+    rar->bytes_remaining--;
+  }
+}
+
+static int
+rar_br_preparation(struct archive_read *a, struct rar_br *br)
+{
+  struct rar *rar = (struct rar *)(a->format->data);
+
+  if (rar->bytes_remaining > 0) {
+    br->next_in = rar_read_ahead(a, 1, &(br->avail_in));
+    if (br->next_in == NULL) {
+      archive_set_error(&a->archive,
+          ARCHIVE_ERRNO_FILE_FORMAT,
+          "Truncated RAR file data");
+      return (ARCHIVE_FATAL);
+    }
+    if (br->cache_avail == 0)
+      (void)rar_br_fillup(a, br);
+  }
+  return (ARCHIVE_OK);
+}
+
+/* Find last bit set */
+static inline int
+rar_fls(unsigned int word)
+{
+  word |= (word >>  1);
+  word |= (word >>  2);
+  word |= (word >>  4);
+  word |= (word >>  8);
+  word |= (word >> 16);
+  return word - (word >> 1);
+}
+
+/* LZSS functions */
+static inline int64_t
+lzss_position(struct lzss *lzss)
+{
+  return lzss->position;
+}
+
+static inline int
+lzss_mask(struct lzss *lzss)
+{
+  return lzss->mask;
+}
+
+static inline int
+lzss_size(struct lzss *lzss)
+{
+  return lzss->mask + 1;
+}
+
+static inline int
+lzss_offset_for_position(struct lzss *lzss, int64_t pos)
+{
+  return (int)(pos & lzss->mask);
+}
+
+static inline unsigned char *
+lzss_pointer_for_position(struct lzss *lzss, int64_t pos)
+{
+  return &lzss->window[lzss_offset_for_position(lzss, pos)];
+}
+
+static inline int
+lzss_current_offset(struct lzss *lzss)
+{
+  return lzss_offset_for_position(lzss, lzss->position);
+}
+
+static inline uint8_t *
+lzss_current_pointer(struct lzss *lzss)
+{
+  return lzss_pointer_for_position(lzss, lzss->position);
+}
+
+static inline void
+lzss_emit_literal(struct rar *rar, uint8_t literal)
+{
+  *lzss_current_pointer(&rar->lzss) = literal;
+  rar->lzss.position++;
+}
+
+static inline void
+lzss_emit_match(struct rar *rar, int offset, int length)
+{
+  int dstoffs = lzss_current_offset(&rar->lzss);
+  int srcoffs = (dstoffs - offset) & lzss_mask(&rar->lzss);
+  int l, li, remaining;
+  unsigned char *d, *s;
+
+  remaining = length;
+  while (remaining > 0) {
+    l = remaining;
+    if (dstoffs > srcoffs) {
+      if (l > lzss_size(&rar->lzss) - dstoffs)
+        l = lzss_size(&rar->lzss) - dstoffs;
+    } else {
+      if (l > lzss_size(&rar->lzss) - srcoffs)
+        l = lzss_size(&rar->lzss) - srcoffs;
+    }
+    d = &(rar->lzss.window[dstoffs]);
+    s = &(rar->lzss.window[srcoffs]);
+    if ((dstoffs + l < srcoffs) || (srcoffs + l < dstoffs))
+      memcpy(d, s, l);
+    else {
+      for (li = 0; li < l; li++)
+        d[li] = s[li];
+    }
+    remaining -= l;
+    dstoffs = (dstoffs + l) & lzss_mask(&(rar->lzss));
+    srcoffs = (srcoffs + l) & lzss_mask(&(rar->lzss));
+  }
+  rar->lzss.position += length;
+}
+
+static Byte
+ppmd_read(void *p)
+{
+  struct archive_read *a = ((IByteIn*)p)->a;
+  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar_br *br = &(rar->br);
+  Byte b;
+  if (!rar_br_read_ahead(a, br, 8))
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Truncated RAR file data");
+    rar->valid = 0;
+    return 0;
+  }
+  b = rar_br_bits(br, 8);
+  rar_br_consume(br, 8);
+  return b;
+}
+
+int
+archive_read_support_format_rar(struct archive *_a)
+{
+  struct archive_read *a = (struct archive_read *)_a;
+  struct rar *rar;
+  int r;
+
+  archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+                      "archive_read_support_format_rar");
+
+  rar = (struct rar *)calloc(sizeof(*rar), 1);
+  if (rar == NULL)
+  {
+    archive_set_error(&a->archive, ENOMEM, "Can't allocate rar data");
+    return (ARCHIVE_FATAL);
+  }
+
+	/*
+	 * Until enough data has been read, we cannot tell about
+	 * any encrypted entries yet.
+	 */
+	rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+
+  r = __archive_read_register_format(a,
+                                     rar,
+                                     "rar",
+                                     archive_read_format_rar_bid,
+                                     archive_read_format_rar_options,
+                                     archive_read_format_rar_read_header,
+                                     archive_read_format_rar_read_data,
+                                     archive_read_format_rar_read_data_skip,
+                                     archive_read_format_rar_seek_data,
+                                     archive_read_format_rar_cleanup,
+                                     archive_read_support_format_rar_capabilities,
+                                     archive_read_format_rar_has_encrypted_entries);
+
+  if (r != ARCHIVE_OK)
+    free(rar);
+  return (r);
+}
+
+static int
+archive_read_support_format_rar_capabilities(struct archive_read * a)
+{
+	(void)a; /* UNUSED */
+	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA
+			| ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+static int
+archive_read_format_rar_has_encrypted_entries(struct archive_read *_a)
+{
+	if (_a && _a->format) {
+		struct rar * rar = (struct rar *)_a->format->data;
+		if (rar) {
+			return rar->has_encrypted_entries;
+		}
+	}
+	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+
+static int
+archive_read_format_rar_bid(struct archive_read *a, int best_bid)
+{
+  const char *p;
+
+  /* If there's already a bid > 30, we'll never win. */
+  if (best_bid > 30)
+	  return (-1);
+
+  if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+    return (-1);
+
+  if (memcmp(p, RAR_SIGNATURE, 7) == 0)
+    return (30);
+
+  if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+    /* This is a PE file */
+    ssize_t offset = 0x10000;
+    ssize_t window = 4096;
+    ssize_t bytes_avail;
+    while (offset + window <= (1024 * 128)) {
+      const char *buff = __archive_read_ahead(a, offset + window, &bytes_avail);
+      if (buff == NULL) {
+        /* Remaining bytes are less than window. */
+        window >>= 1;
+        if (window < 0x40)
+          return (0);
+        continue;
+      }
+      p = buff + offset;
+      while (p + 7 < buff + bytes_avail) {
+        if (memcmp(p, RAR_SIGNATURE, 7) == 0)
+          return (30);
+        p += 0x10;
+      }
+      offset = p - buff;
+    }
+  }
+  return (0);
+}
+
+static int
+skip_sfx(struct archive_read *a)
+{
+  const void *h;
+  const char *p, *q;
+  size_t skip, total;
+  ssize_t bytes, window;
+
+  total = 0;
+  window = 4096;
+  while (total + window <= (1024 * 128)) {
+    h = __archive_read_ahead(a, window, &bytes);
+    if (h == NULL) {
+      /* Remaining bytes are less than window. */
+      window >>= 1;
+      if (window < 0x40)
+      	goto fatal;
+      continue;
+    }
+    if (bytes < 0x40)
+      goto fatal;
+    p = h;
+    q = p + bytes;
+
+    /*
+     * Scan ahead until we find something that looks
+     * like the RAR header.
+     */
+    while (p + 7 < q) {
+      if (memcmp(p, RAR_SIGNATURE, 7) == 0) {
+      	skip = p - (const char *)h;
+      	__archive_read_consume(a, skip);
+      	return (ARCHIVE_OK);
+      }
+      p += 0x10;
+    }
+    skip = p - (const char *)h;
+    __archive_read_consume(a, skip);
+	total += skip;
+  }
+fatal:
+  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+      "Couldn't find out RAR header");
+  return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_rar_options(struct archive_read *a,
+    const char *key, const char *val)
+{
+  struct rar *rar;
+  int ret = ARCHIVE_FAILED;
+
+  rar = (struct rar *)(a->format->data);
+  if (strcmp(key, "hdrcharset")  == 0) {
+    if (val == NULL || val[0] == 0)
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+          "rar: hdrcharset option needs a character-set name");
+    else {
+      rar->opt_sconv =
+          archive_string_conversion_from_charset(
+              &a->archive, val, 0);
+      if (rar->opt_sconv != NULL)
+        ret = ARCHIVE_OK;
+      else
+        ret = ARCHIVE_FATAL;
+    }
+    return (ret);
+  }
+
+  /* Note: The "warn" return is just to inform the options
+   * supervisor that we didn't handle it.  It will generate
+   * a suitable error if no one used this option. */
+  return (ARCHIVE_WARN);
+}
+
+static int
+archive_read_format_rar_read_header(struct archive_read *a,
+                                    struct archive_entry *entry)
+{
+  const void *h;
+  const char *p;
+  struct rar *rar;
+  size_t skip;
+  char head_type;
+  int ret;
+  unsigned flags;
+  unsigned long crc32_expected;
+
+  a->archive.archive_format = ARCHIVE_FORMAT_RAR;
+  if (a->archive.archive_format_name == NULL)
+    a->archive.archive_format_name = "RAR";
+
+  rar = (struct rar *)(a->format->data);
+
+  /*
+   * It should be sufficient to call archive_read_next_header() for
+   * a reader to determine if an entry is encrypted or not. If the
+   * encryption of an entry is only detectable when calling
+   * archive_read_data(), so be it. We'll do the same check there
+   * as well.
+   */
+  if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+	  rar->has_encrypted_entries = 0;
+  }
+
+  /* RAR files can be generated without EOF headers, so return ARCHIVE_EOF if
+  * this fails.
+  */
+  if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
+    return (ARCHIVE_EOF);
+
+  p = h;
+  if (rar->found_first_header == 0 &&
+     ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)) {
+    /* This is an executable ? Must be self-extracting... */
+    ret = skip_sfx(a);
+    if (ret < ARCHIVE_WARN)
+      return (ret);
+  }
+  rar->found_first_header = 1;
+
+  while (1)
+  {
+    unsigned long crc32_val;
+
+    if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
+      return (ARCHIVE_FATAL);
+    p = h;
+
+    head_type = p[2];
+    switch(head_type)
+    {
+    case MARK_HEAD:
+      if (memcmp(p, RAR_SIGNATURE, 7) != 0) {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+          "Invalid marker header");
+        return (ARCHIVE_FATAL);
+      }
+      __archive_read_consume(a, 7);
+      break;
+
+    case MAIN_HEAD:
+      rar->main_flags = archive_le16dec(p + 3);
+      skip = archive_le16dec(p + 5);
+      if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)) {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+          "Invalid header size");
+        return (ARCHIVE_FATAL);
+      }
+      if ((h = __archive_read_ahead(a, skip, NULL)) == NULL)
+        return (ARCHIVE_FATAL);
+      p = h;
+      memcpy(rar->reserved1, p + 7, sizeof(rar->reserved1));
+      memcpy(rar->reserved2, p + 7 + sizeof(rar->reserved1),
+             sizeof(rar->reserved2));
+      if (rar->main_flags & MHD_ENCRYPTVER) {
+        if (skip < 7 + sizeof(rar->reserved1) + sizeof(rar->reserved2)+1) {
+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+            "Invalid header size");
+          return (ARCHIVE_FATAL);
+        }
+        rar->encryptver = *(p + 7 + sizeof(rar->reserved1) +
+                            sizeof(rar->reserved2));
+      }
+
+      /* Main header is password encrypted, so we cannot read any
+         file names or any other info about files from the header. */
+      if (rar->main_flags & MHD_PASSWORD)
+      {
+        archive_entry_set_is_metadata_encrypted(entry, 1);
+        archive_entry_set_is_data_encrypted(entry, 1);
+        rar->has_encrypted_entries = 1;
+         archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "RAR encryption support unavailable.");
+        return (ARCHIVE_FATAL);
+      }
+
+      crc32_val = crc32(0, (const unsigned char *)p + 2, (unsigned)skip - 2);
+      if ((crc32_val & 0xffff) != archive_le16dec(p)) {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+          "Header CRC error");
+        return (ARCHIVE_FATAL);
+      }
+      __archive_read_consume(a, skip);
+      break;
+
+    case FILE_HEAD:
+      return read_header(a, entry, head_type);
+
+    case COMM_HEAD:
+    case AV_HEAD:
+    case SUB_HEAD:
+    case PROTECT_HEAD:
+    case SIGN_HEAD:
+    case ENDARC_HEAD:
+      flags = archive_le16dec(p + 3);
+      skip = archive_le16dec(p + 5);
+      if (skip < 7) {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+          "Invalid header size too small");
+        return (ARCHIVE_FATAL);
+      }
+      if (flags & HD_ADD_SIZE_PRESENT)
+      {
+        if (skip < 7 + 4) {
+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+            "Invalid header size too small");
+          return (ARCHIVE_FATAL);
+        }
+        if ((h = __archive_read_ahead(a, skip, NULL)) == NULL)
+          return (ARCHIVE_FATAL);
+        p = h;
+        skip += archive_le32dec(p + 7);
+      }
+
+      /* Skip over the 2-byte CRC at the beginning of the header. */
+      crc32_expected = archive_le16dec(p);
+      __archive_read_consume(a, 2);
+      skip -= 2;
+
+      /* Skim the entire header and compute the CRC. */
+      crc32_val = 0;
+      while (skip > 0) {
+	      size_t to_read = skip;
+	      ssize_t did_read;
+	      if (to_read > 32 * 1024) {
+		      to_read = 32 * 1024;
+	      }
+	      if ((h = __archive_read_ahead(a, to_read, &did_read)) == NULL) {
+		      return (ARCHIVE_FATAL);
+	      }
+	      p = h;
+	      crc32_val = crc32(crc32_val, (const unsigned char *)p, (unsigned)did_read);
+	      __archive_read_consume(a, did_read);
+	      skip -= did_read;
+      }
+      if ((crc32_val & 0xffff) != crc32_expected) {
+	      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		  "Header CRC error");
+	      return (ARCHIVE_FATAL);
+      }
+      if (head_type == ENDARC_HEAD)
+	      return (ARCHIVE_EOF);
+      break;
+
+    case NEWSUB_HEAD:
+      if ((ret = read_header(a, entry, head_type)) < ARCHIVE_WARN)
+        return ret;
+      break;
+
+    default:
+      archive_set_error(&a->archive,  ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Bad RAR file");
+      return (ARCHIVE_FATAL);
+    }
+  }
+}
+
+static int
+archive_read_format_rar_read_data(struct archive_read *a, const void **buff,
+                                  size_t *size, int64_t *offset)
+{
+  struct rar *rar = (struct rar *)(a->format->data);
+  int ret;
+
+  if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+	  rar->has_encrypted_entries = 0;
+  }
+
+  if (rar->bytes_unconsumed > 0) {
+      /* Consume as much as the decompressor actually used. */
+      __archive_read_consume(a, rar->bytes_unconsumed);
+      rar->bytes_unconsumed = 0;
+  }
+
+  *buff = NULL;
+  if (rar->entry_eof || rar->offset_seek >= rar->unp_size) {
+    *size = 0;
+    *offset = rar->offset;
+    if (*offset < rar->unp_size)
+      *offset = rar->unp_size;
+    return (ARCHIVE_EOF);
+  }
+
+  switch (rar->compression_method)
+  {
+  case COMPRESS_METHOD_STORE:
+    ret = read_data_stored(a, buff, size, offset);
+    break;
+
+  case COMPRESS_METHOD_FASTEST:
+  case COMPRESS_METHOD_FAST:
+  case COMPRESS_METHOD_NORMAL:
+  case COMPRESS_METHOD_GOOD:
+  case COMPRESS_METHOD_BEST:
+    ret = read_data_compressed(a, buff, size, offset);
+    if (ret != ARCHIVE_OK && ret != ARCHIVE_WARN) {
+      __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+      rar->start_new_table = 1;
+      rar->ppmd_valid = 0;
+    }
+    break;
+
+  default:
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Unsupported compression method for RAR file.");
+    ret = ARCHIVE_FATAL;
+    break;
+  }
+  return (ret);
+}
+
+static int
+archive_read_format_rar_read_data_skip(struct archive_read *a)
+{
+  struct rar *rar;
+  int64_t bytes_skipped;
+  int ret;
+
+  rar = (struct rar *)(a->format->data);
+
+  if (rar->bytes_unconsumed > 0) {
+      /* Consume as much as the decompressor actually used. */
+      __archive_read_consume(a, rar->bytes_unconsumed);
+      rar->bytes_unconsumed = 0;
+  }
+
+  if (rar->bytes_remaining > 0) {
+    bytes_skipped = __archive_read_consume(a, rar->bytes_remaining);
+    if (bytes_skipped < 0)
+      return (ARCHIVE_FATAL);
+  }
+
+  /* Compressed data to skip must be read from each header in a multivolume
+   * archive.
+   */
+  if (rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER)
+  {
+    ret = archive_read_format_rar_read_header(a, a->entry);
+    if (ret == (ARCHIVE_EOF))
+      ret = archive_read_format_rar_read_header(a, a->entry);
+    if (ret != (ARCHIVE_OK))
+      return ret;
+    return archive_read_format_rar_read_data_skip(a);
+  }
+
+  return (ARCHIVE_OK);
+}
+
+static int64_t
+archive_read_format_rar_seek_data(struct archive_read *a, int64_t offset,
+    int whence)
+{
+  int64_t client_offset, ret;
+  unsigned int i;
+  struct rar *rar = (struct rar *)(a->format->data);
+
+  if (rar->compression_method == COMPRESS_METHOD_STORE)
+  {
+    /* Modify the offset for use with SEEK_SET */
+    switch (whence)
+    {
+      case SEEK_CUR:
+        client_offset = rar->offset_seek;
+        break;
+      case SEEK_END:
+        client_offset = rar->unp_size;
+        break;
+      case SEEK_SET:
+      default:
+        client_offset = 0;
+    }
+    client_offset += offset;
+    if (client_offset < 0)
+    {
+      /* Can't seek past beginning of data block */
+      return -1;
+    }
+    else if (client_offset > rar->unp_size)
+    {
+      /*
+       * Set the returned offset but only seek to the end of
+       * the data block.
+       */
+      rar->offset_seek = client_offset;
+      client_offset = rar->unp_size;
+    }
+
+    client_offset += rar->dbo[0].start_offset;
+    i = 0;
+    while (i < rar->cursor)
+    {
+      i++;
+      client_offset += rar->dbo[i].start_offset - rar->dbo[i-1].end_offset;
+    }
+    if (rar->main_flags & MHD_VOLUME)
+    {
+      /* Find the appropriate offset among the multivolume archive */
+      while (1)
+      {
+        if (client_offset < rar->dbo[rar->cursor].start_offset &&
+          rar->file_flags & FHD_SPLIT_BEFORE)
+        {
+          /* Search backwards for the correct data block */
+          if (rar->cursor == 0)
+          {
+            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+              "Attempt to seek past beginning of RAR data block");
+            return (ARCHIVE_FAILED);
+          }
+          rar->cursor--;
+          client_offset -= rar->dbo[rar->cursor+1].start_offset -
+            rar->dbo[rar->cursor].end_offset;
+          if (client_offset < rar->dbo[rar->cursor].start_offset)
+            continue;
+          ret = __archive_read_seek(a, rar->dbo[rar->cursor].start_offset -
+            rar->dbo[rar->cursor].header_size, SEEK_SET);
+          if (ret < (ARCHIVE_OK))
+            return ret;
+          ret = archive_read_format_rar_read_header(a, a->entry);
+          if (ret != (ARCHIVE_OK))
+          {
+            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+              "Error during seek of RAR file");
+            return (ARCHIVE_FAILED);
+          }
+          rar->cursor--;
+          break;
+        }
+        else if (client_offset > rar->dbo[rar->cursor].end_offset &&
+          rar->file_flags & FHD_SPLIT_AFTER)
+        {
+          /* Search forward for the correct data block */
+          rar->cursor++;
+          if (rar->cursor < rar->nodes &&
+            client_offset > rar->dbo[rar->cursor].end_offset)
+          {
+            client_offset += rar->dbo[rar->cursor].start_offset -
+              rar->dbo[rar->cursor-1].end_offset;
+            continue;
+          }
+          rar->cursor--;
+          ret = __archive_read_seek(a, rar->dbo[rar->cursor].end_offset,
+                                    SEEK_SET);
+          if (ret < (ARCHIVE_OK))
+            return ret;
+          ret = archive_read_format_rar_read_header(a, a->entry);
+          if (ret == (ARCHIVE_EOF))
+          {
+            rar->has_endarc_header = 1;
+            ret = archive_read_format_rar_read_header(a, a->entry);
+          }
+          if (ret != (ARCHIVE_OK))
+          {
+            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+              "Error during seek of RAR file");
+            return (ARCHIVE_FAILED);
+          }
+          client_offset += rar->dbo[rar->cursor].start_offset -
+            rar->dbo[rar->cursor-1].end_offset;
+          continue;
+        }
+        break;
+      }
+    }
+
+    ret = __archive_read_seek(a, client_offset, SEEK_SET);
+    if (ret < (ARCHIVE_OK))
+      return ret;
+    rar->bytes_remaining = rar->dbo[rar->cursor].end_offset - ret;
+    i = rar->cursor;
+    while (i > 0)
+    {
+      i--;
+      ret -= rar->dbo[i+1].start_offset - rar->dbo[i].end_offset;
+    }
+    ret -= rar->dbo[0].start_offset;
+
+    /* Always restart reading the file after a seek */
+    __archive_reset_read_data(&a->archive);
+
+    rar->bytes_unconsumed = 0;
+    rar->offset = 0;
+
+    /*
+     * If a seek past the end of file was requested, return the requested
+     * offset.
+     */
+    if (ret == rar->unp_size && rar->offset_seek > rar->unp_size)
+      return rar->offset_seek;
+
+    /* Return the new offset */
+    rar->offset_seek = ret;
+    return rar->offset_seek;
+  }
+  else
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+      "Seeking of compressed RAR files is unsupported");
+  }
+  return (ARCHIVE_FAILED);
+}
+
+static int
+archive_read_format_rar_cleanup(struct archive_read *a)
+{
+  struct rar *rar;
+
+  rar = (struct rar *)(a->format->data);
+  free_codes(a);
+  free(rar->filename);
+  free(rar->filename_save);
+  free(rar->dbo);
+  free(rar->unp_buffer);
+  free(rar->lzss.window);
+  __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+  free(rar);
+  (a->format->data) = NULL;
+  return (ARCHIVE_OK);
+}
+
+static int
+read_header(struct archive_read *a, struct archive_entry *entry,
+            char head_type)
+{
+  const void *h;
+  const char *p, *endp;
+  struct rar *rar;
+  struct rar_header rar_header;
+  struct rar_file_header file_header;
+  int64_t header_size;
+  unsigned filename_size, end;
+  char *filename;
+  char *strp;
+  char packed_size[8];
+  char unp_size[8];
+  int ttime;
+  struct archive_string_conv *sconv, *fn_sconv;
+  unsigned long crc32_val;
+  int ret = (ARCHIVE_OK), ret2;
+
+  rar = (struct rar *)(a->format->data);
+
+  /* Setup a string conversion object for non-rar-unicode filenames. */
+  sconv = rar->opt_sconv;
+  if (sconv == NULL) {
+    if (!rar->init_default_conversion) {
+      rar->sconv_default =
+          archive_string_default_conversion_for_read(
+            &(a->archive));
+      rar->init_default_conversion = 1;
+    }
+    sconv = rar->sconv_default;
+  }
+
+
+  if ((h = __archive_read_ahead(a, 7, NULL)) == NULL)
+    return (ARCHIVE_FATAL);
+  p = h;
+  memcpy(&rar_header, p, sizeof(rar_header));
+  rar->file_flags = archive_le16dec(rar_header.flags);
+  header_size = archive_le16dec(rar_header.size);
+  if (header_size < (int64_t)sizeof(file_header) + 7) {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+      "Invalid header size");
+    return (ARCHIVE_FATAL);
+  }
+  crc32_val = crc32(0, (const unsigned char *)p + 2, 7 - 2);
+  __archive_read_consume(a, 7);
+
+  if (!(rar->file_flags & FHD_SOLID))
+  {
+    rar->compression_method = 0;
+    rar->packed_size = 0;
+    rar->unp_size = 0;
+    rar->mtime = 0;
+    rar->ctime = 0;
+    rar->atime = 0;
+    rar->arctime = 0;
+    rar->mode = 0;
+    memset(&rar->salt, 0, sizeof(rar->salt));
+    rar->atime = 0;
+    rar->ansec = 0;
+    rar->ctime = 0;
+    rar->cnsec = 0;
+    rar->mtime = 0;
+    rar->mnsec = 0;
+    rar->arctime = 0;
+    rar->arcnsec = 0;
+  }
+  else
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "RAR solid archive support unavailable.");
+    return (ARCHIVE_FATAL);
+  }
+
+  if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
+    return (ARCHIVE_FATAL);
+
+  /* File Header CRC check. */
+  crc32_val = crc32(crc32_val, h, (unsigned)(header_size - 7));
+  if ((crc32_val & 0xffff) != archive_le16dec(rar_header.crc)) {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+      "Header CRC error");
+    return (ARCHIVE_FATAL);
+  }
+  /* If no CRC error, Go on parsing File Header. */
+  p = h;
+  endp = p + header_size - 7;
+  memcpy(&file_header, p, sizeof(file_header));
+  p += sizeof(file_header);
+
+  rar->compression_method = file_header.method;
+
+  ttime = archive_le32dec(file_header.file_time);
+  rar->mtime = get_time(ttime);
+
+  rar->file_crc = archive_le32dec(file_header.file_crc);
+
+  if (rar->file_flags & FHD_PASSWORD)
+  {
+	archive_entry_set_is_data_encrypted(entry, 1);
+	rar->has_encrypted_entries = 1;
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "RAR encryption support unavailable.");
+    /* Since it is only the data part itself that is encrypted we can at least
+       extract information about the currently processed entry and don't need
+       to return ARCHIVE_FATAL here. */
+    /*return (ARCHIVE_FATAL);*/
+  }
+
+  if (rar->file_flags & FHD_LARGE)
+  {
+    memcpy(packed_size, file_header.pack_size, 4);
+    memcpy(packed_size + 4, p, 4); /* High pack size */
+    p += 4;
+    memcpy(unp_size, file_header.unp_size, 4);
+    memcpy(unp_size + 4, p, 4); /* High unpack size */
+    p += 4;
+    rar->packed_size = archive_le64dec(&packed_size);
+    rar->unp_size = archive_le64dec(&unp_size);
+  }
+  else
+  {
+    rar->packed_size = archive_le32dec(file_header.pack_size);
+    rar->unp_size = archive_le32dec(file_header.unp_size);
+  }
+
+  if (rar->packed_size < 0 || rar->unp_size < 0)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Invalid sizes specified.");
+    return (ARCHIVE_FATAL);
+  }
+
+  rar->bytes_remaining = rar->packed_size;
+
+  /* TODO: RARv3 subblocks contain comments. For now the complete block is
+   * consumed at the end.
+   */
+  if (head_type == NEWSUB_HEAD) {
+    size_t distance = p - (const char *)h;
+    header_size += rar->packed_size;
+    /* Make sure we have the extended data. */
+    if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL)
+        return (ARCHIVE_FATAL);
+    p = h;
+    endp = p + header_size - 7;
+    p += distance;
+  }
+
+  filename_size = archive_le16dec(file_header.name_size);
+  if (p + filename_size > endp) {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+      "Invalid filename size");
+    return (ARCHIVE_FATAL);
+  }
+  if (rar->filename_allocated < filename_size * 2 + 2) {
+    char *newptr;
+    size_t newsize = filename_size * 2 + 2;
+    newptr = realloc(rar->filename, newsize);
+    if (newptr == NULL) {
+      archive_set_error(&a->archive, ENOMEM,
+                        "Couldn't allocate memory.");
+      return (ARCHIVE_FATAL);
+    }
+    rar->filename = newptr;
+    rar->filename_allocated = newsize;
+  }
+  filename = rar->filename;
+  memcpy(filename, p, filename_size);
+  filename[filename_size] = '\0';
+  if (rar->file_flags & FHD_UNICODE)
+  {
+    if (filename_size != strlen(filename))
+    {
+      unsigned char highbyte, flagbits, flagbyte;
+      unsigned fn_end, offset;
+
+      end = filename_size;
+      fn_end = filename_size * 2;
+      filename_size = 0;
+      offset = (unsigned)strlen(filename) + 1;
+      highbyte = *(p + offset++);
+      flagbits = 0;
+      flagbyte = 0;
+      while (offset < end && filename_size < fn_end)
+      {
+        if (!flagbits)
+        {
+          flagbyte = *(p + offset++);
+          flagbits = 8;
+        }
+
+        flagbits -= 2;
+        switch((flagbyte >> flagbits) & 3)
+        {
+          case 0:
+            filename[filename_size++] = '\0';
+            filename[filename_size++] = *(p + offset++);
+            break;
+          case 1:
+            filename[filename_size++] = highbyte;
+            filename[filename_size++] = *(p + offset++);
+            break;
+          case 2:
+            filename[filename_size++] = *(p + offset + 1);
+            filename[filename_size++] = *(p + offset);
+            offset += 2;
+            break;
+          case 3:
+          {
+            char extra, high;
+            uint8_t length = *(p + offset++);
+
+            if (length & 0x80) {
+              extra = *(p + offset++);
+              high = (char)highbyte;
+            } else
+              extra = high = 0;
+            length = (length & 0x7f) + 2;
+            while (length && filename_size < fn_end) {
+              unsigned cp = filename_size >> 1;
+              filename[filename_size++] = high;
+              filename[filename_size++] = p[cp] + extra;
+              length--;
+            }
+          }
+          break;
+        }
+      }
+      if (filename_size > fn_end) {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+          "Invalid filename");
+        return (ARCHIVE_FATAL);
+      }
+      filename[filename_size++] = '\0';
+      /*
+       * Do not increment filename_size here as the computations below
+       * add the space for the terminating NUL explicitly.
+       */
+      filename[filename_size] = '\0';
+
+      /* Decoded unicode form is UTF-16BE, so we have to update a string
+       * conversion object for it. */
+      if (rar->sconv_utf16be == NULL) {
+        rar->sconv_utf16be = archive_string_conversion_from_charset(
+           &a->archive, "UTF-16BE", 1);
+        if (rar->sconv_utf16be == NULL)
+          return (ARCHIVE_FATAL);
+      }
+      fn_sconv = rar->sconv_utf16be;
+
+      strp = filename;
+      while (memcmp(strp, "\x00\x00", 2))
+      {
+        if (!memcmp(strp, "\x00\\", 2))
+          *(strp + 1) = '/';
+        strp += 2;
+      }
+      p += offset;
+    } else {
+      /*
+       * If FHD_UNICODE is set but no unicode data, this file name form
+       * is UTF-8, so we have to update a string conversion object for
+       * it accordingly.
+       */
+      if (rar->sconv_utf8 == NULL) {
+        rar->sconv_utf8 = archive_string_conversion_from_charset(
+           &a->archive, "UTF-8", 1);
+        if (rar->sconv_utf8 == NULL)
+          return (ARCHIVE_FATAL);
+      }
+      fn_sconv = rar->sconv_utf8;
+      while ((strp = strchr(filename, '\\')) != NULL)
+        *strp = '/';
+      p += filename_size;
+    }
+  }
+  else
+  {
+    fn_sconv = sconv;
+    while ((strp = strchr(filename, '\\')) != NULL)
+      *strp = '/';
+    p += filename_size;
+  }
+
+  /* Split file in multivolume RAR. No more need to process header. */
+  if (rar->filename_save &&
+    filename_size == rar->filename_save_size &&
+    !memcmp(rar->filename, rar->filename_save, filename_size + 1))
+  {
+    __archive_read_consume(a, header_size - 7);
+    rar->cursor++;
+    if (rar->cursor >= rar->nodes)
+    {
+      rar->nodes++;
+      if ((rar->dbo =
+        realloc(rar->dbo, sizeof(*rar->dbo) * rar->nodes)) == NULL)
+      {
+        archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+        return (ARCHIVE_FATAL);
+      }
+      rar->dbo[rar->cursor].header_size = header_size;
+      rar->dbo[rar->cursor].start_offset = -1;
+      rar->dbo[rar->cursor].end_offset = -1;
+    }
+    if (rar->dbo[rar->cursor].start_offset < 0)
+    {
+      rar->dbo[rar->cursor].start_offset = a->filter->position;
+      rar->dbo[rar->cursor].end_offset = rar->dbo[rar->cursor].start_offset +
+        rar->packed_size;
+    }
+    return ret;
+  }
+  else if (rar->filename_must_match)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+      "Mismatch of file parts split across multi-volume archive");
+    return (ARCHIVE_FATAL);
+  }
+
+  rar->filename_save = (char*)realloc(rar->filename_save,
+                                      filename_size + 1);
+  memcpy(rar->filename_save, rar->filename, filename_size + 1);
+  rar->filename_save_size = filename_size;
+
+  /* Set info for seeking */
+  free(rar->dbo);
+  if ((rar->dbo = calloc(1, sizeof(*rar->dbo))) == NULL)
+  {
+    archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+    return (ARCHIVE_FATAL);
+  }
+  rar->dbo[0].header_size = header_size;
+  rar->dbo[0].start_offset = -1;
+  rar->dbo[0].end_offset = -1;
+  rar->cursor = 0;
+  rar->nodes = 1;
+
+  if (rar->file_flags & FHD_SALT)
+  {
+    if (p + 8 > endp) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+        "Invalid header size");
+      return (ARCHIVE_FATAL);
+    }
+    memcpy(rar->salt, p, 8);
+    p += 8;
+  }
+
+  if (rar->file_flags & FHD_EXTTIME) {
+    if (read_exttime(p, rar, endp) < 0) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+        "Invalid header size");
+      return (ARCHIVE_FATAL);
+    }
+  }
+
+  __archive_read_consume(a, header_size - 7);
+  rar->dbo[0].start_offset = a->filter->position;
+  rar->dbo[0].end_offset = rar->dbo[0].start_offset + rar->packed_size;
+
+  switch(file_header.host_os)
+  {
+  case OS_MSDOS:
+  case OS_OS2:
+  case OS_WIN32:
+    rar->mode = archive_le32dec(file_header.file_attr);
+    if (rar->mode & FILE_ATTRIBUTE_DIRECTORY)
+      rar->mode = AE_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+    else
+      rar->mode = AE_IFREG;
+    rar->mode |= S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+    break;
+
+  case OS_UNIX:
+  case OS_MAC_OS:
+  case OS_BEOS:
+    rar->mode = archive_le32dec(file_header.file_attr);
+    break;
+
+  default:
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Unknown file attributes from RAR file's host OS");
+    return (ARCHIVE_FATAL);
+  }
+
+  rar->bytes_uncopied = rar->bytes_unconsumed = 0;
+  rar->lzss.position = rar->offset = 0;
+  rar->offset_seek = 0;
+  rar->dictionary_size = 0;
+  rar->offset_outgoing = 0;
+  rar->br.cache_avail = 0;
+  rar->br.avail_in = 0;
+  rar->crc_calculated = 0;
+  rar->entry_eof = 0;
+  rar->valid = 1;
+  rar->is_ppmd_block = 0;
+  rar->start_new_table = 1;
+  free(rar->unp_buffer);
+  rar->unp_buffer = NULL;
+  rar->unp_offset = 0;
+  rar->unp_buffer_size = UNP_BUFFER_SIZE;
+  memset(rar->lengthtable, 0, sizeof(rar->lengthtable));
+  __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+  rar->ppmd_valid = rar->ppmd_eod = 0;
+
+  /* Don't set any archive entries for non-file header types */
+  if (head_type == NEWSUB_HEAD)
+    return ret;
+
+  archive_entry_set_mtime(entry, rar->mtime, rar->mnsec);
+  archive_entry_set_ctime(entry, rar->ctime, rar->cnsec);
+  archive_entry_set_atime(entry, rar->atime, rar->ansec);
+  archive_entry_set_size(entry, rar->unp_size);
+  archive_entry_set_mode(entry, rar->mode);
+
+  if (archive_entry_copy_pathname_l(entry, filename, filename_size, fn_sconv))
+  {
+    if (errno == ENOMEM)
+    {
+      archive_set_error(&a->archive, ENOMEM,
+                        "Can't allocate memory for Pathname");
+      return (ARCHIVE_FATAL);
+    }
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Pathname cannot be converted from %s to current locale.",
+                      archive_string_conversion_charset_name(fn_sconv));
+    ret = (ARCHIVE_WARN);
+  }
+
+  if (((rar->mode) & AE_IFMT) == AE_IFLNK)
+  {
+    /* Make sure a symbolic-link file does not have its body. */
+    rar->bytes_remaining = 0;
+    archive_entry_set_size(entry, 0);
+
+    /* Read a symbolic-link name. */
+    if ((ret2 = read_symlink_stored(a, entry, sconv)) < (ARCHIVE_WARN))
+      return ret2;
+    if (ret > ret2)
+      ret = ret2;
+  }
+
+  if (rar->bytes_remaining == 0)
+    rar->entry_eof = 1;
+
+  return ret;
+}
+
+static time_t
+get_time(int ttime)
+{
+  struct tm tm;
+  tm.tm_sec = 2 * (ttime & 0x1f);
+  tm.tm_min = (ttime >> 5) & 0x3f;
+  tm.tm_hour = (ttime >> 11) & 0x1f;
+  tm.tm_mday = (ttime >> 16) & 0x1f;
+  tm.tm_mon = ((ttime >> 21) & 0x0f) - 1;
+  tm.tm_year = ((ttime >> 25) & 0x7f) + 80;
+  tm.tm_isdst = -1;
+  return mktime(&tm);
+}
+
+static int
+read_exttime(const char *p, struct rar *rar, const char *endp)
+{
+  unsigned rmode, flags, rem, j, count;
+  int ttime, i;
+  struct tm *tm;
+  time_t t;
+  long nsec;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+  struct tm tmbuf;
+#endif
+#if defined(HAVE__LOCALTIME64_S)
+  errno_t terr;
+  __time64_t tmptime;
+#endif
+
+  if (p + 2 > endp)
+    return (-1);
+  flags = archive_le16dec(p);
+  p += 2;
+
+  for (i = 3; i >= 0; i--)
+  {
+    t = 0;
+    if (i == 3)
+      t = rar->mtime;
+    rmode = flags >> i * 4;
+    if (rmode & 8)
+    {
+      if (!t)
+      {
+        if (p + 4 > endp)
+          return (-1);
+        ttime = archive_le32dec(p);
+        t = get_time(ttime);
+        p += 4;
+      }
+      rem = 0;
+      count = rmode & 3;
+      if (p + count > endp)
+        return (-1);
+      for (j = 0; j < count; j++)
+      {
+        rem = (((unsigned)(unsigned char)*p) << 16) | (rem >> 8);
+        p++;
+      }
+#if defined(HAVE_LOCALTIME_R)
+      tm = localtime_r(&t, &tmbuf);
+#elif defined(HAVE__LOCALTIME64_S)
+      tmptime = t;
+      terr = _localtime64_s(&tmbuf, &tmptime);
+      if (terr)
+        tm = NULL;
+      else
+        tm = &tmbuf;
+#else
+      tm = localtime(&t);
+#endif
+      nsec = tm->tm_sec + rem / NS_UNIT;
+      if (rmode & 4)
+      {
+        tm->tm_sec++;
+        t = mktime(tm);
+      }
+      if (i == 3)
+      {
+        rar->mtime = t;
+        rar->mnsec = nsec;
+      }
+      else if (i == 2)
+      {
+        rar->ctime = t;
+        rar->cnsec = nsec;
+      }
+      else if (i == 1)
+      {
+        rar->atime = t;
+        rar->ansec = nsec;
+      }
+      else
+      {
+        rar->arctime = t;
+        rar->arcnsec = nsec;
+      }
+    }
+  }
+  return (0);
+}
+
+static int
+read_symlink_stored(struct archive_read *a, struct archive_entry *entry,
+                    struct archive_string_conv *sconv)
+{
+  const void *h;
+  const char *p;
+  struct rar *rar;
+  int ret = (ARCHIVE_OK);
+
+  rar = (struct rar *)(a->format->data);
+  if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL)
+    return (ARCHIVE_FATAL);
+  p = h;
+
+  if (archive_entry_copy_symlink_l(entry,
+      p, (size_t)rar->packed_size, sconv))
+  {
+    if (errno == ENOMEM)
+    {
+      archive_set_error(&a->archive, ENOMEM,
+                        "Can't allocate memory for link");
+      return (ARCHIVE_FATAL);
+    }
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "link cannot be converted from %s to current locale.",
+                      archive_string_conversion_charset_name(sconv));
+    ret = (ARCHIVE_WARN);
+  }
+  __archive_read_consume(a, rar->packed_size);
+  return ret;
+}
+
+static int
+read_data_stored(struct archive_read *a, const void **buff, size_t *size,
+                 int64_t *offset)
+{
+  struct rar *rar;
+  ssize_t bytes_avail;
+
+  rar = (struct rar *)(a->format->data);
+  if (rar->bytes_remaining == 0 &&
+    !(rar->main_flags & MHD_VOLUME && rar->file_flags & FHD_SPLIT_AFTER))
+  {
+    *buff = NULL;
+    *size = 0;
+    *offset = rar->offset;
+    if (rar->file_crc != rar->crc_calculated) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "File CRC error");
+      return (ARCHIVE_FATAL);
+    }
+    rar->entry_eof = 1;
+    return (ARCHIVE_EOF);
+  }
+
+  *buff = rar_read_ahead(a, 1, &bytes_avail);
+  if (bytes_avail <= 0)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Truncated RAR file data");
+    return (ARCHIVE_FATAL);
+  }
+
+  *size = bytes_avail;
+  *offset = rar->offset;
+  rar->offset += bytes_avail;
+  rar->offset_seek += bytes_avail;
+  rar->bytes_remaining -= bytes_avail;
+  rar->bytes_unconsumed = bytes_avail;
+  /* Calculate File CRC. */
+  rar->crc_calculated = crc32(rar->crc_calculated, *buff,
+    (unsigned)bytes_avail);
+  return (ARCHIVE_OK);
+}
+
+static int
+read_data_compressed(struct archive_read *a, const void **buff, size_t *size,
+               int64_t *offset)
+{
+  struct rar *rar;
+  int64_t start, end, actualend;
+  size_t bs;
+  int ret = (ARCHIVE_OK), sym, code, lzss_offset, length, i;
+
+  rar = (struct rar *)(a->format->data);
+
+  do {
+    if (!rar->valid)
+      return (ARCHIVE_FATAL);
+    if (rar->ppmd_eod ||
+       (rar->dictionary_size && rar->offset >= rar->unp_size))
+    {
+      if (rar->unp_offset > 0) {
+        /*
+         * We have unprocessed extracted data. write it out.
+         */
+        *buff = rar->unp_buffer;
+        *size = rar->unp_offset;
+        *offset = rar->offset_outgoing;
+        rar->offset_outgoing += *size;
+        /* Calculate File CRC. */
+        rar->crc_calculated = crc32(rar->crc_calculated, *buff,
+          (unsigned)*size);
+        rar->unp_offset = 0;
+        return (ARCHIVE_OK);
+      }
+      *buff = NULL;
+      *size = 0;
+      *offset = rar->offset;
+      if (rar->file_crc != rar->crc_calculated) {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "File CRC error");
+        return (ARCHIVE_FATAL);
+      }
+      rar->entry_eof = 1;
+      return (ARCHIVE_EOF);
+    }
+
+    if (!rar->is_ppmd_block && rar->dictionary_size && rar->bytes_uncopied > 0)
+    {
+      if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset))
+        bs = rar->unp_buffer_size - rar->unp_offset;
+      else
+        bs = (size_t)rar->bytes_uncopied;
+      ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs);
+      if (ret != ARCHIVE_OK)
+        return (ret);
+      rar->offset += bs;
+      rar->bytes_uncopied -= bs;
+      if (*buff != NULL) {
+        rar->unp_offset = 0;
+        *size = rar->unp_buffer_size;
+        *offset = rar->offset_outgoing;
+        rar->offset_outgoing += *size;
+        /* Calculate File CRC. */
+        rar->crc_calculated = crc32(rar->crc_calculated, *buff,
+          (unsigned)*size);
+        return (ret);
+      }
+      continue;
+    }
+
+    if (!rar->br.next_in &&
+      (ret = rar_br_preparation(a, &(rar->br))) < ARCHIVE_WARN)
+      return (ret);
+    if (rar->start_new_table && ((ret = parse_codes(a)) < (ARCHIVE_WARN)))
+      return (ret);
+
+    if (rar->is_ppmd_block)
+    {
+      if ((sym = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+        &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+      {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Invalid symbol");
+        return (ARCHIVE_FATAL);
+      }
+      if(sym != rar->ppmd_escape)
+      {
+        lzss_emit_literal(rar, sym);
+        rar->bytes_uncopied++;
+      }
+      else
+      {
+        if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+          &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+        {
+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                            "Invalid symbol");
+          return (ARCHIVE_FATAL);
+        }
+
+        switch(code)
+        {
+          case 0:
+            rar->start_new_table = 1;
+            return read_data_compressed(a, buff, size, offset);
+
+          case 2:
+            rar->ppmd_eod = 1;/* End Of ppmd Data. */
+            continue;
+
+          case 3:
+            archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                              "Parsing filters is unsupported.");
+            return (ARCHIVE_FAILED);
+
+          case 4:
+            lzss_offset = 0;
+            for (i = 2; i >= 0; i--)
+            {
+              if ((code = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+                &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+              {
+                archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                                  "Invalid symbol");
+                return (ARCHIVE_FATAL);
+              }
+              lzss_offset |= code << (i * 8);
+            }
+            if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+              &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+            {
+              archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                                "Invalid symbol");
+              return (ARCHIVE_FATAL);
+            }
+            lzss_emit_match(rar, lzss_offset + 2, length + 32);
+            rar->bytes_uncopied += length + 32;
+            break;
+
+          case 5:
+            if ((length = __archive_ppmd7_functions.Ppmd7_DecodeSymbol(
+              &rar->ppmd7_context, &rar->range_dec.p)) < 0)
+            {
+              archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                                "Invalid symbol");
+              return (ARCHIVE_FATAL);
+            }
+            lzss_emit_match(rar, 1, length + 4);
+            rar->bytes_uncopied += length + 4;
+            break;
+
+         default:
+           lzss_emit_literal(rar, sym);
+           rar->bytes_uncopied++;
+        }
+      }
+    }
+    else
+    {
+      start = rar->offset;
+      end = start + rar->dictionary_size;
+      rar->filterstart = INT64_MAX;
+
+      if ((actualend = expand(a, end)) < 0)
+        return ((int)actualend);
+
+      rar->bytes_uncopied = actualend - start;
+      if (rar->bytes_uncopied == 0) {
+          /* Broken RAR files cause this case.
+          * NOTE: If this case were possible on a normal RAR file
+          * we would find out where it was actually bad and
+          * what we would do to solve it. */
+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                            "Internal error extracting RAR file");
+          return (ARCHIVE_FATAL);
+      }
+    }
+    if (rar->bytes_uncopied > (rar->unp_buffer_size - rar->unp_offset))
+      bs = rar->unp_buffer_size - rar->unp_offset;
+    else
+      bs = (size_t)rar->bytes_uncopied;
+    ret = copy_from_lzss_window(a, buff, rar->offset, (int)bs);
+    if (ret != ARCHIVE_OK)
+      return (ret);
+    rar->offset += bs;
+    rar->bytes_uncopied -= bs;
+    /*
+     * If *buff is NULL, it means unp_buffer is not full.
+     * So we have to continue extracting a RAR file.
+     */
+  } while (*buff == NULL);
+
+  rar->unp_offset = 0;
+  *size = rar->unp_buffer_size;
+  *offset = rar->offset_outgoing;
+  rar->offset_outgoing += *size;
+  /* Calculate File CRC. */
+  rar->crc_calculated = crc32(rar->crc_calculated, *buff, (unsigned)*size);
+  return ret;
+}
+
+static int
+parse_codes(struct archive_read *a)
+{
+  int i, j, val, n, r;
+  unsigned char bitlengths[MAX_SYMBOLS], zerocount, ppmd_flags;
+  unsigned int maxorder;
+  struct huffman_code precode;
+  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar_br *br = &(rar->br);
+
+  free_codes(a);
+
+  /* Skip to the next byte */
+  rar_br_consume_unalined_bits(br);
+
+  /* PPMd block flag */
+  if (!rar_br_read_ahead(a, br, 1))
+    goto truncated_data;
+  if ((rar->is_ppmd_block = rar_br_bits(br, 1)) != 0)
+  {
+    rar_br_consume(br, 1);
+    if (!rar_br_read_ahead(a, br, 7))
+      goto truncated_data;
+    ppmd_flags = rar_br_bits(br, 7);
+    rar_br_consume(br, 7);
+
+    /* Memory is allocated in MB */
+    if (ppmd_flags & 0x20)
+    {
+      if (!rar_br_read_ahead(a, br, 8))
+        goto truncated_data;
+      rar->dictionary_size = (rar_br_bits(br, 8) + 1) << 20;
+      rar_br_consume(br, 8);
+    }
+
+    if (ppmd_flags & 0x40)
+    {
+      if (!rar_br_read_ahead(a, br, 8))
+        goto truncated_data;
+      rar->ppmd_escape = rar->ppmd7_context.InitEsc = rar_br_bits(br, 8);
+      rar_br_consume(br, 8);
+    }
+    else
+      rar->ppmd_escape = 2;
+
+    if (ppmd_flags & 0x20)
+    {
+      maxorder = (ppmd_flags & 0x1F) + 1;
+      if(maxorder > 16)
+        maxorder = 16 + (maxorder - 16) * 3;
+
+      if (maxorder == 1)
+      {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Truncated RAR file data");
+        return (ARCHIVE_FATAL);
+      }
+
+      /* Make sure ppmd7_contest is freed before Ppmd7_Construct
+       * because reading a broken file cause this abnormal sequence. */
+      __archive_ppmd7_functions.Ppmd7_Free(&rar->ppmd7_context);
+
+      rar->bytein.a = a;
+      rar->bytein.Read = &ppmd_read;
+      __archive_ppmd7_functions.PpmdRAR_RangeDec_CreateVTable(&rar->range_dec);
+      rar->range_dec.Stream = &rar->bytein;
+      __archive_ppmd7_functions.Ppmd7_Construct(&rar->ppmd7_context);
+
+      if (rar->dictionary_size == 0) {
+	      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Invalid zero dictionary size");
+	      return (ARCHIVE_FATAL);
+      }
+
+      if (!__archive_ppmd7_functions.Ppmd7_Alloc(&rar->ppmd7_context,
+        rar->dictionary_size))
+      {
+        archive_set_error(&a->archive, ENOMEM,
+                          "Out of memory");
+        return (ARCHIVE_FATAL);
+      }
+      if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec))
+      {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Unable to initialize PPMd range decoder");
+        return (ARCHIVE_FATAL);
+      }
+      __archive_ppmd7_functions.Ppmd7_Init(&rar->ppmd7_context, maxorder);
+      rar->ppmd_valid = 1;
+    }
+    else
+    {
+      if (!rar->ppmd_valid) {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Invalid PPMd sequence");
+        return (ARCHIVE_FATAL);
+      }
+      if (!__archive_ppmd7_functions.PpmdRAR_RangeDec_Init(&rar->range_dec))
+      {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Unable to initialize PPMd range decoder");
+        return (ARCHIVE_FATAL);
+      }
+    }
+  }
+  else
+  {
+    rar_br_consume(br, 1);
+
+    /* Keep existing table flag */
+    if (!rar_br_read_ahead(a, br, 1))
+      goto truncated_data;
+    if (!rar_br_bits(br, 1))
+      memset(rar->lengthtable, 0, sizeof(rar->lengthtable));
+    rar_br_consume(br, 1);
+
+    memset(&bitlengths, 0, sizeof(bitlengths));
+    for (i = 0; i < MAX_SYMBOLS;)
+    {
+      if (!rar_br_read_ahead(a, br, 4))
+        goto truncated_data;
+      bitlengths[i++] = rar_br_bits(br, 4);
+      rar_br_consume(br, 4);
+      if (bitlengths[i-1] == 0xF)
+      {
+        if (!rar_br_read_ahead(a, br, 4))
+          goto truncated_data;
+        zerocount = rar_br_bits(br, 4);
+        rar_br_consume(br, 4);
+        if (zerocount)
+        {
+          i--;
+          for (j = 0; j < zerocount + 2 && i < MAX_SYMBOLS; j++)
+            bitlengths[i++] = 0;
+        }
+      }
+    }
+
+    memset(&precode, 0, sizeof(precode));
+    r = create_code(a, &precode, bitlengths, MAX_SYMBOLS, MAX_SYMBOL_LENGTH);
+    if (r != ARCHIVE_OK) {
+      free(precode.tree);
+      free(precode.table);
+      return (r);
+    }
+
+    for (i = 0; i < HUFFMAN_TABLE_SIZE;)
+    {
+      if ((val = read_next_symbol(a, &precode)) < 0) {
+        free(precode.tree);
+        free(precode.table);
+        return (ARCHIVE_FATAL);
+      }
+      if (val < 16)
+      {
+        rar->lengthtable[i] = (rar->lengthtable[i] + val) & 0xF;
+        i++;
+      }
+      else if (val < 18)
+      {
+        if (i == 0)
+        {
+          free(precode.tree);
+          free(precode.table);
+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                            "Internal error extracting RAR file.");
+          return (ARCHIVE_FATAL);
+        }
+
+        if(val == 16) {
+          if (!rar_br_read_ahead(a, br, 3)) {
+            free(precode.tree);
+            free(precode.table);
+            goto truncated_data;
+          }
+          n = rar_br_bits(br, 3) + 3;
+          rar_br_consume(br, 3);
+        } else {
+          if (!rar_br_read_ahead(a, br, 7)) {
+            free(precode.tree);
+            free(precode.table);
+            goto truncated_data;
+          }
+          n = rar_br_bits(br, 7) + 11;
+          rar_br_consume(br, 7);
+        }
+
+        for (j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++)
+        {
+          rar->lengthtable[i] = rar->lengthtable[i-1];
+          i++;
+        }
+      }
+      else
+      {
+        if(val == 18) {
+          if (!rar_br_read_ahead(a, br, 3)) {
+            free(precode.tree);
+            free(precode.table);
+            goto truncated_data;
+          }
+          n = rar_br_bits(br, 3) + 3;
+          rar_br_consume(br, 3);
+        } else {
+          if (!rar_br_read_ahead(a, br, 7)) {
+            free(precode.tree);
+            free(precode.table);
+            goto truncated_data;
+          }
+          n = rar_br_bits(br, 7) + 11;
+          rar_br_consume(br, 7);
+        }
+
+        for(j = 0; j < n && i < HUFFMAN_TABLE_SIZE; j++)
+          rar->lengthtable[i++] = 0;
+      }
+    }
+    free(precode.tree);
+    free(precode.table);
+
+    r = create_code(a, &rar->maincode, &rar->lengthtable[0], MAINCODE_SIZE,
+                MAX_SYMBOL_LENGTH);
+    if (r != ARCHIVE_OK)
+      return (r);
+    r = create_code(a, &rar->offsetcode, &rar->lengthtable[MAINCODE_SIZE],
+                OFFSETCODE_SIZE, MAX_SYMBOL_LENGTH);
+    if (r != ARCHIVE_OK)
+      return (r);
+    r = create_code(a, &rar->lowoffsetcode,
+                &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE],
+                LOWOFFSETCODE_SIZE, MAX_SYMBOL_LENGTH);
+    if (r != ARCHIVE_OK)
+      return (r);
+    r = create_code(a, &rar->lengthcode,
+                &rar->lengthtable[MAINCODE_SIZE + OFFSETCODE_SIZE +
+                LOWOFFSETCODE_SIZE], LENGTHCODE_SIZE, MAX_SYMBOL_LENGTH);
+    if (r != ARCHIVE_OK)
+      return (r);
+  }
+
+  if (!rar->dictionary_size || !rar->lzss.window)
+  {
+    /* Seems as though dictionary sizes are not used. Even so, minimize
+     * memory usage as much as possible.
+     */
+    void *new_window;
+    unsigned int new_size;
+
+    if (rar->unp_size >= DICTIONARY_MAX_SIZE)
+      new_size = DICTIONARY_MAX_SIZE;
+    else
+      new_size = rar_fls((unsigned int)rar->unp_size) << 1;
+    if (new_size == 0) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Zero window size is invalid.");
+      return (ARCHIVE_FATAL);
+    }
+    new_window = realloc(rar->lzss.window, new_size);
+    if (new_window == NULL) {
+      archive_set_error(&a->archive, ENOMEM,
+                        "Unable to allocate memory for uncompressed data.");
+      return (ARCHIVE_FATAL);
+    }
+    rar->lzss.window = (unsigned char *)new_window;
+    rar->dictionary_size = new_size;
+    memset(rar->lzss.window, 0, rar->dictionary_size);
+    rar->lzss.mask = rar->dictionary_size - 1;
+  }
+
+  rar->start_new_table = 0;
+  return (ARCHIVE_OK);
+truncated_data:
+  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                    "Truncated RAR file data");
+  rar->valid = 0;
+  return (ARCHIVE_FATAL);
+}
+
+static void
+free_codes(struct archive_read *a)
+{
+  struct rar *rar = (struct rar *)(a->format->data);
+  free(rar->maincode.tree);
+  free(rar->offsetcode.tree);
+  free(rar->lowoffsetcode.tree);
+  free(rar->lengthcode.tree);
+  free(rar->maincode.table);
+  free(rar->offsetcode.table);
+  free(rar->lowoffsetcode.table);
+  free(rar->lengthcode.table);
+  memset(&rar->maincode, 0, sizeof(rar->maincode));
+  memset(&rar->offsetcode, 0, sizeof(rar->offsetcode));
+  memset(&rar->lowoffsetcode, 0, sizeof(rar->lowoffsetcode));
+  memset(&rar->lengthcode, 0, sizeof(rar->lengthcode));
+}
+
+
+static int
+read_next_symbol(struct archive_read *a, struct huffman_code *code)
+{
+  unsigned char bit;
+  unsigned int bits;
+  int length, value, node;
+  struct rar *rar;
+  struct rar_br *br;
+
+  if (!code->table)
+  {
+    if (make_table(a, code) != (ARCHIVE_OK))
+      return -1;
+  }
+
+  rar = (struct rar *)(a->format->data);
+  br = &(rar->br);
+
+  /* Look ahead (peek) at bits */
+  if (!rar_br_read_ahead(a, br, code->tablesize)) {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Truncated RAR file data");
+    rar->valid = 0;
+    return -1;
+  }
+  bits = rar_br_bits(br, code->tablesize);
+
+  length = code->table[bits].length;
+  value = code->table[bits].value;
+
+  if (length < 0)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Invalid prefix code in bitstream");
+    return -1;
+  }
+
+  if (length <= code->tablesize)
+  {
+    /* Skip length bits */
+    rar_br_consume(br, length);
+    return value;
+  }
+
+  /* Skip tablesize bits */
+  rar_br_consume(br, code->tablesize);
+
+  node = value;
+  while (!(code->tree[node].branches[0] ==
+    code->tree[node].branches[1]))
+  {
+    if (!rar_br_read_ahead(a, br, 1)) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Truncated RAR file data");
+      rar->valid = 0;
+      return -1;
+    }
+    bit = rar_br_bits(br, 1);
+    rar_br_consume(br, 1);
+
+    if (code->tree[node].branches[bit] < 0)
+    {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Invalid prefix code in bitstream");
+      return -1;
+    }
+    node = code->tree[node].branches[bit];
+  }
+
+  return code->tree[node].branches[0];
+}
+
+static int
+create_code(struct archive_read *a, struct huffman_code *code,
+            unsigned char *lengths, int numsymbols, char maxlength)
+{
+  int i, j, codebits = 0, symbolsleft = numsymbols;
+
+  code->numentries = 0;
+  code->numallocatedentries = 0;
+  if (new_node(code) < 0) {
+    archive_set_error(&a->archive, ENOMEM,
+                      "Unable to allocate memory for node data.");
+    return (ARCHIVE_FATAL);
+  }
+  code->numentries = 1;
+  code->minlength = INT_MAX;
+  code->maxlength = INT_MIN;
+  codebits = 0;
+  for(i = 1; i <= maxlength; i++)
+  {
+    for(j = 0; j < numsymbols; j++)
+    {
+      if (lengths[j] != i) continue;
+      if (add_value(a, code, j, codebits, i) != ARCHIVE_OK)
+        return (ARCHIVE_FATAL);
+      codebits++;
+      if (--symbolsleft <= 0)
+        break;
+    }
+    if (symbolsleft <= 0)
+      break;
+    codebits <<= 1;
+  }
+  return (ARCHIVE_OK);
+}
+
+static int
+add_value(struct archive_read *a, struct huffman_code *code, int value,
+          int codebits, int length)
+{
+  int lastnode, bitpos, bit;
+  /* int repeatpos, repeatnode, nextnode; */
+
+  free(code->table);
+  code->table = NULL;
+
+  if(length > code->maxlength)
+    code->maxlength = length;
+  if(length < code->minlength)
+    code->minlength = length;
+
+  /*
+   * Dead code, repeatpos was is -1
+   *
+  repeatpos = -1;
+  if (repeatpos == 0 || (repeatpos >= 0
+    && (((codebits >> (repeatpos - 1)) & 3) == 0
+    || ((codebits >> (repeatpos - 1)) & 3) == 3)))
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Invalid repeat position");
+    return (ARCHIVE_FATAL);
+  }
+  */
+
+  lastnode = 0;
+  for (bitpos = length - 1; bitpos >= 0; bitpos--)
+  {
+    bit = (codebits >> bitpos) & 1;
+
+    /* Leaf node check */
+    if (code->tree[lastnode].branches[0] ==
+      code->tree[lastnode].branches[1])
+    {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Prefix found");
+      return (ARCHIVE_FATAL);
+    }
+
+    /*
+     * Dead code, repeatpos was -1, bitpos >=0
+     *
+    if (bitpos == repeatpos)
+    {
+      * Open branch check *
+      if (!(code->tree[lastnode].branches[bit] < 0))
+      {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Invalid repeating code");
+        return (ARCHIVE_FATAL);
+      }
+
+      if ((repeatnode = new_node(code)) < 0) {
+        archive_set_error(&a->archive, ENOMEM,
+                          "Unable to allocate memory for node data.");
+        return (ARCHIVE_FATAL);
+      }
+      if ((nextnode = new_node(code)) < 0) {
+        archive_set_error(&a->archive, ENOMEM,
+                          "Unable to allocate memory for node data.");
+        return (ARCHIVE_FATAL);
+      }
+
+      * Set branches *
+      code->tree[lastnode].branches[bit] = repeatnode;
+      code->tree[repeatnode].branches[bit] = repeatnode;
+      code->tree[repeatnode].branches[bit^1] = nextnode;
+      lastnode = nextnode;
+
+      bitpos++; * terminating bit already handled, skip it *
+    }
+    else
+    {
+    */
+      /* Open branch check */
+      if (code->tree[lastnode].branches[bit] < 0)
+      {
+        if (new_node(code) < 0) {
+          archive_set_error(&a->archive, ENOMEM,
+                            "Unable to allocate memory for node data.");
+          return (ARCHIVE_FATAL);
+        }
+        code->tree[lastnode].branches[bit] = code->numentries++;
+      }
+
+      /* set to branch */
+      lastnode = code->tree[lastnode].branches[bit];
+ /* } */
+  }
+
+  if (!(code->tree[lastnode].branches[0] == -1
+    && code->tree[lastnode].branches[1] == -2))
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Prefix found");
+    return (ARCHIVE_FATAL);
+  }
+
+  /* Set leaf value */
+  code->tree[lastnode].branches[0] = value;
+  code->tree[lastnode].branches[1] = value;
+
+  return (ARCHIVE_OK);
+}
+
+static int
+new_node(struct huffman_code *code)
+{
+  void *new_tree;
+  if (code->numallocatedentries == code->numentries) {
+    int new_num_entries = 256;
+    if (code->numentries > 0) {
+        new_num_entries = code->numentries * 2;
+    }
+    new_tree = realloc(code->tree, new_num_entries * sizeof(*code->tree));
+    if (new_tree == NULL)
+        return (-1);
+    code->tree = (struct huffman_tree_node *)new_tree;
+    code->numallocatedentries = new_num_entries;
+  }
+  code->tree[code->numentries].branches[0] = -1;
+  code->tree[code->numentries].branches[1] = -2;
+  return 1;
+}
+
+static int
+make_table(struct archive_read *a, struct huffman_code *code)
+{
+  if (code->maxlength < code->minlength || code->maxlength > 10)
+    code->tablesize = 10;
+  else
+    code->tablesize = code->maxlength;
+
+  code->table =
+    (struct huffman_table_entry *)calloc(1, sizeof(*code->table)
+    * ((size_t)1 << code->tablesize));
+
+  return make_table_recurse(a, code, 0, code->table, 0, code->tablesize);
+}
+
+static int
+make_table_recurse(struct archive_read *a, struct huffman_code *code, int node,
+                   struct huffman_table_entry *table, int depth,
+                   int maxdepth)
+{
+  int currtablesize, i, ret = (ARCHIVE_OK);
+
+  if (!code->tree)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Huffman tree was not created.");
+    return (ARCHIVE_FATAL);
+  }
+  if (node < 0 || node >= code->numentries)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Invalid location to Huffman tree specified.");
+    return (ARCHIVE_FATAL);
+  }
+
+  currtablesize = 1 << (maxdepth - depth);
+
+  if (code->tree[node].branches[0] ==
+    code->tree[node].branches[1])
+  {
+    for(i = 0; i < currtablesize; i++)
+    {
+      table[i].length = depth;
+      table[i].value = code->tree[node].branches[0];
+    }
+  }
+  /*
+   * Dead code, node >= 0
+   *
+  else if (node < 0)
+  {
+    for(i = 0; i < currtablesize; i++)
+      table[i].length = -1;
+  }
+   */
+  else
+  {
+    if(depth == maxdepth)
+    {
+      table[0].length = maxdepth + 1;
+      table[0].value = node;
+    }
+    else
+    {
+      ret |= make_table_recurse(a, code, code->tree[node].branches[0], table,
+                                depth + 1, maxdepth);
+      ret |= make_table_recurse(a, code, code->tree[node].branches[1],
+                         table + currtablesize / 2, depth + 1, maxdepth);
+    }
+  }
+  return ret;
+}
+
+static int64_t
+expand(struct archive_read *a, int64_t end)
+{
+  static const unsigned char lengthbases[] =
+    {   0,   1,   2,   3,   4,   5,   6,
+        7,   8,  10,  12,  14,  16,  20,
+       24,  28,  32,  40,  48,  56,  64,
+       80,  96, 112, 128, 160, 192, 224 };
+  static const unsigned char lengthbits[] =
+    { 0, 0, 0, 0, 0, 0, 0,
+      0, 1, 1, 1, 1, 2, 2,
+      2, 2, 3, 3, 3, 3, 4,
+      4, 4, 4, 5, 5, 5, 5 };
+  static const int lengthb_min = minimum(
+    (int)(sizeof(lengthbases)/sizeof(lengthbases[0])),
+    (int)(sizeof(lengthbits)/sizeof(lengthbits[0]))
+  );
+  static const unsigned int offsetbases[] =
+    {       0,       1,       2,       3,       4,       6,
+            8,      12,      16,      24,      32,      48,
+           64,      96,     128,     192,     256,     384,
+          512,     768,    1024,    1536,    2048,    3072,
+         4096,    6144,    8192,   12288,   16384,   24576,
+        32768,   49152,   65536,   98304,  131072,  196608,
+       262144,  327680,  393216,  458752,  524288,  589824,
+       655360,  720896,  786432,  851968,  917504,  983040,
+      1048576, 1310720, 1572864, 1835008, 2097152, 2359296,
+      2621440, 2883584, 3145728, 3407872, 3670016, 3932160 };
+  static const unsigned char offsetbits[] =
+    {  0,  0,  0,  0,  1,  1,  2,  2,  3,  3,  4,  4,
+       5,  5,  6,  6,  7,  7,  8,  8,  9,  9, 10, 10,
+      11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16,
+      16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+      18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 };
+  static const int offsetb_min = minimum(
+    (int)(sizeof(offsetbases)/sizeof(offsetbases[0])),
+    (int)(sizeof(offsetbits)/sizeof(offsetbits[0]))
+  );
+  static const unsigned char shortbases[] =
+    { 0, 4, 8, 16, 32, 64, 128, 192 };
+  static const unsigned char shortbits[] =
+    { 2, 2, 3, 4, 5, 6, 6, 6 };
+
+  int symbol, offs, len, offsindex, lensymbol, i, offssymbol, lowoffsetsymbol;
+  unsigned char newfile;
+  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar_br *br = &(rar->br);
+
+  if (rar->filterstart < end)
+    end = rar->filterstart;
+
+  while (1)
+  {
+    if (rar->output_last_match &&
+      lzss_position(&rar->lzss) + rar->lastlength <= end)
+    {
+      lzss_emit_match(rar, rar->lastoffset, rar->lastlength);
+      rar->output_last_match = 0;
+    }
+
+    if(rar->is_ppmd_block || rar->output_last_match ||
+      lzss_position(&rar->lzss) >= end)
+      return lzss_position(&rar->lzss);
+
+    if ((symbol = read_next_symbol(a, &rar->maincode)) < 0)
+      return (ARCHIVE_FATAL);
+    rar->output_last_match = 0;
+
+    if (symbol < 256)
+    {
+      lzss_emit_literal(rar, symbol);
+      continue;
+    }
+    else if (symbol == 256)
+    {
+      if (!rar_br_read_ahead(a, br, 1))
+        goto truncated_data;
+      newfile = !rar_br_bits(br, 1);
+      rar_br_consume(br, 1);
+
+      if(newfile)
+      {
+        rar->start_new_block = 1;
+        if (!rar_br_read_ahead(a, br, 1))
+          goto truncated_data;
+        rar->start_new_table = rar_br_bits(br, 1);
+        rar_br_consume(br, 1);
+        return lzss_position(&rar->lzss);
+      }
+      else
+      {
+        if (parse_codes(a) != ARCHIVE_OK)
+          return (ARCHIVE_FATAL);
+        continue;
+      }
+    }
+    else if(symbol==257)
+    {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                        "Parsing filters is unsupported.");
+      return (ARCHIVE_FAILED);
+    }
+    else if(symbol==258)
+    {
+      if(rar->lastlength == 0)
+        continue;
+
+      offs = rar->lastoffset;
+      len = rar->lastlength;
+    }
+    else if (symbol <= 262)
+    {
+      offsindex = symbol - 259;
+      offs = rar->oldoffset[offsindex];
+
+      if ((lensymbol = read_next_symbol(a, &rar->lengthcode)) < 0)
+        goto bad_data;
+      if (lensymbol > lengthb_min)
+        goto bad_data;
+      len = lengthbases[lensymbol] + 2;
+      if (lengthbits[lensymbol] > 0) {
+        if (!rar_br_read_ahead(a, br, lengthbits[lensymbol]))
+          goto truncated_data;
+        len += rar_br_bits(br, lengthbits[lensymbol]);
+        rar_br_consume(br, lengthbits[lensymbol]);
+      }
+
+      for (i = offsindex; i > 0; i--)
+        rar->oldoffset[i] = rar->oldoffset[i-1];
+      rar->oldoffset[0] = offs;
+    }
+    else if(symbol<=270)
+    {
+      offs = shortbases[symbol-263] + 1;
+      if(shortbits[symbol-263] > 0) {
+        if (!rar_br_read_ahead(a, br, shortbits[symbol-263]))
+          goto truncated_data;
+        offs += rar_br_bits(br, shortbits[symbol-263]);
+        rar_br_consume(br, shortbits[symbol-263]);
+      }
+
+      len = 2;
+
+      for(i = 3; i > 0; i--)
+        rar->oldoffset[i] = rar->oldoffset[i-1];
+      rar->oldoffset[0] = offs;
+    }
+    else
+    {
+      if (symbol-271 > lengthb_min)
+        goto bad_data;
+      len = lengthbases[symbol-271]+3;
+      if(lengthbits[symbol-271] > 0) {
+        if (!rar_br_read_ahead(a, br, lengthbits[symbol-271]))
+          goto truncated_data;
+        len += rar_br_bits(br, lengthbits[symbol-271]);
+        rar_br_consume(br, lengthbits[symbol-271]);
+      }
+
+      if ((offssymbol = read_next_symbol(a, &rar->offsetcode)) < 0)
+        goto bad_data;
+      if (offssymbol > offsetb_min)
+        goto bad_data;
+      offs = offsetbases[offssymbol]+1;
+      if(offsetbits[offssymbol] > 0)
+      {
+        if(offssymbol > 9)
+        {
+          if(offsetbits[offssymbol] > 4) {
+            if (!rar_br_read_ahead(a, br, offsetbits[offssymbol] - 4))
+              goto truncated_data;
+            offs += rar_br_bits(br, offsetbits[offssymbol] - 4) << 4;
+            rar_br_consume(br, offsetbits[offssymbol] - 4);
+	  }
+
+          if(rar->numlowoffsetrepeats > 0)
+          {
+            rar->numlowoffsetrepeats--;
+            offs += rar->lastlowoffset;
+          }
+          else
+          {
+            if ((lowoffsetsymbol =
+              read_next_symbol(a, &rar->lowoffsetcode)) < 0)
+              return (ARCHIVE_FATAL);
+            if(lowoffsetsymbol == 16)
+            {
+              rar->numlowoffsetrepeats = 15;
+              offs += rar->lastlowoffset;
+            }
+            else
+            {
+              offs += lowoffsetsymbol;
+              rar->lastlowoffset = lowoffsetsymbol;
+            }
+          }
+        }
+        else {
+          if (!rar_br_read_ahead(a, br, offsetbits[offssymbol]))
+            goto truncated_data;
+          offs += rar_br_bits(br, offsetbits[offssymbol]);
+          rar_br_consume(br, offsetbits[offssymbol]);
+        }
+      }
+
+      if (offs >= 0x40000)
+        len++;
+      if (offs >= 0x2000)
+        len++;
+
+      for(i = 3; i > 0; i--)
+        rar->oldoffset[i] = rar->oldoffset[i-1];
+      rar->oldoffset[0] = offs;
+    }
+
+    rar->lastoffset = offs;
+    rar->lastlength = len;
+    rar->output_last_match = 1;
+  }
+truncated_data:
+  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                    "Truncated RAR file data");
+  rar->valid = 0;
+  return (ARCHIVE_FATAL);
+bad_data:
+  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                    "Bad RAR file data");
+  return (ARCHIVE_FATAL);
+}
+
+static int
+copy_from_lzss_window(struct archive_read *a, const void **buffer,
+                        int64_t startpos, int length)
+{
+  int windowoffs, firstpart;
+  struct rar *rar = (struct rar *)(a->format->data);
+
+  if (!rar->unp_buffer)
+  {
+    if ((rar->unp_buffer = malloc(rar->unp_buffer_size)) == NULL)
+    {
+      archive_set_error(&a->archive, ENOMEM,
+                        "Unable to allocate memory for uncompressed data.");
+      return (ARCHIVE_FATAL);
+    }
+  }
+
+  windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
+  if(windowoffs + length <= lzss_size(&rar->lzss)) {
+    memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs],
+           length);
+  } else if (length <= lzss_size(&rar->lzss)) {
+    firstpart = lzss_size(&rar->lzss) - windowoffs;
+    if (firstpart < 0) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Bad RAR file data");
+      return (ARCHIVE_FATAL);
+    }
+    if (firstpart < length) {
+      memcpy(&rar->unp_buffer[rar->unp_offset],
+             &rar->lzss.window[windowoffs], firstpart);
+      memcpy(&rar->unp_buffer[rar->unp_offset + firstpart],
+             &rar->lzss.window[0], length - firstpart);
+    } else {
+      memcpy(&rar->unp_buffer[rar->unp_offset],
+             &rar->lzss.window[windowoffs], length);
+    }
+  } else {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Bad RAR file data");
+      return (ARCHIVE_FATAL);
+  }
+  rar->unp_offset += length;
+  if (rar->unp_offset >= rar->unp_buffer_size)
+    *buffer = rar->unp_buffer;
+  else
+    *buffer = NULL;
+  return (ARCHIVE_OK);
+}
+
+static const void *
+rar_read_ahead(struct archive_read *a, size_t min, ssize_t *avail)
+{
+  struct rar *rar = (struct rar *)(a->format->data);
+  const void *h = __archive_read_ahead(a, min, avail);
+  int ret;
+  if (avail)
+  {
+    if (a->archive.read_data_is_posix_read && *avail > (ssize_t)a->archive.read_data_requested)
+      *avail = a->archive.read_data_requested;
+    if (*avail > rar->bytes_remaining)
+      *avail = (ssize_t)rar->bytes_remaining;
+    if (*avail < 0)
+      return NULL;
+    else if (*avail == 0 && rar->main_flags & MHD_VOLUME &&
+      rar->file_flags & FHD_SPLIT_AFTER)
+    {
+      rar->filename_must_match = 1;
+      ret = archive_read_format_rar_read_header(a, a->entry);
+      if (ret == (ARCHIVE_EOF))
+      {
+        rar->has_endarc_header = 1;
+        ret = archive_read_format_rar_read_header(a, a->entry);
+      }
+      rar->filename_must_match = 0;
+      if (ret != (ARCHIVE_OK))
+        return NULL;
+      return rar_read_ahead(a, min, avail);
+    }
+  }
+  return h;
+}
diff --git a/c/archive_read_support_format_rar5.c b/c/archive_read_support_format_rar5.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_rar5.c
@@ -0,0 +1,4097 @@
+/*-
+* Copyright (c) 2018 Grzegorz Antoniak (http://antoniak.org)
+* 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"
+#include "archive_endian.h"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h> /* crc32 */
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "archive.h"
+#ifndef HAVE_ZLIB_H
+#include "archive_crc32.h"
+#endif
+
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_entry_private.h"
+
+#ifdef HAVE_BLAKE2_H
+#include <blake2.h>
+#else
+#include "archive_blake2.h"
+#endif
+
+/*#define CHECK_CRC_ON_SOLID_SKIP*/
+/*#define DONT_FAIL_ON_CRC_ERROR*/
+/*#define DEBUG*/
+
+#define rar5_min(a, b) (((a) > (b)) ? (b) : (a))
+#define rar5_max(a, b) (((a) > (b)) ? (a) : (b))
+#define rar5_countof(X) ((const ssize_t) (sizeof(X) / sizeof(*X)))
+
+#if defined DEBUG
+#define DEBUG_CODE if(1)
+#define LOG(...) do { printf("rar5: " __VA_ARGS__); puts(""); } while(0)
+#else
+#define DEBUG_CODE if(0)
+#endif
+
+/* Real RAR5 magic number is:
+ *
+ * 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00
+ * "Rar!→•☺·\x00"
+ *
+ * Retrieved with `rar5_signature()` by XOR'ing it with 0xA1, because I don't
+ * want to put this magic sequence in each binary that uses libarchive, so
+ * applications that scan through the file for this marker won't trigger on
+ * this "false" one.
+ *
+ * The array itself is decrypted in `rar5_init` function. */
+
+static unsigned char rar5_signature_xor[] = { 243, 192, 211, 128, 187, 166, 160, 161 };
+static const size_t g_unpack_window_size = 0x20000;
+
+/* These could have been static const's, but they aren't, because of
+ * Visual Studio. */
+#define MAX_NAME_IN_CHARS 2048
+#define MAX_NAME_IN_BYTES (4 * MAX_NAME_IN_CHARS)
+
+struct file_header {
+	ssize_t bytes_remaining;
+	ssize_t unpacked_size;
+	int64_t last_offset;         /* Used in sanity checks. */
+	int64_t last_size;           /* Used in sanity checks. */
+
+	uint8_t solid : 1;           /* Is this a solid stream? */
+	uint8_t service : 1;         /* Is this file a service data? */
+	uint8_t eof : 1;             /* Did we finish unpacking the file? */
+	uint8_t dir : 1;             /* Is this file entry a directory? */
+
+	/* Optional time fields. */
+	uint64_t e_mtime;
+	uint64_t e_ctime;
+	uint64_t e_atime;
+	uint32_t e_unix_ns;
+
+	/* Optional hash fields. */
+	uint32_t stored_crc32;
+	uint32_t calculated_crc32;
+	uint8_t blake2sp[32];
+	blake2sp_state b2state;
+	char has_blake2;
+
+	/* Optional redir fields */
+	uint64_t redir_type;
+	uint64_t redir_flags;
+
+	ssize_t solid_window_size; /* Used in file format check. */
+};
+
+enum EXTRA {
+	EX_CRYPT = 0x01,
+	EX_HASH = 0x02,
+	EX_HTIME = 0x03,
+	EX_VERSION = 0x04,
+	EX_REDIR = 0x05,
+	EX_UOWNER = 0x06,
+	EX_SUBDATA = 0x07
+};
+
+#define REDIR_SYMLINK_IS_DIR	1
+
+enum REDIR_TYPE {
+	REDIR_TYPE_NONE = 0,
+	REDIR_TYPE_UNIXSYMLINK = 1,
+	REDIR_TYPE_WINSYMLINK = 2,
+	REDIR_TYPE_JUNCTION = 3,
+	REDIR_TYPE_HARDLINK = 4,
+	REDIR_TYPE_FILECOPY = 5,
+};
+
+#define	OWNER_USER_NAME		0x01
+#define	OWNER_GROUP_NAME	0x02
+#define	OWNER_USER_UID		0x04
+#define	OWNER_GROUP_GID		0x08
+#define	OWNER_MAXNAMELEN	256
+
+enum FILTER_TYPE {
+	FILTER_DELTA = 0,   /* Generic pattern. */
+	FILTER_E8    = 1,   /* Intel x86 code. */
+	FILTER_E8E9  = 2,   /* Intel x86 code. */
+	FILTER_ARM   = 3,   /* ARM code. */
+	FILTER_AUDIO = 4,   /* Audio filter, not used in RARv5. */
+	FILTER_RGB   = 5,   /* Color palette, not used in RARv5. */
+	FILTER_ITANIUM = 6, /* Intel's Itanium, not used in RARv5. */
+	FILTER_PPM   = 7,   /* Predictive pattern matching, not used in
+			       RARv5. */
+	FILTER_NONE  = 8,
+};
+
+struct filter_info {
+	int type;
+	int channels;
+	int pos_r;
+
+	int64_t block_start;
+	ssize_t block_length;
+	uint16_t width;
+};
+
+struct data_ready {
+	char used;
+	const uint8_t* buf;
+	size_t size;
+	int64_t offset;
+};
+
+struct cdeque {
+	uint16_t beg_pos;
+	uint16_t end_pos;
+	uint16_t cap_mask;
+	uint16_t size;
+	size_t* arr;
+};
+
+struct decode_table {
+	uint32_t size;
+	int32_t decode_len[16];
+	uint32_t decode_pos[16];
+	uint32_t quick_bits;
+	uint8_t quick_len[1 << 10];
+	uint16_t quick_num[1 << 10];
+	uint16_t decode_num[306];
+};
+
+struct comp_state {
+	/* Flag used to specify if unpacker needs to reinitialize the
+	   uncompression context. */
+	uint8_t initialized : 1;
+
+	/* Flag used when applying filters. */
+	uint8_t all_filters_applied : 1;
+
+	/* Flag used to skip file context reinitialization, used when unpacker
+	   is skipping through different multivolume archives. */
+	uint8_t switch_multivolume : 1;
+
+	/* Flag used to specify if unpacker has processed the whole data block
+	   or just a part of it. */
+	uint8_t block_parsing_finished : 1;
+
+	signed int notused : 4;
+
+	int flags;                   /* Uncompression flags. */
+	int method;                  /* Uncompression algorithm method. */
+	int version;                 /* Uncompression algorithm version. */
+	ssize_t window_size;         /* Size of window_buf. */
+	uint8_t* window_buf;         /* Circular buffer used during
+	                                decompression. */
+	uint8_t* filtered_buf;       /* Buffer used when applying filters. */
+	const uint8_t* block_buf;    /* Buffer used when merging blocks. */
+	size_t window_mask;          /* Convenience field; window_size - 1. */
+	int64_t write_ptr;           /* This amount of data has been unpacked
+					in the window buffer. */
+	int64_t last_write_ptr;      /* This amount of data has been stored in
+	                                the output file. */
+	int64_t last_unstore_ptr;    /* Counter of bytes extracted during
+	                                unstoring. This is separate from
+	                                last_write_ptr because of how SERVICE
+	                                base blocks are handled during skipping
+	                                in solid multiarchive archives. */
+	int64_t solid_offset;        /* Additional offset inside the window
+	                                buffer, used in unpacking solid
+	                                archives. */
+	ssize_t cur_block_size;      /* Size of current data block. */
+	int last_len;                /* Flag used in lzss decompression. */
+
+	/* Decode tables used during lzss uncompression. */
+
+#define HUFF_BC 20
+	struct decode_table bd;      /* huffman bit lengths */
+#define HUFF_NC 306
+	struct decode_table ld;      /* literals */
+#define HUFF_DC 64
+	struct decode_table dd;      /* distances */
+#define HUFF_LDC 16
+	struct decode_table ldd;     /* lower bits of distances */
+#define HUFF_RC 44
+	struct decode_table rd;      /* repeating distances */
+#define HUFF_TABLE_SIZE (HUFF_NC + HUFF_DC + HUFF_RC + HUFF_LDC)
+
+	/* Circular deque for storing filters. */
+	struct cdeque filters;
+	int64_t last_block_start;    /* Used for sanity checking. */
+	ssize_t last_block_length;   /* Used for sanity checking. */
+
+	/* Distance cache used during lzss uncompression. */
+	int dist_cache[4];
+
+	/* Data buffer stack. */
+	struct data_ready dready[2];
+};
+
+/* Bit reader state. */
+struct bit_reader {
+	int8_t bit_addr;    /* Current bit pointer inside current byte. */
+	int in_addr;        /* Current byte pointer. */
+};
+
+/* RARv5 block header structure. Use bf_* functions to get values from
+ * block_flags_u8 field. I.e. bf_byte_count, etc. */
+struct compressed_block_header {
+	/* block_flags_u8 contain fields encoded in little-endian bitfield:
+	 *
+	 * - table present flag (shr 7, and 1),
+	 * - last block flag    (shr 6, and 1),
+	 * - byte_count         (shr 3, and 7),
+	 * - bit_size           (shr 0, and 7).
+	 */
+	uint8_t block_flags_u8;
+	uint8_t block_cksum;
+};
+
+/* RARv5 main header structure. */
+struct main_header {
+	/* Does the archive contain solid streams? */
+	uint8_t solid : 1;
+
+	/* If this a multi-file archive? */
+	uint8_t volume : 1;
+	uint8_t endarc : 1;
+	uint8_t notused : 5;
+
+	unsigned int vol_no;
+};
+
+struct generic_header {
+	uint8_t split_after : 1;
+	uint8_t split_before : 1;
+	uint8_t padding : 6;
+	int size;
+	int last_header_id;
+};
+
+struct multivolume {
+	unsigned int expected_vol_no;
+	uint8_t* push_buf;
+};
+
+/* Main context structure. */
+struct rar5 {
+	int header_initialized;
+
+	/* Set to 1 if current file is positioned AFTER the magic value
+	 * of the archive file. This is used in header reading functions. */
+	int skipped_magic;
+
+	/* Set to not zero if we're in skip mode (either by calling
+	 * rar5_data_skip function or when skipping over solid streams).
+	 * Set to 0 when in * extraction mode. This is used during checksum
+	 * calculation functions. */
+	int skip_mode;
+
+	/* Set to not zero if we're in block merging mode (i.e. when switching
+	 * to another file in multivolume archive, last block from 1st archive
+	 * needs to be merged with 1st block from 2nd archive). This flag
+	 * guards against recursive use of the merging function, which doesn't
+	 * support recursive calls. */
+	int merge_mode;
+
+	/* An offset to QuickOpen list. This is not supported by this unpacker,
+	 * because we're focusing on streaming interface. QuickOpen is designed
+	 * to make things quicker for non-stream interfaces, so it's not our
+	 * use case. */
+	uint64_t qlist_offset;
+
+	/* An offset to additional Recovery data. This is not supported by this
+	 * unpacker. Recovery data are additional Reed-Solomon codes that could
+	 * be used to calculate bytes that are missing in archive or are
+	 * corrupted. */
+	uint64_t rr_offset;
+
+	/* Various context variables grouped to different structures. */
+	struct generic_header generic;
+	struct main_header main;
+	struct comp_state cstate;
+	struct file_header file;
+	struct bit_reader bits;
+	struct multivolume vol;
+
+	/* The header of currently processed RARv5 block. Used in main
+	 * decompression logic loop. */
+	struct compressed_block_header last_block_hdr;
+};
+
+/* Forward function declarations. */
+
+static void rar5_signature(char *buf);
+static int verify_global_checksums(struct archive_read* a);
+static int rar5_read_data_skip(struct archive_read *a);
+static int push_data_ready(struct archive_read* a, struct rar5* rar,
+	const uint8_t* buf, size_t size, int64_t offset);
+
+/* CDE_xxx = Circular Double Ended (Queue) return values. */
+enum CDE_RETURN_VALUES {
+	CDE_OK, CDE_ALLOC, CDE_PARAM, CDE_OUT_OF_BOUNDS,
+};
+
+/* Clears the contents of this circular deque. */
+static void cdeque_clear(struct cdeque* d) {
+	d->size = 0;
+	d->beg_pos = 0;
+	d->end_pos = 0;
+}
+
+/* Creates a new circular deque object. Capacity must be power of 2: 8, 16, 32,
+ * 64, 256, etc. When the user will add another item above current capacity,
+ * the circular deque will overwrite the oldest entry. */
+static int cdeque_init(struct cdeque* d, int max_capacity_power_of_2) {
+	if(d == NULL || max_capacity_power_of_2 == 0)
+		return CDE_PARAM;
+
+	d->cap_mask = max_capacity_power_of_2 - 1;
+	d->arr = NULL;
+
+	if((max_capacity_power_of_2 & d->cap_mask) != 0)
+		return CDE_PARAM;
+
+	cdeque_clear(d);
+	d->arr = malloc(sizeof(void*) * max_capacity_power_of_2);
+
+	return d->arr ? CDE_OK : CDE_ALLOC;
+}
+
+/* Return the current size (not capacity) of circular deque `d`. */
+static size_t cdeque_size(struct cdeque* d) {
+	return d->size;
+}
+
+/* Returns the first element of current circular deque. Note that this function
+ * doesn't perform any bounds checking. If you need bounds checking, use
+ * `cdeque_front()` function instead. */
+static void cdeque_front_fast(struct cdeque* d, void** value) {
+	*value = (void*) d->arr[d->beg_pos];
+}
+
+/* Returns the first element of current circular deque. This function
+ * performs bounds checking. */
+static int cdeque_front(struct cdeque* d, void** value) {
+	if(d->size > 0) {
+		cdeque_front_fast(d, value);
+		return CDE_OK;
+	} else
+		return CDE_OUT_OF_BOUNDS;
+}
+
+/* Pushes a new element into the end of this circular deque object. If current
+ * size will exceed capacity, the oldest element will be overwritten. */
+static int cdeque_push_back(struct cdeque* d, void* item) {
+	if(d == NULL)
+		return CDE_PARAM;
+
+	if(d->size == d->cap_mask + 1)
+		return CDE_OUT_OF_BOUNDS;
+
+	d->arr[d->end_pos] = (size_t) item;
+	d->end_pos = (d->end_pos + 1) & d->cap_mask;
+	d->size++;
+
+	return CDE_OK;
+}
+
+/* Pops a front element of this circular deque object and returns its value.
+ * This function doesn't perform any bounds checking. */
+static void cdeque_pop_front_fast(struct cdeque* d, void** value) {
+	*value = (void*) d->arr[d->beg_pos];
+	d->beg_pos = (d->beg_pos + 1) & d->cap_mask;
+	d->size--;
+}
+
+/* Pops a front element of this circular deque object and returns its value.
+ * This function performs bounds checking. */
+static int cdeque_pop_front(struct cdeque* d, void** value) {
+	if(!d || !value)
+		return CDE_PARAM;
+
+	if(d->size == 0)
+		return CDE_OUT_OF_BOUNDS;
+
+	cdeque_pop_front_fast(d, value);
+	return CDE_OK;
+}
+
+/* Convenience function to cast filter_info** to void **. */
+static void** cdeque_filter_p(struct filter_info** f) {
+	return (void**) (size_t) f;
+}
+
+/* Convenience function to cast filter_info* to void *. */
+static void* cdeque_filter(struct filter_info* f) {
+	return (void**) (size_t) f;
+}
+
+/* Destroys this circular deque object. Deallocates the memory of the
+ * collection buffer, but doesn't deallocate the memory of any pointer passed
+ * to this deque as a value. */
+static void cdeque_free(struct cdeque* d) {
+	if(!d)
+		return;
+
+	if(!d->arr)
+		return;
+
+	free(d->arr);
+
+	d->arr = NULL;
+	d->beg_pos = -1;
+	d->end_pos = -1;
+	d->cap_mask = 0;
+}
+
+static inline
+uint8_t bf_bit_size(const struct compressed_block_header* hdr) {
+	return hdr->block_flags_u8 & 7;
+}
+
+static inline
+uint8_t bf_byte_count(const struct compressed_block_header* hdr) {
+	return (hdr->block_flags_u8 >> 3) & 7;
+}
+
+static inline
+uint8_t bf_is_table_present(const struct compressed_block_header* hdr) {
+	return (hdr->block_flags_u8 >> 7) & 1;
+}
+
+static inline struct rar5* get_context(struct archive_read* a) {
+	return (struct rar5*) a->format->data;
+}
+
+/* Convenience functions used by filter implementations. */
+static void circular_memcpy(uint8_t* dst, uint8_t* window, const uint64_t mask,
+    int64_t start, int64_t end)
+{
+	if((start & mask) > (end & mask)) {
+		ssize_t len1 = mask + 1 - (start & mask);
+		ssize_t len2 = end & mask;
+
+		memcpy(dst, &window[start & mask], len1);
+		memcpy(dst + len1, window, len2);
+	} else {
+		memcpy(dst, &window[start & mask], (size_t) (end - start));
+	}
+}
+
+static uint32_t read_filter_data(struct rar5* rar, uint32_t offset) {
+	uint8_t linear_buf[4];
+	circular_memcpy(linear_buf, rar->cstate.window_buf,
+	    rar->cstate.window_mask, offset, offset + 4);
+	return archive_le32dec(linear_buf);
+}
+
+static void write_filter_data(struct rar5* rar, uint32_t offset,
+    uint32_t value)
+{
+	archive_le32enc(&rar->cstate.filtered_buf[offset], value);
+}
+
+/* Allocates a new filter descriptor and adds it to the filter array. */
+static struct filter_info* add_new_filter(struct rar5* rar) {
+	struct filter_info* f =
+		(struct filter_info*) calloc(1, sizeof(struct filter_info));
+
+	if(!f) {
+		return NULL;
+	}
+
+	cdeque_push_back(&rar->cstate.filters, cdeque_filter(f));
+	return f;
+}
+
+static int run_delta_filter(struct rar5* rar, struct filter_info* flt) {
+	int i;
+	ssize_t dest_pos, src_pos = 0;
+
+	for(i = 0; i < flt->channels; i++) {
+		uint8_t prev_byte = 0;
+		for(dest_pos = i;
+				dest_pos < flt->block_length;
+				dest_pos += flt->channels)
+		{
+			uint8_t byte;
+
+			byte = rar->cstate.window_buf[
+			    (rar->cstate.solid_offset + flt->block_start +
+			    src_pos) & rar->cstate.window_mask];
+
+			prev_byte -= byte;
+			rar->cstate.filtered_buf[dest_pos] = prev_byte;
+			src_pos++;
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int run_e8e9_filter(struct rar5* rar, struct filter_info* flt,
+		int extended)
+{
+	const uint32_t file_size = 0x1000000;
+	ssize_t i;
+
+	circular_memcpy(rar->cstate.filtered_buf,
+	    rar->cstate.window_buf, rar->cstate.window_mask,
+	    rar->cstate.solid_offset + flt->block_start,
+	    rar->cstate.solid_offset + flt->block_start + flt->block_length);
+
+	for(i = 0; i < flt->block_length - 4;) {
+		uint8_t b = rar->cstate.window_buf[
+		    (rar->cstate.solid_offset + flt->block_start +
+		    i++) & rar->cstate.window_mask];
+
+		/*
+		 * 0xE8 = x86's call <relative_addr_uint32> (function call)
+		 * 0xE9 = x86's jmp <relative_addr_uint32> (unconditional jump)
+		 */
+		if(b == 0xE8 || (extended && b == 0xE9)) {
+
+			uint32_t addr;
+			uint32_t offset = (i + flt->block_start) % file_size;
+
+			addr = read_filter_data(rar,
+			    (uint32_t)(rar->cstate.solid_offset +
+			    flt->block_start + i) & rar->cstate.window_mask);
+
+			if(addr & 0x80000000) {
+				if(((addr + offset) & 0x80000000) == 0) {
+					write_filter_data(rar, (uint32_t)i,
+					    addr + file_size);
+				}
+			} else {
+				if((addr - file_size) & 0x80000000) {
+					uint32_t naddr = addr - offset;
+					write_filter_data(rar, (uint32_t)i,
+					    naddr);
+				}
+			}
+
+			i += 4;
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int run_arm_filter(struct rar5* rar, struct filter_info* flt) {
+	ssize_t i = 0;
+	uint32_t offset;
+
+	circular_memcpy(rar->cstate.filtered_buf,
+	    rar->cstate.window_buf, rar->cstate.window_mask,
+	    rar->cstate.solid_offset + flt->block_start,
+	    rar->cstate.solid_offset + flt->block_start + flt->block_length);
+
+	for(i = 0; i < flt->block_length - 3; i += 4) {
+		uint8_t* b = &rar->cstate.window_buf[
+		    (rar->cstate.solid_offset +
+		    flt->block_start + i + 3) & rar->cstate.window_mask];
+
+		if(*b == 0xEB) {
+			/* 0xEB = ARM's BL (branch + link) instruction. */
+			offset = read_filter_data(rar,
+			    (rar->cstate.solid_offset + flt->block_start + i) &
+			     rar->cstate.window_mask) & 0x00ffffff;
+
+			offset -= (uint32_t) ((i + flt->block_start) / 4);
+			offset = (offset & 0x00ffffff) | 0xeb000000;
+			write_filter_data(rar, (uint32_t)i, offset);
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int run_filter(struct archive_read* a, struct filter_info* flt) {
+	int ret;
+	struct rar5* rar = get_context(a);
+
+	free(rar->cstate.filtered_buf);
+
+	rar->cstate.filtered_buf = malloc(flt->block_length);
+	if(!rar->cstate.filtered_buf) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for filter data.");
+		return ARCHIVE_FATAL;
+	}
+
+	switch(flt->type) {
+		case FILTER_DELTA:
+			ret = run_delta_filter(rar, flt);
+			break;
+
+		case FILTER_E8:
+			/* fallthrough */
+		case FILTER_E8E9:
+			ret = run_e8e9_filter(rar, flt,
+			    flt->type == FILTER_E8E9);
+			break;
+
+		case FILTER_ARM:
+			ret = run_arm_filter(rar, flt);
+			break;
+
+		default:
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Unsupported filter type: 0x%x", flt->type);
+			return ARCHIVE_FATAL;
+	}
+
+	if(ret != ARCHIVE_OK) {
+		/* Filter has failed. */
+		return ret;
+	}
+
+	if(ARCHIVE_OK != push_data_ready(a, rar, rar->cstate.filtered_buf,
+	    flt->block_length, rar->cstate.last_write_ptr))
+	{
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Stack overflow when submitting unpacked data");
+
+		return ARCHIVE_FATAL;
+	}
+
+	rar->cstate.last_write_ptr += flt->block_length;
+	return ARCHIVE_OK;
+}
+
+/* The `push_data` function submits the selected data range to the user.
+ * Next call of `use_data` will use the pointer, size and offset arguments
+ * that are specified here. These arguments are pushed to the FIFO stack here,
+ * and popped from the stack by the `use_data` function. */
+static void push_data(struct archive_read* a, struct rar5* rar,
+    const uint8_t* buf, int64_t idx_begin, int64_t idx_end)
+{
+	const uint64_t wmask = rar->cstate.window_mask;
+	const ssize_t solid_write_ptr = (rar->cstate.solid_offset +
+	    rar->cstate.last_write_ptr) & wmask;
+
+	idx_begin += rar->cstate.solid_offset;
+	idx_end += rar->cstate.solid_offset;
+
+	/* Check if our unpacked data is wrapped inside the window circular
+	 * buffer.  If it's not wrapped, it can be copied out by using
+	 * a single memcpy, but when it's wrapped, we need to copy the first
+	 * part with one memcpy, and the second part with another memcpy. */
+
+	if((idx_begin & wmask) > (idx_end & wmask)) {
+		/* The data is wrapped (begin offset sis bigger than end
+		 * offset). */
+		const ssize_t frag1_size = rar->cstate.window_size -
+		    (idx_begin & wmask);
+		const ssize_t frag2_size = idx_end & wmask;
+
+		/* Copy the first part of the buffer first. */
+		push_data_ready(a, rar, buf + solid_write_ptr, frag1_size,
+		    rar->cstate.last_write_ptr);
+
+		/* Copy the second part of the buffer. */
+		push_data_ready(a, rar, buf, frag2_size,
+		    rar->cstate.last_write_ptr + frag1_size);
+
+		rar->cstate.last_write_ptr += frag1_size + frag2_size;
+	} else {
+		/* Data is not wrapped, so we can just use one call to copy the
+		 * data. */
+		push_data_ready(a, rar,
+		    buf + solid_write_ptr, (idx_end - idx_begin) & wmask,
+		    rar->cstate.last_write_ptr);
+
+		rar->cstate.last_write_ptr += idx_end - idx_begin;
+	}
+}
+
+/* Convenience function that submits the data to the user. It uses the
+ * unpack window buffer as a source location. */
+static void push_window_data(struct archive_read* a, struct rar5* rar,
+    int64_t idx_begin, int64_t idx_end)
+{
+	push_data(a, rar, rar->cstate.window_buf, idx_begin, idx_end);
+}
+
+static int apply_filters(struct archive_read* a) {
+	struct filter_info* flt;
+	struct rar5* rar = get_context(a);
+	int ret;
+
+	rar->cstate.all_filters_applied = 0;
+
+	/* Get the first filter that can be applied to our data. The data
+	 * needs to be fully unpacked before the filter can be run. */
+	if(CDE_OK == cdeque_front(&rar->cstate.filters,
+	    cdeque_filter_p(&flt))) {
+		/* Check if our unpacked data fully covers this filter's
+		 * range. */
+		if(rar->cstate.write_ptr > flt->block_start &&
+		    rar->cstate.write_ptr >= flt->block_start +
+		    flt->block_length) {
+			/* Check if we have some data pending to be written
+			 * right before the filter's start offset. */
+			if(rar->cstate.last_write_ptr == flt->block_start) {
+				/* Run the filter specified by descriptor
+				 * `flt`. */
+				ret = run_filter(a, flt);
+				if(ret != ARCHIVE_OK) {
+					/* Filter failure, return error. */
+					return ret;
+				}
+
+				/* Filter descriptor won't be needed anymore
+				 * after it's used, * so remove it from the
+				 * filter list and free its memory. */
+				(void) cdeque_pop_front(&rar->cstate.filters,
+				    cdeque_filter_p(&flt));
+
+				free(flt);
+			} else {
+				/* We can't run filters yet, dump the memory
+				 * right before the filter. */
+				push_window_data(a, rar,
+				    rar->cstate.last_write_ptr,
+				    flt->block_start);
+			}
+
+			/* Return 'filter applied or not needed' state to the
+			 * caller. */
+			return ARCHIVE_RETRY;
+		}
+	}
+
+	rar->cstate.all_filters_applied = 1;
+	return ARCHIVE_OK;
+}
+
+static void dist_cache_push(struct rar5* rar, int value) {
+	int* q = rar->cstate.dist_cache;
+
+	q[3] = q[2];
+	q[2] = q[1];
+	q[1] = q[0];
+	q[0] = value;
+}
+
+static int dist_cache_touch(struct rar5* rar, int idx) {
+	int* q = rar->cstate.dist_cache;
+	int i, dist = q[idx];
+
+	for(i = idx; i > 0; i--)
+		q[i] = q[i - 1];
+
+	q[0] = dist;
+	return dist;
+}
+
+static void free_filters(struct rar5* rar) {
+	struct cdeque* d = &rar->cstate.filters;
+
+	/* Free any remaining filters. All filters should be naturally
+	 * consumed by the unpacking function, so remaining filters after
+	 * unpacking normally mean that unpacking wasn't successful.
+	 * But still of course we shouldn't leak memory in such case. */
+
+	/* cdeque_size() is a fast operation, so we can use it as a loop
+	 * expression. */
+	while(cdeque_size(d) > 0) {
+		struct filter_info* f = NULL;
+
+		/* Pop_front will also decrease the collection's size. */
+		if (CDE_OK == cdeque_pop_front(d, cdeque_filter_p(&f)))
+			free(f);
+	}
+
+	cdeque_clear(d);
+
+	/* Also clear out the variables needed for sanity checking. */
+	rar->cstate.last_block_start = 0;
+	rar->cstate.last_block_length = 0;
+}
+
+static void reset_file_context(struct rar5* rar) {
+	memset(&rar->file, 0, sizeof(rar->file));
+	blake2sp_init(&rar->file.b2state, 32);
+
+	if(rar->main.solid) {
+		rar->cstate.solid_offset += rar->cstate.write_ptr;
+	} else {
+		rar->cstate.solid_offset = 0;
+	}
+
+	rar->cstate.write_ptr = 0;
+	rar->cstate.last_write_ptr = 0;
+	rar->cstate.last_unstore_ptr = 0;
+
+	rar->file.redir_type = REDIR_TYPE_NONE;
+	rar->file.redir_flags = 0;
+
+	free_filters(rar);
+}
+
+static inline int get_archive_read(struct archive* a,
+    struct archive_read** ar)
+{
+	*ar = (struct archive_read*) a;
+	archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
+	    "archive_read_support_format_rar5");
+
+	return ARCHIVE_OK;
+}
+
+static int read_ahead(struct archive_read* a, size_t how_many,
+    const uint8_t** ptr)
+{
+	ssize_t avail = -1;
+	if(!ptr)
+		return 0;
+
+	*ptr = __archive_read_ahead(a, how_many, &avail);
+	if(*ptr == NULL) {
+		return 0;
+	}
+
+	return 1;
+}
+
+static int consume(struct archive_read* a, int64_t how_many) {
+	int ret;
+
+	ret = how_many == __archive_read_consume(a, how_many)
+		? ARCHIVE_OK
+		: ARCHIVE_FATAL;
+
+	return ret;
+}
+
+/**
+ * Read a RAR5 variable sized numeric value. This value will be stored in
+ * `pvalue`. The `pvalue_len` argument points to a variable that will receive
+ * the byte count that was consumed in order to decode the `pvalue` value, plus
+ * one.
+ *
+ * pvalue_len is optional and can be NULL.
+ *
+ * NOTE: if `pvalue_len` is NOT NULL, the caller needs to manually consume
+ * the number of bytes that `pvalue_len` value contains. If the `pvalue_len`
+ * is NULL, this consuming operation is done automatically.
+ *
+ * Returns 1 if *pvalue was successfully read.
+ * Returns 0 if there was an error. In this case, *pvalue contains an
+ *           invalid value.
+ */
+
+static int read_var(struct archive_read* a, uint64_t* pvalue,
+    uint64_t* pvalue_len)
+{
+	uint64_t result = 0;
+	size_t shift, i;
+	const uint8_t* p;
+	uint8_t b;
+
+	/* We will read maximum of 8 bytes. We don't have to handle the
+	 * situation to read the RAR5 variable-sized value stored at the end of
+	 * the file, because such situation will never happen. */
+	if(!read_ahead(a, 8, &p))
+		return 0;
+
+	for(shift = 0, i = 0; i < 8; i++, shift += 7) {
+		b = p[i];
+
+		/* Strip the MSB from the input byte and add the resulting
+		 * number to the `result`. */
+		result += (b & (uint64_t)0x7F) << shift;
+
+		/* MSB set to 1 means we need to continue decoding process.
+		 * MSB set to 0 means we're done.
+		 *
+		 * This conditional checks for the second case. */
+		if((b & 0x80) == 0) {
+			if(pvalue) {
+				*pvalue = result;
+			}
+
+			/* If the caller has passed the `pvalue_len` pointer,
+			 * store the number of consumed bytes in it and do NOT
+			 * consume those bytes, since the caller has all the
+			 * information it needs to perform */
+			if(pvalue_len) {
+				*pvalue_len = 1 + i;
+			} else {
+				/* If the caller did not provide the
+				 * `pvalue_len` pointer, it will not have the
+				 * possibility to advance the file pointer,
+				 * because it will not know how many bytes it
+				 * needs to consume. This is why we handle
+				 * such situation here automatically. */
+				if(ARCHIVE_OK != consume(a, 1 + i)) {
+					return 0;
+				}
+			}
+
+			/* End of decoding process, return success. */
+			return 1;
+		}
+	}
+
+	/* The decoded value takes the maximum number of 8 bytes.
+	 * It's a maximum number of bytes, so end decoding process here
+	 * even if the first bit of last byte is 1. */
+	if(pvalue) {
+		*pvalue = result;
+	}
+
+	if(pvalue_len) {
+		*pvalue_len = 9;
+	} else {
+		if(ARCHIVE_OK != consume(a, 9)) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+static int read_var_sized(struct archive_read* a, size_t* pvalue,
+    size_t* pvalue_len)
+{
+	uint64_t v;
+	uint64_t v_size = 0;
+
+	const int ret = pvalue_len ? read_var(a, &v, &v_size)
+				   : read_var(a, &v, NULL);
+
+	if(ret == 1 && pvalue) {
+		*pvalue = (size_t) v;
+	}
+
+	if(pvalue_len) {
+		/* Possible data truncation should be safe. */
+		*pvalue_len = (size_t) v_size;
+	}
+
+	return ret;
+}
+
+static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) {
+	uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24;
+	bits |= p[rar->bits.in_addr + 1] << 16;
+	bits |= p[rar->bits.in_addr + 2] << 8;
+	bits |= p[rar->bits.in_addr + 3];
+	bits <<= rar->bits.bit_addr;
+	bits |= p[rar->bits.in_addr + 4] >> (8 - rar->bits.bit_addr);
+	*value = bits;
+	return ARCHIVE_OK;
+}
+
+static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) {
+	int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16;
+	bits |= (int) p[rar->bits.in_addr + 1] << 8;
+	bits |= (int) p[rar->bits.in_addr + 2];
+	bits >>= (8 - rar->bits.bit_addr);
+	*value = bits & 0xffff;
+	return ARCHIVE_OK;
+}
+
+static void skip_bits(struct rar5* rar, int bits) {
+	const int new_bits = rar->bits.bit_addr + bits;
+	rar->bits.in_addr += new_bits >> 3;
+	rar->bits.bit_addr = new_bits & 7;
+}
+
+/* n = up to 16 */
+static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n,
+    int* value)
+{
+	uint16_t v;
+	int ret, num;
+
+	if(n == 0 || n > 16) {
+		/* This is a programmer error and should never happen
+		 * in runtime. */
+		return ARCHIVE_FATAL;
+	}
+
+	ret = read_bits_16(rar, p, &v);
+	if(ret != ARCHIVE_OK)
+		return ret;
+
+	num = (int) v;
+	num >>= 16 - n;
+
+	skip_bits(rar, n);
+
+	if(value)
+		*value = num;
+
+	return ARCHIVE_OK;
+}
+
+static int read_u32(struct archive_read* a, uint32_t* pvalue) {
+	const uint8_t* p;
+	if(!read_ahead(a, 4, &p))
+		return 0;
+
+	*pvalue = archive_le32dec(p);
+	return ARCHIVE_OK == consume(a, 4) ? 1 : 0;
+}
+
+static int read_u64(struct archive_read* a, uint64_t* pvalue) {
+	const uint8_t* p;
+	if(!read_ahead(a, 8, &p))
+		return 0;
+
+	*pvalue = archive_le64dec(p);
+	return ARCHIVE_OK == consume(a, 8) ? 1 : 0;
+}
+
+static int bid_standard(struct archive_read* a) {
+	const uint8_t* p;
+	char signature[sizeof(rar5_signature_xor)];
+
+	rar5_signature(signature);
+
+	if(!read_ahead(a, sizeof(rar5_signature_xor), &p))
+		return -1;
+
+	if(!memcmp(signature, p, sizeof(rar5_signature_xor)))
+		return 30;
+
+	return -1;
+}
+
+static int rar5_bid(struct archive_read* a, int best_bid) {
+	int my_bid;
+
+	if(best_bid > 30)
+		return -1;
+
+	my_bid = bid_standard(a);
+	if(my_bid > -1) {
+		return my_bid;
+	}
+
+	return -1;
+}
+
+static int rar5_options(struct archive_read *a, const char *key,
+    const char *val) {
+	(void) a;
+	(void) key;
+	(void) val;
+
+	/* No options supported in this version. Return the ARCHIVE_WARN code
+	 * to signal the options supervisor that the unpacker didn't handle
+	 * setting this option. */
+
+	return ARCHIVE_WARN;
+}
+
+static void init_header(struct archive_read* a) {
+	a->archive.archive_format = ARCHIVE_FORMAT_RAR_V5;
+	a->archive.archive_format_name = "RAR5";
+}
+
+static void init_window_mask(struct rar5* rar) {
+	if (rar->cstate.window_size)
+		rar->cstate.window_mask = rar->cstate.window_size - 1;
+	else
+		rar->cstate.window_mask = 0;
+}
+
+enum HEADER_FLAGS {
+	HFL_EXTRA_DATA = 0x0001,
+	HFL_DATA = 0x0002,
+	HFL_SKIP_IF_UNKNOWN = 0x0004,
+	HFL_SPLIT_BEFORE = 0x0008,
+	HFL_SPLIT_AFTER = 0x0010,
+	HFL_CHILD = 0x0020,
+	HFL_INHERITED = 0x0040
+};
+
+static int process_main_locator_extra_block(struct archive_read* a,
+    struct rar5* rar)
+{
+	uint64_t locator_flags;
+
+	enum LOCATOR_FLAGS {
+		QLIST = 0x01, RECOVERY = 0x02,
+	};
+
+	if(!read_var(a, &locator_flags, NULL)) {
+		return ARCHIVE_EOF;
+	}
+
+	if(locator_flags & QLIST) {
+		if(!read_var(a, &rar->qlist_offset, NULL)) {
+			return ARCHIVE_EOF;
+		}
+
+		/* qlist is not used */
+	}
+
+	if(locator_flags & RECOVERY) {
+		if(!read_var(a, &rar->rr_offset, NULL)) {
+			return ARCHIVE_EOF;
+		}
+
+		/* rr is not used */
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_hash(struct archive_read* a, struct rar5* rar,
+    ssize_t* extra_data_size)
+{
+	size_t hash_type = 0;
+	size_t value_len;
+
+	enum HASH_TYPE {
+		BLAKE2sp = 0x00
+	};
+
+	if(!read_var_sized(a, &hash_type, &value_len))
+		return ARCHIVE_EOF;
+
+	*extra_data_size -= value_len;
+	if(ARCHIVE_OK != consume(a, value_len)) {
+		return ARCHIVE_EOF;
+	}
+
+	/* The file uses BLAKE2sp checksum algorithm instead of plain old
+	 * CRC32. */
+	if(hash_type == BLAKE2sp) {
+		const uint8_t* p;
+		const int hash_size = sizeof(rar->file.blake2sp);
+
+		if(!read_ahead(a, hash_size, &p))
+			return ARCHIVE_EOF;
+
+		rar->file.has_blake2 = 1;
+		memcpy(&rar->file.blake2sp, p, hash_size);
+
+		if(ARCHIVE_OK != consume(a, hash_size)) {
+			return ARCHIVE_EOF;
+		}
+
+		*extra_data_size -= hash_size;
+	} else {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unsupported hash type (0x%x)", (int) hash_type);
+		return ARCHIVE_FATAL;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static uint64_t time_win_to_unix(uint64_t win_time) {
+	const size_t ns_in_sec = 10000000;
+	const uint64_t sec_to_unix = 11644473600LL;
+	return win_time / ns_in_sec - sec_to_unix;
+}
+
+static int parse_htime_item(struct archive_read* a, char unix_time,
+    uint64_t* where, ssize_t* extra_data_size)
+{
+	if(unix_time) {
+		uint32_t time_val;
+		if(!read_u32(a, &time_val))
+			return ARCHIVE_EOF;
+
+		*extra_data_size -= 4;
+		*where = (uint64_t) time_val;
+	} else {
+		uint64_t windows_time;
+		if(!read_u64(a, &windows_time))
+			return ARCHIVE_EOF;
+
+		*where = time_win_to_unix(windows_time);
+		*extra_data_size -= 8;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_version(struct archive_read* a,
+    struct archive_entry* e, ssize_t* extra_data_size)
+{
+	size_t flags = 0;
+	size_t version = 0;
+	size_t value_len = 0;
+	struct archive_string version_string;
+	struct archive_string name_utf8_string;
+	const char* cur_filename;
+
+	/* Flags are ignored. */
+	if(!read_var_sized(a, &flags, &value_len))
+		return ARCHIVE_EOF;
+
+	*extra_data_size -= value_len;
+	if(ARCHIVE_OK != consume(a, value_len))
+		return ARCHIVE_EOF;
+
+	if(!read_var_sized(a, &version, &value_len))
+		return ARCHIVE_EOF;
+
+	*extra_data_size -= value_len;
+	if(ARCHIVE_OK != consume(a, value_len))
+		return ARCHIVE_EOF;
+
+	/* extra_data_size should be zero here. */
+
+	cur_filename = archive_entry_pathname_utf8(e);
+	if(cur_filename == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Version entry without file name");
+		return ARCHIVE_FATAL;
+	}
+
+	archive_string_init(&version_string);
+	archive_string_init(&name_utf8_string);
+
+	/* Prepare a ;123 suffix for the filename, where '123' is the version
+	 * value of this file. */
+	archive_string_sprintf(&version_string, ";%zu", version);
+
+	/* Build the new filename. */
+	archive_strcat(&name_utf8_string, cur_filename);
+	archive_strcat(&name_utf8_string, version_string.s);
+
+	/* Apply the new filename into this file's context. */
+	archive_entry_update_pathname_utf8(e, name_utf8_string.s);
+
+	/* Free buffers. */
+	archive_string_free(&version_string);
+	archive_string_free(&name_utf8_string);
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_htime(struct archive_read* a,
+    struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)
+{
+	char unix_time = 0;
+	size_t flags = 0;
+	size_t value_len;
+
+	enum HTIME_FLAGS {
+		IS_UNIX       = 0x01,
+		HAS_MTIME     = 0x02,
+		HAS_CTIME     = 0x04,
+		HAS_ATIME     = 0x08,
+		HAS_UNIX_NS   = 0x10,
+	};
+
+	if(!read_var_sized(a, &flags, &value_len))
+		return ARCHIVE_EOF;
+
+	*extra_data_size -= value_len;
+	if(ARCHIVE_OK != consume(a, value_len)) {
+		return ARCHIVE_EOF;
+	}
+
+	unix_time = flags & IS_UNIX;
+
+	if(flags & HAS_MTIME) {
+		parse_htime_item(a, unix_time, &rar->file.e_mtime,
+		    extra_data_size);
+		archive_entry_set_mtime(e, rar->file.e_mtime, 0);
+	}
+
+	if(flags & HAS_CTIME) {
+		parse_htime_item(a, unix_time, &rar->file.e_ctime,
+		    extra_data_size);
+		archive_entry_set_ctime(e, rar->file.e_ctime, 0);
+	}
+
+	if(flags & HAS_ATIME) {
+		parse_htime_item(a, unix_time, &rar->file.e_atime,
+		    extra_data_size);
+		archive_entry_set_atime(e, rar->file.e_atime, 0);
+	}
+
+	if(flags & HAS_UNIX_NS) {
+		if(!read_u32(a, &rar->file.e_unix_ns))
+			return ARCHIVE_EOF;
+
+		*extra_data_size -= 4;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_redir(struct archive_read* a,
+    struct archive_entry* e, struct rar5* rar, ssize_t* extra_data_size)
+{
+	uint64_t value_size = 0;
+	size_t target_size = 0;
+	char target_utf8_buf[MAX_NAME_IN_BYTES];
+	const uint8_t* p;
+
+	if(!read_var(a, &rar->file.redir_type, &value_size))
+		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+		return ARCHIVE_EOF;
+	*extra_data_size -= value_size;
+
+	if(!read_var(a, &rar->file.redir_flags, &value_size))
+		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+		return ARCHIVE_EOF;
+	*extra_data_size -= value_size;
+
+	if(!read_var_sized(a, &target_size, NULL))
+		return ARCHIVE_EOF;
+	*extra_data_size -= target_size + 1;
+
+	if(!read_ahead(a, target_size, &p))
+		return ARCHIVE_EOF;
+
+	if(target_size > (MAX_NAME_IN_CHARS - 1)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Link target is too long");
+		return ARCHIVE_FATAL;
+	}
+
+	if(target_size == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "No link target specified");
+		return ARCHIVE_FATAL;
+	}
+
+	memcpy(target_utf8_buf, p, target_size);
+	target_utf8_buf[target_size] = 0;
+
+	if(ARCHIVE_OK != consume(a, (int64_t)target_size))
+		return ARCHIVE_EOF;
+
+	switch(rar->file.redir_type) {
+		case REDIR_TYPE_UNIXSYMLINK:
+		case REDIR_TYPE_WINSYMLINK:
+			archive_entry_set_filetype(e, AE_IFLNK);
+			archive_entry_update_symlink_utf8(e, target_utf8_buf);
+			if (rar->file.redir_flags & REDIR_SYMLINK_IS_DIR) {
+				archive_entry_set_symlink_type(e,
+					AE_SYMLINK_TYPE_DIRECTORY);
+			} else {
+				archive_entry_set_symlink_type(e,
+				AE_SYMLINK_TYPE_FILE);
+			}
+			break;
+
+		case REDIR_TYPE_HARDLINK:
+			archive_entry_set_filetype(e, AE_IFREG);
+			archive_entry_update_hardlink_utf8(e, target_utf8_buf);
+			break;
+
+		default:
+			/* Unknown redir type, skip it. */
+			break;
+	}
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_owner(struct archive_read* a,
+    struct archive_entry* e, ssize_t* extra_data_size)
+{
+	uint64_t flags = 0;
+	uint64_t value_size = 0;
+	uint64_t id = 0;
+	size_t name_len = 0;
+	size_t name_size = 0;
+	char namebuf[OWNER_MAXNAMELEN];
+	const uint8_t* p;
+
+	if(!read_var(a, &flags, &value_size))
+		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+		return ARCHIVE_EOF;
+	*extra_data_size -= value_size;
+
+	if ((flags & OWNER_USER_NAME) != 0) {
+		if(!read_var_sized(a, &name_size, NULL))
+			return ARCHIVE_EOF;
+		*extra_data_size -= name_size + 1;
+
+		if(!read_ahead(a, name_size, &p))
+			return ARCHIVE_EOF;
+
+		if (name_size >= OWNER_MAXNAMELEN) {
+			name_len = OWNER_MAXNAMELEN - 1;
+		} else {
+			name_len = name_size;
+		}
+
+		memcpy(namebuf, p, name_len);
+		namebuf[name_len] = 0;
+		if(ARCHIVE_OK != consume(a, (int64_t)name_size))
+			return ARCHIVE_EOF;
+
+		archive_entry_set_uname(e, namebuf);
+	}
+	if ((flags & OWNER_GROUP_NAME) != 0) {
+		if(!read_var_sized(a, &name_size, NULL))
+			return ARCHIVE_EOF;
+		*extra_data_size -= name_size + 1;
+
+		if(!read_ahead(a, name_size, &p))
+			return ARCHIVE_EOF;
+
+		if (name_size >= OWNER_MAXNAMELEN) {
+			name_len = OWNER_MAXNAMELEN - 1;
+		} else {
+			name_len = name_size;
+		}
+
+		memcpy(namebuf, p, name_len);
+		namebuf[name_len] = 0;
+		if(ARCHIVE_OK != consume(a, (int64_t)name_size))
+			return ARCHIVE_EOF;
+
+		archive_entry_set_gname(e, namebuf);
+	}
+	if ((flags & OWNER_USER_UID) != 0) {
+		if(!read_var(a, &id, &value_size))
+			return ARCHIVE_EOF;
+		if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+			return ARCHIVE_EOF;
+		*extra_data_size -= value_size;
+
+		archive_entry_set_uid(e, (la_int64_t)id);
+	}
+	if ((flags & OWNER_GROUP_GID) != 0) {
+		if(!read_var(a, &id, &value_size))
+			return ARCHIVE_EOF;
+		if(ARCHIVE_OK != consume(a, (int64_t)value_size))
+			return ARCHIVE_EOF;
+		*extra_data_size -= value_size;
+
+		archive_entry_set_gid(e, (la_int64_t)id);
+	}
+	return ARCHIVE_OK;
+}
+
+static int process_head_file_extra(struct archive_read* a,
+    struct archive_entry* e, struct rar5* rar, ssize_t extra_data_size)
+{
+	size_t extra_field_size;
+	size_t extra_field_id = 0;
+	int ret = ARCHIVE_FATAL;
+	size_t var_size;
+
+	while(extra_data_size > 0) {
+		if(!read_var_sized(a, &extra_field_size, &var_size))
+			return ARCHIVE_EOF;
+
+		extra_data_size -= var_size;
+		if(ARCHIVE_OK != consume(a, var_size)) {
+			return ARCHIVE_EOF;
+		}
+
+		if(!read_var_sized(a, &extra_field_id, &var_size))
+			return ARCHIVE_EOF;
+
+		extra_data_size -= var_size;
+		if(ARCHIVE_OK != consume(a, var_size)) {
+			return ARCHIVE_EOF;
+		}
+
+		switch(extra_field_id) {
+			case EX_HASH:
+				ret = parse_file_extra_hash(a, rar,
+				    &extra_data_size);
+				break;
+			case EX_HTIME:
+				ret = parse_file_extra_htime(a, e, rar,
+				    &extra_data_size);
+				break;
+			case EX_REDIR:
+				ret = parse_file_extra_redir(a, e, rar,
+				    &extra_data_size);
+				break;
+			case EX_UOWNER:
+				ret = parse_file_extra_owner(a, e,
+				    &extra_data_size);
+				break;
+			case EX_VERSION:
+				ret = parse_file_extra_version(a, e,
+				    &extra_data_size);
+				break;
+			case EX_CRYPT:
+				/* fallthrough */
+			case EX_SUBDATA:
+				/* fallthrough */
+			default:
+				/* Skip unsupported entry. */
+				return consume(a, extra_data_size);
+		}
+	}
+
+	if(ret != ARCHIVE_OK) {
+		/* Attribute not implemented. */
+		return ret;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int process_head_file(struct archive_read* a, struct rar5* rar,
+    struct archive_entry* entry, size_t block_flags)
+{
+	ssize_t extra_data_size = 0;
+	size_t data_size = 0;
+	size_t file_flags = 0;
+	size_t file_attr = 0;
+	size_t compression_info = 0;
+	size_t host_os = 0;
+	size_t name_size = 0;
+	uint64_t unpacked_size, window_size;
+	uint32_t mtime = 0, crc = 0;
+	int c_method = 0, c_version = 0;
+	char name_utf8_buf[MAX_NAME_IN_BYTES];
+	const uint8_t* p;
+
+	enum FILE_FLAGS {
+		DIRECTORY = 0x0001, UTIME = 0x0002, CRC32 = 0x0004,
+		UNKNOWN_UNPACKED_SIZE = 0x0008,
+	};
+
+	enum FILE_ATTRS {
+		ATTR_READONLY = 0x1, ATTR_HIDDEN = 0x2, ATTR_SYSTEM = 0x4,
+		ATTR_DIRECTORY = 0x10,
+	};
+
+	enum COMP_INFO_FLAGS {
+		SOLID = 0x0040,
+	};
+
+	enum HOST_OS {
+		HOST_WINDOWS = 0,
+		HOST_UNIX = 1,
+	};
+
+	archive_entry_clear(entry);
+
+	/* Do not reset file context if we're switching archives. */
+	if(!rar->cstate.switch_multivolume) {
+		reset_file_context(rar);
+	}
+
+	if(block_flags & HFL_EXTRA_DATA) {
+		size_t edata_size = 0;
+		if(!read_var_sized(a, &edata_size, NULL))
+			return ARCHIVE_EOF;
+
+		/* Intentional type cast from unsigned to signed. */
+		extra_data_size = (ssize_t) edata_size;
+	}
+
+	if(block_flags & HFL_DATA) {
+		if(!read_var_sized(a, &data_size, NULL))
+			return ARCHIVE_EOF;
+
+		rar->file.bytes_remaining = data_size;
+	} else {
+		rar->file.bytes_remaining = 0;
+
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				"no data found in file/service block");
+		return ARCHIVE_FATAL;
+	}
+
+	if(!read_var_sized(a, &file_flags, NULL))
+		return ARCHIVE_EOF;
+
+	if(!read_var(a, &unpacked_size, NULL))
+		return ARCHIVE_EOF;
+
+	if(file_flags & UNKNOWN_UNPACKED_SIZE) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Files with unknown unpacked size are not supported");
+		return ARCHIVE_FATAL;
+	}
+
+	rar->file.dir = (uint8_t) ((file_flags & DIRECTORY) > 0);
+
+	if(!read_var_sized(a, &file_attr, NULL))
+		return ARCHIVE_EOF;
+
+	if(file_flags & UTIME) {
+		if(!read_u32(a, &mtime))
+			return ARCHIVE_EOF;
+	}
+
+	if(file_flags & CRC32) {
+		if(!read_u32(a, &crc))
+			return ARCHIVE_EOF;
+	}
+
+	if(!read_var_sized(a, &compression_info, NULL))
+		return ARCHIVE_EOF;
+
+	c_method = (int) (compression_info >> 7) & 0x7;
+	c_version = (int) (compression_info & 0x3f);
+
+	/* RAR5 seems to limit the dictionary size to 64MB. */
+	window_size = (rar->file.dir > 0) ?
+		0 :
+		g_unpack_window_size << ((compression_info >> 10) & 15);
+	rar->cstate.method = c_method;
+	rar->cstate.version = c_version + 50;
+	rar->file.solid = (compression_info & SOLID) > 0;
+
+	/* Archives which declare solid files without initializing the window
+	 * buffer first are invalid. */
+
+	if(rar->file.solid > 0 && rar->cstate.window_buf == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				  "Declared solid file, but no window buffer "
+				  "initialized yet.");
+		return ARCHIVE_FATAL;
+	}
+
+	/* Check if window_size is a sane value. Also, if the file is not
+	 * declared as a directory, disallow window_size == 0. */
+	if(window_size > (64 * 1024 * 1024) ||
+	    (rar->file.dir == 0 && window_size == 0))
+	{
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Declared dictionary size is not supported.");
+		return ARCHIVE_FATAL;
+	}
+
+	if(rar->file.solid > 0) {
+		/* Re-check if current window size is the same as previous
+		 * window size (for solid files only). */
+		if(rar->file.solid_window_size > 0 &&
+		    rar->file.solid_window_size != (ssize_t) window_size)
+		{
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Window size for this solid file doesn't match "
+			    "the window size used in previous solid file. ");
+			return ARCHIVE_FATAL;
+		}
+	}
+
+	/* If we're currently switching volumes, ignore the new definition of
+	 * window_size. */
+	if(rar->cstate.switch_multivolume == 0) {
+		/* Values up to 64M should fit into ssize_t on every
+		 * architecture. */
+		rar->cstate.window_size = (ssize_t) window_size;
+	}
+
+	if(rar->file.solid > 0 && rar->file.solid_window_size == 0) {
+		/* Solid files have to have the same window_size across
+		   whole archive. Remember the window_size parameter
+		   for first solid file found. */
+		rar->file.solid_window_size = rar->cstate.window_size;
+	}
+
+	init_window_mask(rar);
+
+	rar->file.service = 0;
+
+	if(!read_var_sized(a, &host_os, NULL))
+		return ARCHIVE_EOF;
+
+	if(host_os == HOST_WINDOWS) {
+		/* Host OS is Windows */
+
+		__LA_MODE_T mode;
+
+		if(file_attr & ATTR_DIRECTORY) {
+			if (file_attr & ATTR_READONLY) {
+				mode = 0555 | AE_IFDIR;
+			} else {
+				mode = 0755 | AE_IFDIR;
+			}
+		} else {
+			if (file_attr & ATTR_READONLY) {
+				mode = 0444 | AE_IFREG;
+			} else {
+				mode = 0644 | AE_IFREG;
+			}
+		}
+
+		archive_entry_set_mode(entry, mode);
+
+		if (file_attr & (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM)) {
+			char *fflags_text, *ptr;
+			/* allocate for "rdonly,hidden,system," */
+			fflags_text = malloc(22 * sizeof(char));
+			if (fflags_text != NULL) {
+				ptr = fflags_text;
+				if (file_attr & ATTR_READONLY) {
+					strcpy(ptr, "rdonly,");
+					ptr = ptr + 7;
+				}
+				if (file_attr & ATTR_HIDDEN) {
+					strcpy(ptr, "hidden,");
+					ptr = ptr + 7;
+				}
+				if (file_attr & ATTR_SYSTEM) {
+					strcpy(ptr, "system,");
+					ptr = ptr + 7;
+				}
+				if (ptr > fflags_text) {
+					/* Delete trailing comma */
+					*(ptr - 1) = '\0';
+					archive_entry_copy_fflags_text(entry,
+					    fflags_text);
+				}
+				free(fflags_text);
+			}
+		}
+	} else if(host_os == HOST_UNIX) {
+		/* Host OS is Unix */
+		archive_entry_set_mode(entry, (__LA_MODE_T) file_attr);
+	} else {
+		/* Unknown host OS */
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				"Unsupported Host OS: 0x%x", (int) host_os);
+
+		return ARCHIVE_FATAL;
+	}
+
+	if(!read_var_sized(a, &name_size, NULL))
+		return ARCHIVE_EOF;
+
+	if(!read_ahead(a, name_size, &p))
+		return ARCHIVE_EOF;
+
+	if(name_size > (MAX_NAME_IN_CHARS - 1)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				"Filename is too long");
+
+		return ARCHIVE_FATAL;
+	}
+
+	if(name_size == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				"No filename specified");
+
+		return ARCHIVE_FATAL;
+	}
+
+	memcpy(name_utf8_buf, p, name_size);
+	name_utf8_buf[name_size] = 0;
+	if(ARCHIVE_OK != consume(a, name_size)) {
+		return ARCHIVE_EOF;
+	}
+
+	archive_entry_update_pathname_utf8(entry, name_utf8_buf);
+
+	if(extra_data_size > 0) {
+		int ret = process_head_file_extra(a, entry, rar,
+		    extra_data_size);
+
+		/*
+		 * TODO: rewrite or remove useless sanity check
+		 *       as extra_data_size is not passed as a pointer
+		 *
+		if(extra_data_size < 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+			    "File extra data size is not zero");
+			return ARCHIVE_FATAL;
+		}
+		 */
+
+		if(ret != ARCHIVE_OK)
+			return ret;
+	}
+
+	if((file_flags & UNKNOWN_UNPACKED_SIZE) == 0) {
+		rar->file.unpacked_size = (ssize_t) unpacked_size;
+		if(rar->file.redir_type == REDIR_TYPE_NONE)
+			archive_entry_set_size(entry, unpacked_size);
+	}
+
+	if(file_flags & UTIME) {
+		archive_entry_set_mtime(entry, (time_t) mtime, 0);
+	}
+
+	if(file_flags & CRC32) {
+		rar->file.stored_crc32 = crc;
+	}
+
+	if(!rar->cstate.switch_multivolume) {
+		/* Do not reinitialize unpacking state if we're switching
+		 * archives. */
+		rar->cstate.block_parsing_finished = 1;
+		rar->cstate.all_filters_applied = 1;
+		rar->cstate.initialized = 0;
+	}
+
+	if(rar->generic.split_before > 0) {
+		/* If now we're standing on a header that has a 'split before'
+		 * mark, it means we're standing on a 'continuation' file
+		 * header. Signal the caller that if it wants to move to
+		 * another file, it must call rar5_read_header() function
+		 * again. */
+
+		return ARCHIVE_RETRY;
+	} else {
+		return ARCHIVE_OK;
+	}
+}
+
+static int process_head_service(struct archive_read* a, struct rar5* rar,
+    struct archive_entry* entry, size_t block_flags)
+{
+	/* Process this SERVICE block the same way as FILE blocks. */
+	int ret = process_head_file(a, rar, entry, block_flags);
+	if(ret != ARCHIVE_OK)
+		return ret;
+
+	rar->file.service = 1;
+
+	/* But skip the data part automatically. It's no use for the user
+	 * anyway.  It contains only service data, not even needed to
+	 * properly unpack the file. */
+	ret = rar5_read_data_skip(a);
+	if(ret != ARCHIVE_OK)
+		return ret;
+
+	/* After skipping, try parsing another block automatically. */
+	return ARCHIVE_RETRY;
+}
+
+static int process_head_main(struct archive_read* a, struct rar5* rar,
+    struct archive_entry* entry, size_t block_flags)
+{
+	int ret;
+	size_t extra_data_size = 0;
+	size_t extra_field_size = 0;
+	size_t extra_field_id = 0;
+	size_t archive_flags = 0;
+
+	enum MAIN_FLAGS {
+		VOLUME = 0x0001,         /* multi-volume archive */
+		VOLUME_NUMBER = 0x0002,  /* volume number, first vol doesn't
+					  * have it */
+		SOLID = 0x0004,          /* solid archive */
+		PROTECT = 0x0008,        /* contains Recovery info */
+		LOCK = 0x0010,           /* readonly flag, not used */
+	};
+
+	enum MAIN_EXTRA {
+		// Just one attribute here.
+		LOCATOR = 0x01,
+	};
+
+	(void) entry;
+
+	if(block_flags & HFL_EXTRA_DATA) {
+		if(!read_var_sized(a, &extra_data_size, NULL))
+			return ARCHIVE_EOF;
+	} else {
+		extra_data_size = 0;
+	}
+
+	if(!read_var_sized(a, &archive_flags, NULL)) {
+		return ARCHIVE_EOF;
+	}
+
+	rar->main.volume = (archive_flags & VOLUME) > 0;
+	rar->main.solid = (archive_flags & SOLID) > 0;
+
+	if(archive_flags & VOLUME_NUMBER) {
+		size_t v = 0;
+		if(!read_var_sized(a, &v, NULL)) {
+			return ARCHIVE_EOF;
+		}
+
+		if (v > UINT_MAX) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Invalid volume number");
+			return ARCHIVE_FATAL;
+		}
+
+		rar->main.vol_no = (unsigned int) v;
+	} else {
+		rar->main.vol_no = 0;
+	}
+
+	if(rar->vol.expected_vol_no > 0 &&
+		rar->main.vol_no != rar->vol.expected_vol_no)
+	{
+		/* Returning EOF instead of FATAL because of strange
+		 * libarchive behavior. When opening multiple files via
+		 * archive_read_open_filenames(), after reading up the whole
+		 * last file, the __archive_read_ahead function wraps up to
+		 * the first archive instead of returning EOF. */
+		return ARCHIVE_EOF;
+	}
+
+	if(extra_data_size == 0) {
+		/* Early return. */
+		return ARCHIVE_OK;
+	}
+
+	if(!read_var_sized(a, &extra_field_size, NULL)) {
+		return ARCHIVE_EOF;
+	}
+
+	if(!read_var_sized(a, &extra_field_id, NULL)) {
+		return ARCHIVE_EOF;
+	}
+
+	if(extra_field_size == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Invalid extra field size");
+		return ARCHIVE_FATAL;
+	}
+
+	switch(extra_field_id) {
+		case LOCATOR:
+			ret = process_main_locator_extra_block(a, rar);
+			if(ret != ARCHIVE_OK) {
+				/* Error while parsing main locator extra
+				 * block. */
+				return ret;
+			}
+
+			break;
+		default:
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Unsupported extra type (0x%x)",
+			    (int) extra_field_id);
+			return ARCHIVE_FATAL;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int skip_unprocessed_bytes(struct archive_read* a) {
+	struct rar5* rar = get_context(a);
+	int ret;
+
+	if(rar->file.bytes_remaining) {
+		/* Use different skipping method in block merging mode than in
+		 * normal mode. If merge mode is active, rar5_read_data_skip
+		 * can't be used, because it could allow recursive use of
+		 * merge_block() * function, and this function doesn't support
+		 * recursive use. */
+		if(rar->merge_mode) {
+			/* Discard whole merged block. This is valid in solid
+			 * mode as well, because the code will discard blocks
+			 * only if those blocks are safe to discard (i.e.
+			 * they're not FILE blocks).  */
+			ret = consume(a, rar->file.bytes_remaining);
+			if(ret != ARCHIVE_OK) {
+				return ret;
+			}
+			rar->file.bytes_remaining = 0;
+		} else {
+			/* If we're not in merge mode, use safe skipping code.
+			 * This will ensure we'll handle solid archives
+			 * properly. */
+			ret = rar5_read_data_skip(a);
+			if(ret != ARCHIVE_OK) {
+				return ret;
+			}
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int scan_for_signature(struct archive_read* a);
+
+/* Base block processing function. A 'base block' is a RARv5 header block
+ * that tells the reader what kind of data is stored inside the block.
+ *
+ * From the birds-eye view a RAR file looks file this:
+ *
+ * <magic><base_block_1><base_block_2>...<base_block_n>
+ *
+ * There are a few types of base blocks. Those types are specified inside
+ * the 'switch' statement in this function. For example purposes, I'll write
+ * how a standard RARv5 file could look like here:
+ *
+ * <magic><MAIN><FILE><FILE><FILE><SERVICE><ENDARC>
+ *
+ * The structure above could describe an archive file with 3 files in it,
+ * one service "QuickOpen" block (that is ignored by this parser), and an
+ * end of file base block marker.
+ *
+ * If the file is stored in multiple archive files ("multiarchive"), it might
+ * look like this:
+ *
+ * .part01.rar: <magic><MAIN><FILE><ENDARC>
+ * .part02.rar: <magic><MAIN><FILE><ENDARC>
+ * .part03.rar: <magic><MAIN><FILE><ENDARC>
+ *
+ * This example could describe 3 RAR files that contain ONE archived file.
+ * Or it could describe 3 RAR files that contain 3 different files. Or 3
+ * RAR files than contain 2 files. It all depends what metadata is stored in
+ * the headers of <FILE> blocks.
+ *
+ * Each <FILE> block contains info about its size, the name of the file it's
+ * storing inside, and whether this FILE block is a continuation block of
+ * previous archive ('split before'), and is this FILE block should be
+ * continued in another archive ('split after'). By parsing the 'split before'
+ * and 'split after' flags, we're able to tell if multiple <FILE> base blocks
+ * are describing one file, or multiple files (with the same filename, for
+ * example).
+ *
+ * One thing to note is that if we're parsing the first <FILE> block, and
+ * we see 'split after' flag, then we need to jump over to another <FILE>
+ * block to be able to decompress rest of the data. To do this, we need
+ * to skip the <ENDARC> block, then switch to another file, then skip the
+ * <magic> block, <MAIN> block, and then we're standing on the proper
+ * <FILE> block.
+ */
+
+static int process_base_block(struct archive_read* a,
+    struct archive_entry* entry)
+{
+	const size_t SMALLEST_RAR5_BLOCK_SIZE = 3;
+
+	struct rar5* rar = get_context(a);
+	uint32_t hdr_crc, computed_crc;
+	size_t raw_hdr_size = 0, hdr_size_len, hdr_size;
+	size_t header_id = 0;
+	size_t header_flags = 0;
+	const uint8_t* p;
+	int ret;
+
+	enum HEADER_TYPE {
+		HEAD_MARK    = 0x00, HEAD_MAIN  = 0x01, HEAD_FILE   = 0x02,
+		HEAD_SERVICE = 0x03, HEAD_CRYPT = 0x04, HEAD_ENDARC = 0x05,
+		HEAD_UNKNOWN = 0xff,
+	};
+
+	/* Skip any unprocessed data for this file. */
+	ret = skip_unprocessed_bytes(a);
+	if(ret != ARCHIVE_OK)
+		return ret;
+
+	/* Read the expected CRC32 checksum. */
+	if(!read_u32(a, &hdr_crc)) {
+		return ARCHIVE_EOF;
+	}
+
+	/* Read header size. */
+	if(!read_var_sized(a, &raw_hdr_size, &hdr_size_len)) {
+		return ARCHIVE_EOF;
+	}
+
+	hdr_size = raw_hdr_size + hdr_size_len;
+
+	/* Sanity check, maximum header size for RAR5 is 2MB. */
+	if(hdr_size > (2 * 1024 * 1024)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Base block header is too large");
+
+		return ARCHIVE_FATAL;
+	}
+
+	/* Additional sanity checks to weed out invalid files. */
+	if(raw_hdr_size == 0 || hdr_size_len == 0 ||
+		hdr_size < SMALLEST_RAR5_BLOCK_SIZE)
+	{
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Too small block encountered (%zu bytes)",
+		    raw_hdr_size);
+
+		return ARCHIVE_FATAL;
+	}
+
+	/* Read the whole header data into memory, maximum memory use here is
+	 * 2MB. */
+	if(!read_ahead(a, hdr_size, &p)) {
+		return ARCHIVE_EOF;
+	}
+
+	/* Verify the CRC32 of the header data. */
+	computed_crc = (uint32_t) crc32(0, p, (int) hdr_size);
+	if(computed_crc != hdr_crc) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Header CRC error");
+
+		return ARCHIVE_FATAL;
+	}
+
+	/* If the checksum is OK, we proceed with parsing. */
+	if(ARCHIVE_OK != consume(a, hdr_size_len)) {
+		return ARCHIVE_EOF;
+	}
+
+	if(!read_var_sized(a, &header_id, NULL))
+		return ARCHIVE_EOF;
+
+	if(!read_var_sized(a, &header_flags, NULL))
+		return ARCHIVE_EOF;
+
+	rar->generic.split_after = (header_flags & HFL_SPLIT_AFTER) > 0;
+	rar->generic.split_before = (header_flags & HFL_SPLIT_BEFORE) > 0;
+	rar->generic.size = (int)hdr_size;
+	rar->generic.last_header_id = (int)header_id;
+	rar->main.endarc = 0;
+
+	/* Those are possible header ids in RARv5. */
+	switch(header_id) {
+		case HEAD_MAIN:
+			ret = process_head_main(a, rar, entry, header_flags);
+
+			/* Main header doesn't have any files in it, so it's
+			 * pointless to return to the caller. Retry to next
+			 * header, which should be HEAD_FILE/HEAD_SERVICE. */
+			if(ret == ARCHIVE_OK)
+				return ARCHIVE_RETRY;
+
+			return ret;
+		case HEAD_SERVICE:
+			ret = process_head_service(a, rar, entry, header_flags);
+			return ret;
+		case HEAD_FILE:
+			ret = process_head_file(a, rar, entry, header_flags);
+			return ret;
+		case HEAD_CRYPT:
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Encryption is not supported");
+			return ARCHIVE_FATAL;
+		case HEAD_ENDARC:
+			rar->main.endarc = 1;
+
+			/* After encountering an end of file marker, we need
+			 * to take into consideration if this archive is
+			 * continued in another file (i.e. is it part01.rar:
+			 * is there a part02.rar?) */
+			if(rar->main.volume) {
+				/* In case there is part02.rar, position the
+				 * read pointer in a proper place, so we can
+				 * resume parsing. */
+				ret = scan_for_signature(a);
+				if(ret == ARCHIVE_FATAL) {
+					return ARCHIVE_EOF;
+				} else {
+					if(rar->vol.expected_vol_no ==
+					    UINT_MAX) {
+						archive_set_error(&a->archive,
+						    ARCHIVE_ERRNO_FILE_FORMAT,
+						    "Header error");
+							return ARCHIVE_FATAL;
+					}
+
+					rar->vol.expected_vol_no =
+					    rar->main.vol_no + 1;
+					return ARCHIVE_OK;
+				}
+			} else {
+				return ARCHIVE_EOF;
+			}
+		case HEAD_MARK:
+			return ARCHIVE_EOF;
+		default:
+			if((header_flags & HFL_SKIP_IF_UNKNOWN) == 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Header type error");
+				return ARCHIVE_FATAL;
+			} else {
+				/* If the block is marked as 'skip if unknown',
+				 * do as the flag says: skip the block
+				 * instead on failing on it. */
+				return ARCHIVE_RETRY;
+			}
+	}
+
+#if !defined WIN32
+	// Not reached.
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+	    "Internal unpacker error");
+	return ARCHIVE_FATAL;
+#endif
+}
+
+static int skip_base_block(struct archive_read* a) {
+	int ret;
+	struct rar5* rar = get_context(a);
+
+	/* Create a new local archive_entry structure that will be operated on
+	 * by header reader; operations on this archive_entry will be discarded.
+	 */
+	struct archive_entry* entry = archive_entry_new();
+	ret = process_base_block(a, entry);
+
+	/* Discard operations on this archive_entry structure. */
+	archive_entry_free(entry);
+	if(ret == ARCHIVE_FATAL)
+		return ret;
+
+	if(rar->generic.last_header_id == 2 && rar->generic.split_before > 0)
+		return ARCHIVE_OK;
+
+	if(ret == ARCHIVE_OK)
+		return ARCHIVE_RETRY;
+	else
+		return ret;
+}
+
+static int rar5_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct rar5* rar = get_context(a);
+	int ret;
+
+	if(rar->header_initialized == 0) {
+		init_header(a);
+		rar->header_initialized = 1;
+	}
+
+	if(rar->skipped_magic == 0) {
+		if(ARCHIVE_OK != consume(a, sizeof(rar5_signature_xor))) {
+			return ARCHIVE_EOF;
+		}
+
+		rar->skipped_magic = 1;
+	}
+
+	do {
+		ret = process_base_block(a, entry);
+	} while(ret == ARCHIVE_RETRY ||
+			(rar->main.endarc > 0 && ret == ARCHIVE_OK));
+
+	return ret;
+}
+
+static void init_unpack(struct rar5* rar) {
+	rar->file.calculated_crc32 = 0;
+	init_window_mask(rar);
+
+	free(rar->cstate.window_buf);
+	free(rar->cstate.filtered_buf);
+
+	if(rar->cstate.window_size > 0) {
+		rar->cstate.window_buf = calloc(1, rar->cstate.window_size);
+		rar->cstate.filtered_buf = calloc(1, rar->cstate.window_size);
+	} else {
+		rar->cstate.window_buf = NULL;
+		rar->cstate.filtered_buf = NULL;
+	}
+
+	rar->cstate.write_ptr = 0;
+	rar->cstate.last_write_ptr = 0;
+
+	memset(&rar->cstate.bd, 0, sizeof(rar->cstate.bd));
+	memset(&rar->cstate.ld, 0, sizeof(rar->cstate.ld));
+	memset(&rar->cstate.dd, 0, sizeof(rar->cstate.dd));
+	memset(&rar->cstate.ldd, 0, sizeof(rar->cstate.ldd));
+	memset(&rar->cstate.rd, 0, sizeof(rar->cstate.rd));
+}
+
+static void update_crc(struct rar5* rar, const uint8_t* p, size_t to_read) {
+    int verify_crc;
+
+	if(rar->skip_mode) {
+#if defined CHECK_CRC_ON_SOLID_SKIP
+		verify_crc = 1;
+#else
+		verify_crc = 0;
+#endif
+	} else
+		verify_crc = 1;
+
+	if(verify_crc) {
+		/* Don't update CRC32 if the file doesn't have the
+		 * `stored_crc32` info filled in. */
+		if(rar->file.stored_crc32 > 0) {
+			rar->file.calculated_crc32 =
+				crc32(rar->file.calculated_crc32, p, to_read);
+		}
+
+		/* Check if the file uses an optional BLAKE2sp checksum
+		 * algorithm. */
+		if(rar->file.has_blake2 > 0) {
+			/* Return value of the `update` function is always 0,
+			 * so we can explicitly ignore it here. */
+			(void) blake2sp_update(&rar->file.b2state, p, to_read);
+		}
+	}
+}
+
+static int create_decode_tables(uint8_t* bit_length,
+    struct decode_table* table, int size)
+{
+	int code, upper_limit = 0, i, lc[16];
+	uint32_t decode_pos_clone[rar5_countof(table->decode_pos)];
+	ssize_t cur_len, quick_data_size;
+
+	memset(&lc, 0, sizeof(lc));
+	memset(table->decode_num, 0, sizeof(table->decode_num));
+	table->size = size;
+	table->quick_bits = size == HUFF_NC ? 10 : 7;
+
+	for(i = 0; i < size; i++) {
+		lc[bit_length[i] & 15]++;
+	}
+
+	lc[0] = 0;
+	table->decode_pos[0] = 0;
+	table->decode_len[0] = 0;
+
+	for(i = 1; i < 16; i++) {
+		upper_limit += lc[i];
+
+		table->decode_len[i] = upper_limit << (16 - i);
+		table->decode_pos[i] = table->decode_pos[i - 1] + lc[i - 1];
+
+		upper_limit <<= 1;
+	}
+
+	memcpy(decode_pos_clone, table->decode_pos, sizeof(decode_pos_clone));
+
+	for(i = 0; i < size; i++) {
+		uint8_t clen = bit_length[i] & 15;
+		if(clen > 0) {
+			int last_pos = decode_pos_clone[clen];
+			table->decode_num[last_pos] = i;
+			decode_pos_clone[clen]++;
+		}
+	}
+
+	quick_data_size = (int64_t)1 << table->quick_bits;
+	cur_len = 1;
+	for(code = 0; code < quick_data_size; code++) {
+		int bit_field = code << (16 - table->quick_bits);
+		int dist, pos;
+
+		while(cur_len < rar5_countof(table->decode_len) &&
+				bit_field >= table->decode_len[cur_len]) {
+			cur_len++;
+		}
+
+		table->quick_len[code] = (uint8_t) cur_len;
+
+		dist = bit_field - table->decode_len[cur_len - 1];
+		dist >>= (16 - cur_len);
+
+		pos = table->decode_pos[cur_len & 15] + dist;
+		if(cur_len < rar5_countof(table->decode_pos) && pos < size) {
+			table->quick_num[code] = table->decode_num[pos];
+		} else {
+			table->quick_num[code] = 0;
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int decode_number(struct archive_read* a, struct decode_table* table,
+    const uint8_t* p, uint16_t* num)
+{
+	int i, bits, dist;
+	uint16_t bitfield;
+	uint32_t pos;
+	struct rar5* rar = get_context(a);
+
+	if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) {
+		return ARCHIVE_EOF;
+	}
+
+	bitfield &= 0xfffe;
+
+	if(bitfield < table->decode_len[table->quick_bits]) {
+		int code = bitfield >> (16 - table->quick_bits);
+		skip_bits(rar, table->quick_len[code]);
+		*num = table->quick_num[code];
+		return ARCHIVE_OK;
+	}
+
+	bits = 15;
+
+	for(i = table->quick_bits + 1; i < 15; i++) {
+		if(bitfield < table->decode_len[i]) {
+			bits = i;
+			break;
+		}
+	}
+
+	skip_bits(rar, bits);
+
+	dist = bitfield - table->decode_len[bits - 1];
+	dist >>= (16 - bits);
+	pos = table->decode_pos[bits] + dist;
+
+	if(pos >= table->size)
+		pos = 0;
+
+	*num = table->decode_num[pos];
+	return ARCHIVE_OK;
+}
+
+/* Reads and parses Huffman tables from the beginning of the block. */
+static int parse_tables(struct archive_read* a, struct rar5* rar,
+    const uint8_t* p)
+{
+	int ret, value, i, w, idx = 0;
+	uint8_t bit_length[HUFF_BC],
+		table[HUFF_TABLE_SIZE],
+		nibble_mask = 0xF0,
+		nibble_shift = 4;
+
+	enum { ESCAPE = 15 };
+
+	/* The data for table generation is compressed using a simple RLE-like
+	 * algorithm when storing zeroes, so we need to unpack it first. */
+	for(w = 0, i = 0; w < HUFF_BC;) {
+		if(i >= rar->cstate.cur_block_size) {
+			/* Truncated data, can't continue. */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated data in huffman tables");
+			return ARCHIVE_FATAL;
+		}
+
+		value = (p[i] & nibble_mask) >> nibble_shift;
+
+		if(nibble_mask == 0x0F)
+			++i;
+
+		nibble_mask ^= 0xFF;
+		nibble_shift ^= 4;
+
+		/* Values smaller than 15 is data, so we write it directly.
+		 * Value 15 is a flag telling us that we need to unpack more
+		 * bytes. */
+		if(value == ESCAPE) {
+			value = (p[i] & nibble_mask) >> nibble_shift;
+			if(nibble_mask == 0x0F)
+				++i;
+			nibble_mask ^= 0xFF;
+			nibble_shift ^= 4;
+
+			if(value == 0) {
+				/* We sometimes need to write the actual value
+				 * of 15, so this case handles that. */
+				bit_length[w++] = ESCAPE;
+			} else {
+				int k;
+
+				/* Fill zeroes. */
+				for(k = 0; (k < value + 2) && (w < HUFF_BC);
+				    k++) {
+					bit_length[w++] = 0;
+				}
+			}
+		} else {
+			bit_length[w++] = value;
+		}
+	}
+
+	rar->bits.in_addr = i;
+	rar->bits.bit_addr = nibble_shift ^ 4;
+
+	ret = create_decode_tables(bit_length, &rar->cstate.bd, HUFF_BC);
+	if(ret != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Decoding huffman tables failed");
+		return ARCHIVE_FATAL;
+	}
+
+	for(i = 0; i < HUFF_TABLE_SIZE;) {
+		uint16_t num;
+
+		if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) {
+			/* Truncated data, can't continue. */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated data in huffman tables (#2)");
+			return ARCHIVE_FATAL;
+		}
+
+		ret = decode_number(a, &rar->cstate.bd, p, &num);
+		if(ret != ARCHIVE_OK) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Decoding huffman tables failed");
+			return ARCHIVE_FATAL;
+		}
+
+		if(num < 16) {
+			/* 0..15: store directly */
+			table[i] = (uint8_t) num;
+			i++;
+		} else if(num < 18) {
+			/* 16..17: repeat previous code */
+			uint16_t n;
+
+			if(ARCHIVE_OK != read_bits_16(rar, p, &n))
+				return ARCHIVE_EOF;
+
+			if(num == 16) {
+				n >>= 13;
+				n += 3;
+				skip_bits(rar, 3);
+			} else {
+				n >>= 9;
+				n += 11;
+				skip_bits(rar, 7);
+			}
+
+			if(i > 0) {
+				while(n-- > 0 && i < HUFF_TABLE_SIZE) {
+					table[i] = table[i - 1];
+					i++;
+				}
+			} else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Unexpected error when decoding "
+				    "huffman tables");
+				return ARCHIVE_FATAL;
+			}
+		} else {
+			/* other codes: fill with zeroes `n` times */
+			uint16_t n;
+
+			if(ARCHIVE_OK != read_bits_16(rar, p, &n))
+				return ARCHIVE_EOF;
+
+			if(num == 18) {
+				n >>= 13;
+				n += 3;
+				skip_bits(rar, 3);
+			} else {
+				n >>= 9;
+				n += 11;
+				skip_bits(rar, 7);
+			}
+
+			while(n-- > 0 && i < HUFF_TABLE_SIZE)
+				table[i++] = 0;
+		}
+	}
+
+	ret = create_decode_tables(&table[idx], &rar->cstate.ld, HUFF_NC);
+	if(ret != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		     "Failed to create literal table");
+		return ARCHIVE_FATAL;
+	}
+
+	idx += HUFF_NC;
+
+	ret = create_decode_tables(&table[idx], &rar->cstate.dd, HUFF_DC);
+	if(ret != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Failed to create distance table");
+		return ARCHIVE_FATAL;
+	}
+
+	idx += HUFF_DC;
+
+	ret = create_decode_tables(&table[idx], &rar->cstate.ldd, HUFF_LDC);
+	if(ret != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Failed to create lower bits of distances table");
+		return ARCHIVE_FATAL;
+	}
+
+	idx += HUFF_LDC;
+
+	ret = create_decode_tables(&table[idx], &rar->cstate.rd, HUFF_RC);
+	if(ret != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Failed to create repeating distances table");
+		return ARCHIVE_FATAL;
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Parses the block header, verifies its CRC byte, and saves the header
+ * fields inside the `hdr` pointer. */
+static int parse_block_header(struct archive_read* a, const uint8_t* p,
+    ssize_t* block_size, struct compressed_block_header* hdr)
+{
+	uint8_t calculated_cksum;
+	memcpy(hdr, p, sizeof(struct compressed_block_header));
+
+	if(bf_byte_count(hdr) > 2) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unsupported block header size (was %d, max is 2)",
+		    bf_byte_count(hdr));
+		return ARCHIVE_FATAL;
+	}
+
+	/* This should probably use bit reader interface in order to be more
+	 * future-proof. */
+	*block_size = 0;
+	switch(bf_byte_count(hdr)) {
+		/* 1-byte block size */
+		case 0:
+			*block_size = *(const uint8_t*) &p[2];
+			break;
+
+		/* 2-byte block size */
+		case 1:
+			*block_size = archive_le16dec(&p[2]);
+			break;
+
+		/* 3-byte block size */
+		case 2:
+			*block_size = archive_le32dec(&p[2]);
+			*block_size &= 0x00FFFFFF;
+			break;
+
+		/* Other block sizes are not supported. This case is not
+		 * reached, because we have an 'if' guard before the switch
+		 * that makes sure of it. */
+		default:
+			return ARCHIVE_FATAL;
+	}
+
+	/* Verify the block header checksum. 0x5A is a magic value and is
+	 * always * constant. */
+	calculated_cksum = 0x5A
+	    ^ (uint8_t) hdr->block_flags_u8
+	    ^ (uint8_t) *block_size
+	    ^ (uint8_t) (*block_size >> 8)
+	    ^ (uint8_t) (*block_size >> 16);
+
+	if(calculated_cksum != hdr->block_cksum) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Block checksum error: got 0x%x, expected 0x%x",
+		    hdr->block_cksum, calculated_cksum);
+
+		return ARCHIVE_FATAL;
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Convenience function used during filter processing. */
+static int parse_filter_data(struct rar5* rar, const uint8_t* p,
+    uint32_t* filter_data)
+{
+	int i, bytes;
+	uint32_t data = 0;
+
+	if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes))
+		return ARCHIVE_EOF;
+
+	bytes++;
+
+	for(i = 0; i < bytes; i++) {
+		uint16_t byte;
+
+		if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) {
+			return ARCHIVE_EOF;
+		}
+
+		/* Cast to uint32_t will ensure the shift operation will not
+		 * produce undefined result. */
+		data += ((uint32_t) byte >> 8) << (i * 8);
+		skip_bits(rar, 8);
+	}
+
+	*filter_data = data;
+	return ARCHIVE_OK;
+}
+
+/* Function is used during sanity checking. */
+static int is_valid_filter_block_start(struct rar5* rar,
+    uint32_t start)
+{
+	const int64_t block_start = (ssize_t) start + rar->cstate.write_ptr;
+	const int64_t last_bs = rar->cstate.last_block_start;
+	const ssize_t last_bl = rar->cstate.last_block_length;
+
+	if(last_bs == 0 || last_bl == 0) {
+		/* We didn't have any filters yet, so accept this offset. */
+		return 1;
+	}
+
+	if(block_start >= last_bs + last_bl) {
+		/* Current offset is bigger than last block's end offset, so
+		 * accept current offset. */
+		return 1;
+	}
+
+	/* Any other case is not a normal situation and we should fail. */
+	return 0;
+}
+
+/* The function will create a new filter, read its parameters from the input
+ * stream and add it to the filter collection. */
+static int parse_filter(struct archive_read* ar, const uint8_t* p) {
+	uint32_t block_start, block_length;
+	uint16_t filter_type;
+	struct filter_info* filt = NULL;
+	struct rar5* rar = get_context(ar);
+
+	/* Read the parameters from the input stream. */
+	if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start))
+		return ARCHIVE_EOF;
+
+	if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length))
+		return ARCHIVE_EOF;
+
+	if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type))
+		return ARCHIVE_EOF;
+
+	filter_type >>= 13;
+	skip_bits(rar, 3);
+
+	/* Perform some sanity checks on this filter parameters. Note that we
+	 * allow only DELTA, E8/E9 and ARM filters here, because rest of
+	 * filters are not used in RARv5. */
+
+	if(block_length < 4 ||
+	    block_length > 0x400000 ||
+	    filter_type > FILTER_ARM ||
+	    !is_valid_filter_block_start(rar, block_start))
+	{
+		archive_set_error(&ar->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Invalid filter encountered");
+		return ARCHIVE_FATAL;
+	}
+
+	/* Allocate a new filter. */
+	filt = add_new_filter(rar);
+	if(filt == NULL) {
+		archive_set_error(&ar->archive, ENOMEM,
+		    "Can't allocate memory for a filter descriptor.");
+		return ARCHIVE_FATAL;
+	}
+
+	filt->type = filter_type;
+	filt->block_start = rar->cstate.write_ptr + block_start;
+	filt->block_length = block_length;
+
+	rar->cstate.last_block_start = filt->block_start;
+	rar->cstate.last_block_length = filt->block_length;
+
+	/* Read some more data in case this is a DELTA filter. Other filter
+	 * types don't require any additional data over what was already
+	 * read. */
+	if(filter_type == FILTER_DELTA) {
+		int channels;
+
+		if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels))
+			return ARCHIVE_EOF;
+
+		filt->channels = channels + 1;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int decode_code_length(struct rar5* rar, const uint8_t* p,
+    uint16_t code)
+{
+	int lbits, length = 2;
+	if(code < 8) {
+		lbits = 0;
+		length += code;
+	} else {
+		lbits = code / 4 - 1;
+		length += (4 | (code & 3)) << lbits;
+	}
+
+	if(lbits > 0) {
+		int add;
+
+		if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add))
+			return -1;
+
+		length += add;
+	}
+
+	return length;
+}
+
+static int copy_string(struct archive_read* a, int len, int dist) {
+	struct rar5* rar = get_context(a);
+	const uint64_t cmask = rar->cstate.window_mask;
+	const uint64_t write_ptr = rar->cstate.write_ptr +
+	    rar->cstate.solid_offset;
+	int i;
+
+	if (rar->cstate.window_buf == NULL)
+		return ARCHIVE_FATAL;
+
+	/* The unpacker spends most of the time in this function. It would be
+	 * a good idea to introduce some optimizations here.
+	 *
+	 * Just remember that this loop treats buffers that overlap differently
+	 * than buffers that do not overlap. This is why a simple memcpy(3)
+	 * call will not be enough. */
+
+	for(i = 0; i < len; i++) {
+		const ssize_t write_idx = (write_ptr + i) & cmask;
+		const ssize_t read_idx = (write_ptr + i - dist) & cmask;
+		rar->cstate.window_buf[write_idx] =
+		    rar->cstate.window_buf[read_idx];
+	}
+
+	rar->cstate.write_ptr += len;
+	return ARCHIVE_OK;
+}
+
+static int do_uncompress_block(struct archive_read* a, const uint8_t* p) {
+	struct rar5* rar = get_context(a);
+	uint16_t num;
+	int ret;
+
+	const uint64_t cmask = rar->cstate.window_mask;
+	const struct compressed_block_header* hdr = &rar->last_block_hdr;
+	const uint8_t bit_size = 1 + bf_bit_size(hdr);
+
+	while(1) {
+		if(rar->cstate.write_ptr - rar->cstate.last_write_ptr >
+		    (rar->cstate.window_size >> 1)) {
+			/* Don't allow growing data by more than half of the
+			 * window size at a time. In such case, break the loop;
+			 *  next call to this function will continue processing
+			 *  from this moment. */
+			break;
+		}
+
+		if(rar->bits.in_addr > rar->cstate.cur_block_size - 1 ||
+		    (rar->bits.in_addr == rar->cstate.cur_block_size - 1 &&
+		    rar->bits.bit_addr >= bit_size))
+		{
+			/* If the program counter is here, it means the
+			 * function has finished processing the block. */
+			rar->cstate.block_parsing_finished = 1;
+			break;
+		}
+
+		/* Decode the next literal. */
+		if(ARCHIVE_OK != decode_number(a, &rar->cstate.ld, p, &num)) {
+			return ARCHIVE_EOF;
+		}
+
+		/* Num holds a decompression literal, or 'command code'.
+		 *
+		 * - Values lower than 256 are just bytes. Those codes
+		 *   can be stored in the output buffer directly.
+		 *
+		 * - Code 256 defines a new filter, which is later used to
+		 *   ransform the data block accordingly to the filter type.
+		 *   The data block needs to be fully uncompressed first.
+		 *
+		 * - Code bigger than 257 and smaller than 262 define
+		 *   a repetition pattern that should be copied from
+		 *   an already uncompressed chunk of data.
+		 */
+
+		if(num < 256) {
+			/* Directly store the byte. */
+			int64_t write_idx = rar->cstate.solid_offset +
+			    rar->cstate.write_ptr++;
+
+			rar->cstate.window_buf[write_idx & cmask] =
+			    (uint8_t) num;
+			continue;
+		} else if(num >= 262) {
+			uint16_t dist_slot;
+			int len = decode_code_length(rar, p, num - 262),
+				dbits,
+				dist = 1;
+
+			if(len == -1) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_PROGRAMMER,
+				    "Failed to decode the code length");
+
+				return ARCHIVE_FATAL;
+			}
+
+			if(ARCHIVE_OK != decode_number(a, &rar->cstate.dd, p,
+			    &dist_slot))
+			{
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_PROGRAMMER,
+				    "Failed to decode the distance slot");
+
+				return ARCHIVE_FATAL;
+			}
+
+			if(dist_slot < 4) {
+				dbits = 0;
+				dist += dist_slot;
+			} else {
+				dbits = dist_slot / 2 - 1;
+
+				/* Cast to uint32_t will make sure the shift
+				 * left operation won't produce undefined
+				 * result. Then, the uint32_t type will
+				 * be implicitly casted to int. */
+				dist += (uint32_t) (2 |
+				    (dist_slot & 1)) << dbits;
+			}
+
+			if(dbits > 0) {
+				if(dbits >= 4) {
+					uint32_t add = 0;
+					uint16_t low_dist;
+
+					if(dbits > 4) {
+						if(ARCHIVE_OK != read_bits_32(
+						    rar, p, &add)) {
+							/* Return EOF if we
+							 * can't read more
+							 * data. */
+							return ARCHIVE_EOF;
+						}
+
+						skip_bits(rar, dbits - 4);
+						add = (add >> (
+						    36 - dbits)) << 4;
+						dist += add;
+					}
+
+					if(ARCHIVE_OK != decode_number(a,
+					    &rar->cstate.ldd, p, &low_dist))
+					{
+						archive_set_error(&a->archive,
+						    ARCHIVE_ERRNO_PROGRAMMER,
+						    "Failed to decode the "
+						    "distance slot");
+
+						return ARCHIVE_FATAL;
+					}
+
+					if(dist >= INT_MAX - low_dist - 1) {
+						/* This only happens in
+						 * invalid archives. */
+						archive_set_error(&a->archive,
+						    ARCHIVE_ERRNO_FILE_FORMAT,
+						    "Distance pointer "
+						    "overflow");
+						return ARCHIVE_FATAL;
+					}
+
+					dist += low_dist;
+				} else {
+					/* dbits is one of [0,1,2,3] */
+					int add;
+
+					if(ARCHIVE_OK != read_consume_bits(rar,
+					     p, dbits, &add)) {
+						/* Return EOF if we can't read
+						 * more data. */
+						return ARCHIVE_EOF;
+					}
+
+					dist += add;
+				}
+			}
+
+			if(dist > 0x100) {
+				len++;
+
+				if(dist > 0x2000) {
+					len++;
+
+					if(dist > 0x40000) {
+						len++;
+					}
+				}
+			}
+
+			dist_cache_push(rar, dist);
+			rar->cstate.last_len = len;
+
+			if(ARCHIVE_OK != copy_string(a, len, dist))
+				return ARCHIVE_FATAL;
+
+			continue;
+		} else if(num == 256) {
+			/* Create a filter. */
+			ret = parse_filter(a, p);
+			if(ret != ARCHIVE_OK)
+				return ret;
+
+			continue;
+		} else if(num == 257) {
+			if(rar->cstate.last_len != 0) {
+				if(ARCHIVE_OK != copy_string(a,
+				    rar->cstate.last_len,
+				    rar->cstate.dist_cache[0]))
+				{
+					return ARCHIVE_FATAL;
+				}
+			}
+
+			continue;
+		} else {
+			/* num < 262 */
+			const int idx = num - 258;
+			const int dist = dist_cache_touch(rar, idx);
+
+			uint16_t len_slot;
+			int len;
+
+			if(ARCHIVE_OK != decode_number(a, &rar->cstate.rd, p,
+			    &len_slot)) {
+				return ARCHIVE_FATAL;
+			}
+
+			len = decode_code_length(rar, p, len_slot);
+			rar->cstate.last_len = len;
+
+			if(ARCHIVE_OK != copy_string(a, len, dist))
+				return ARCHIVE_FATAL;
+
+			continue;
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Binary search for the RARv5 signature. */
+static int scan_for_signature(struct archive_read* a) {
+	const uint8_t* p;
+	const int chunk_size = 512;
+	ssize_t i;
+	char signature[sizeof(rar5_signature_xor)];
+
+	/* If we're here, it means we're on an 'unknown territory' data.
+	 * There's no indication what kind of data we're reading here.
+	 * It could be some text comment, any kind of binary data,
+	 * digital sign, dragons, etc.
+	 *
+	 * We want to find a valid RARv5 magic header inside this unknown
+	 * data. */
+
+	/* Is it possible in libarchive to just skip everything until the
+	 * end of the file? If so, it would be a better approach than the
+	 * current implementation of this function. */
+
+	rar5_signature(signature);
+
+	while(1) {
+		if(!read_ahead(a, chunk_size, &p))
+			return ARCHIVE_EOF;
+
+		for(i = 0; i < chunk_size - (int)sizeof(rar5_signature_xor);
+		    i++) {
+			if(memcmp(&p[i], signature,
+			    sizeof(rar5_signature_xor)) == 0) {
+				/* Consume the number of bytes we've used to
+				 * search for the signature, as well as the
+				 * number of bytes used by the signature
+				 * itself. After this we should be standing
+				 * on a valid base block header. */
+				(void) consume(a,
+				    i + sizeof(rar5_signature_xor));
+				return ARCHIVE_OK;
+			}
+		}
+
+		consume(a, chunk_size);
+	}
+
+	return ARCHIVE_FATAL;
+}
+
+/* This function will switch the multivolume archive file to another file,
+ * i.e. from part03 to part 04. */
+static int advance_multivolume(struct archive_read* a) {
+	int lret;
+	struct rar5* rar = get_context(a);
+
+	/* A small state machine that will skip unnecessary data, needed to
+	 * switch from one multivolume to another. Such skipping is needed if
+	 * we want to be an stream-oriented (instead of file-oriented)
+	 * unpacker.
+	 *
+	 * The state machine starts with `rar->main.endarc` == 0. It also
+	 * assumes that current stream pointer points to some base block
+	 * header.
+	 *
+	 * The `endarc` field is being set when the base block parsing
+	 * function encounters the 'end of archive' marker.
+	 */
+
+	while(1) {
+		if(rar->main.endarc == 1) {
+			int looping = 1;
+
+			rar->main.endarc = 0;
+
+			while(looping) {
+				lret = skip_base_block(a);
+				switch(lret) {
+					case ARCHIVE_RETRY:
+						/* Continue looping. */
+						break;
+					case ARCHIVE_OK:
+						/* Break loop. */
+						looping = 0;
+						break;
+					default:
+						/* Forward any errors to the
+						 * caller. */
+						return lret;
+				}
+			}
+
+			break;
+		} else {
+			/* Skip current base block. In order to properly skip
+			 * it, we really need to simply parse it and discard
+			 * the results. */
+
+			lret = skip_base_block(a);
+			if(lret == ARCHIVE_FATAL || lret == ARCHIVE_FAILED)
+				return lret;
+
+			/* The `skip_base_block` function tells us if we
+			 * should continue with skipping, or we should stop
+			 * skipping. We're trying to skip everything up to
+			 * a base FILE block. */
+
+			if(lret != ARCHIVE_RETRY) {
+				/* If there was an error during skipping, or we
+				 * have just skipped a FILE base block... */
+
+				if(rar->main.endarc == 0) {
+					return lret;
+				} else {
+					continue;
+				}
+			}
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Merges the partial block from the first multivolume archive file, and
+ * partial block from the second multivolume archive file. The result is
+ * a chunk of memory containing the whole block, and the stream pointer
+ * is advanced to the next block in the second multivolume archive file. */
+static int merge_block(struct archive_read* a, ssize_t block_size,
+    const uint8_t** p)
+{
+	struct rar5* rar = get_context(a);
+	ssize_t cur_block_size, partial_offset = 0;
+	const uint8_t* lp;
+	int ret;
+
+	if(rar->merge_mode) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Recursive merge is not allowed");
+
+		return ARCHIVE_FATAL;
+	}
+
+	/* Set a flag that we're in the switching mode. */
+	rar->cstate.switch_multivolume = 1;
+
+	/* Reallocate the memory which will hold the whole block. */
+	if(rar->vol.push_buf)
+		free((void*) rar->vol.push_buf);
+
+	/* Increasing the allocation block by 8 is due to bit reading functions,
+	 * which are using additional 2 or 4 bytes. Allocating the block size
+	 * by exact value would make bit reader perform reads from invalid
+	 * memory block when reading the last byte from the buffer. */
+	rar->vol.push_buf = malloc(block_size + 8);
+	if(!rar->vol.push_buf) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for a merge block buffer.");
+		return ARCHIVE_FATAL;
+	}
+
+	/* Valgrind complains if the extension block for bit reader is not
+	 * initialized, so initialize it. */
+	memset(&rar->vol.push_buf[block_size], 0, 8);
+
+	/* A single block can span across multiple multivolume archive files,
+	 * so we use a loop here. This loop will consume enough multivolume
+	 * archive files until the whole block is read. */
+
+	while(1) {
+		/* Get the size of current block chunk in this multivolume
+		 * archive file and read it. */
+		cur_block_size = rar5_min(rar->file.bytes_remaining,
+		    block_size - partial_offset);
+
+		if(cur_block_size == 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Encountered block size == 0 during block merge");
+			return ARCHIVE_FATAL;
+		}
+
+		if(!read_ahead(a, cur_block_size, &lp))
+			return ARCHIVE_EOF;
+
+		/* Sanity check; there should never be a situation where this
+		 * function reads more data than the block's size. */
+		if(partial_offset + cur_block_size > block_size) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_PROGRAMMER,
+			    "Consumed too much data when merging blocks.");
+			return ARCHIVE_FATAL;
+		}
+
+		/* Merge previous block chunk with current block chunk,
+		 * or create first block chunk if this is our first
+		 * iteration. */
+		memcpy(&rar->vol.push_buf[partial_offset], lp, cur_block_size);
+
+		/* Advance the stream read pointer by this block chunk size. */
+		if(ARCHIVE_OK != consume(a, cur_block_size))
+			return ARCHIVE_EOF;
+
+		/* Update the pointers. `partial_offset` contains information
+		 * about the sum of merged block chunks. */
+		partial_offset += cur_block_size;
+		rar->file.bytes_remaining -= cur_block_size;
+
+		/* If `partial_offset` is the same as `block_size`, this means
+		 * we've merged all block chunks and we have a valid full
+		 * block. */
+		if(partial_offset == block_size) {
+			break;
+		}
+
+		/* If we don't have any bytes to read, this means we should
+		 * switch to another multivolume archive file. */
+		if(rar->file.bytes_remaining == 0) {
+			rar->merge_mode++;
+			ret = advance_multivolume(a);
+			rar->merge_mode--;
+			if(ret != ARCHIVE_OK) {
+				return ret;
+			}
+		}
+	}
+
+	*p = rar->vol.push_buf;
+
+	/* If we're here, we can resume unpacking by processing the block
+	 * pointed to by the `*p` memory pointer. */
+
+	return ARCHIVE_OK;
+}
+
+static int process_block(struct archive_read* a) {
+	const uint8_t* p;
+	struct rar5* rar = get_context(a);
+	int ret;
+
+	/* If we don't have any data to be processed, this most probably means
+	 * we need to switch to the next volume. */
+	if(rar->main.volume && rar->file.bytes_remaining == 0) {
+		ret = advance_multivolume(a);
+		if(ret != ARCHIVE_OK)
+			return ret;
+	}
+
+	if(rar->cstate.block_parsing_finished) {
+		ssize_t block_size;
+		ssize_t to_skip;
+		ssize_t cur_block_size;
+
+		/* The header size won't be bigger than 6 bytes. */
+		if(!read_ahead(a, 6, &p)) {
+			/* Failed to prefetch data block header. */
+			return ARCHIVE_EOF;
+		}
+
+		/*
+		 * Read block_size by parsing block header. Validate the header
+		 * by calculating CRC byte stored inside the header. Size of
+		 * the header is not constant (block size can be stored either
+		 * in 1 or 2 bytes), that's why block size is left out from the
+		 * `compressed_block_header` structure and returned by
+		 * `parse_block_header` as the second argument. */
+
+		ret = parse_block_header(a, p, &block_size,
+		    &rar->last_block_hdr);
+		if(ret != ARCHIVE_OK) {
+			return ret;
+		}
+
+		/* Skip block header. Next data is huffman tables,
+		 * if present. */
+		to_skip = sizeof(struct compressed_block_header) +
+			bf_byte_count(&rar->last_block_hdr) + 1;
+
+		if(ARCHIVE_OK != consume(a, to_skip))
+			return ARCHIVE_EOF;
+
+		rar->file.bytes_remaining -= to_skip;
+
+		/* The block size gives information about the whole block size,
+		 * but the block could be stored in split form when using
+		 * multi-volume archives. In this case, the block size will be
+		 * bigger than the actual data stored in this file. Remaining
+		 * part of the data will be in another file. */
+
+		cur_block_size =
+			rar5_min(rar->file.bytes_remaining, block_size);
+
+		if(block_size > rar->file.bytes_remaining) {
+			/* If current blocks' size is bigger than our data
+			 * size, this means we have a multivolume archive.
+			 * In this case, skip all base headers until the end
+			 * of the file, proceed to next "partXXX.rar" volume,
+			 * find its signature, skip all headers up to the first
+			 * FILE base header, and continue from there.
+			 *
+			 * Note that `merge_block` will update the `rar`
+			 * context structure quite extensively. */
+
+			ret = merge_block(a, block_size, &p);
+			if(ret != ARCHIVE_OK) {
+				return ret;
+			}
+
+			cur_block_size = block_size;
+
+			/* Current stream pointer should be now directly
+			 * *after* the block that spanned through multiple
+			 * archive files. `p` pointer should have the data of
+			 * the *whole* block (merged from partial blocks
+			 * stored in multiple archives files). */
+		} else {
+			rar->cstate.switch_multivolume = 0;
+
+			/* Read the whole block size into memory. This can take
+			 * up to  8 megabytes of memory in theoretical cases.
+			 * Might be worth to optimize this and use a standard
+			 * chunk of 4kb's. */
+			if(!read_ahead(a, 4 + cur_block_size, &p)) {
+				/* Failed to prefetch block data. */
+				return ARCHIVE_EOF;
+			}
+		}
+
+		rar->cstate.block_buf = p;
+		rar->cstate.cur_block_size = cur_block_size;
+		rar->cstate.block_parsing_finished = 0;
+
+		rar->bits.in_addr = 0;
+		rar->bits.bit_addr = 0;
+
+		if(bf_is_table_present(&rar->last_block_hdr)) {
+			/* Load Huffman tables. */
+			ret = parse_tables(a, rar, p);
+			if(ret != ARCHIVE_OK) {
+				/* Error during decompression of Huffman
+				 * tables. */
+				return ret;
+			}
+		}
+	} else {
+		/* Block parsing not finished, reuse previous memory buffer. */
+		p = rar->cstate.block_buf;
+	}
+
+	/* Uncompress the block, or a part of it, depending on how many bytes
+	 * will be generated by uncompressing the block.
+	 *
+	 * In case too many bytes will be generated, calling this function
+	 * again will resume the uncompression operation. */
+	ret = do_uncompress_block(a, p);
+	if(ret != ARCHIVE_OK) {
+		return ret;
+	}
+
+	if(rar->cstate.block_parsing_finished &&
+	    rar->cstate.switch_multivolume == 0 &&
+	    rar->cstate.cur_block_size > 0)
+	{
+		/* If we're processing a normal block, consume the whole
+		 * block. We can do this because we've already read the whole
+		 * block to memory. */
+		if(ARCHIVE_OK != consume(a, rar->cstate.cur_block_size))
+			return ARCHIVE_FATAL;
+
+		rar->file.bytes_remaining -= rar->cstate.cur_block_size;
+	} else if(rar->cstate.switch_multivolume) {
+		/* Don't consume the block if we're doing multivolume
+		 * processing. The volume switching function will consume
+		 * the proper count of bytes instead. */
+		rar->cstate.switch_multivolume = 0;
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Pops the `buf`, `size` and `offset` from the "data ready" stack.
+ *
+ * Returns ARCHIVE_OK when those arguments can be used, ARCHIVE_RETRY
+ * when there is no data on the stack. */
+static int use_data(struct rar5* rar, const void** buf, size_t* size,
+    int64_t* offset)
+{
+	int i;
+
+	for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
+		struct data_ready *d = &rar->cstate.dready[i];
+
+		if(d->used) {
+			if(buf)    *buf = d->buf;
+			if(size)   *size = d->size;
+			if(offset) *offset = d->offset;
+
+			d->used = 0;
+			return ARCHIVE_OK;
+		}
+	}
+
+	return ARCHIVE_RETRY;
+}
+
+/* Pushes the `buf`, `size` and `offset` arguments to the rar->cstate.dready
+ * FIFO stack. Those values will be popped from this stack by the `use_data`
+ * function. */
+static int push_data_ready(struct archive_read* a, struct rar5* rar,
+    const uint8_t* buf, size_t size, int64_t offset)
+{
+	int i;
+
+	/* Don't push if we're in skip mode. This is needed because solid
+	 * streams need full processing even if we're skipping data. After
+	 * fully processing the stream, we need to discard the generated bytes,
+	 * because we're interested only in the side effect: building up the
+	 * internal window circular buffer. This window buffer will be used
+	 * later during unpacking of requested data. */
+	if(rar->skip_mode)
+		return ARCHIVE_OK;
+
+	/* Sanity check. */
+	if(offset != rar->file.last_offset + rar->file.last_size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Sanity check error: output stream is not continuous");
+		return ARCHIVE_FATAL;
+	}
+
+	for(i = 0; i < rar5_countof(rar->cstate.dready); i++) {
+		struct data_ready* d = &rar->cstate.dready[i];
+		if(!d->used) {
+			d->used = 1;
+			d->buf = buf;
+			d->size = size;
+			d->offset = offset;
+
+			/* These fields are used only in sanity checking. */
+			rar->file.last_offset = offset;
+			rar->file.last_size = size;
+
+			/* Calculate the checksum of this new block before
+			 * submitting data to libarchive's engine. */
+			update_crc(rar, d->buf, d->size);
+
+			return ARCHIVE_OK;
+		}
+	}
+
+	/* Program counter will reach this code if the `rar->cstate.data_ready`
+	 * stack will be filled up so that no new entries will be allowed. The
+	 * code shouldn't allow such situation to occur. So we treat this case
+	 * as an internal error. */
+
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+	    "Error: premature end of data_ready stack");
+	return ARCHIVE_FATAL;
+}
+
+/* This function uncompresses the data that is stored in the <FILE> base
+ * block.
+ *
+ * The FILE base block looks like this:
+ *
+ * <header><huffman tables><block_1><block_2>...<block_n>
+ *
+ * The <header> is a block header, that is parsed in parse_block_header().
+ * It's a "compressed_block_header" structure, containing metadata needed
+ * to know when we should stop looking for more <block_n> blocks.
+ *
+ * <huffman tables> contain data needed to set up the huffman tables, needed
+ * for the actual decompression.
+ *
+ * Each <block_n> consists of series of literals:
+ *
+ * <literal><literal><literal>...<literal>
+ *
+ * Those literals generate the uncompression data. They operate on a circular
+ * buffer, sometimes writing raw data into it, sometimes referencing
+ * some previous data inside this buffer, and sometimes declaring a filter
+ * that will need to be executed on the data stored in the circular buffer.
+ * It all depends on the literal that is used.
+ *
+ * Sometimes blocks produce output data, sometimes they don't. For example, for
+ * some huge files that use lots of filters, sometimes a block is filled with
+ * only filter declaration literals. Such blocks won't produce any data in the
+ * circular buffer.
+ *
+ * Sometimes blocks will produce 4 bytes of data, and sometimes 1 megabyte,
+ * because a literal can reference previously decompressed data. For example,
+ * there can be a literal that says: 'append a byte 0xFE here', and after
+ * it another literal can say 'append 1 megabyte of data from circular buffer
+ * offset 0x12345'. This is how RAR format handles compressing repeated
+ * patterns.
+ *
+ * The RAR compressor creates those literals and the actual efficiency of
+ * compression depends on what those literals are. The literals can also
+ * be seen as a kind of a non-turing-complete virtual machine that simply
+ * tells the decompressor what it should do.
+ * */
+
+static int do_uncompress_file(struct archive_read* a) {
+	struct rar5* rar = get_context(a);
+	int ret;
+	int64_t max_end_pos;
+
+	if(!rar->cstate.initialized) {
+		/* Don't perform full context reinitialization if we're
+		 * processing a solid archive. */
+		if(!rar->main.solid || !rar->cstate.window_buf) {
+			init_unpack(rar);
+		}
+
+		rar->cstate.initialized = 1;
+	}
+
+	if(rar->cstate.all_filters_applied == 1) {
+		/* We use while(1) here, but standard case allows for just 1
+		 * iteration. The loop will iterate if process_block() didn't
+		 * generate any data at all. This can happen if the block
+		 * contains only filter definitions (this is common in big
+		 * files). */
+		while(1) {
+			ret = process_block(a);
+			if(ret == ARCHIVE_EOF || ret == ARCHIVE_FATAL)
+				return ret;
+
+			if(rar->cstate.last_write_ptr ==
+			    rar->cstate.write_ptr) {
+				/* The block didn't generate any new data,
+				 * so just process a new block. */
+				continue;
+			}
+
+			/* The block has generated some new data, so break
+			 * the loop. */
+			break;
+		}
+	}
+
+	/* Try to run filters. If filters won't be applied, it means that
+	 * insufficient data was generated. */
+	ret = apply_filters(a);
+	if(ret == ARCHIVE_RETRY) {
+		return ARCHIVE_OK;
+	} else if(ret == ARCHIVE_FATAL) {
+		return ARCHIVE_FATAL;
+	}
+
+	/* If apply_filters() will return ARCHIVE_OK, we can continue here. */
+
+	if(cdeque_size(&rar->cstate.filters) > 0) {
+		/* Check if we can write something before hitting first
+		 * filter. */
+		struct filter_info* flt;
+
+		/* Get the block_start offset from the first filter. */
+		if(CDE_OK != cdeque_front(&rar->cstate.filters,
+		    cdeque_filter_p(&flt)))
+		{
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_PROGRAMMER,
+			    "Can't read first filter");
+			return ARCHIVE_FATAL;
+		}
+
+		max_end_pos = rar5_min(flt->block_start,
+		    rar->cstate.write_ptr);
+	} else {
+		/* There are no filters defined, or all filters were applied.
+		 * This means we can just store the data without any
+		 * postprocessing. */
+		max_end_pos = rar->cstate.write_ptr;
+	}
+
+	if(max_end_pos == rar->cstate.last_write_ptr) {
+		/* We can't write anything yet. The block uncompression
+		 * function did not generate enough data, and no filter can be
+		 * applied. At the same time we don't have any data that can be
+		 *  stored without filter postprocessing. This means we need to
+		 *  wait for more data to be generated, so we can apply the
+		 * filters.
+		 *
+		 * Signal the caller that we need more data to be able to do
+		 * anything.
+		 */
+		return ARCHIVE_RETRY;
+	} else {
+		/* We can write the data before hitting the first filter.
+		 * So let's do it. The push_window_data() function will
+		 * effectively return the selected data block to the user
+		 * application. */
+		push_window_data(a, rar, rar->cstate.last_write_ptr,
+		    max_end_pos);
+		rar->cstate.last_write_ptr = max_end_pos;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int uncompress_file(struct archive_read* a) {
+	int ret;
+
+	while(1) {
+		/* Sometimes the uncompression function will return a
+		 * 'retry' signal. If this will happen, we have to retry
+		 * the function. */
+		ret = do_uncompress_file(a);
+		if(ret != ARCHIVE_RETRY)
+			return ret;
+	}
+}
+
+
+static int do_unstore_file(struct archive_read* a,
+    struct rar5* rar, const void** buf, size_t* size, int64_t* offset)
+{
+	size_t to_read;
+	const uint8_t* p;
+
+	if(rar->file.bytes_remaining == 0 && rar->main.volume > 0 &&
+	    rar->generic.split_after > 0)
+	{
+		int ret;
+
+		rar->cstate.switch_multivolume = 1;
+		ret = advance_multivolume(a);
+		rar->cstate.switch_multivolume = 0;
+
+		if(ret != ARCHIVE_OK) {
+			/* Failed to advance to next multivolume archive
+			 * file. */
+			return ret;
+		}
+	}
+
+	to_read = rar5_min(rar->file.bytes_remaining, 64 * 1024);
+	if(to_read == 0) {
+		return ARCHIVE_EOF;
+	}
+
+	if(!read_ahead(a, to_read, &p)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "I/O error when unstoring file");
+		return ARCHIVE_FATAL;
+	}
+
+	if(ARCHIVE_OK != consume(a, to_read)) {
+		return ARCHIVE_EOF;
+	}
+
+	if(buf)    *buf = p;
+	if(size)   *size = to_read;
+	if(offset) *offset = rar->cstate.last_unstore_ptr;
+
+	rar->file.bytes_remaining -= to_read;
+	rar->cstate.last_unstore_ptr += to_read;
+
+	update_crc(rar, p, to_read);
+	return ARCHIVE_OK;
+}
+
+static int do_unpack(struct archive_read* a, struct rar5* rar,
+    const void** buf, size_t* size, int64_t* offset)
+{
+	enum COMPRESSION_METHOD {
+		STORE = 0, FASTEST = 1, FAST = 2, NORMAL = 3, GOOD = 4,
+		BEST = 5
+	};
+
+	if(rar->file.service > 0) {
+		return do_unstore_file(a, rar, buf, size, offset);
+	} else {
+		switch(rar->cstate.method) {
+			case STORE:
+				return do_unstore_file(a, rar, buf, size,
+				    offset);
+			case FASTEST:
+				/* fallthrough */
+			case FAST:
+				/* fallthrough */
+			case NORMAL:
+				/* fallthrough */
+			case GOOD:
+				/* fallthrough */
+			case BEST:
+				return uncompress_file(a);
+			default:
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Compression method not supported: 0x%x",
+				    rar->cstate.method);
+
+				return ARCHIVE_FATAL;
+		}
+	}
+
+#if !defined WIN32
+	/* Not reached. */
+	return ARCHIVE_OK;
+#endif
+}
+
+static int verify_checksums(struct archive_read* a) {
+	int verify_crc;
+	struct rar5* rar = get_context(a);
+
+	/* Check checksums only when actually unpacking the data. There's no
+	 * need to calculate checksum when we're skipping data in solid archives
+	 * (skipping in solid archives is the same thing as unpacking compressed
+	 * data and discarding the result). */
+
+	if(!rar->skip_mode) {
+		/* Always check checksums if we're not in skip mode */
+		verify_crc = 1;
+	} else {
+		/* We can override the logic above with a compile-time option
+		 * NO_CRC_ON_SOLID_SKIP. This option is used during debugging,
+		 * and it will check checksums of unpacked data even when
+		 * we're skipping it. */
+
+#if defined CHECK_CRC_ON_SOLID_SKIP
+		/* Debug case */
+		verify_crc = 1;
+#else
+		/* Normal case */
+		verify_crc = 0;
+#endif
+	}
+
+	if(verify_crc) {
+		/* During unpacking, on each unpacked block we're calling the
+		 * update_crc() function. Since we are here, the unpacking
+		 * process is already over and we can check if calculated
+		 * checksum (CRC32 or BLAKE2sp) is the same as what is stored
+		 * in the archive. */
+		if(rar->file.stored_crc32 > 0) {
+			/* Check CRC32 only when the file contains a CRC32
+			 * value for this file. */
+
+			if(rar->file.calculated_crc32 !=
+			    rar->file.stored_crc32) {
+				/* Checksums do not match; the unpacked file
+				 * is corrupted. */
+
+				DEBUG_CODE {
+					printf("Checksum error: CRC32 "
+					    "(was: %08x, expected: %08x)\n",
+					    rar->file.calculated_crc32,
+					    rar->file.stored_crc32);
+				}
+
+#ifndef DONT_FAIL_ON_CRC_ERROR
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Checksum error: CRC32");
+				return ARCHIVE_FATAL;
+#endif
+			} else {
+				DEBUG_CODE {
+					printf("Checksum OK: CRC32 "
+					    "(%08x/%08x)\n",
+					    rar->file.stored_crc32,
+					    rar->file.calculated_crc32);
+				}
+			}
+		}
+
+		if(rar->file.has_blake2 > 0) {
+			/* BLAKE2sp is an optional checksum algorithm that is
+			 * added to RARv5 archives when using the `-htb` switch
+			 *  during creation of archive.
+			 *
+			 * We now finalize the hash calculation by calling the
+			 * `final` function. This will generate the final hash
+			 * value we can use to compare it with the BLAKE2sp
+			 * checksum that is stored in the archive.
+			 *
+			 * The return value of this `final` function is not
+			 * very helpful, as it guards only against improper use.
+ 			 * This is why we're explicitly ignoring it. */
+
+			uint8_t b2_buf[32];
+			(void) blake2sp_final(&rar->file.b2state, b2_buf, 32);
+
+			if(memcmp(&rar->file.blake2sp, b2_buf, 32) != 0) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Checksum error: BLAKE2");
+
+				return ARCHIVE_FATAL;
+#endif
+			}
+		}
+	}
+
+	/* Finalization for this file has been successfully completed. */
+	return ARCHIVE_OK;
+}
+
+static int verify_global_checksums(struct archive_read* a) {
+	return verify_checksums(a);
+}
+
+/*
+ * Decryption function for the magic signature pattern. Check the comment near
+ * the `rar5_signature_xor` symbol to read the rationale behind this.
+ */
+static void rar5_signature(char *buf) {
+		size_t i;
+
+		for(i = 0; i < sizeof(rar5_signature_xor); i++) {
+			buf[i] = rar5_signature_xor[i] ^ 0xA1;
+		}
+}
+
+static int rar5_read_data(struct archive_read *a, const void **buff,
+    size_t *size, int64_t *offset) {
+	int ret;
+	struct rar5* rar = get_context(a);
+
+	if(rar->file.dir > 0) {
+		/* Don't process any data if this file entry was declared
+		 * as a directory. This is needed, because entries marked as
+		 * directory doesn't have any dictionary buffer allocated, so
+		 * it's impossible to perform any decompression. */
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't decompress an entry marked as a directory");
+		return ARCHIVE_FAILED;
+	}
+
+	if(!rar->skip_mode && (rar->cstate.last_write_ptr > rar->file.unpacked_size)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Unpacker has written too many bytes");
+		return ARCHIVE_FATAL;
+	}
+
+	ret = use_data(rar, buff, size, offset);
+	if(ret == ARCHIVE_OK) {
+		return ret;
+	}
+
+	if(rar->file.eof == 1) {
+		return ARCHIVE_EOF;
+	}
+
+	ret = do_unpack(a, rar, buff, size, offset);
+	if(ret != ARCHIVE_OK) {
+		return ret;
+	}
+
+	if(rar->file.bytes_remaining == 0 &&
+			rar->cstate.last_write_ptr == rar->file.unpacked_size)
+	{
+		/* If all bytes of current file were processed, run
+		 * finalization.
+		 *
+		 * Finalization will check checksum against proper values. If
+		 * some of the checksums will not match, we'll return an error
+		 * value in the last `archive_read_data` call to signal an error
+		 * to the user. */
+
+		rar->file.eof = 1;
+		return verify_global_checksums(a);
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int rar5_read_data_skip(struct archive_read *a) {
+	struct rar5* rar = get_context(a);
+
+	if(rar->main.solid) {
+		/* In solid archives, instead of skipping the data, we need to
+		 * extract it, and dispose the result. The side effect of this
+		 * operation will be setting up the initial window buffer state
+		 * needed to be able to extract the selected file. */
+
+		int ret;
+
+		/* Make sure to process all blocks in the compressed stream. */
+		while(rar->file.bytes_remaining > 0) {
+			/* Setting the "skip mode" will allow us to skip
+			 * checksum checks during data skipping. Checking the
+			 * checksum of skipped data isn't really necessary and
+			 * it's only slowing things down.
+			 *
+			 * This is incremented instead of setting to 1 because
+			 * this data skipping function can be called
+			 * recursively. */
+			rar->skip_mode++;
+
+			/* We're disposing 1 block of data, so we use triple
+			 * NULLs in arguments. */
+			ret = rar5_read_data(a, NULL, NULL, NULL);
+
+			/* Turn off "skip mode". */
+			rar->skip_mode--;
+
+			if(ret < 0 || ret == ARCHIVE_EOF) {
+				/* Propagate any potential error conditions
+				 * to the caller. */
+				return ret;
+			}
+		}
+	} else {
+		/* In standard archives, we can just jump over the compressed
+		 * stream. Each file in non-solid archives starts from an empty
+		 * window buffer. */
+
+		if(ARCHIVE_OK != consume(a, rar->file.bytes_remaining)) {
+			return ARCHIVE_FATAL;
+		}
+
+		rar->file.bytes_remaining = 0;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int64_t rar5_seek_data(struct archive_read *a, int64_t offset,
+    int whence)
+{
+	(void) a;
+	(void) offset;
+	(void) whence;
+
+	/* We're a streaming unpacker, and we don't support seeking. */
+
+	return ARCHIVE_FATAL;
+}
+
+static int rar5_cleanup(struct archive_read *a) {
+	struct rar5* rar = get_context(a);
+
+	free(rar->cstate.window_buf);
+	free(rar->cstate.filtered_buf);
+
+	free(rar->vol.push_buf);
+
+	free_filters(rar);
+	cdeque_free(&rar->cstate.filters);
+
+	free(rar);
+	a->format->data = NULL;
+
+	return ARCHIVE_OK;
+}
+
+static int rar5_capabilities(struct archive_read * a) {
+	(void) a;
+	return 0;
+}
+
+static int rar5_has_encrypted_entries(struct archive_read *_a) {
+	(void) _a;
+
+	/* Unsupported for now. */
+	return ARCHIVE_READ_FORMAT_ENCRYPTION_UNSUPPORTED;
+}
+
+static int rar5_init(struct rar5* rar) {
+	memset(rar, 0, sizeof(struct rar5));
+
+	if(CDE_OK != cdeque_init(&rar->cstate.filters, 8192))
+		return ARCHIVE_FATAL;
+
+	return ARCHIVE_OK;
+}
+
+int archive_read_support_format_rar5(struct archive *_a) {
+	struct archive_read* ar;
+	int ret;
+	struct rar5* rar;
+
+	if(ARCHIVE_OK != (ret = get_archive_read(_a, &ar)))
+		return ret;
+
+	rar = malloc(sizeof(*rar));
+	if(rar == NULL) {
+		archive_set_error(&ar->archive, ENOMEM,
+		    "Can't allocate rar5 data");
+		return ARCHIVE_FATAL;
+	}
+
+	if(ARCHIVE_OK != rar5_init(rar)) {
+		archive_set_error(&ar->archive, ENOMEM,
+		    "Can't allocate rar5 filter buffer");
+		return ARCHIVE_FATAL;
+	}
+
+	ret = __archive_read_register_format(ar,
+	    rar,
+	    "rar5",
+	    rar5_bid,
+	    rar5_options,
+	    rar5_read_header,
+	    rar5_read_data,
+	    rar5_read_data_skip,
+	    rar5_seek_data,
+	    rar5_cleanup,
+	    rar5_capabilities,
+	    rar5_has_encrypted_entries);
+
+	if(ret != ARCHIVE_OK) {
+		(void) rar5_cleanup(ar);
+	}
+
+	return ret;
+}
diff --git a/c/archive_read_support_format_raw.c b/c/archive_read_support_format_raw.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_raw.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 2003-2009 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: head/lib/libarchive/archive_read_support_format_raw.c 201107 2009-12-28 03:25:33Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+struct raw_info {
+	int64_t offset; /* Current position in the file. */
+	int64_t unconsumed;
+	int     end_of_file;
+};
+
+static int	archive_read_format_raw_bid(struct archive_read *, int);
+static int	archive_read_format_raw_cleanup(struct archive_read *);
+static int	archive_read_format_raw_read_data(struct archive_read *,
+		    const void **, size_t *, int64_t *);
+static int	archive_read_format_raw_read_data_skip(struct archive_read *);
+static int	archive_read_format_raw_read_header(struct archive_read *,
+		    struct archive_entry *);
+
+int
+archive_read_support_format_raw(struct archive *_a)
+{
+	struct raw_info *info;
+	struct archive_read *a = (struct archive_read *)_a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_raw");
+
+	info = (struct raw_info *)calloc(1, sizeof(*info));
+	if (info == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate raw_info data");
+		return (ARCHIVE_FATAL);
+	}
+
+	r = __archive_read_register_format(a,
+	    info,
+	    "raw",
+	    archive_read_format_raw_bid,
+	    NULL,
+	    archive_read_format_raw_read_header,
+	    archive_read_format_raw_read_data,
+	    archive_read_format_raw_read_data_skip,
+	    NULL,
+	    archive_read_format_raw_cleanup,
+	    NULL,
+	    NULL);
+	if (r != ARCHIVE_OK)
+		free(info);
+	return (r);
+}
+
+/*
+ * Bid 1 if this is a non-empty file.  Anyone who can really support
+ * this should outbid us, so it should generally be safe to use "raw"
+ * in conjunction with other formats.  But, this could really confuse
+ * folks if there are bid errors or minor file damage, so we don't
+ * include "raw" as part of support_format_all().
+ */
+static int
+archive_read_format_raw_bid(struct archive_read *a, int best_bid)
+{
+	if (best_bid < 1 && __archive_read_ahead(a, 1, NULL) != NULL)
+		return (1);
+	return (-1);
+}
+
+/*
+ * Mock up a fake header.
+ */
+static int
+archive_read_format_raw_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct raw_info *info;
+
+	info = (struct raw_info *)(a->format->data);
+	if (info->end_of_file)
+		return (ARCHIVE_EOF);
+
+	a->archive.archive_format = ARCHIVE_FORMAT_RAW;
+	a->archive.archive_format_name = "raw";
+	archive_entry_set_pathname(entry, "data");
+	archive_entry_set_filetype(entry, AE_IFREG);
+	archive_entry_set_perm(entry, 0644);
+	/* I'm deliberately leaving most fields unset here. */
+
+	/* Let the filter fill out any fields it might have. */
+	return __archive_read_header(a, entry);
+}
+
+static int
+archive_read_format_raw_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	struct raw_info *info;
+	ssize_t avail;
+
+	info = (struct raw_info *)(a->format->data);
+
+	/* Consume the bytes we read last time. */
+	if (info->unconsumed) {
+		__archive_read_consume(a, info->unconsumed);
+		info->unconsumed = 0;
+	}
+
+	if (info->end_of_file)
+		return (ARCHIVE_EOF);
+
+	/* Get whatever bytes are immediately available. */
+	*buff = __archive_read_ahead(a, 1, &avail);
+	if (avail > 0) {
+		/* Return the bytes we just read */
+		*size = avail;
+		*offset = info->offset;
+		info->offset += *size;
+		info->unconsumed = avail;
+		return (ARCHIVE_OK);
+	} else if (0 == avail) {
+		/* Record and return end-of-file. */
+		info->end_of_file = 1;
+		*size = 0;
+		*offset = info->offset;
+		return (ARCHIVE_EOF);
+	} else {
+		/* Record and return an error. */
+		*size = 0;
+		*offset = info->offset;
+		return ((int)avail);
+	}
+}
+
+static int
+archive_read_format_raw_read_data_skip(struct archive_read *a)
+{
+	struct raw_info *info = (struct raw_info *)(a->format->data);
+
+	/* Consume the bytes we read last time. */
+	if (info->unconsumed) {
+		__archive_read_consume(a, info->unconsumed);
+		info->unconsumed = 0;
+	}
+	info->end_of_file = 1;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_raw_cleanup(struct archive_read *a)
+{
+	struct raw_info *info;
+
+	info = (struct raw_info *)(a->format->data);
+	free(info);
+	a->format->data = NULL;
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_read_support_format_tar.c b/c/archive_read_support_format_tar.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_tar.c
@@ -0,0 +1,2922 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
+ * 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: head/lib/libarchive/archive_read_support_format_tar.c 201161 2009-12-29 05:44:39Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stddef.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_acl_private.h" /* For ACL parsing routines. */
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#define tar_min(a,b) ((a) < (b) ? (a) : (b))
+
+/*
+ * Layout of POSIX 'ustar' tar header.
+ */
+struct archive_entry_header_ustar {
+	char	name[100];
+	char	mode[8];
+	char	uid[8];
+	char	gid[8];
+	char	size[12];
+	char	mtime[12];
+	char	checksum[8];
+	char	typeflag[1];
+	char	linkname[100];	/* "old format" header ends here */
+	char	magic[6];	/* For POSIX: "ustar\0" */
+	char	version[2];	/* For POSIX: "00" */
+	char	uname[32];
+	char	gname[32];
+	char	rdevmajor[8];
+	char	rdevminor[8];
+	char	prefix[155];
+};
+
+/*
+ * Structure of GNU tar header
+ */
+struct gnu_sparse {
+	char	offset[12];
+	char	numbytes[12];
+};
+
+struct archive_entry_header_gnutar {
+	char	name[100];
+	char	mode[8];
+	char	uid[8];
+	char	gid[8];
+	char	size[12];
+	char	mtime[12];
+	char	checksum[8];
+	char	typeflag[1];
+	char	linkname[100];
+	char	magic[8];  /* "ustar  \0" (note blank/blank/null at end) */
+	char	uname[32];
+	char	gname[32];
+	char	rdevmajor[8];
+	char	rdevminor[8];
+	char	atime[12];
+	char	ctime[12];
+	char	offset[12];
+	char	longnames[4];
+	char	unused[1];
+	struct gnu_sparse sparse[4];
+	char	isextended[1];
+	char	realsize[12];
+	/*
+	 * Old GNU format doesn't use POSIX 'prefix' field; they use
+	 * the 'L' (longname) entry instead.
+	 */
+};
+
+/*
+ * Data specific to this format.
+ */
+struct sparse_block {
+	struct sparse_block	*next;
+	int64_t	offset;
+	int64_t	remaining;
+	int hole;
+};
+
+struct tar {
+	struct archive_string	 acl_text;
+	struct archive_string	 entry_pathname;
+	/* For "GNU.sparse.name" and other similar path extensions. */
+	struct archive_string	 entry_pathname_override;
+	struct archive_string	 entry_linkpath;
+	struct archive_string	 entry_uname;
+	struct archive_string	 entry_gname;
+	struct archive_string	 longlink;
+	struct archive_string	 longname;
+	struct archive_string	 pax_header;
+	struct archive_string	 pax_global;
+	struct archive_string	 line;
+	int			 pax_hdrcharset_binary;
+	int			 header_recursion_depth;
+	int64_t			 entry_bytes_remaining;
+	int64_t			 entry_offset;
+	int64_t			 entry_padding;
+	int64_t 		 entry_bytes_unconsumed;
+	int64_t			 realsize;
+	int			 sparse_allowed;
+	struct sparse_block	*sparse_list;
+	struct sparse_block	*sparse_last;
+	int64_t			 sparse_offset;
+	int64_t			 sparse_numbytes;
+	int			 sparse_gnu_major;
+	int			 sparse_gnu_minor;
+	char			 sparse_gnu_pending;
+
+	struct archive_string	 localname;
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv;
+	struct archive_string_conv *sconv_acl;
+	struct archive_string_conv *sconv_default;
+	int			 init_default_conversion;
+	int			 compat_2x;
+	int			 process_mac_extensions;
+	int			 read_concatenated_archives;
+	int			 realsize_override;
+};
+
+static int	archive_block_is_null(const char *p);
+static char	*base64_decode(const char *, size_t, size_t *);
+static int	gnu_add_sparse_entry(struct archive_read *, struct tar *,
+		    int64_t offset, int64_t remaining);
+
+static void	gnu_clear_sparse_list(struct tar *);
+static int	gnu_sparse_old_read(struct archive_read *, struct tar *,
+		    const struct archive_entry_header_gnutar *header, size_t *);
+static int	gnu_sparse_old_parse(struct archive_read *, struct tar *,
+		    const struct gnu_sparse *sparse, int length);
+static int	gnu_sparse_01_parse(struct archive_read *, struct tar *,
+		    const char *);
+static ssize_t	gnu_sparse_10_read(struct archive_read *, struct tar *,
+			size_t *);
+static int	header_Solaris_ACL(struct archive_read *,  struct tar *,
+		    struct archive_entry *, const void *, size_t *);
+static int	header_common(struct archive_read *,  struct tar *,
+		    struct archive_entry *, const void *);
+static int	header_old_tar(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *);
+static int	header_pax_extensions(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *, size_t *);
+static int	header_pax_global(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, size_t *);
+static int	header_longlink(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, size_t *);
+static int	header_longname(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, size_t *);
+static int	read_mac_metadata_blob(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, size_t *);
+static int	header_volume(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, size_t *);
+static int	header_ustar(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h);
+static int	header_gnutar(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, size_t *);
+static int	archive_read_format_tar_bid(struct archive_read *, int);
+static int	archive_read_format_tar_options(struct archive_read *,
+		    const char *, const char *);
+static int	archive_read_format_tar_cleanup(struct archive_read *);
+static int	archive_read_format_tar_read_data(struct archive_read *a,
+		    const void **buff, size_t *size, int64_t *offset);
+static int	archive_read_format_tar_skip(struct archive_read *a);
+static int	archive_read_format_tar_read_header(struct archive_read *,
+		    struct archive_entry *);
+static int	checksum(struct archive_read *, const void *);
+static int 	pax_attribute(struct archive_read *, struct tar *,
+		    struct archive_entry *, const char *key, const char *value,
+		    size_t value_length);
+static int	pax_attribute_acl(struct archive_read *, struct tar *,
+		    struct archive_entry *, const char *, int);
+static int	pax_attribute_xattr(struct archive_entry *, const char *,
+		    const char *);
+static int 	pax_header(struct archive_read *, struct tar *,
+		    struct archive_entry *, struct archive_string *);
+static void	pax_time(const char *, int64_t *sec, long *nanos);
+static ssize_t	readline(struct archive_read *, struct tar *, const char **,
+		    ssize_t limit, size_t *);
+static int	read_body_to_string(struct archive_read *, struct tar *,
+		    struct archive_string *, const void *h, size_t *);
+static int	solaris_sparse_parse(struct archive_read *, struct tar *,
+		    struct archive_entry *, const char *);
+static int64_t	tar_atol(const char *, size_t);
+static int64_t	tar_atol10(const char *, size_t);
+static int64_t	tar_atol256(const char *, size_t);
+static int64_t	tar_atol8(const char *, size_t);
+static int	tar_read_header(struct archive_read *, struct tar *,
+		    struct archive_entry *, size_t *);
+static int	tohex(int c);
+static char	*url_decode(const char *);
+static void	tar_flush_unconsumed(struct archive_read *, size_t *);
+
+
+int
+archive_read_support_format_gnutar(struct archive *a)
+{
+	archive_check_magic(a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_gnutar");
+	return (archive_read_support_format_tar(a));
+}
+
+
+int
+archive_read_support_format_tar(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct tar *tar;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_tar");
+
+	tar = (struct tar *)calloc(1, sizeof(*tar));
+	if (tar == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate tar data");
+		return (ARCHIVE_FATAL);
+	}
+#ifdef HAVE_COPYFILE_H
+	/* Set this by default on Mac OS. */
+	tar->process_mac_extensions = 1;
+#endif
+
+	r = __archive_read_register_format(a, tar, "tar",
+	    archive_read_format_tar_bid,
+	    archive_read_format_tar_options,
+	    archive_read_format_tar_read_header,
+	    archive_read_format_tar_read_data,
+	    archive_read_format_tar_skip,
+	    NULL,
+	    archive_read_format_tar_cleanup,
+	    NULL,
+	    NULL);
+
+	if (r != ARCHIVE_OK)
+		free(tar);
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_tar_cleanup(struct archive_read *a)
+{
+	struct tar *tar;
+
+	tar = (struct tar *)(a->format->data);
+	gnu_clear_sparse_list(tar);
+	archive_string_free(&tar->acl_text);
+	archive_string_free(&tar->entry_pathname);
+	archive_string_free(&tar->entry_pathname_override);
+	archive_string_free(&tar->entry_linkpath);
+	archive_string_free(&tar->entry_uname);
+	archive_string_free(&tar->entry_gname);
+	archive_string_free(&tar->line);
+	archive_string_free(&tar->pax_global);
+	archive_string_free(&tar->pax_header);
+	archive_string_free(&tar->longname);
+	archive_string_free(&tar->longlink);
+	archive_string_free(&tar->localname);
+	free(tar);
+	(a->format->data) = NULL;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Validate number field
+ *
+ * This has to be pretty lenient in order to accommodate the enormous
+ * variety of tar writers in the world:
+ *  = POSIX (IEEE Std 1003.1-1988) ustar requires octal values with leading
+ *    zeros and allows fields to be terminated with space or null characters
+ *  = Many writers use different termination (in particular, libarchive
+ *    omits terminator bytes to squeeze one or two more digits)
+ *  = Many writers pad with space and omit leading zeros
+ *  = GNU tar and star write base-256 values if numbers are too
+ *    big to be represented in octal
+ *
+ *  Examples of specific tar headers that we should support:
+ *  = Perl Archive::Tar terminates uid, gid, devminor and devmajor with two
+ *    null bytes, pads size with spaces and other numeric fields with zeroes
+ *  = plexus-archiver prior to 2.6.3 (before switching to commons-compress)
+ *    may have uid and gid fields filled with spaces without any octal digits
+ *    at all and pads all numeric fields with spaces
+ *
+ * This should tolerate all variants in use.  It will reject a field
+ * where the writer just left garbage after a trailing NUL.
+ */
+static int
+validate_number_field(const char* p_field, size_t i_size)
+{
+	unsigned char marker = (unsigned char)p_field[0];
+	if (marker == 128 || marker == 255 || marker == 0) {
+		/* Base-256 marker, there's nothing we can check. */
+		return 1;
+	} else {
+		/* Must be octal */
+		size_t i = 0;
+		/* Skip any leading spaces */
+		while (i < i_size && p_field[i] == ' ') {
+			++i;
+		}
+		/* Skip octal digits. */
+		while (i < i_size && p_field[i] >= '0' && p_field[i] <= '7') {
+			++i;
+		}
+		/* Any remaining characters must be space or NUL padding. */
+		while (i < i_size) {
+			if (p_field[i] != ' ' && p_field[i] != 0) {
+				return 0;
+			}
+			++i;
+		}
+		return 1;
+	}
+}
+
+static int
+archive_read_format_tar_bid(struct archive_read *a, int best_bid)
+{
+	int bid;
+	const char *h;
+	const struct archive_entry_header_ustar *header;
+
+	(void)best_bid; /* UNUSED */
+
+	bid = 0;
+
+	/* Now let's look at the actual header and see if it matches. */
+	h = __archive_read_ahead(a, 512, NULL);
+	if (h == NULL)
+		return (-1);
+
+	/* If it's an end-of-archive mark, we can handle it. */
+	if (h[0] == 0 && archive_block_is_null(h)) {
+		/*
+		 * Usually, I bid the number of bits verified, but
+		 * in this case, 4096 seems excessive so I picked 10 as
+		 * an arbitrary but reasonable-seeming value.
+		 */
+		return (10);
+	}
+
+	/* If it's not an end-of-archive mark, it must have a valid checksum.*/
+	if (!checksum(a, h))
+		return (0);
+	bid += 48;  /* Checksum is usually 6 octal digits. */
+
+	header = (const struct archive_entry_header_ustar *)h;
+
+	/* Recognize POSIX formats. */
+	if ((memcmp(header->magic, "ustar\0", 6) == 0)
+	    && (memcmp(header->version, "00", 2) == 0))
+		bid += 56;
+
+	/* Recognize GNU tar format. */
+	if ((memcmp(header->magic, "ustar ", 6) == 0)
+	    && (memcmp(header->version, " \0", 2) == 0))
+		bid += 56;
+
+	/* Type flag must be null, digit or A-Z, a-z. */
+	if (header->typeflag[0] != 0 &&
+	    !( header->typeflag[0] >= '0' && header->typeflag[0] <= '9') &&
+	    !( header->typeflag[0] >= 'A' && header->typeflag[0] <= 'Z') &&
+	    !( header->typeflag[0] >= 'a' && header->typeflag[0] <= 'z') )
+		return (0);
+	bid += 2;  /* 6 bits of variation in an 8-bit field leaves 2 bits. */
+
+	/*
+	 * Check format of mode/uid/gid/mtime/size/rdevmajor/rdevminor fields.
+	 */
+	if (bid > 0 && (
+	    validate_number_field(header->mode, sizeof(header->mode)) == 0
+	    || validate_number_field(header->uid, sizeof(header->uid)) == 0
+	    || validate_number_field(header->gid, sizeof(header->gid)) == 0
+	    || validate_number_field(header->mtime, sizeof(header->mtime)) == 0
+	    || validate_number_field(header->size, sizeof(header->size)) == 0
+	    || validate_number_field(header->rdevmajor, sizeof(header->rdevmajor)) == 0
+	    || validate_number_field(header->rdevminor, sizeof(header->rdevminor)) == 0)) {
+		bid = 0;
+	}
+
+	return (bid);
+}
+
+static int
+archive_read_format_tar_options(struct archive_read *a,
+    const char *key, const char *val)
+{
+	struct tar *tar;
+	int ret = ARCHIVE_FAILED;
+
+	tar = (struct tar *)(a->format->data);
+	if (strcmp(key, "compat-2x")  == 0) {
+		/* Handle UTF-8 filenames as libarchive 2.x */
+		tar->compat_2x = (val != NULL && val[0] != 0);
+		tar->init_default_conversion = tar->compat_2x;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "tar: hdrcharset option needs a character-set name");
+		else {
+			tar->opt_sconv =
+			    archive_string_conversion_from_charset(
+				&a->archive, val, 0);
+			if (tar->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	} else if (strcmp(key, "mac-ext") == 0) {
+		tar->process_mac_extensions = (val != NULL && val[0] != 0);
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "read_concatenated_archives") == 0) {
+		tar->read_concatenated_archives = (val != NULL && val[0] != 0);
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+/* utility function- this exists to centralize the logic of tracking
+ * how much unconsumed data we have floating around, and to consume
+ * anything outstanding since we're going to do read_aheads
+ */
+static void
+tar_flush_unconsumed(struct archive_read *a, size_t *unconsumed)
+{
+	if (*unconsumed) {
+/*
+		void *data = (void *)__archive_read_ahead(a, *unconsumed, NULL);
+		 * this block of code is to poison claimed unconsumed space, ensuring
+		 * things break if it is in use still.
+		 * currently it WILL break things, so enable it only for debugging this issue
+		if (data) {
+			memset(data, 0xff, *unconsumed);
+		}
+*/
+		__archive_read_consume(a, *unconsumed);
+		*unconsumed = 0;
+	}
+}
+
+/*
+ * The function invoked by archive_read_next_header().  This
+ * just sets up a few things and then calls the internal
+ * tar_read_header() function below.
+ */
+static int
+archive_read_format_tar_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	/*
+	 * When converting tar archives to cpio archives, it is
+	 * essential that each distinct file have a distinct inode
+	 * number.  To simplify this, we keep a static count here to
+	 * assign fake dev/inode numbers to each tar entry.  Note that
+	 * pax format archives may overwrite this with something more
+	 * useful.
+	 *
+	 * Ideally, we would track every file read from the archive so
+	 * that we could assign the same dev/ino pair to hardlinks,
+	 * but the memory required to store a complete lookup table is
+	 * probably not worthwhile just to support the relatively
+	 * obscure tar->cpio conversion case.
+	 */
+	static int default_inode;
+	static int default_dev;
+	struct tar *tar;
+	const char *p;
+	const wchar_t *wp;
+	int r;
+	size_t l, unconsumed = 0;
+
+	/* Assign default device/inode values. */
+	archive_entry_set_dev(entry, 1 + default_dev); /* Don't use zero. */
+	archive_entry_set_ino(entry, ++default_inode); /* Don't use zero. */
+	/* Limit generated st_ino number to 16 bits. */
+	if (default_inode >= 0xffff) {
+		++default_dev;
+		default_inode = 0;
+	}
+
+	tar = (struct tar *)(a->format->data);
+	tar->entry_offset = 0;
+	gnu_clear_sparse_list(tar);
+	tar->realsize = -1; /* Mark this as "unset" */
+	tar->realsize_override = 0;
+
+	/* Setup default string conversion. */
+	tar->sconv = tar->opt_sconv;
+	if (tar->sconv == NULL) {
+		if (!tar->init_default_conversion) {
+			tar->sconv_default =
+			    archive_string_default_conversion_for_read(&(a->archive));
+			tar->init_default_conversion = 1;
+		}
+		tar->sconv = tar->sconv_default;
+	}
+
+	r = tar_read_header(a, tar, entry, &unconsumed);
+
+	tar_flush_unconsumed(a, &unconsumed);
+
+	/*
+	 * "non-sparse" files are really just sparse files with
+	 * a single block.
+	 */
+	if (tar->sparse_list == NULL) {
+		if (gnu_add_sparse_entry(a, tar, 0, tar->entry_bytes_remaining)
+		    != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	} else {
+		struct sparse_block *sb;
+
+		for (sb = tar->sparse_list; sb != NULL; sb = sb->next) {
+			if (!sb->hole)
+				archive_entry_sparse_add_entry(entry,
+				    sb->offset, sb->remaining);
+		}
+	}
+
+	if (r == ARCHIVE_OK && archive_entry_filetype(entry) == AE_IFREG) {
+		/*
+		 * "Regular" entry with trailing '/' is really
+		 * directory: This is needed for certain old tar
+		 * variants and even for some broken newer ones.
+		 */
+		if ((wp = archive_entry_pathname_w(entry)) != NULL) {
+			l = wcslen(wp);
+			if (l > 0 && wp[l - 1] == L'/') {
+				archive_entry_set_filetype(entry, AE_IFDIR);
+			}
+		} else if ((p = archive_entry_pathname(entry)) != NULL) {
+			l = strlen(p);
+			if (l > 0 && p[l - 1] == '/') {
+				archive_entry_set_filetype(entry, AE_IFDIR);
+			}
+		}
+	}
+	return (r);
+}
+
+static int
+archive_read_format_tar_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	ssize_t bytes_read;
+	struct tar *tar;
+	struct sparse_block *p;
+
+	tar = (struct tar *)(a->format->data);
+
+	for (;;) {
+		/* Remove exhausted entries from sparse list. */
+		while (tar->sparse_list != NULL &&
+		    tar->sparse_list->remaining == 0) {
+			p = tar->sparse_list;
+			tar->sparse_list = p->next;
+			free(p);
+		}
+
+		if (tar->entry_bytes_unconsumed) {
+			__archive_read_consume(a, tar->entry_bytes_unconsumed);
+			tar->entry_bytes_unconsumed = 0;
+		}
+
+		/* If we're at end of file, return EOF. */
+		if (tar->sparse_list == NULL ||
+		    tar->entry_bytes_remaining == 0) {
+			if (__archive_read_consume(a, tar->entry_padding) < 0)
+				return (ARCHIVE_FATAL);
+			tar->entry_padding = 0;
+			*buff = NULL;
+			*size = 0;
+			*offset = tar->realsize;
+			return (ARCHIVE_EOF);
+		}
+
+		*buff = __archive_read_ahead(a, 1, &bytes_read);
+		if (bytes_read < 0)
+			return (ARCHIVE_FATAL);
+		if (*buff == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Truncated tar archive");
+			return (ARCHIVE_FATAL);
+		}
+		if (bytes_read > tar->entry_bytes_remaining)
+			bytes_read = (ssize_t)tar->entry_bytes_remaining;
+		/* Don't read more than is available in the
+		 * current sparse block. */
+		if (tar->sparse_list->remaining < bytes_read)
+			bytes_read = (ssize_t)tar->sparse_list->remaining;
+		*size = bytes_read;
+		*offset = tar->sparse_list->offset;
+		tar->sparse_list->remaining -= bytes_read;
+		tar->sparse_list->offset += bytes_read;
+		tar->entry_bytes_remaining -= bytes_read;
+		tar->entry_bytes_unconsumed = bytes_read;
+
+		if (!tar->sparse_list->hole)
+			return (ARCHIVE_OK);
+		/* Current is hole data and skip this. */
+	}
+}
+
+static int
+archive_read_format_tar_skip(struct archive_read *a)
+{
+	int64_t bytes_skipped;
+	int64_t request;
+	struct sparse_block *p;
+	struct tar* tar;
+
+	tar = (struct tar *)(a->format->data);
+
+	/* Do not consume the hole of a sparse file. */
+	request = 0;
+	for (p = tar->sparse_list; p != NULL; p = p->next) {
+		if (!p->hole) {
+			if (p->remaining >= INT64_MAX - request) {
+				return ARCHIVE_FATAL;
+			}
+			request += p->remaining;
+		}
+	}
+	if (request > tar->entry_bytes_remaining)
+		request = tar->entry_bytes_remaining;
+	request += tar->entry_padding + tar->entry_bytes_unconsumed;
+
+	bytes_skipped = __archive_read_consume(a, request);
+	if (bytes_skipped < 0)
+		return (ARCHIVE_FATAL);
+
+	tar->entry_bytes_remaining = 0;
+	tar->entry_bytes_unconsumed = 0;
+	tar->entry_padding = 0;
+
+	/* Free the sparse list. */
+	gnu_clear_sparse_list(tar);
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * This function recursively interprets all of the headers associated
+ * with a single entry.
+ */
+static int
+tar_read_header(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, size_t *unconsumed)
+{
+	ssize_t bytes;
+	int err, eof_vol_header;
+	const char *h;
+	const struct archive_entry_header_ustar *header;
+	const struct archive_entry_header_gnutar *gnuheader;
+
+	eof_vol_header = 0;
+
+	/* Loop until we find a workable header record. */
+	for (;;) {
+		tar_flush_unconsumed(a, unconsumed);
+
+		/* Read 512-byte header record */
+		h = __archive_read_ahead(a, 512, &bytes);
+		if (bytes < 0)
+			return ((int)bytes);
+		if (bytes == 0) { /* EOF at a block boundary. */
+			/* Some writers do omit the block of nulls. <sigh> */
+			return (ARCHIVE_EOF);
+		}
+		if (bytes < 512) {  /* Short block at EOF; this is bad. */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated tar archive");
+			return (ARCHIVE_FATAL);
+		}
+		*unconsumed = 512;
+
+		/* Header is workable if it's not an end-of-archive mark. */
+		if (h[0] != 0 || !archive_block_is_null(h))
+			break;
+
+		/* Ensure format is set for archives with only null blocks. */
+		if (a->archive.archive_format_name == NULL) {
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+			a->archive.archive_format_name = "tar";
+		}
+
+		if (!tar->read_concatenated_archives) {
+			/* Try to consume a second all-null record, as well. */
+			tar_flush_unconsumed(a, unconsumed);
+			h = __archive_read_ahead(a, 512, NULL);
+			if (h != NULL && h[0] == 0 && archive_block_is_null(h))
+				__archive_read_consume(a, 512);
+			archive_clear_error(&a->archive);
+			return (ARCHIVE_EOF);
+		}
+
+		/*
+		 * We're reading concatenated archives, ignore this block and
+		 * loop to get the next.
+		 */
+	}
+
+	/*
+	 * Note: If the checksum fails and we return ARCHIVE_RETRY,
+	 * then the client is likely to just retry.  This is a very
+	 * crude way to search for the next valid header!
+	 *
+	 * TODO: Improve this by implementing a real header scan.
+	 */
+	if (!checksum(a, h)) {
+		tar_flush_unconsumed(a, unconsumed);
+		archive_set_error(&a->archive, EINVAL, "Damaged tar archive");
+		return (ARCHIVE_RETRY); /* Retryable: Invalid header */
+	}
+
+	if (++tar->header_recursion_depth > 32) {
+		tar_flush_unconsumed(a, unconsumed);
+		archive_set_error(&a->archive, EINVAL, "Too many special headers");
+		return (ARCHIVE_WARN);
+	}
+
+	/* Determine the format variant. */
+	header = (const struct archive_entry_header_ustar *)h;
+
+	switch(header->typeflag[0]) {
+	case 'A': /* Solaris tar ACL */
+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+		a->archive.archive_format_name = "Solaris tar";
+		err = header_Solaris_ACL(a, tar, entry, h, unconsumed);
+		break;
+	case 'g': /* POSIX-standard 'g' header. */
+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+		a->archive.archive_format_name = "POSIX pax interchange format";
+		err = header_pax_global(a, tar, entry, h, unconsumed);
+		if (err == ARCHIVE_EOF)
+			return (err);
+		break;
+	case 'K': /* Long link name (GNU tar, others) */
+		err = header_longlink(a, tar, entry, h, unconsumed);
+		break;
+	case 'L': /* Long filename (GNU tar, others) */
+		err = header_longname(a, tar, entry, h, unconsumed);
+		break;
+	case 'V': /* GNU volume header */
+		err = header_volume(a, tar, entry, h, unconsumed);
+		if (err == ARCHIVE_EOF)
+			eof_vol_header = 1;
+		break;
+	case 'X': /* Used by SUN tar; same as 'x'. */
+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+		a->archive.archive_format_name =
+		    "POSIX pax interchange format (Sun variant)";
+		err = header_pax_extensions(a, tar, entry, h, unconsumed);
+		break;
+	case 'x': /* POSIX-standard 'x' header. */
+		a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+		a->archive.archive_format_name = "POSIX pax interchange format";
+		err = header_pax_extensions(a, tar, entry, h, unconsumed);
+		break;
+	default:
+		gnuheader = (const struct archive_entry_header_gnutar *)h;
+		if (memcmp(gnuheader->magic, "ustar  \0", 8) == 0) {
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
+			a->archive.archive_format_name = "GNU tar format";
+			err = header_gnutar(a, tar, entry, h, unconsumed);
+		} else if (memcmp(header->magic, "ustar", 5) == 0) {
+			if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
+				a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
+				a->archive.archive_format_name = "POSIX ustar format";
+			}
+			err = header_ustar(a, tar, entry, h);
+		} else {
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+			a->archive.archive_format_name = "tar (non-POSIX)";
+			err = header_old_tar(a, tar, entry, h);
+		}
+	}
+	if (err == ARCHIVE_FATAL)
+		return (err);
+
+	tar_flush_unconsumed(a, unconsumed);
+
+	h = NULL;
+	header = NULL;
+
+	--tar->header_recursion_depth;
+	/* Yuck.  Apple's design here ends up storing long pathname
+	 * extensions for both the AppleDouble extension entry and the
+	 * regular entry.
+	 */
+	if ((err == ARCHIVE_WARN || err == ARCHIVE_OK) &&
+	    tar->header_recursion_depth == 0 &&
+	    tar->process_mac_extensions) {
+		int err2 = read_mac_metadata_blob(a, tar, entry, h, unconsumed);
+		if (err2 < err)
+			err = err2;
+	}
+
+	/* We return warnings or success as-is.  Anything else is fatal. */
+	if (err == ARCHIVE_WARN || err == ARCHIVE_OK) {
+		if (tar->sparse_gnu_pending) {
+			if (tar->sparse_gnu_major == 1 &&
+			    tar->sparse_gnu_minor == 0) {
+				ssize_t bytes_read;
+
+				tar->sparse_gnu_pending = 0;
+				/* Read initial sparse map. */
+				bytes_read = gnu_sparse_10_read(a, tar, unconsumed);
+				if (bytes_read < 0)
+					return ((int)bytes_read);
+				tar->entry_bytes_remaining -= bytes_read;
+			} else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Unrecognized GNU sparse file format");
+				return (ARCHIVE_WARN);
+			}
+			tar->sparse_gnu_pending = 0;
+		}
+		return (err);
+	}
+	if (err == ARCHIVE_EOF) {
+		if (!eof_vol_header) {
+			/* EOF when recursively reading a header is bad. */
+			archive_set_error(&a->archive, EINVAL,
+			    "Damaged tar archive");
+		} else {
+			/* If we encounter just a GNU volume header treat
+			 * this situation as an empty archive */
+			return (ARCHIVE_EOF);
+		}
+	}
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Return true if block checksum is correct.
+ */
+static int
+checksum(struct archive_read *a, const void *h)
+{
+	const unsigned char *bytes;
+	const struct archive_entry_header_ustar	*header;
+	int check, sum;
+	size_t i;
+
+	(void)a; /* UNUSED */
+	bytes = (const unsigned char *)h;
+	header = (const struct archive_entry_header_ustar *)h;
+
+	/* Checksum field must hold an octal number */
+	for (i = 0; i < sizeof(header->checksum); ++i) {
+		char c = header->checksum[i];
+		if (c != ' ' && c != '\0' && (c < '0' || c > '7'))
+			return 0;
+	}
+
+	/*
+	 * Test the checksum.  Note that POSIX specifies _unsigned_
+	 * bytes for this calculation.
+	 */
+	sum = (int)tar_atol(header->checksum, sizeof(header->checksum));
+	check = 0;
+	for (i = 0; i < 148; i++)
+		check += (unsigned char)bytes[i];
+	for (; i < 156; i++)
+		check += 32;
+	for (; i < 512; i++)
+		check += (unsigned char)bytes[i];
+	if (sum == check)
+		return (1);
+
+	/*
+	 * Repeat test with _signed_ bytes, just in case this archive
+	 * was created by an old BSD, Solaris, or HP-UX tar with a
+	 * broken checksum calculation.
+	 */
+	check = 0;
+	for (i = 0; i < 148; i++)
+		check += (signed char)bytes[i];
+	for (; i < 156; i++)
+		check += 32;
+	for (; i < 512; i++)
+		check += (signed char)bytes[i];
+	if (sum == check)
+		return (1);
+
+	return (0);
+}
+
+/*
+ * Return true if this block contains only nulls.
+ */
+static int
+archive_block_is_null(const char *p)
+{
+	unsigned i;
+
+	for (i = 0; i < 512; i++)
+		if (*p++)
+			return (0);
+	return (1);
+}
+
+/*
+ * Interpret 'A' Solaris ACL header
+ */
+static int
+header_Solaris_ACL(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+	const struct archive_entry_header_ustar *header;
+	size_t size;
+	int err, acl_type;
+	int64_t type;
+	char *acl, *p;
+
+	/*
+	 * read_body_to_string adds a NUL terminator, but we need a little
+	 * more to make sure that we don't overrun acl_text later.
+	 */
+	header = (const struct archive_entry_header_ustar *)h;
+	size = (size_t)tar_atol(header->size, sizeof(header->size));
+	err = read_body_to_string(a, tar, &(tar->acl_text), h, unconsumed);
+	if (err != ARCHIVE_OK)
+		return (err);
+
+	/* Recursively read next header */
+	err = tar_read_header(a, tar, entry, unconsumed);
+	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+		return (err);
+
+	/* TODO: Examine the first characters to see if this
+	 * is an AIX ACL descriptor.  We'll likely never support
+	 * them, but it would be polite to recognize and warn when
+	 * we do see them. */
+
+	/* Leading octal number indicates ACL type and number of entries. */
+	p = acl = tar->acl_text.s;
+	type = 0;
+	while (*p != '\0' && p < acl + size) {
+		if (*p < '0' || *p > '7') {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Malformed Solaris ACL attribute (invalid digit)");
+			return(ARCHIVE_WARN);
+		}
+		type <<= 3;
+		type += *p - '0';
+		if (type > 077777777) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Malformed Solaris ACL attribute (count too large)");
+			return (ARCHIVE_WARN);
+		}
+		p++;
+	}
+	switch ((int)type & ~0777777) {
+	case 01000000:
+		/* POSIX.1e ACL */
+		acl_type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
+		break;
+	case 03000000:
+		/* NFSv4 ACL */
+		acl_type = ARCHIVE_ENTRY_ACL_TYPE_NFS4;
+		break;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Malformed Solaris ACL attribute (unsupported type %o)",
+		    (int)type);
+		return (ARCHIVE_WARN);
+	}
+	p++;
+
+	if (p >= acl + size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Malformed Solaris ACL attribute (body overflow)");
+		return(ARCHIVE_WARN);
+	}
+
+	/* ACL text is null-terminated; find the end. */
+	size -= (p - acl);
+	acl = p;
+
+	while (*p != '\0' && p < acl + size)
+		p++;
+
+	if (tar->sconv_acl == NULL) {
+		tar->sconv_acl = archive_string_conversion_from_charset(
+		    &(a->archive), "UTF-8", 1);
+		if (tar->sconv_acl == NULL)
+			return (ARCHIVE_FATAL);
+	}
+	archive_strncpy(&(tar->localname), acl, p - acl);
+	err = archive_acl_from_text_l(archive_entry_acl(entry),
+	    tar->localname.s, acl_type, tar->sconv_acl);
+	if (err != ARCHIVE_OK) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for ACL");
+		} else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Malformed Solaris ACL attribute (unparsable)");
+	}
+	return (err);
+}
+
+/*
+ * Interpret 'K' long linkname header.
+ */
+static int
+header_longlink(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+	int err;
+
+	err = read_body_to_string(a, tar, &(tar->longlink), h, unconsumed);
+	if (err != ARCHIVE_OK)
+		return (err);
+	err = tar_read_header(a, tar, entry, unconsumed);
+	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+		return (err);
+	/* Set symlink if symlink already set, else hardlink. */
+	archive_entry_copy_link(entry, tar->longlink.s);
+	return (ARCHIVE_OK);
+}
+
+static int
+set_conversion_failed_error(struct archive_read *a,
+    struct archive_string_conv *sconv, const char *name)
+{
+	if (errno == ENOMEM) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for %s", name);
+		return (ARCHIVE_FATAL);
+	}
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "%s can't be converted from %s to current locale.",
+	    name, archive_string_conversion_charset_name(sconv));
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Interpret 'L' long filename header.
+ */
+static int
+header_longname(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+	int err;
+
+	err = read_body_to_string(a, tar, &(tar->longname), h, unconsumed);
+	if (err != ARCHIVE_OK)
+		return (err);
+	/* Read and parse "real" header, then override name. */
+	err = tar_read_header(a, tar, entry, unconsumed);
+	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+		return (err);
+	if (archive_entry_copy_pathname_l(entry, tar->longname.s,
+	    archive_strlen(&(tar->longname)), tar->sconv) != 0)
+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+	return (err);
+}
+
+
+/*
+ * Interpret 'V' GNU tar volume header.
+ */
+static int
+header_volume(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+	(void)h;
+
+	/* Just skip this and read the next header. */
+	return (tar_read_header(a, tar, entry, unconsumed));
+}
+
+/*
+ * Read body of an archive entry into an archive_string object.
+ */
+static int
+read_body_to_string(struct archive_read *a, struct tar *tar,
+    struct archive_string *as, const void *h, size_t *unconsumed)
+{
+	int64_t size;
+	const struct archive_entry_header_ustar *header;
+	const void *src;
+
+	(void)tar; /* UNUSED */
+	header = (const struct archive_entry_header_ustar *)h;
+	size  = tar_atol(header->size, sizeof(header->size));
+	if ((size > 1048576) || (size < 0)) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Special header too large");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Fail if we can't make our buffer big enough. */
+	if (archive_string_ensure(as, (size_t)size+1) == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "No memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	tar_flush_unconsumed(a, unconsumed);
+
+	/* Read the body into the string. */
+	*unconsumed = (size_t)((size + 511) & ~ 511);
+	src = __archive_read_ahead(a, *unconsumed, NULL);
+	if (src == NULL) {
+		*unconsumed = 0;
+		return (ARCHIVE_FATAL);
+	}
+	memcpy(as->s, src, (size_t)size);
+	as->s[size] = '\0';
+	as->length = (size_t)size;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Parse out common header elements.
+ *
+ * This would be the same as header_old_tar, except that the
+ * filename is handled slightly differently for old and POSIX
+ * entries  (POSIX entries support a 'prefix').  This factoring
+ * allows header_old_tar and header_ustar
+ * to handle filenames differently, while still putting most of the
+ * common parsing into one place.
+ */
+static int
+header_common(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
+{
+	const struct archive_entry_header_ustar	*header;
+	char	tartype;
+	int     err = ARCHIVE_OK;
+
+	header = (const struct archive_entry_header_ustar *)h;
+	if (header->linkname[0])
+		archive_strncpy(&(tar->entry_linkpath),
+		    header->linkname, sizeof(header->linkname));
+	else
+		archive_string_empty(&(tar->entry_linkpath));
+
+	/* Parse out the numeric fields (all are octal) */
+	archive_entry_set_mode(entry,
+		(mode_t)tar_atol(header->mode, sizeof(header->mode)));
+	archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid)));
+	archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid)));
+	tar->entry_bytes_remaining = tar_atol(header->size, sizeof(header->size));
+	if (tar->entry_bytes_remaining < 0) {
+		tar->entry_bytes_remaining = 0;
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Tar entry has negative size");
+		return (ARCHIVE_FATAL);
+	}
+	if (tar->entry_bytes_remaining == INT64_MAX) {
+		/* Note: tar_atol returns INT64_MAX on overflow */
+		tar->entry_bytes_remaining = 0;
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Tar entry size overflow");
+		return (ARCHIVE_FATAL);
+	}
+	tar->realsize = tar->entry_bytes_remaining;
+	archive_entry_set_size(entry, tar->entry_bytes_remaining);
+	archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0);
+
+	/* Handle the tar type flag appropriately. */
+	tartype = header->typeflag[0];
+
+	switch (tartype) {
+	case '1': /* Hard link */
+		if (archive_entry_copy_hardlink_l(entry, tar->entry_linkpath.s,
+		    archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) {
+			err = set_conversion_failed_error(a, tar->sconv,
+			    "Linkname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+		}
+		/*
+		 * The following may seem odd, but: Technically, tar
+		 * does not store the file type for a "hard link"
+		 * entry, only the fact that it is a hard link.  So, I
+		 * leave the type zero normally.  But, pax interchange
+		 * format allows hard links to have data, which
+		 * implies that the underlying entry is a regular
+		 * file.
+		 */
+		if (archive_entry_size(entry) > 0)
+			archive_entry_set_filetype(entry, AE_IFREG);
+
+		/*
+		 * A tricky point: Traditionally, tar readers have
+		 * ignored the size field when reading hardlink
+		 * entries, and some writers put non-zero sizes even
+		 * though the body is empty.  POSIX blessed this
+		 * convention in the 1988 standard, but broke with
+		 * this tradition in 2001 by permitting hardlink
+		 * entries to store valid bodies in pax interchange
+		 * format, but not in ustar format.  Since there is no
+		 * hard and fast way to distinguish pax interchange
+		 * from earlier archives (the 'x' and 'g' entries are
+		 * optional, after all), we need a heuristic.
+		 */
+		if (archive_entry_size(entry) == 0) {
+			/* If the size is already zero, we're done. */
+		}  else if (a->archive.archive_format
+		    == ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE) {
+			/* Definitely pax extended; must obey hardlink size. */
+		} else if (a->archive.archive_format == ARCHIVE_FORMAT_TAR
+		    || a->archive.archive_format == ARCHIVE_FORMAT_TAR_GNUTAR)
+		{
+			/* Old-style or GNU tar: we must ignore the size. */
+			archive_entry_set_size(entry, 0);
+			tar->entry_bytes_remaining = 0;
+		} else if (archive_read_format_tar_bid(a, 50) > 50) {
+			/*
+			 * We don't know if it's pax: If the bid
+			 * function sees a valid ustar header
+			 * immediately following, then let's ignore
+			 * the hardlink size.
+			 */
+			archive_entry_set_size(entry, 0);
+			tar->entry_bytes_remaining = 0;
+		}
+		/*
+		 * TODO: There are still two cases I'd like to handle:
+		 *   = a ustar non-pax archive with a hardlink entry at
+		 *     end-of-archive.  (Look for block of nulls following?)
+		 *   = a pax archive that has not seen any pax headers
+		 *     and has an entry which is a hardlink entry storing
+		 *     a body containing an uncompressed tar archive.
+		 * The first is worth addressing; I don't see any reliable
+		 * way to deal with the second possibility.
+		 */
+		break;
+	case '2': /* Symlink */
+		archive_entry_set_filetype(entry, AE_IFLNK);
+		archive_entry_set_size(entry, 0);
+		tar->entry_bytes_remaining = 0;
+		if (archive_entry_copy_symlink_l(entry, tar->entry_linkpath.s,
+		    archive_strlen(&(tar->entry_linkpath)), tar->sconv) != 0) {
+			err = set_conversion_failed_error(a, tar->sconv,
+			    "Linkname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+		}
+		break;
+	case '3': /* Character device */
+		archive_entry_set_filetype(entry, AE_IFCHR);
+		archive_entry_set_size(entry, 0);
+		tar->entry_bytes_remaining = 0;
+		break;
+	case '4': /* Block device */
+		archive_entry_set_filetype(entry, AE_IFBLK);
+		archive_entry_set_size(entry, 0);
+		tar->entry_bytes_remaining = 0;
+		break;
+	case '5': /* Dir */
+		archive_entry_set_filetype(entry, AE_IFDIR);
+		archive_entry_set_size(entry, 0);
+		tar->entry_bytes_remaining = 0;
+		break;
+	case '6': /* FIFO device */
+		archive_entry_set_filetype(entry, AE_IFIFO);
+		archive_entry_set_size(entry, 0);
+		tar->entry_bytes_remaining = 0;
+		break;
+	case 'D': /* GNU incremental directory type */
+		/*
+		 * No special handling is actually required here.
+		 * It might be nice someday to preprocess the file list and
+		 * provide it to the client, though.
+		 */
+		archive_entry_set_filetype(entry, AE_IFDIR);
+		break;
+	case 'M': /* GNU "Multi-volume" (remainder of file from last archive)*/
+		/*
+		 * As far as I can tell, this is just like a regular file
+		 * entry, except that the contents should be _appended_ to
+		 * the indicated file at the indicated offset.  This may
+		 * require some API work to fully support.
+		 */
+		break;
+	case 'N': /* Old GNU "long filename" entry. */
+		/* The body of this entry is a script for renaming
+		 * previously-extracted entries.  Ugh.  It will never
+		 * be supported by libarchive. */
+		archive_entry_set_filetype(entry, AE_IFREG);
+		break;
+	case 'S': /* GNU sparse files */
+		/*
+		 * Sparse files are really just regular files with
+		 * sparse information in the extended area.
+		 */
+		/* FALLTHROUGH */
+	case '0':
+		/*
+		 * Enable sparse file "read" support only for regular
+		 * files and explicit GNU sparse files.  However, we
+		 * don't allow non-standard file types to be sparse.
+		 */
+		tar->sparse_allowed = 1;
+		/* FALLTHROUGH */
+	default: /* Regular file  and non-standard types */
+		/*
+		 * Per POSIX: non-recognized types should always be
+		 * treated as regular files.
+		 */
+		archive_entry_set_filetype(entry, AE_IFREG);
+		break;
+	}
+	return (err);
+}
+
+/*
+ * Parse out header elements for "old-style" tar archives.
+ */
+static int
+header_old_tar(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
+{
+	const struct archive_entry_header_ustar	*header;
+	int err = ARCHIVE_OK, err2;
+
+	/* Copy filename over (to ensure null termination). */
+	header = (const struct archive_entry_header_ustar *)h;
+	if (archive_entry_copy_pathname_l(entry,
+	    header->name, sizeof(header->name), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
+
+	/* Grab rest of common fields */
+	err2 = header_common(a, tar, entry, h);
+	if (err > err2)
+		err = err2;
+
+	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+	return (err);
+}
+
+/*
+ * Read a Mac AppleDouble-encoded blob of file metadata,
+ * if there is one.
+ */
+static int
+read_mac_metadata_blob(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+	int64_t size;
+	const void *data;
+	const char *p, *name;
+	const wchar_t *wp, *wname;
+
+	(void)h; /* UNUSED */
+
+	wname = wp = archive_entry_pathname_w(entry);
+	if (wp != NULL) {
+		/* Find the last path element. */
+		for (; *wp != L'\0'; ++wp) {
+			if (wp[0] == '/' && wp[1] != L'\0')
+				wname = wp + 1;
+		}
+		/*
+		 * If last path element starts with "._", then
+		 * this is a Mac extension.
+		 */
+		if (wname[0] != L'.' || wname[1] != L'_' || wname[2] == L'\0')
+			return ARCHIVE_OK;
+	} else {
+		/* Find the last path element. */
+		name = p = archive_entry_pathname(entry);
+		if (p == NULL)
+			return (ARCHIVE_FAILED);
+		for (; *p != '\0'; ++p) {
+			if (p[0] == '/' && p[1] != '\0')
+				name = p + 1;
+		}
+		/*
+		 * If last path element starts with "._", then
+		 * this is a Mac extension.
+		 */
+		if (name[0] != '.' || name[1] != '_' || name[2] == '\0')
+			return ARCHIVE_OK;
+	}
+
+ 	/* Read the body as a Mac OS metadata blob. */
+	size = archive_entry_size(entry);
+
+	/*
+	 * TODO: Look beyond the body here to peek at the next header.
+	 * If it's a regular header (not an extension header)
+	 * that has the wrong name, just return the current
+	 * entry as-is, without consuming the body here.
+	 * That would reduce the risk of us mis-identifying
+	 * an ordinary file that just happened to have
+	 * a name starting with "._".
+	 *
+	 * Q: Is the above idea really possible?  Even
+	 * when there are GNU or pax extension entries?
+	 */
+	data = __archive_read_ahead(a, (size_t)size, NULL);
+	if (data == NULL) {
+		*unconsumed = 0;
+		return (ARCHIVE_FATAL);
+	}
+	archive_entry_copy_mac_metadata(entry, data, (size_t)size);
+	*unconsumed = (size_t)((size + 511) & ~ 511);
+	tar_flush_unconsumed(a, unconsumed);
+	return (tar_read_header(a, tar, entry, unconsumed));
+}
+
+/*
+ * Parse a file header for a pax extended archive entry.
+ */
+static int
+header_pax_global(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+	int err;
+
+	err = read_body_to_string(a, tar, &(tar->pax_global), h, unconsumed);
+	if (err != ARCHIVE_OK)
+		return (err);
+	err = tar_read_header(a, tar, entry, unconsumed);
+	return (err);
+}
+
+static int
+header_pax_extensions(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+	int err, err2;
+
+	err = read_body_to_string(a, tar, &(tar->pax_header), h, unconsumed);
+	if (err != ARCHIVE_OK)
+		return (err);
+
+	/* Parse the next header. */
+	err = tar_read_header(a, tar, entry, unconsumed);
+	if ((err != ARCHIVE_OK) && (err != ARCHIVE_WARN))
+		return (err);
+
+	/*
+	 * TODO: Parse global/default options into 'entry' struct here
+	 * before handling file-specific options.
+	 *
+	 * This design (parse standard header, then overwrite with pax
+	 * extended attribute data) usually works well, but isn't ideal;
+	 * it would be better to parse the pax extended attributes first
+	 * and then skip any fields in the standard header that were
+	 * defined in the pax header.
+	 */
+	err2 = pax_header(a, tar, entry, &tar->pax_header);
+	err =  err_combine(err, err2);
+	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+	return (err);
+}
+
+
+/*
+ * Parse a file header for a Posix "ustar" archive entry.  This also
+ * handles "pax" or "extended ustar" entries.
+ */
+static int
+header_ustar(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h)
+{
+	const struct archive_entry_header_ustar	*header;
+	struct archive_string *as;
+	int err = ARCHIVE_OK, r;
+
+	header = (const struct archive_entry_header_ustar *)h;
+
+	/* Copy name into an internal buffer to ensure null-termination. */
+	as = &(tar->entry_pathname);
+	if (header->prefix[0]) {
+		archive_strncpy(as, header->prefix, sizeof(header->prefix));
+		if (as->s[archive_strlen(as) - 1] != '/')
+			archive_strappend_char(as, '/');
+		archive_strncat(as, header->name, sizeof(header->name));
+	} else {
+		archive_strncpy(as, header->name, sizeof(header->name));
+	}
+	if (archive_entry_copy_pathname_l(entry, as->s, archive_strlen(as),
+	    tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
+
+	/* Handle rest of common fields. */
+	r = header_common(a, tar, entry, h);
+	if (r == ARCHIVE_FATAL)
+		return (r);
+	if (r < err)
+		err = r;
+
+	/* Handle POSIX ustar fields. */
+	if (archive_entry_copy_uname_l(entry,
+	    header->uname, sizeof(header->uname), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Uname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
+
+	if (archive_entry_copy_gname_l(entry,
+	    header->gname, sizeof(header->gname), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Gname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
+
+	/* Parse out device numbers only for char and block specials. */
+	if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
+		archive_entry_set_rdevmajor(entry, (dev_t)
+		    tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));
+		archive_entry_set_rdevminor(entry, (dev_t)
+		    tar_atol(header->rdevminor, sizeof(header->rdevminor)));
+	}
+
+	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+
+	return (err);
+}
+
+
+/*
+ * Parse the pax extended attributes record.
+ *
+ * Returns non-zero if there's an error in the data.
+ */
+static int
+pax_header(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, struct archive_string *in_as)
+{
+	size_t attr_length, l, line_length, value_length;
+	char *p;
+	char *key, *value;
+	struct archive_string *as;
+	struct archive_string_conv *sconv;
+	int err, err2;
+	char *attr = in_as->s;
+
+	attr_length = in_as->length;
+	tar->pax_hdrcharset_binary = 0;
+	archive_string_empty(&(tar->entry_gname));
+	archive_string_empty(&(tar->entry_linkpath));
+	archive_string_empty(&(tar->entry_pathname));
+	archive_string_empty(&(tar->entry_pathname_override));
+	archive_string_empty(&(tar->entry_uname));
+	err = ARCHIVE_OK;
+	while (attr_length > 0) {
+		/* Parse decimal length field at start of line. */
+		line_length = 0;
+		l = attr_length;
+		p = attr; /* Record start of line. */
+		while (l>0) {
+			if (*p == ' ') {
+				p++;
+				l--;
+				break;
+			}
+			if (*p < '0' || *p > '9') {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+				    "Ignoring malformed pax extended attributes");
+				return (ARCHIVE_WARN);
+			}
+			line_length *= 10;
+			line_length += *p - '0';
+			if (line_length > 999999) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+				    "Rejecting pax extended attribute > 1MB");
+				return (ARCHIVE_WARN);
+			}
+			p++;
+			l--;
+		}
+
+		/*
+		 * Parsed length must be no bigger than available data,
+		 * at least 1, and the last character of the line must
+		 * be '\n'.
+		 */
+		if (line_length > attr_length
+		    || line_length < 1
+		    || attr[line_length - 1] != '\n')
+		{
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Ignoring malformed pax extended attribute");
+			return (ARCHIVE_WARN);
+		}
+
+		/* Null-terminate the line. */
+		attr[line_length - 1] = '\0';
+
+		/* Find end of key and null terminate it. */
+		key = p;
+		if (key[0] == '=')
+			return (-1);
+		while (*p && *p != '=')
+			++p;
+		if (*p == '\0') {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Invalid pax extended attributes");
+			return (ARCHIVE_WARN);
+		}
+		*p = '\0';
+
+		value = p + 1;
+
+		/* Some values may be binary data */
+		value_length = attr + line_length - 1 - value;
+
+		/* Identify this attribute and set it in the entry. */
+		err2 = pax_attribute(a, tar, entry, key, value, value_length);
+		if (err2 == ARCHIVE_FATAL)
+			return (err2);
+		err = err_combine(err, err2);
+
+		/* Skip to next line */
+		attr += line_length;
+		attr_length -= line_length;
+	}
+
+	/*
+	 * PAX format uses UTF-8 as default charset for its metadata
+	 * unless hdrcharset=BINARY is present in its header.
+	 * We apply the charset specified by the hdrcharset option only
+	 * when the hdrcharset attribute(in PAX header) is BINARY because
+	 * we respect the charset described in PAX header and BINARY also
+	 * means that metadata(filename,uname and gname) character-set
+	 * is unknown.
+	 */
+	if (tar->pax_hdrcharset_binary)
+		sconv = tar->opt_sconv;
+	else {
+		sconv = archive_string_conversion_from_charset(
+		    &(a->archive), "UTF-8", 1);
+		if (sconv == NULL)
+			return (ARCHIVE_FATAL);
+		if (tar->compat_2x)
+			archive_string_conversion_set_opt(sconv,
+			    SCONV_SET_OPT_UTF8_LIBARCHIVE2X);
+	}
+
+	if (archive_strlen(&(tar->entry_gname)) > 0) {
+		if (archive_entry_copy_gname_l(entry, tar->entry_gname.s,
+		    archive_strlen(&(tar->entry_gname)), sconv) != 0) {
+			err = set_conversion_failed_error(a, sconv, "Gname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+			/* Use a converted an original name. */
+			archive_entry_copy_gname(entry, tar->entry_gname.s);
+		}
+	}
+	if (archive_strlen(&(tar->entry_linkpath)) > 0) {
+		if (archive_entry_copy_link_l(entry, tar->entry_linkpath.s,
+		    archive_strlen(&(tar->entry_linkpath)), sconv) != 0) {
+			err = set_conversion_failed_error(a, sconv, "Linkname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+			/* Use a converted an original name. */
+			archive_entry_copy_link(entry, tar->entry_linkpath.s);
+		}
+	}
+	/*
+	 * Some extensions (such as the GNU sparse file extensions)
+	 * deliberately store a synthetic name under the regular 'path'
+	 * attribute and the real file name under a different attribute.
+	 * Since we're supposed to not care about the order, we
+	 * have no choice but to store all of the various filenames
+	 * we find and figure it all out afterwards.  This is the
+	 * figuring out part.
+	 */
+	as = NULL;
+	if (archive_strlen(&(tar->entry_pathname_override)) > 0)
+		as = &(tar->entry_pathname_override);
+	else if (archive_strlen(&(tar->entry_pathname)) > 0)
+		as = &(tar->entry_pathname);
+	if (as != NULL) {
+		if (archive_entry_copy_pathname_l(entry, as->s,
+		    archive_strlen(as), sconv) != 0) {
+			err = set_conversion_failed_error(a, sconv, "Pathname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+			/* Use a converted an original name. */
+			archive_entry_copy_pathname(entry, as->s);
+		}
+	}
+	if (archive_strlen(&(tar->entry_uname)) > 0) {
+		if (archive_entry_copy_uname_l(entry, tar->entry_uname.s,
+		    archive_strlen(&(tar->entry_uname)), sconv) != 0) {
+			err = set_conversion_failed_error(a, sconv, "Uname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+			/* Use a converted an original name. */
+			archive_entry_copy_uname(entry, tar->entry_uname.s);
+		}
+	}
+	return (err);
+}
+
+static int
+pax_attribute_xattr(struct archive_entry *entry,
+	const char *name, const char *value)
+{
+	char *name_decoded;
+	void *value_decoded;
+	size_t value_len;
+
+	if (strlen(name) < 18 || (memcmp(name, "LIBARCHIVE.xattr.", 17)) != 0)
+		return 3;
+
+	name += 17;
+
+	/* URL-decode name */
+	name_decoded = url_decode(name);
+	if (name_decoded == NULL)
+		return 2;
+
+	/* Base-64 decode value */
+	value_decoded = base64_decode(value, strlen(value), &value_len);
+	if (value_decoded == NULL) {
+		free(name_decoded);
+		return 1;
+	}
+
+	archive_entry_xattr_add_entry(entry, name_decoded,
+		value_decoded, value_len);
+
+	free(name_decoded);
+	free(value_decoded);
+	return 0;
+}
+
+static int
+pax_attribute_schily_xattr(struct archive_entry *entry,
+	const char *name, const char *value, size_t value_length)
+{
+	if (strlen(name) < 14 || (memcmp(name, "SCHILY.xattr.", 13)) != 0)
+		return 1;
+
+	name += 13;
+
+	archive_entry_xattr_add_entry(entry, name, value, value_length);
+
+	return 0;
+}
+
+static int
+pax_attribute_rht_security_selinux(struct archive_entry *entry,
+	const char *value, size_t value_length)
+{
+	archive_entry_xattr_add_entry(entry, "security.selinux",
+            value, value_length);
+
+	return 0;
+}
+
+static int
+pax_attribute_acl(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const char *value, int type)
+{
+	int r;
+	const char* errstr;
+
+	switch (type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+		errstr = "SCHILY.acl.access";
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+		errstr = "SCHILY.acl.default";
+		break;
+	case ARCHIVE_ENTRY_ACL_TYPE_NFS4:
+		errstr = "SCHILY.acl.ace";
+		break;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Unknown ACL type: %d", type);
+		return(ARCHIVE_FATAL);
+	}
+
+	if (tar->sconv_acl == NULL) {
+		tar->sconv_acl =
+		    archive_string_conversion_from_charset(
+			&(a->archive), "UTF-8", 1);
+		if (tar->sconv_acl == NULL)
+			return (ARCHIVE_FATAL);
+	}
+
+	r = archive_acl_from_text_l(archive_entry_acl(entry), value, type,
+	    tar->sconv_acl);
+	if (r != ARCHIVE_OK) {
+		if (r == ARCHIVE_FATAL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "%s %s", "Can't allocate memory for ",
+			    errstr);
+			return (r);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC, "%s %s", "Parse error: ", errstr);
+	}
+	return (r);
+}
+
+/*
+ * Parse a single key=value attribute.  key/value pointers are
+ * assumed to point into reasonably long-lived storage.
+ *
+ * Note that POSIX reserves all-lowercase keywords.  Vendor-specific
+ * extensions should always have keywords of the form "VENDOR.attribute"
+ * In particular, it's quite feasible to support many different
+ * vendor extensions here.  I'm using "LIBARCHIVE" for extensions
+ * unique to this library.
+ *
+ * Investigate other vendor-specific extensions and see if
+ * any of them look useful.
+ */
+static int
+pax_attribute(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const char *key, const char *value, size_t value_length)
+{
+	int64_t s;
+	long n;
+	int err = ARCHIVE_OK, r;
+
+	if (value == NULL)
+		value = "";	/* Disable compiler warning; do not pass
+				 * NULL pointer to strlen().  */
+	switch (key[0]) {
+	case 'G':
+		/* Reject GNU.sparse.* headers on non-regular files. */
+		if (strncmp(key, "GNU.sparse", 10) == 0 &&
+		    !tar->sparse_allowed) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Non-regular file cannot be sparse");
+			return (ARCHIVE_FATAL);
+		}
+
+		/* GNU "0.0" sparse pax format. */
+		if (strcmp(key, "GNU.sparse.numblocks") == 0) {
+			tar->sparse_offset = -1;
+			tar->sparse_numbytes = -1;
+			tar->sparse_gnu_major = 0;
+			tar->sparse_gnu_minor = 0;
+		}
+		if (strcmp(key, "GNU.sparse.offset") == 0) {
+			tar->sparse_offset = tar_atol10(value, strlen(value));
+			if (tar->sparse_numbytes != -1) {
+				if (gnu_add_sparse_entry(a, tar,
+				    tar->sparse_offset, tar->sparse_numbytes)
+				    != ARCHIVE_OK)
+					return (ARCHIVE_FATAL);
+				tar->sparse_offset = -1;
+				tar->sparse_numbytes = -1;
+			}
+		}
+		if (strcmp(key, "GNU.sparse.numbytes") == 0) {
+			tar->sparse_numbytes = tar_atol10(value, strlen(value));
+			if (tar->sparse_numbytes != -1) {
+				if (gnu_add_sparse_entry(a, tar,
+				    tar->sparse_offset, tar->sparse_numbytes)
+				    != ARCHIVE_OK)
+					return (ARCHIVE_FATAL);
+				tar->sparse_offset = -1;
+				tar->sparse_numbytes = -1;
+			}
+		}
+		if (strcmp(key, "GNU.sparse.size") == 0) {
+			tar->realsize = tar_atol10(value, strlen(value));
+			archive_entry_set_size(entry, tar->realsize);
+			tar->realsize_override = 1;
+		}
+
+		/* GNU "0.1" sparse pax format. */
+		if (strcmp(key, "GNU.sparse.map") == 0) {
+			tar->sparse_gnu_major = 0;
+			tar->sparse_gnu_minor = 1;
+			if (gnu_sparse_01_parse(a, tar, value) != ARCHIVE_OK)
+				return (ARCHIVE_WARN);
+		}
+
+		/* GNU "1.0" sparse pax format */
+		if (strcmp(key, "GNU.sparse.major") == 0) {
+			tar->sparse_gnu_major = (int)tar_atol10(value, strlen(value));
+			tar->sparse_gnu_pending = 1;
+		}
+		if (strcmp(key, "GNU.sparse.minor") == 0) {
+			tar->sparse_gnu_minor = (int)tar_atol10(value, strlen(value));
+			tar->sparse_gnu_pending = 1;
+		}
+		if (strcmp(key, "GNU.sparse.name") == 0) {
+			/*
+			 * The real filename; when storing sparse
+			 * files, GNU tar puts a synthesized name into
+			 * the regular 'path' attribute in an attempt
+			 * to limit confusion. ;-)
+			 */
+			archive_strcpy(&(tar->entry_pathname_override), value);
+		}
+		if (strcmp(key, "GNU.sparse.realsize") == 0) {
+			tar->realsize = tar_atol10(value, strlen(value));
+			archive_entry_set_size(entry, tar->realsize);
+			tar->realsize_override = 1;
+		}
+		break;
+	case 'L':
+		/* Our extensions */
+/* TODO: Handle arbitrary extended attributes... */
+/*
+		if (strcmp(key, "LIBARCHIVE.xxxxxxx") == 0)
+			archive_entry_set_xxxxxx(entry, value);
+*/
+		if (strcmp(key, "LIBARCHIVE.creationtime") == 0) {
+			pax_time(value, &s, &n);
+			archive_entry_set_birthtime(entry, s, n);
+		}
+		if (strcmp(key, "LIBARCHIVE.symlinktype") == 0) {
+			if (strcmp(value, "file") == 0) {
+				archive_entry_set_symlink_type(entry,
+				    AE_SYMLINK_TYPE_FILE);
+			} else if (strcmp(value, "dir") == 0) {
+				archive_entry_set_symlink_type(entry,
+				    AE_SYMLINK_TYPE_DIRECTORY);
+			}
+		}
+		if (memcmp(key, "LIBARCHIVE.xattr.", 17) == 0)
+			pax_attribute_xattr(entry, key, value);
+		break;
+	case 'R':
+		/* GNU tar uses RHT.security header to store SELinux xattrs
+		 * SCHILY.xattr.security.selinux == RHT.security.selinux */
+		if (strcmp(key, "RHT.security.selinux") == 0) {
+			pax_attribute_rht_security_selinux(entry, value,
+			    value_length);
+			}
+		break;
+	case 'S':
+		/* We support some keys used by the "star" archiver */
+		if (strcmp(key, "SCHILY.acl.access") == 0) {
+			r = pax_attribute_acl(a, tar, entry, value,
+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+			if (r == ARCHIVE_FATAL)
+				return (r);
+		} else if (strcmp(key, "SCHILY.acl.default") == 0) {
+			r = pax_attribute_acl(a, tar, entry, value,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+			if (r == ARCHIVE_FATAL)
+				return (r);
+		} else if (strcmp(key, "SCHILY.acl.ace") == 0) {
+			r = pax_attribute_acl(a, tar, entry, value,
+			    ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+			if (r == ARCHIVE_FATAL)
+				return (r);
+		} else if (strcmp(key, "SCHILY.devmajor") == 0) {
+			archive_entry_set_rdevmajor(entry,
+			    (dev_t)tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "SCHILY.devminor") == 0) {
+			archive_entry_set_rdevminor(entry,
+			    (dev_t)tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "SCHILY.fflags") == 0) {
+			archive_entry_copy_fflags_text(entry, value);
+		} else if (strcmp(key, "SCHILY.dev") == 0) {
+			archive_entry_set_dev(entry,
+			    (dev_t)tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "SCHILY.ino") == 0) {
+			archive_entry_set_ino(entry,
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "SCHILY.nlink") == 0) {
+			archive_entry_set_nlink(entry, (unsigned)
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "SCHILY.realsize") == 0) {
+			tar->realsize = tar_atol10(value, strlen(value));
+			tar->realsize_override = 1;
+			archive_entry_set_size(entry, tar->realsize);
+		} else if (strncmp(key, "SCHILY.xattr.", 13) == 0) {
+			pax_attribute_schily_xattr(entry, key, value,
+			    value_length);
+		} else if (strcmp(key, "SUN.holesdata") == 0) {
+			/* A Solaris extension for sparse. */
+			r = solaris_sparse_parse(a, tar, entry, value);
+			if (r < err) {
+				if (r == ARCHIVE_FATAL)
+					return (r);
+				err = r;
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Parse error: SUN.holesdata");
+			}
+		}
+		break;
+	case 'a':
+		if (strcmp(key, "atime") == 0) {
+			pax_time(value, &s, &n);
+			archive_entry_set_atime(entry, s, n);
+		}
+		break;
+	case 'c':
+		if (strcmp(key, "ctime") == 0) {
+			pax_time(value, &s, &n);
+			archive_entry_set_ctime(entry, s, n);
+		} else if (strcmp(key, "charset") == 0) {
+			/* TODO: Publish charset information in entry. */
+		} else if (strcmp(key, "comment") == 0) {
+			/* TODO: Publish comment in entry. */
+		}
+		break;
+	case 'g':
+		if (strcmp(key, "gid") == 0) {
+			archive_entry_set_gid(entry,
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "gname") == 0) {
+			archive_strcpy(&(tar->entry_gname), value);
+		}
+		break;
+	case 'h':
+		if (strcmp(key, "hdrcharset") == 0) {
+			if (strcmp(value, "BINARY") == 0)
+				/* Binary  mode. */
+				tar->pax_hdrcharset_binary = 1;
+			else if (strcmp(value, "ISO-IR 10646 2000 UTF-8") == 0)
+				tar->pax_hdrcharset_binary = 0;
+		}
+		break;
+	case 'l':
+		/* pax interchange doesn't distinguish hardlink vs. symlink. */
+		if (strcmp(key, "linkpath") == 0) {
+			archive_strcpy(&(tar->entry_linkpath), value);
+		}
+		break;
+	case 'm':
+		if (strcmp(key, "mtime") == 0) {
+			pax_time(value, &s, &n);
+			archive_entry_set_mtime(entry, s, n);
+		}
+		break;
+	case 'p':
+		if (strcmp(key, "path") == 0) {
+			archive_strcpy(&(tar->entry_pathname), value);
+		}
+		break;
+	case 'r':
+		/* POSIX has reserved 'realtime.*' */
+		break;
+	case 's':
+		/* POSIX has reserved 'security.*' */
+		/* Someday: if (strcmp(key, "security.acl") == 0) { ... } */
+		if (strcmp(key, "size") == 0) {
+			/* "size" is the size of the data in the entry. */
+			tar->entry_bytes_remaining
+			    = tar_atol10(value, strlen(value));
+			/*
+			 * The "size" pax header keyword always overrides the
+			 * "size" field in the tar header.
+			 * GNU.sparse.realsize, GNU.sparse.size and
+			 * SCHILY.realsize override this value.
+			 */
+			if (!tar->realsize_override) {
+				archive_entry_set_size(entry,
+				    tar->entry_bytes_remaining);
+				tar->realsize
+				    = tar->entry_bytes_remaining;
+			}
+		}
+		break;
+	case 'u':
+		if (strcmp(key, "uid") == 0) {
+			archive_entry_set_uid(entry,
+			    tar_atol10(value, strlen(value)));
+		} else if (strcmp(key, "uname") == 0) {
+			archive_strcpy(&(tar->entry_uname), value);
+		}
+		break;
+	}
+	return (err);
+}
+
+
+
+/*
+ * parse a decimal time value, which may include a fractional portion
+ */
+static void
+pax_time(const char *p, int64_t *ps, long *pn)
+{
+	char digit;
+	int64_t	s;
+	unsigned long l;
+	int sign;
+	int64_t limit, last_digit_limit;
+
+	limit = INT64_MAX / 10;
+	last_digit_limit = INT64_MAX % 10;
+
+	s = 0;
+	sign = 1;
+	if (*p == '-') {
+		sign = -1;
+		p++;
+	}
+	while (*p >= '0' && *p <= '9') {
+		digit = *p - '0';
+		if (s > limit ||
+		    (s == limit && digit > last_digit_limit)) {
+			s = INT64_MAX;
+			break;
+		}
+		s = (s * 10) + digit;
+		++p;
+	}
+
+	*ps = s * sign;
+
+	/* Calculate nanoseconds. */
+	*pn = 0;
+
+	if (*p != '.')
+		return;
+
+	l = 100000000UL;
+	do {
+		++p;
+		if (*p >= '0' && *p <= '9')
+			*pn += (*p - '0') * l;
+		else
+			break;
+	} while (l /= 10);
+}
+
+/*
+ * Parse GNU tar header
+ */
+static int
+header_gnutar(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, size_t *unconsumed)
+{
+	const struct archive_entry_header_gnutar *header;
+	int64_t t;
+	int err = ARCHIVE_OK;
+
+	/*
+	 * GNU header is like POSIX ustar, except 'prefix' is
+	 * replaced with some other fields. This also means the
+	 * filename is stored as in old-style archives.
+	 */
+
+	/* Grab fields common to all tar variants. */
+	err = header_common(a, tar, entry, h);
+	if (err == ARCHIVE_FATAL)
+		return (err);
+
+	/* Copy filename over (to ensure null termination). */
+	header = (const struct archive_entry_header_gnutar *)h;
+	if (archive_entry_copy_pathname_l(entry,
+	    header->name, sizeof(header->name), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
+
+	/* Fields common to ustar and GNU */
+	/* XXX Can the following be factored out since it's common
+	 * to ustar and gnu tar?  Is it okay to move it down into
+	 * header_common, perhaps?  */
+	if (archive_entry_copy_uname_l(entry,
+	    header->uname, sizeof(header->uname), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Uname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
+
+	if (archive_entry_copy_gname_l(entry,
+	    header->gname, sizeof(header->gname), tar->sconv) != 0) {
+		err = set_conversion_failed_error(a, tar->sconv, "Gname");
+		if (err == ARCHIVE_FATAL)
+			return (err);
+	}
+
+	/* Parse out device numbers only for char and block specials */
+	if (header->typeflag[0] == '3' || header->typeflag[0] == '4') {
+		archive_entry_set_rdevmajor(entry, (dev_t)
+		    tar_atol(header->rdevmajor, sizeof(header->rdevmajor)));
+		archive_entry_set_rdevminor(entry, (dev_t)
+		    tar_atol(header->rdevminor, sizeof(header->rdevminor)));
+	} else
+		archive_entry_set_rdev(entry, 0);
+
+	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+
+	/* Grab GNU-specific fields. */
+	t = tar_atol(header->atime, sizeof(header->atime));
+	if (t > 0)
+		archive_entry_set_atime(entry, t, 0);
+	t = tar_atol(header->ctime, sizeof(header->ctime));
+	if (t > 0)
+		archive_entry_set_ctime(entry, t, 0);
+
+	if (header->realsize[0] != 0) {
+		tar->realsize
+		    = tar_atol(header->realsize, sizeof(header->realsize));
+		archive_entry_set_size(entry, tar->realsize);
+		tar->realsize_override = 1;
+	}
+
+	if (header->sparse[0].offset[0] != 0) {
+		if (gnu_sparse_old_read(a, tar, header, unconsumed)
+		    != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	} else {
+		if (header->isextended[0] != 0) {
+			/* XXX WTF? XXX */
+		}
+	}
+
+	return (err);
+}
+
+static int
+gnu_add_sparse_entry(struct archive_read *a, struct tar *tar,
+    int64_t offset, int64_t remaining)
+{
+	struct sparse_block *p;
+
+	p = (struct sparse_block *)calloc(1, sizeof(*p));
+	if (p == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	if (tar->sparse_last != NULL)
+		tar->sparse_last->next = p;
+	else
+		tar->sparse_list = p;
+	tar->sparse_last = p;
+	if (remaining < 0 || offset < 0 || offset > INT64_MAX - remaining) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Malformed sparse map data");
+		return (ARCHIVE_FATAL);
+	}
+	p->offset = offset;
+	p->remaining = remaining;
+	return (ARCHIVE_OK);
+}
+
+static void
+gnu_clear_sparse_list(struct tar *tar)
+{
+	struct sparse_block *p;
+
+	while (tar->sparse_list != NULL) {
+		p = tar->sparse_list;
+		tar->sparse_list = p->next;
+		free(p);
+	}
+	tar->sparse_last = NULL;
+}
+
+/*
+ * GNU tar old-format sparse data.
+ *
+ * GNU old-format sparse data is stored in a fixed-field
+ * format.  Offset/size values are 11-byte octal fields (same
+ * format as 'size' field in ustart header).  These are
+ * stored in the header, allocating subsequent header blocks
+ * as needed.  Extending the header in this way is a pretty
+ * severe POSIX violation; this design has earned GNU tar a
+ * lot of criticism.
+ */
+
+static int
+gnu_sparse_old_read(struct archive_read *a, struct tar *tar,
+    const struct archive_entry_header_gnutar *header, size_t *unconsumed)
+{
+	ssize_t bytes_read;
+	const void *data;
+	struct extended {
+		struct gnu_sparse sparse[21];
+		char	isextended[1];
+		char	padding[7];
+	};
+	const struct extended *ext;
+
+	if (gnu_sparse_old_parse(a, tar, header->sparse, 4) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	if (header->isextended[0] == 0)
+		return (ARCHIVE_OK);
+
+	do {
+		tar_flush_unconsumed(a, unconsumed);
+		data = __archive_read_ahead(a, 512, &bytes_read);
+		if (bytes_read < 0)
+			return (ARCHIVE_FATAL);
+		if (bytes_read < 512) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated tar archive "
+			    "detected while reading sparse file data");
+			return (ARCHIVE_FATAL);
+		}
+		*unconsumed = 512;
+		ext = (const struct extended *)data;
+		if (gnu_sparse_old_parse(a, tar, ext->sparse, 21) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	} while (ext->isextended[0] != 0);
+	if (tar->sparse_list != NULL)
+		tar->entry_offset = tar->sparse_list->offset;
+	return (ARCHIVE_OK);
+}
+
+static int
+gnu_sparse_old_parse(struct archive_read *a, struct tar *tar,
+    const struct gnu_sparse *sparse, int length)
+{
+	while (length > 0 && sparse->offset[0] != 0) {
+		if (gnu_add_sparse_entry(a, tar,
+		    tar_atol(sparse->offset, sizeof(sparse->offset)),
+		    tar_atol(sparse->numbytes, sizeof(sparse->numbytes)))
+		    != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		sparse++;
+		length--;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * GNU tar sparse format 0.0
+ *
+ * Beginning with GNU tar 1.15, sparse files are stored using
+ * information in the pax extended header.  The GNU tar maintainers
+ * have gone through a number of variations in the process of working
+ * out this scheme; fortunately, they're all numbered.
+ *
+ * Sparse format 0.0 uses attribute GNU.sparse.numblocks to store the
+ * number of blocks, and GNU.sparse.offset/GNU.sparse.numbytes to
+ * store offset/size for each block.  The repeated instances of these
+ * latter fields violate the pax specification (which frowns on
+ * duplicate keys), so this format was quickly replaced.
+ */
+
+/*
+ * GNU tar sparse format 0.1
+ *
+ * This version replaced the offset/numbytes attributes with
+ * a single "map" attribute that stored a list of integers.  This
+ * format had two problems: First, the "map" attribute could be very
+ * long, which caused problems for some implementations.  More
+ * importantly, the sparse data was lost when extracted by archivers
+ * that didn't recognize this extension.
+ */
+
+static int
+gnu_sparse_01_parse(struct archive_read *a, struct tar *tar, const char *p)
+{
+	const char *e;
+	int64_t offset = -1, size = -1;
+
+	for (;;) {
+		e = p;
+		while (*e != '\0' && *e != ',') {
+			if (*e < '0' || *e > '9')
+				return (ARCHIVE_WARN);
+			e++;
+		}
+		if (offset < 0) {
+			offset = tar_atol10(p, e - p);
+			if (offset < 0)
+				return (ARCHIVE_WARN);
+		} else {
+			size = tar_atol10(p, e - p);
+			if (size < 0)
+				return (ARCHIVE_WARN);
+			if (gnu_add_sparse_entry(a, tar, offset, size)
+			    != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			offset = -1;
+		}
+		if (*e == '\0')
+			return (ARCHIVE_OK);
+		p = e + 1;
+	}
+}
+
+/*
+ * GNU tar sparse format 1.0
+ *
+ * The idea: The offset/size data is stored as a series of base-10
+ * ASCII numbers prepended to the file data, so that dearchivers that
+ * don't support this format will extract the block map along with the
+ * data and a separate post-process can restore the sparseness.
+ *
+ * Unfortunately, GNU tar 1.16 had a bug that added unnecessary
+ * padding to the body of the file when using this format.  GNU tar
+ * 1.17 corrected this bug without bumping the version number, so
+ * it's not possible to support both variants.  This code supports
+ * the later variant at the expense of not supporting the former.
+ *
+ * This variant also replaced GNU.sparse.size with GNU.sparse.realsize
+ * and introduced the GNU.sparse.major/GNU.sparse.minor attributes.
+ */
+
+/*
+ * Read the next line from the input, and parse it as a decimal
+ * integer followed by '\n'.  Returns positive integer value or
+ * negative on error.
+ */
+static int64_t
+gnu_sparse_10_atol(struct archive_read *a, struct tar *tar,
+    int64_t *remaining, size_t *unconsumed)
+{
+	int64_t l, limit, last_digit_limit;
+	const char *p;
+	ssize_t bytes_read;
+	int base, digit;
+
+	base = 10;
+	limit = INT64_MAX / base;
+	last_digit_limit = INT64_MAX % base;
+
+	/*
+	 * Skip any lines starting with '#'; GNU tar specs
+	 * don't require this, but they should.
+	 */
+	do {
+		bytes_read = readline(a, tar, &p,
+			(ssize_t)tar_min(*remaining, 100), unconsumed);
+		if (bytes_read <= 0)
+			return (ARCHIVE_FATAL);
+		*remaining -= bytes_read;
+	} while (p[0] == '#');
+
+	l = 0;
+	while (bytes_read > 0) {
+		if (*p == '\n')
+			return (l);
+		if (*p < '0' || *p >= '0' + base)
+			return (ARCHIVE_WARN);
+		digit = *p - '0';
+		if (l > limit || (l == limit && digit > last_digit_limit))
+			l = INT64_MAX; /* Truncate on overflow. */
+		else
+			l = (l * base) + digit;
+		p++;
+		bytes_read--;
+	}
+	/* TODO: Error message. */
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Returns length (in bytes) of the sparse data description
+ * that was read.
+ */
+static ssize_t
+gnu_sparse_10_read(struct archive_read *a, struct tar *tar, size_t *unconsumed)
+{
+	ssize_t bytes_read;
+	int entries;
+	int64_t offset, size, to_skip, remaining;
+
+	/* Clear out the existing sparse list. */
+	gnu_clear_sparse_list(tar);
+
+	remaining = tar->entry_bytes_remaining;
+
+	/* Parse entries. */
+	entries = (int)gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
+	if (entries < 0)
+		return (ARCHIVE_FATAL);
+	/* Parse the individual entries. */
+	while (entries-- > 0) {
+		/* Parse offset/size */
+		offset = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
+		if (offset < 0)
+			return (ARCHIVE_FATAL);
+		size = gnu_sparse_10_atol(a, tar, &remaining, unconsumed);
+		if (size < 0)
+			return (ARCHIVE_FATAL);
+		/* Add a new sparse entry. */
+		if (gnu_add_sparse_entry(a, tar, offset, size) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+	/* Skip rest of block... */
+	tar_flush_unconsumed(a, unconsumed);
+	bytes_read = (ssize_t)(tar->entry_bytes_remaining - remaining);
+	to_skip = 0x1ff & -bytes_read;
+	/* Fail if tar->entry_bytes_remaing would get negative */
+	if (to_skip > remaining)
+		return (ARCHIVE_FATAL);
+	if (to_skip != __archive_read_consume(a, to_skip))
+		return (ARCHIVE_FATAL);
+	return ((ssize_t)(bytes_read + to_skip));
+}
+
+/*
+ * Solaris pax extension for a sparse file. This is recorded with the
+ * data and hole pairs. The way recording sparse information by Solaris'
+ * pax simply indicates where data and sparse are, so the stored contents
+ * consist of both data and hole.
+ */
+static int
+solaris_sparse_parse(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const char *p)
+{
+	const char *e;
+	int64_t start, end;
+	int hole = 1;
+
+	(void)entry; /* UNUSED */
+
+	end = 0;
+	if (*p == ' ')
+		p++;
+	else
+		return (ARCHIVE_WARN);
+	for (;;) {
+		e = p;
+		while (*e != '\0' && *e != ' ') {
+			if (*e < '0' || *e > '9')
+				return (ARCHIVE_WARN);
+			e++;
+		}
+		start = end;
+		end = tar_atol10(p, e - p);
+		if (end < 0)
+			return (ARCHIVE_WARN);
+		if (start < end) {
+			if (gnu_add_sparse_entry(a, tar, start,
+			    end - start) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			tar->sparse_last->hole = hole;
+		}
+		if (*e == '\0')
+			return (ARCHIVE_OK);
+		p = e + 1;
+		hole = hole == 0;
+	}
+}
+
+/*-
+ * Convert text->integer.
+ *
+ * Traditional tar formats (including POSIX) specify base-8 for
+ * all of the standard numeric fields.  This is a significant limitation
+ * in practice:
+ *   = file size is limited to 8GB
+ *   = rdevmajor and rdevminor are limited to 21 bits
+ *   = uid/gid are limited to 21 bits
+ *
+ * There are two workarounds for this:
+ *   = pax extended headers, which use variable-length string fields
+ *   = GNU tar and STAR both allow either base-8 or base-256 in
+ *      most fields.  The high bit is set to indicate base-256.
+ *
+ * On read, this implementation supports both extensions.
+ */
+static int64_t
+tar_atol(const char *p, size_t char_cnt)
+{
+	/*
+	 * Technically, GNU tar considers a field to be in base-256
+	 * only if the first byte is 0xff or 0x80.
+	 */
+	if (*p & 0x80)
+		return (tar_atol256(p, char_cnt));
+	return (tar_atol8(p, char_cnt));
+}
+
+/*
+ * 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
+tar_atol_base_n(const char *p, size_t char_cnt, int base)
+{
+	int64_t	l, maxval, limit, last_digit_limit;
+	int digit, sign;
+
+	maxval = INT64_MAX;
+	limit = INT64_MAX / base;
+	last_digit_limit = INT64_MAX % base;
+
+	/* the pointer will not be dereferenced if char_cnt is zero
+	 * due to the way the && operator is evaluated.
+	 */
+	while (char_cnt != 0 && (*p == ' ' || *p == '\t')) {
+		p++;
+		char_cnt--;
+	}
+
+	sign = 1;
+	if (char_cnt != 0 && *p == '-') {
+		sign = -1;
+		p++;
+		char_cnt--;
+
+		maxval = INT64_MIN;
+		limit = -(INT64_MIN / base);
+		last_digit_limit = INT64_MIN % base;
+	}
+
+	l = 0;
+	if (char_cnt != 0) {
+		digit = *p - '0';
+		while (digit >= 0 && digit < base  && char_cnt != 0) {
+			if (l>limit || (l == limit && digit > last_digit_limit)) {
+				return maxval; /* Truncate on overflow. */
+			}
+			l = (l * base) + digit;
+			digit = *++p - '0';
+			char_cnt--;
+		}
+	}
+	return (sign < 0) ? -l : l;
+}
+
+static int64_t
+tar_atol8(const char *p, size_t char_cnt)
+{
+	return tar_atol_base_n(p, char_cnt, 8);
+}
+
+static int64_t
+tar_atol10(const char *p, size_t char_cnt)
+{
+	return tar_atol_base_n(p, char_cnt, 10);
+}
+
+/*
+ * Parse a base-256 integer.  This is just a variable-length
+ * twos-complement signed binary value in big-endian order, except
+ * that the high-order bit is ignored.  The values here can be up to
+ * 12 bytes, so we need to be careful about overflowing 64-bit
+ * (8-byte) integers.
+ *
+ * This code unashamedly assumes that the local machine uses 8-bit
+ * bytes and twos-complement arithmetic.
+ */
+static int64_t
+tar_atol256(const char *_p, size_t char_cnt)
+{
+	uint64_t l;
+	const unsigned char *p = (const unsigned char *)_p;
+	unsigned char c, neg;
+
+	/* Extend 7-bit 2s-comp to 8-bit 2s-comp, decide sign. */
+	c = *p;
+	if (c & 0x40) {
+		neg = 0xff;
+		c |= 0x80;
+		l = ~ARCHIVE_LITERAL_ULL(0);
+	} else {
+		neg = 0;
+		c &= 0x7f;
+		l = 0;
+	}
+
+	/* If more than 8 bytes, check that we can ignore
+	 * high-order bits without overflow. */
+	while (char_cnt > sizeof(int64_t)) {
+		--char_cnt;
+		if (c != neg)
+			return neg ? INT64_MIN : INT64_MAX;
+		c = *++p;
+	}
+
+	/* c is first byte that fits; if sign mismatch, return overflow */
+	if ((c ^ neg) & 0x80) {
+		return neg ? INT64_MIN : INT64_MAX;
+	}
+
+	/* Accumulate remaining bytes. */
+	while (--char_cnt > 0) {
+		l = (l << 8) | c;
+		c = *++p;
+	}
+	l = (l << 8) | c;
+	/* Return signed twos-complement value. */
+	return (int64_t)(l);
+}
+
+/*
+ * Returns length of line (including trailing newline)
+ * or negative on error.  'start' argument is updated to
+ * point to first character of line.  This avoids copying
+ * when possible.
+ */
+static ssize_t
+readline(struct archive_read *a, struct tar *tar, const char **start,
+    ssize_t limit, size_t *unconsumed)
+{
+	ssize_t bytes_read;
+	ssize_t total_size = 0;
+	const void *t;
+	const char *s;
+	void *p;
+
+	tar_flush_unconsumed(a, unconsumed);
+
+	t = __archive_read_ahead(a, 1, &bytes_read);
+	if (bytes_read <= 0)
+		return (ARCHIVE_FATAL);
+	s = t;  /* Start of line? */
+	p = memchr(t, '\n', bytes_read);
+	/* If we found '\n' in the read buffer, return pointer to that. */
+	if (p != NULL) {
+		bytes_read = 1 + ((const char *)p) - s;
+		if (bytes_read > limit) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Line too long");
+			return (ARCHIVE_FATAL);
+		}
+		*unconsumed = bytes_read;
+		*start = s;
+		return (bytes_read);
+	}
+	*unconsumed = bytes_read;
+	/* Otherwise, we need to accumulate in a line buffer. */
+	for (;;) {
+		if (total_size + bytes_read > limit) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Line too long");
+			return (ARCHIVE_FATAL);
+		}
+		if (archive_string_ensure(&tar->line, total_size + bytes_read) == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate working buffer");
+			return (ARCHIVE_FATAL);
+		}
+		memcpy(tar->line.s + total_size, t, bytes_read);
+		tar_flush_unconsumed(a, unconsumed);
+		total_size += bytes_read;
+		/* If we found '\n', clean up and return. */
+		if (p != NULL) {
+			*start = tar->line.s;
+			return (total_size);
+		}
+		/* Read some more. */
+		t = __archive_read_ahead(a, 1, &bytes_read);
+		if (bytes_read <= 0)
+			return (ARCHIVE_FATAL);
+		s = t;  /* Start of line? */
+		p = memchr(t, '\n', bytes_read);
+		/* If we found '\n', trim the read. */
+		if (p != NULL) {
+			bytes_read = 1 + ((const char *)p) - s;
+		}
+		*unconsumed = bytes_read;
+	}
+}
+
+/*
+ * base64_decode - Base64 decode
+ *
+ * This accepts most variations of base-64 encoding, including:
+ *    * with or without line breaks
+ *    * with or without the final group padded with '=' or '_' characters
+ * (The most economical Base-64 variant does not pad the last group and
+ * omits line breaks; RFC1341 used for MIME requires both.)
+ */
+static char *
+base64_decode(const char *s, size_t len, size_t *out_len)
+{
+	static const unsigned char digits[64] = {
+		'A','B','C','D','E','F','G','H','I','J','K','L','M','N',
+		'O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b',
+		'c','d','e','f','g','h','i','j','k','l','m','n','o','p',
+		'q','r','s','t','u','v','w','x','y','z','0','1','2','3',
+		'4','5','6','7','8','9','+','/' };
+	static unsigned char decode_table[128];
+	char *out, *d;
+	const unsigned char *src = (const unsigned char *)s;
+
+	/* If the decode table is not yet initialized, prepare it. */
+	if (decode_table[digits[1]] != 1) {
+		unsigned i;
+		memset(decode_table, 0xff, sizeof(decode_table));
+		for (i = 0; i < sizeof(digits); i++)
+			decode_table[digits[i]] = i;
+	}
+
+	/* Allocate enough space to hold the entire output. */
+	/* Note that we may not use all of this... */
+	out = (char *)malloc(len - len / 4 + 1);
+	if (out == NULL) {
+		*out_len = 0;
+		return (NULL);
+	}
+	d = out;
+
+	while (len > 0) {
+		/* Collect the next group of (up to) four characters. */
+		int v = 0;
+		int group_size = 0;
+		while (group_size < 4 && len > 0) {
+			/* '=' or '_' padding indicates final group. */
+			if (*src == '=' || *src == '_') {
+				len = 0;
+				break;
+			}
+			/* Skip illegal characters (including line breaks) */
+			if (*src > 127 || *src < 32
+			    || decode_table[*src] == 0xff) {
+				len--;
+				src++;
+				continue;
+			}
+			v <<= 6;
+			v |= decode_table[*src++];
+			len --;
+			group_size++;
+		}
+		/* Align a short group properly. */
+		v <<= 6 * (4 - group_size);
+		/* Unpack the group we just collected. */
+		switch (group_size) {
+		case 4: d[2] = v & 0xff;
+			/* FALLTHROUGH */
+		case 3: d[1] = (v >> 8) & 0xff;
+			/* FALLTHROUGH */
+		case 2: d[0] = (v >> 16) & 0xff;
+			break;
+		case 1: /* this is invalid! */
+			break;
+		}
+		d += group_size * 3 / 4;
+	}
+
+	*out_len = d - out;
+	return (out);
+}
+
+static char *
+url_decode(const char *in)
+{
+	char *out, *d;
+	const char *s;
+
+	out = (char *)malloc(strlen(in) + 1);
+	if (out == NULL)
+		return (NULL);
+	for (s = in, d = out; *s != '\0'; ) {
+		if (s[0] == '%' && s[1] != '\0' && s[2] != '\0') {
+			/* Try to convert % escape */
+			int digit1 = tohex(s[1]);
+			int digit2 = tohex(s[2]);
+			if (digit1 >= 0 && digit2 >= 0) {
+				/* Looks good, consume three chars */
+				s += 3;
+				/* Convert output */
+				*d++ = ((digit1 << 4) | digit2);
+				continue;
+			}
+			/* Else fall through and treat '%' as normal char */
+		}
+		*d++ = *s++;
+	}
+	*d = '\0';
+	return (out);
+}
+
+static int
+tohex(int c)
+{
+	if (c >= '0' && c <= '9')
+		return (c - '0');
+	else if (c >= 'A' && c <= 'F')
+		return (c - 'A' + 10);
+	else if (c >= 'a' && c <= 'f')
+		return (c - 'a' + 10);
+	else
+		return (-1);
+}
diff --git a/c/archive_read_support_format_warc.c b/c/archive_read_support_format_warc.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_warc.c
@@ -0,0 +1,832 @@
+/*-
+ * Copyright (c) 2014 Sebastian Freundt
+ * 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$");
+
+/**
+ * WARC is standardised by ISO TC46/SC4/WG12 and currently available as
+ * ISO 28500:2009.
+ * For the purposes of this file we used the final draft from:
+ * http://bibnum.bnf.fr/warc/WARC_ISO_28500_version1_latestdraft.pdf
+ *
+ * Todo:
+ * [ ] real-world warcs can contain resources at endpoints ending in /
+ *     e.g. http://bibnum.bnf.fr/warc/
+ *     if you're lucky their response contains a Content-Location: header
+ *     pointing to a unix-compliant filename, in the example above it's
+ *     Content-Location: http://bibnum.bnf.fr/warc/index.html
+ *     however, that's not mandated and github for example doesn't follow
+ *     this convention.
+ *     We need a set of archive options to control what to do with
+ *     entries like these, at the moment care is taken to skip them.
+ *
+ **/
+
+#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_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+typedef enum {
+	WT_NONE,
+	/* warcinfo */
+	WT_INFO,
+	/* metadata */
+	WT_META,
+	/* resource */
+	WT_RSRC,
+	/* request, unsupported */
+	WT_REQ,
+	/* response, unsupported */
+	WT_RSP,
+	/* revisit, unsupported */
+	WT_RVIS,
+	/* conversion, unsupported */
+	WT_CONV,
+	/* continuation, unsupported at the moment */
+	WT_CONT,
+	/* invalid type */
+	LAST_WT
+} warc_type_t;
+
+typedef struct {
+	size_t len;
+	const char *str;
+} warc_string_t;
+
+typedef struct {
+	size_t len;
+	char *str;
+} warc_strbuf_t;
+
+struct warc_s {
+	/* content length ahead */
+	size_t cntlen;
+	/* and how much we've processed so far */
+	size_t cntoff;
+	/* and how much we need to consume between calls */
+	size_t unconsumed;
+
+	/* string pool */
+	warc_strbuf_t pool;
+	/* previous version */
+	unsigned int pver;
+	/* stringified format name */
+	struct archive_string sver;
+};
+
+static int _warc_bid(struct archive_read *a, int);
+static int _warc_cleanup(struct archive_read *a);
+static int _warc_read(struct archive_read*, const void**, size_t*, int64_t*);
+static int _warc_skip(struct archive_read *a);
+static int _warc_rdhdr(struct archive_read *a, struct archive_entry *e);
+
+/* private routines */
+static unsigned int _warc_rdver(const char buf[10], size_t bsz);
+static unsigned int _warc_rdtyp(const char *buf, size_t bsz);
+static warc_string_t _warc_rduri(const char *buf, size_t bsz);
+static ssize_t _warc_rdlen(const char *buf, size_t bsz);
+static time_t _warc_rdrtm(const char *buf, size_t bsz);
+static time_t _warc_rdmtm(const char *buf, size_t bsz);
+static const char *_warc_find_eoh(const char *buf, size_t bsz);
+static const char *_warc_find_eol(const char *buf, size_t bsz);
+
+int
+archive_read_support_format_warc(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct warc_s *w;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_warc");
+
+	if ((w = calloc(1, sizeof(*w))) == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate warc data");
+		return (ARCHIVE_FATAL);
+	}
+
+	r = __archive_read_register_format(
+		a, w, "warc",
+		_warc_bid, NULL, _warc_rdhdr, _warc_read,
+		_warc_skip, NULL, _warc_cleanup, NULL, NULL);
+
+	if (r != ARCHIVE_OK) {
+		free(w);
+		return (r);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+_warc_cleanup(struct archive_read *a)
+{
+	struct warc_s *w = a->format->data;
+
+	if (w->pool.len > 0U) {
+		free(w->pool.str);
+	}
+	archive_string_free(&w->sver);
+	free(w);
+	a->format->data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+_warc_bid(struct archive_read *a, int best_bid)
+{
+	const char *hdr;
+	ssize_t nrd;
+	unsigned int ver;
+
+	(void)best_bid; /* UNUSED */
+
+	/* check first line of file, it should be a record already */
+	if ((hdr = __archive_read_ahead(a, 12U, &nrd)) == NULL) {
+		/* no idea what to do */
+		return -1;
+	} else if (nrd < 12) {
+		/* nah, not for us, our magic cookie is at least 12 bytes */
+		return -1;
+	}
+
+	/* otherwise snarf the record's version number */
+	ver = _warc_rdver(hdr, nrd);
+	if (ver < 1200U || ver > 10000U) {
+		/* we only support WARC 0.12 to 1.0 */
+		return -1;
+	}
+
+	/* otherwise be confident */
+	return (64);
+}
+
+static int
+_warc_rdhdr(struct archive_read *a, struct archive_entry *entry)
+{
+#define HDR_PROBE_LEN		(12U)
+	struct warc_s *w = a->format->data;
+	unsigned int ver;
+	const char *buf;
+	ssize_t nrd;
+	const char *eoh;
+	/* for the file name, saves some strndup()'ing */
+	warc_string_t fnam;
+	/* warc record type, not that we really use it a lot */
+	warc_type_t ftyp;
+	/* content-length+error monad */
+	ssize_t cntlen;
+	/* record time is the WARC-Date time we reinterpret it as ctime */
+	time_t rtime;
+	/* mtime is the Last-Modified time which will be the entry's mtime */
+	time_t mtime;
+
+start_over:
+	/* just use read_ahead() they keep track of unconsumed
+	 * bits and bobs for us; no need to put an extra shift in
+	 * and reproduce that functionality here */
+	buf = __archive_read_ahead(a, HDR_PROBE_LEN, &nrd);
+
+	if (nrd < 0) {
+		/* no good */
+		archive_set_error(
+			&a->archive, ARCHIVE_ERRNO_MISC,
+			"Bad record header");
+		return (ARCHIVE_FATAL);
+	} else if (buf == NULL) {
+		/* there should be room for at least WARC/bla\r\n
+		 * must be EOF therefore */
+		return (ARCHIVE_EOF);
+	}
+ 	/* looks good so far, try and find the end of the header now */
+	eoh = _warc_find_eoh(buf, nrd);
+	if (eoh == NULL) {
+		/* still no good, the header end might be beyond the
+		 * probe we've requested, but then again who'd cram
+		 * so much stuff into the header *and* be 28500-compliant */
+		archive_set_error(
+			&a->archive, ARCHIVE_ERRNO_MISC,
+			"Bad record header");
+		return (ARCHIVE_FATAL);
+	}
+	ver = _warc_rdver(buf, eoh - buf);
+	/* we currently support WARC 0.12 to 1.0 */
+	if (ver == 0U) {
+		archive_set_error(
+			&a->archive, ARCHIVE_ERRNO_MISC,
+			"Invalid record version");
+		return (ARCHIVE_FATAL);
+	} else if (ver < 1200U || ver > 10000U) {
+		archive_set_error(
+			&a->archive, ARCHIVE_ERRNO_MISC,
+			"Unsupported record version: %u.%u",
+			ver / 10000, (ver % 10000) / 100);
+		return (ARCHIVE_FATAL);
+	}
+	cntlen = _warc_rdlen(buf, eoh - buf);
+	if (cntlen < 0) {
+		/* nightmare!  the specs say content-length is mandatory
+		 * so I don't feel overly bad stopping the reader here */
+		archive_set_error(
+			&a->archive, EINVAL,
+			"Bad content length");
+		return (ARCHIVE_FATAL);
+	}
+	rtime = _warc_rdrtm(buf, eoh - buf);
+	if (rtime == (time_t)-1) {
+		/* record time is mandatory as per WARC/1.0,
+		 * so just barf here, fast and loud */
+		archive_set_error(
+			&a->archive, EINVAL,
+			"Bad record time");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* let the world know we're a WARC archive */
+	a->archive.archive_format = ARCHIVE_FORMAT_WARC;
+	if (ver != w->pver) {
+		/* stringify this entry's version */
+		archive_string_sprintf(&w->sver,
+			"WARC/%u.%u", ver / 10000, (ver % 10000) / 100);
+		/* remember the version */
+		w->pver = ver;
+	}
+	/* start off with the type */
+	ftyp = _warc_rdtyp(buf, eoh - buf);
+	/* and let future calls know about the content */
+	w->cntlen = cntlen;
+	w->cntoff = 0U;
+	mtime = 0;/* Avoid compiling error on some platform. */
+
+	switch (ftyp) {
+	case WT_RSRC:
+	case WT_RSP:
+		/* only try and read the filename in the cases that are
+		 * guaranteed to have one */
+		fnam = _warc_rduri(buf, eoh - buf);
+		/* check the last character in the URI to avoid creating
+		 * directory endpoints as files, see Todo above */
+		if (fnam.len == 0 || fnam.str[fnam.len - 1] == '/') {
+			/* break here for now */
+			fnam.len = 0U;
+			fnam.str = NULL;
+			break;
+		}
+		/* bang to our string pool, so we save a
+		 * malloc()+free() roundtrip */
+		if (fnam.len + 1U > w->pool.len) {
+			w->pool.len = ((fnam.len + 64U) / 64U) * 64U;
+			w->pool.str = realloc(w->pool.str, w->pool.len);
+		}
+		memcpy(w->pool.str, fnam.str, fnam.len);
+		w->pool.str[fnam.len] = '\0';
+		/* let no one else know about the pool, it's a secret, shhh */
+		fnam.str = w->pool.str;
+
+		/* snarf mtime or deduce from rtime
+		 * this is a custom header added by our writer, it's quite
+		 * hard to believe anyone else would go through with it
+		 * (apart from being part of some http responses of course) */
+		if ((mtime = _warc_rdmtm(buf, eoh - buf)) == (time_t)-1) {
+			mtime = rtime;
+		}
+		break;
+	default:
+		fnam.len = 0U;
+		fnam.str = NULL;
+		break;
+	}
+
+	/* now eat some of those delicious buffer bits */
+	__archive_read_consume(a, eoh - buf);
+
+	switch (ftyp) {
+	case WT_RSRC:
+	case WT_RSP:
+		if (fnam.len > 0U) {
+			/* populate entry object */
+			archive_entry_set_filetype(entry, AE_IFREG);
+			archive_entry_copy_pathname(entry, fnam.str);
+			archive_entry_set_size(entry, cntlen);
+			archive_entry_set_perm(entry, 0644);
+			/* rtime is the new ctime, mtime stays mtime */
+			archive_entry_set_ctime(entry, rtime, 0L);
+			archive_entry_set_mtime(entry, mtime, 0L);
+			break;
+		}
+		/* FALLTHROUGH */
+	default:
+		/* consume the content and start over */
+		_warc_skip(a);
+		goto start_over;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+_warc_read(struct archive_read *a, const void **buf, size_t *bsz, int64_t *off)
+{
+	struct warc_s *w = a->format->data;
+	const char *rab;
+	ssize_t nrd;
+
+	if (w->cntoff >= w->cntlen) {
+	eof:
+		/* it's our lucky day, no work, we can leave early */
+		*buf = NULL;
+		*bsz = 0U;
+		*off = w->cntoff + 4U/*for \r\n\r\n separator*/;
+		w->unconsumed = 0U;
+		return (ARCHIVE_EOF);
+	}
+
+	if (w->unconsumed) {
+		__archive_read_consume(a, w->unconsumed);
+		w->unconsumed = 0U;
+	}
+
+	rab = __archive_read_ahead(a, 1U, &nrd);
+	if (nrd < 0) {
+		*bsz = 0U;
+		/* big catastrophe */
+		return (int)nrd;
+	} else if (nrd == 0) {
+		goto eof;
+	} else if ((size_t)nrd > w->cntlen - w->cntoff) {
+		/* clamp to content-length */
+		nrd = w->cntlen - w->cntoff;
+	}
+	*off = w->cntoff;
+	*bsz = nrd;
+	*buf = rab;
+
+	w->cntoff += nrd;
+	w->unconsumed = (size_t)nrd;
+	return (ARCHIVE_OK);
+}
+
+static int
+_warc_skip(struct archive_read *a)
+{
+	struct warc_s *w = a->format->data;
+
+	__archive_read_consume(a, w->cntlen + 4U/*\r\n\r\n separator*/);
+	w->cntlen = 0U;
+	w->cntoff = 0U;
+	return (ARCHIVE_OK);
+}
+
+
+/* private routines */
+static void*
+deconst(const void *c)
+{
+	return (char *)0x1 + (((const char *)c) - (const char *)0x1);
+}
+
+static char*
+xmemmem(const char *hay, const size_t haysize,
+	const char *needle, const size_t needlesize)
+{
+	const char *const eoh = hay + haysize;
+	const char *const eon = needle + needlesize;
+	const char *hp;
+	const char *np;
+	const char *cand;
+	unsigned int hsum;
+	unsigned int nsum;
+	unsigned int eqp;
+
+	/* trivial checks first
+         * a 0-sized needle is defined to be found anywhere in haystack
+         * then run strchr() to find a candidate in HAYSTACK (i.e. a portion
+         * that happens to begin with *NEEDLE) */
+	if (needlesize == 0UL) {
+		return deconst(hay);
+	} else if ((hay = memchr(hay, *needle, haysize)) == NULL) {
+		/* trivial */
+		return NULL;
+	}
+
+	/* First characters of haystack and needle are the same now. Both are
+	 * guaranteed to be at least one character long.  Now computes the sum
+	 * of characters values of needle together with the sum of the first
+	 * needle_len characters of haystack. */
+	for (hp = hay + 1U, np = needle + 1U, hsum = *hay, nsum = *hay, eqp = 1U;
+	     hp < eoh && np < eon;
+	     hsum ^= *hp, nsum ^= *np, eqp &= *hp == *np, hp++, np++);
+
+	/* HP now references the (NEEDLESIZE + 1)-th character. */
+	if (np < eon) {
+		/* haystack is smaller than needle, :O */
+		return NULL;
+	} else if (eqp) {
+		/* found a match */
+		return deconst(hay);
+	}
+
+	/* now loop through the rest of haystack,
+	 * updating the sum iteratively */
+	for (cand = hay; hp < eoh; hp++) {
+		hsum ^= *cand++;
+		hsum ^= *hp;
+
+		/* Since the sum of the characters is already known to be
+		 * equal at that point, it is enough to check just NEEDLESIZE - 1
+		 * characters for equality,
+		 * also CAND is by design < HP, so no need for range checks */
+		if (hsum == nsum && memcmp(cand, needle, needlesize - 1U) == 0) {
+			return deconst(cand);
+		}
+	}
+	return NULL;
+}
+
+static int
+strtoi_lim(const char *str, const char **ep, int llim, int ulim)
+{
+	int res = 0;
+	const char *sp;
+	/* we keep track of the number of digits via rulim */
+	int rulim;
+
+	for (sp = str, rulim = ulim > 10 ? ulim : 10;
+	     res * 10 <= ulim && rulim && *sp >= '0' && *sp <= '9';
+	     sp++, rulim /= 10) {
+		res *= 10;
+		res += *sp - '0';
+	}
+	if (sp == str) {
+		res = -1;
+	} else if (res < llim || res > ulim) {
+		res = -2;
+	}
+	*ep = (const char*)sp;
+	return res;
+}
+
+static time_t
+time_from_tm(struct tm *t)
+{
+#if HAVE_TIMEGM
+        /* Use platform timegm() if available. */
+        return (timegm(t));
+#elif HAVE__MKGMTIME64
+        return (_mkgmtime64(t));
+#else
+        /* Else use direct calculation using POSIX assumptions. */
+        /* First, fix up tm_yday based on the year/month/day. */
+        if (mktime(t) == (time_t)-1)
+                return ((time_t)-1);
+        /* Then we can compute timegm() from first principles. */
+        return (t->tm_sec
+            + t->tm_min * 60
+            + t->tm_hour * 3600
+            + t->tm_yday * 86400
+            + (t->tm_year - 70) * 31536000
+            + ((t->tm_year - 69) / 4) * 86400
+            - ((t->tm_year - 1) / 100) * 86400
+            + ((t->tm_year + 299) / 400) * 86400);
+#endif
+}
+
+static time_t
+xstrpisotime(const char *s, char **endptr)
+{
+/** like strptime() but strictly for ISO 8601 Zulu strings */
+	struct tm tm;
+	time_t res = (time_t)-1;
+
+	/* make sure tm is clean */
+	memset(&tm, 0, sizeof(tm));
+
+	/* as a courtesy to our callers, and since this is a non-standard
+	 * routine, we skip leading whitespace */
+	while (*s == ' ' || *s == '\t')
+		++s;
+
+	/* read year */
+	if ((tm.tm_year = strtoi_lim(s, &s, 1583, 4095)) < 0 || *s++ != '-') {
+		goto out;
+	}
+	/* read month */
+	if ((tm.tm_mon = strtoi_lim(s, &s, 1, 12)) < 0 || *s++ != '-') {
+		goto out;
+	}
+	/* read day-of-month */
+	if ((tm.tm_mday = strtoi_lim(s, &s, 1, 31)) < 0 || *s++ != 'T') {
+		goto out;
+	}
+	/* read hour */
+	if ((tm.tm_hour = strtoi_lim(s, &s, 0, 23)) < 0 || *s++ != ':') {
+		goto out;
+	}
+	/* read minute */
+	if ((tm.tm_min = strtoi_lim(s, &s, 0, 59)) < 0 || *s++ != ':') {
+		goto out;
+	}
+	/* read second */
+	if ((tm.tm_sec = strtoi_lim(s, &s, 0, 60)) < 0 || *s++ != 'Z') {
+		goto out;
+	}
+
+	/* massage TM to fulfill some of POSIX' constraints */
+	tm.tm_year -= 1900;
+	tm.tm_mon--;
+
+	/* now convert our custom tm struct to a unix stamp using UTC */
+	res = time_from_tm(&tm);
+
+out:
+	if (endptr != NULL) {
+		*endptr = deconst(s);
+	}
+	return res;
+}
+
+static unsigned int
+_warc_rdver(const char *buf, size_t bsz)
+{
+	static const char magic[] = "WARC/";
+	const char *c;
+	unsigned int ver = 0U;
+	unsigned int end = 0U;
+
+	if (bsz < 12 || memcmp(buf, magic, sizeof(magic) - 1U) != 0) {
+		/* buffer too small or invalid magic */
+		return ver;
+	}
+	/* looks good so far, read the version number for a laugh */
+	buf += sizeof(magic) - 1U;
+
+	if (isdigit((unsigned char)buf[0U]) && (buf[1U] == '.') &&
+	    isdigit((unsigned char)buf[2U])) {
+		/* we support a maximum of 2 digits in the minor version */
+		if (isdigit((unsigned char)buf[3U]))
+			end = 1U;
+		/* set up major version */
+		ver = (buf[0U] - '0') * 10000U;
+		/* set up minor version */
+		if (end == 1U) {
+			ver += (buf[2U] - '0') * 1000U;
+			ver += (buf[3U] - '0') * 100U;
+		} else
+			ver += (buf[2U] - '0') * 100U;
+		/*
+		 * WARC below version 0.12 has a space-separated header
+		 * WARC 0.12 and above terminates the version with a CRLF
+		 */
+		c = buf + 3U + end;
+		if (ver >= 1200U) {
+			if (memcmp(c, "\r\n", 2U) != 0)
+				ver = 0U;
+		} else {
+			/* ver < 1200U */
+			if (*c != ' ' && *c != '\t')
+				ver = 0U;
+		}
+	}
+	return ver;
+}
+
+static unsigned int
+_warc_rdtyp(const char *buf, size_t bsz)
+{
+	static const char _key[] = "\r\nWARC-Type:";
+	const char *val, *eol;
+
+	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+		/* no bother */
+		return WT_NONE;
+	}
+	val += sizeof(_key) - 1U;
+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+		/* no end of line */
+		return WT_NONE;
+	}
+
+	/* overread whitespace */
+	while (val < eol && (*val == ' ' || *val == '\t'))
+		++val;
+
+	if (val + 8U == eol) {
+		if (memcmp(val, "resource", 8U) == 0)
+			return WT_RSRC;
+		else if (memcmp(val, "response", 8U) == 0)
+			return WT_RSP;
+	}
+	return WT_NONE;
+}
+
+static warc_string_t
+_warc_rduri(const char *buf, size_t bsz)
+{
+	static const char _key[] = "\r\nWARC-Target-URI:";
+	const char *val, *uri, *eol, *p;
+	warc_string_t res = {0U, NULL};
+
+	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+		/* no bother */
+		return res;
+	}
+	/* overread whitespace */
+	val += sizeof(_key) - 1U;
+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+		/* no end of line */
+		return res;
+	}
+
+	while (val < eol && (*val == ' ' || *val == '\t'))
+		++val;
+
+	/* overread URL designators */
+	if ((uri = xmemmem(val, eol - val, "://", 3U)) == NULL) {
+		/* not touching that! */
+		return res;
+	}
+
+	/* spaces inside uri are not allowed, CRLF should follow */
+	for (p = val; p < eol; p++) {
+		if (isspace((unsigned char)*p))
+			return res;
+	}
+
+	/* there must be at least space for ftp */
+	if (uri < (val + 3U))
+		return res;
+
+	/* move uri to point to after :// */
+	uri += 3U;
+
+	/* now then, inspect the URI */
+	if (memcmp(val, "file", 4U) == 0) {
+		/* perfect, nothing left to do here */
+
+	} else if (memcmp(val, "http", 4U) == 0 ||
+		   memcmp(val, "ftp", 3U) == 0) {
+		/* overread domain, and the first / */
+		while (uri < eol && *uri++ != '/');
+	} else {
+		/* not sure what to do? best to bugger off */
+		return res;
+	}
+	res.str = uri;
+	res.len = eol - uri;
+	return res;
+}
+
+static ssize_t
+_warc_rdlen(const char *buf, size_t bsz)
+{
+	static const char _key[] = "\r\nContent-Length:";
+	const char *val, *eol;
+	char *on = NULL;
+	long int len;
+
+	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+		/* no bother */
+		return -1;
+	}
+	val += sizeof(_key) - 1U;
+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL) {
+		/* no end of line */
+		return -1;
+	}
+
+	/* skip leading whitespace */
+	while (val < eol && (*val == ' ' || *val == '\t'))
+		val++;
+	/* there must be at least one digit */
+	if (!isdigit((unsigned char)*val))
+		return -1;
+	errno = 0;
+	len = strtol(val, &on, 10);
+	if (errno != 0 || on != eol) {
+		/* line must end here */
+		return -1;
+	}
+
+	return (size_t)len;
+}
+
+static time_t
+_warc_rdrtm(const char *buf, size_t bsz)
+{
+	static const char _key[] = "\r\nWARC-Date:";
+	const char *val, *eol;
+	char *on = NULL;
+	time_t res;
+
+	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+		/* no bother */
+		return (time_t)-1;
+	}
+	val += sizeof(_key) - 1U;
+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
+		/* no end of line */
+		return -1;
+	}
+
+	/* xstrpisotime() kindly overreads whitespace for us, so use that */
+	res = xstrpisotime(val, &on);
+	if (on != eol) {
+		/* line must end here */
+		return -1;
+	}
+	return res;
+}
+
+static time_t
+_warc_rdmtm(const char *buf, size_t bsz)
+{
+	static const char _key[] = "\r\nLast-Modified:";
+	const char *val, *eol;
+	char *on = NULL;
+	time_t res;
+
+	if ((val = xmemmem(buf, bsz, _key, sizeof(_key) - 1U)) == NULL) {
+		/* no bother */
+		return (time_t)-1;
+	}
+	val += sizeof(_key) - 1U;
+	if ((eol = _warc_find_eol(val, buf + bsz - val)) == NULL ) {
+		/* no end of line */
+		return -1;
+	}
+
+	/* xstrpisotime() kindly overreads whitespace for us, so use that */
+	res = xstrpisotime(val, &on);
+	if (on != eol) {
+		/* line must end here */
+		return -1;
+	}
+	return res;
+}
+
+static const char*
+_warc_find_eoh(const char *buf, size_t bsz)
+{
+	static const char _marker[] = "\r\n\r\n";
+	const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
+
+	if (hit != NULL) {
+		hit += sizeof(_marker) - 1U;
+	}
+	return hit;
+}
+
+static const char*
+_warc_find_eol(const char *buf, size_t bsz)
+{
+	static const char _marker[] = "\r\n";
+	const char *hit = xmemmem(buf, bsz, _marker, sizeof(_marker) - 1U);
+
+	return hit;
+}
+/* archive_read_support_format_warc.c ends here */
diff --git a/c/archive_read_support_format_xar.c b/c/archive_read_support_format_xar.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_xar.c
@@ -0,0 +1,3328 @@
+/*-
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_LIBXML_XMLREADER_H
+#include <libxml/xmlreader.h>
+#elif HAVE_BSDXML_H
+#include <bsdxml.h>
+#elif HAVE_EXPAT_H
+#include <expat.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_read_private.h"
+
+#if (!defined(HAVE_LIBXML_XMLREADER_H) && \
+     !defined(HAVE_BSDXML_H) && !defined(HAVE_EXPAT_H)) ||\
+	!defined(HAVE_ZLIB_H) || \
+	!defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
+/*
+ * xar needs several external libraries.
+ *   o libxml2 or expat --- XML parser
+ *   o openssl or MD5/SHA1 hash function
+ *   o zlib
+ *   o bzlib2 (option)
+ *   o liblzma (option)
+ */
+int
+archive_read_support_format_xar(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_xar");
+
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Xar not supported on this platform");
+	return (ARCHIVE_WARN);
+}
+
+#else	/* Support xar format */
+
+/* #define DEBUG 1 */
+/* #define DEBUG_PRINT_TOC 1 */
+#if DEBUG_PRINT_TOC
+#define PRINT_TOC(d, outbytes)	do {				\
+	unsigned char *x = (unsigned char *)(uintptr_t)d;	\
+	unsigned char c = x[outbytes-1];			\
+	x[outbytes - 1] = 0;					\
+	fprintf(stderr, "%s", x);				\
+	fprintf(stderr, "%c", c);				\
+	x[outbytes - 1] = c;					\
+} while (0)
+#else
+#define PRINT_TOC(d, outbytes)
+#endif
+
+#define HEADER_MAGIC	0x78617221
+#define HEADER_SIZE	28
+#define HEADER_VERSION	1
+#define CKSUM_NONE	0
+#define CKSUM_SHA1	1
+#define CKSUM_MD5	2
+
+#define MD5_SIZE	16
+#define SHA1_SIZE	20
+#define MAX_SUM_SIZE	20
+
+enum enctype {
+	NONE,
+	GZIP,
+	BZIP2,
+	LZMA,
+	XZ,
+};
+
+struct chksumval {
+	int			 alg;
+	size_t			 len;
+	unsigned char		 val[MAX_SUM_SIZE];
+};
+
+struct chksumwork {
+	int			 alg;
+#ifdef ARCHIVE_HAS_MD5
+	archive_md5_ctx		 md5ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	archive_sha1_ctx	 sha1ctx;
+#endif
+};
+
+struct xattr {
+	struct xattr		*next;
+	struct archive_string	 name;
+	uint64_t		 id;
+	uint64_t		 length;
+	uint64_t		 offset;
+	uint64_t		 size;
+	enum enctype		 encoding;
+	struct chksumval	 a_sum;
+	struct chksumval	 e_sum;
+	struct archive_string	 fstype;
+};
+
+struct xar_file {
+	struct xar_file		*next;
+	struct xar_file		*hdnext;
+	struct xar_file		*parent;
+	int			 subdirs;
+
+	unsigned int		 has;
+#define HAS_DATA		0x00001
+#define HAS_PATHNAME		0x00002
+#define HAS_SYMLINK		0x00004
+#define HAS_TIME		0x00008
+#define HAS_UID			0x00010
+#define HAS_GID			0x00020
+#define HAS_MODE		0x00040
+#define HAS_TYPE		0x00080
+#define HAS_DEV			0x00100
+#define HAS_DEVMAJOR		0x00200
+#define HAS_DEVMINOR		0x00400
+#define HAS_INO			0x00800
+#define HAS_FFLAGS		0x01000
+#define HAS_XATTR		0x02000
+#define HAS_ACL			0x04000
+#define HAS_CTIME		0x08000
+#define HAS_MTIME		0x10000
+#define HAS_ATIME		0x20000
+
+	uint64_t		 id;
+	uint64_t		 length;
+	uint64_t		 offset;
+	uint64_t		 size;
+	enum enctype		 encoding;
+	struct chksumval	 a_sum;
+	struct chksumval	 e_sum;
+	struct archive_string	 pathname;
+	struct archive_string	 symlink;
+	time_t			 ctime;
+	time_t			 mtime;
+	time_t			 atime;
+	struct archive_string	 uname;
+	int64_t			 uid;
+	struct archive_string	 gname;
+	int64_t			 gid;
+	mode_t			 mode;
+	dev_t			 dev;
+	dev_t			 devmajor;
+	dev_t			 devminor;
+	int64_t			 ino64;
+	struct archive_string	 fflags_text;
+	unsigned int		 link;
+	unsigned int		 nlink;
+	struct archive_string	 hardlink;
+	struct xattr		*xattr_list;
+};
+
+struct hdlink {
+	struct hdlink		 *next;
+
+	unsigned int		 id;
+	int			 cnt;
+	struct xar_file		 *files;
+};
+
+struct heap_queue {
+	struct xar_file		**files;
+	int			 allocated;
+	int			 used;
+};
+
+enum xmlstatus {
+	INIT,
+	XAR,
+	TOC,
+	TOC_CREATION_TIME,
+	TOC_CHECKSUM,
+	TOC_CHECKSUM_OFFSET,
+	TOC_CHECKSUM_SIZE,
+	TOC_FILE,
+	FILE_DATA,
+	FILE_DATA_LENGTH,
+	FILE_DATA_OFFSET,
+	FILE_DATA_SIZE,
+	FILE_DATA_ENCODING,
+	FILE_DATA_A_CHECKSUM,
+	FILE_DATA_E_CHECKSUM,
+	FILE_DATA_CONTENT,
+	FILE_EA,
+	FILE_EA_LENGTH,
+	FILE_EA_OFFSET,
+	FILE_EA_SIZE,
+	FILE_EA_ENCODING,
+	FILE_EA_A_CHECKSUM,
+	FILE_EA_E_CHECKSUM,
+	FILE_EA_NAME,
+	FILE_EA_FSTYPE,
+	FILE_CTIME,
+	FILE_MTIME,
+	FILE_ATIME,
+	FILE_GROUP,
+	FILE_GID,
+	FILE_USER,
+	FILE_UID,
+	FILE_MODE,
+	FILE_DEVICE,
+	FILE_DEVICE_MAJOR,
+	FILE_DEVICE_MINOR,
+	FILE_DEVICENO,
+	FILE_INODE,
+	FILE_LINK,
+	FILE_TYPE,
+	FILE_NAME,
+	FILE_ACL,
+	FILE_ACL_DEFAULT,
+	FILE_ACL_ACCESS,
+	FILE_ACL_APPLEEXTENDED,
+	/* BSD file flags. */
+	FILE_FLAGS,
+	FILE_FLAGS_USER_NODUMP,
+	FILE_FLAGS_USER_IMMUTABLE,
+	FILE_FLAGS_USER_APPEND,
+	FILE_FLAGS_USER_OPAQUE,
+	FILE_FLAGS_USER_NOUNLINK,
+	FILE_FLAGS_SYS_ARCHIVED,
+	FILE_FLAGS_SYS_IMMUTABLE,
+	FILE_FLAGS_SYS_APPEND,
+	FILE_FLAGS_SYS_NOUNLINK,
+	FILE_FLAGS_SYS_SNAPSHOT,
+	/* Linux file flags. */
+	FILE_EXT2,
+	FILE_EXT2_SecureDeletion,
+	FILE_EXT2_Undelete,
+	FILE_EXT2_Compress,
+	FILE_EXT2_Synchronous,
+	FILE_EXT2_Immutable,
+	FILE_EXT2_AppendOnly,
+	FILE_EXT2_NoDump,
+	FILE_EXT2_NoAtime,
+	FILE_EXT2_CompDirty,
+	FILE_EXT2_CompBlock,
+	FILE_EXT2_NoCompBlock,
+	FILE_EXT2_CompError,
+	FILE_EXT2_BTree,
+	FILE_EXT2_HashIndexed,
+	FILE_EXT2_iMagic,
+	FILE_EXT2_Journaled,
+	FILE_EXT2_NoTail,
+	FILE_EXT2_DirSync,
+	FILE_EXT2_TopDir,
+	FILE_EXT2_Reserved,
+	UNKNOWN,
+};
+
+struct unknown_tag {
+	struct unknown_tag	*next;
+	struct archive_string	 name;
+};
+
+struct xar {
+	uint64_t		 offset; /* Current position in the file. */
+	int64_t			 total;
+	uint64_t		 h_base;
+	int			 end_of_file;
+#define OUTBUFF_SIZE	(1024 * 64)
+	unsigned char		*outbuff;
+
+	enum xmlstatus		 xmlsts;
+	enum xmlstatus		 xmlsts_unknown;
+	struct unknown_tag	*unknowntags;
+	int			 base64text;
+
+	/*
+	 * TOC
+	 */
+	uint64_t		 toc_remaining;
+	uint64_t		 toc_total;
+	uint64_t		 toc_chksum_offset;
+	uint64_t		 toc_chksum_size;
+
+	/*
+	 * For Decoding data.
+	 */
+	enum enctype 		 rd_encoding;
+	z_stream		 stream;
+	int			 stream_valid;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	bz_stream		 bzstream;
+	int			 bzstream_valid;
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+	lzma_stream		 lzstream;
+	int			 lzstream_valid;
+#endif
+	/*
+	 * For Checksum data.
+	 */
+	struct chksumwork	 a_sumwrk;
+	struct chksumwork	 e_sumwrk;
+
+	struct xar_file		*file;	/* current reading file. */
+	struct xattr		*xattr; /* current reading extended attribute. */
+	struct heap_queue	 file_queue;
+	struct xar_file		*hdlink_orgs;
+	struct hdlink		*hdlink_list;
+
+	int	 		 entry_init;
+	uint64_t		 entry_total;
+	uint64_t		 entry_remaining;
+	size_t			 entry_unconsumed;
+	uint64_t		 entry_size;
+	enum enctype 		 entry_encoding;
+	struct chksumval	 entry_a_sum;
+	struct chksumval	 entry_e_sum;
+
+	struct archive_string_conv *sconv;
+};
+
+struct xmlattr {
+	struct xmlattr	*next;
+	char		*name;
+	char		*value;
+};
+
+struct xmlattr_list {
+	struct xmlattr	*first;
+	struct xmlattr	**last;
+};
+
+static int	xar_bid(struct archive_read *, int);
+static int	xar_read_header(struct archive_read *,
+		    struct archive_entry *);
+static int	xar_read_data(struct archive_read *,
+		    const void **, size_t *, int64_t *);
+static int	xar_read_data_skip(struct archive_read *);
+static int	xar_cleanup(struct archive_read *);
+static int	move_reading_point(struct archive_read *, uint64_t);
+static int	rd_contents_init(struct archive_read *,
+		    enum enctype, int, int);
+static int	rd_contents(struct archive_read *, const void **,
+		    size_t *, size_t *, uint64_t);
+static uint64_t	atol10(const char *, size_t);
+static int64_t	atol8(const char *, size_t);
+static size_t	atohex(unsigned char *, size_t, const char *, size_t);
+static time_t	parse_time(const char *p, size_t n);
+static int	heap_add_entry(struct archive_read *a,
+    struct heap_queue *, struct xar_file *);
+static struct xar_file *heap_get_entry(struct heap_queue *);
+static int	add_link(struct archive_read *,
+    struct xar *, struct xar_file *);
+static void	checksum_init(struct archive_read *, int, int);
+static void	checksum_update(struct archive_read *, const void *,
+		    size_t, const void *, size_t);
+static int	checksum_final(struct archive_read *, const void *,
+		    size_t, const void *, size_t);
+static void	checksum_cleanup(struct archive_read *);
+static int	decompression_init(struct archive_read *, enum enctype);
+static int	decompress(struct archive_read *, const void **,
+		    size_t *, const void *, size_t *);
+static int	decompression_cleanup(struct archive_read *);
+static void	xmlattr_cleanup(struct xmlattr_list *);
+static int	file_new(struct archive_read *,
+    struct xar *, struct xmlattr_list *);
+static void	file_free(struct xar_file *);
+static int	xattr_new(struct archive_read *,
+    struct xar *, struct xmlattr_list *);
+static void	xattr_free(struct xattr *);
+static int	getencoding(struct xmlattr_list *);
+static int	getsumalgorithm(struct xmlattr_list *);
+static int	unknowntag_start(struct archive_read *,
+    struct xar *, const char *);
+static void	unknowntag_end(struct xar *, const char *);
+static int	xml_start(struct archive_read *,
+    const char *, struct xmlattr_list *);
+static void	xml_end(void *, const char *);
+static void	xml_data(void *, const char *, int);
+static int	xml_parse_file_flags(struct xar *, const char *);
+static int	xml_parse_file_ext2(struct xar *, const char *);
+#if defined(HAVE_LIBXML_XMLREADER_H)
+static int	xml2_xmlattr_setup(struct archive_read *,
+    struct xmlattr_list *, xmlTextReaderPtr);
+static int	xml2_read_cb(void *, char *, int);
+static int	xml2_close_cb(void *);
+static void	xml2_error_hdr(void *, const char *, xmlParserSeverities,
+		    xmlTextReaderLocatorPtr);
+static int	xml2_read_toc(struct archive_read *);
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+struct expat_userData {
+	int state;
+	struct archive_read *archive;
+};
+static int	expat_xmlattr_setup(struct archive_read *,
+    struct xmlattr_list *, const XML_Char **);
+static void	expat_start_cb(void *, const XML_Char *, const XML_Char **);
+static void	expat_end_cb(void *, const XML_Char *);
+static void	expat_data_cb(void *, const XML_Char *, int);
+static int	expat_read_toc(struct archive_read *);
+#endif
+
+int
+archive_read_support_format_xar(struct archive *_a)
+{
+	struct xar *xar;
+	struct archive_read *a = (struct archive_read *)_a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_xar");
+
+	xar = (struct xar *)calloc(1, sizeof(*xar));
+	if (xar == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate xar data");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* initialize xar->file_queue */
+	xar->file_queue.allocated = 0;
+	xar->file_queue.used = 0;
+	xar->file_queue.files = NULL;
+
+	r = __archive_read_register_format(a,
+	    xar,
+	    "xar",
+	    xar_bid,
+	    NULL,
+	    xar_read_header,
+	    xar_read_data,
+	    xar_read_data_skip,
+	    NULL,
+	    xar_cleanup,
+	    NULL,
+	    NULL);
+	if (r != ARCHIVE_OK)
+		free(xar);
+	return (r);
+}
+
+static int
+xar_bid(struct archive_read *a, int best_bid)
+{
+	const unsigned char *b;
+	int bid;
+
+	(void)best_bid; /* UNUSED */
+
+	b = __archive_read_ahead(a, HEADER_SIZE, NULL);
+	if (b == NULL)
+		return (-1);
+
+	bid = 0;
+	/*
+	 * Verify magic code
+	 */
+	if (archive_be32dec(b) != HEADER_MAGIC)
+		return (0);
+	bid += 32;
+	/*
+	 * Verify header size
+	 */
+	if (archive_be16dec(b+4) != HEADER_SIZE)
+		return (0);
+	bid += 16;
+	/*
+	 * Verify header version
+	 */
+	if (archive_be16dec(b+6) != HEADER_VERSION)
+		return (0);
+	bid += 16;
+	/*
+	 * Verify type of checksum
+	 */
+	switch (archive_be32dec(b+24)) {
+	case CKSUM_NONE:
+	case CKSUM_SHA1:
+	case CKSUM_MD5:
+		bid += 32;
+		break;
+	default:
+		return (0);
+	}
+
+	return (bid);
+}
+
+static int
+read_toc(struct archive_read *a)
+{
+	struct xar *xar;
+	struct xar_file *file;
+	const unsigned char *b;
+	uint64_t toc_compressed_size;
+	uint64_t toc_uncompressed_size;
+	uint32_t toc_chksum_alg;
+	ssize_t bytes;
+	int r;
+
+	xar = (struct xar *)(a->format->data);
+
+	/*
+	 * Read xar header.
+	 */
+	b = __archive_read_ahead(a, HEADER_SIZE, &bytes);
+	if (bytes < 0)
+		return ((int)bytes);
+	if (bytes < HEADER_SIZE) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated archive header");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (archive_be32dec(b) != HEADER_MAGIC) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Invalid header magic");
+		return (ARCHIVE_FATAL);
+	}
+	if (archive_be16dec(b+6) != HEADER_VERSION) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unsupported header version(%d)",
+		    archive_be16dec(b+6));
+		return (ARCHIVE_FATAL);
+	}
+	toc_compressed_size = archive_be64dec(b+8);
+	xar->toc_remaining = toc_compressed_size;
+	toc_uncompressed_size = archive_be64dec(b+16);
+	toc_chksum_alg = archive_be32dec(b+24);
+	__archive_read_consume(a, HEADER_SIZE);
+	xar->offset += HEADER_SIZE;
+	xar->toc_total = 0;
+
+	/*
+	 * Read TOC(Table of Contents).
+	 */
+	/* Initialize reading contents. */
+	r = move_reading_point(a, HEADER_SIZE);
+	if (r != ARCHIVE_OK)
+		return (r);
+	r = rd_contents_init(a, GZIP, toc_chksum_alg, CKSUM_NONE);
+	if (r != ARCHIVE_OK)
+		return (r);
+
+#ifdef HAVE_LIBXML_XMLREADER_H
+	r = xml2_read_toc(a);
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+	r = expat_read_toc(a);
+#endif
+	if (r != ARCHIVE_OK)
+		return (r);
+
+	/* Set 'The HEAP' base. */
+	xar->h_base = xar->offset;
+	if (xar->toc_total != toc_uncompressed_size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "TOC uncompressed size error");
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Checksum TOC
+	 */
+	if (toc_chksum_alg != CKSUM_NONE) {
+		r = move_reading_point(a, xar->toc_chksum_offset);
+		if (r != ARCHIVE_OK)
+			return (r);
+		b = __archive_read_ahead(a,
+			(size_t)xar->toc_chksum_size, &bytes);
+		if (bytes < 0)
+			return ((int)bytes);
+		if ((uint64_t)bytes < xar->toc_chksum_size) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated archive file");
+			return (ARCHIVE_FATAL);
+		}
+		r = checksum_final(a, b,
+			(size_t)xar->toc_chksum_size, NULL, 0);
+		__archive_read_consume(a, xar->toc_chksum_size);
+		xar->offset += xar->toc_chksum_size;
+		if (r != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Connect hardlinked files.
+	 */
+	for (file = xar->hdlink_orgs; file != NULL; file = file->hdnext) {
+		struct hdlink **hdlink;
+
+		for (hdlink = &(xar->hdlink_list); *hdlink != NULL;
+		    hdlink = &((*hdlink)->next)) {
+			if ((*hdlink)->id == file->id) {
+				struct hdlink *hltmp;
+				struct xar_file *f2;
+				int nlink = (*hdlink)->cnt + 1;
+
+				file->nlink = nlink;
+				for (f2 = (*hdlink)->files; f2 != NULL;
+				    f2 = f2->hdnext) {
+					f2->nlink = nlink;
+					archive_string_copy(
+					    &(f2->hardlink), &(file->pathname));
+				}
+				/* Remove resolved files from hdlist_list. */
+				hltmp = *hdlink;
+				*hdlink = hltmp->next;
+				free(hltmp);
+				break;
+			}
+		}
+	}
+	a->archive.archive_format = ARCHIVE_FORMAT_XAR;
+	a->archive.archive_format_name = "xar";
+
+	return (ARCHIVE_OK);
+}
+
+static int
+xar_read_header(struct archive_read *a, struct archive_entry *entry)
+{
+	struct xar *xar;
+	struct xar_file *file;
+	struct xattr *xattr;
+	int r;
+
+	xar = (struct xar *)(a->format->data);
+	r = ARCHIVE_OK;
+
+	if (xar->offset == 0) {
+		/* Create a character conversion object. */
+		if (xar->sconv == NULL) {
+			xar->sconv = archive_string_conversion_from_charset(
+			    &(a->archive), "UTF-8", 1);
+			if (xar->sconv == NULL)
+				return (ARCHIVE_FATAL);
+		}
+
+		/* Read TOC. */
+		r = read_toc(a);
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	for (;;) {
+		file = xar->file = heap_get_entry(&(xar->file_queue));
+		if (file == NULL) {
+			xar->end_of_file = 1;
+			return (ARCHIVE_EOF);
+		}
+		if ((file->mode & AE_IFMT) != AE_IFDIR)
+			break;
+		if (file->has != (HAS_PATHNAME | HAS_TYPE))
+			break;
+		/*
+		 * If a file type is a directory and it does not have
+		 * any metadata, do not export.
+		 */
+		file_free(file);
+	}
+        if (file->has & HAS_ATIME) {
+          archive_entry_set_atime(entry, file->atime, 0);
+        }
+        if (file->has & HAS_CTIME) {
+          archive_entry_set_ctime(entry, file->ctime, 0);
+        }
+        if (file->has & HAS_MTIME) {
+          archive_entry_set_mtime(entry, file->mtime, 0);
+        }
+	archive_entry_set_gid(entry, file->gid);
+	if (file->gname.length > 0 &&
+	    archive_entry_copy_gname_l(entry, file->gname.s,
+		archive_strlen(&(file->gname)), xar->sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Gname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Gname cannot be converted from %s to current locale.",
+		    archive_string_conversion_charset_name(xar->sconv));
+		r = ARCHIVE_WARN;
+	}
+	archive_entry_set_uid(entry, file->uid);
+	if (file->uname.length > 0 &&
+	    archive_entry_copy_uname_l(entry, file->uname.s,
+		archive_strlen(&(file->uname)), xar->sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Uname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Uname cannot be converted from %s to current locale.",
+		    archive_string_conversion_charset_name(xar->sconv));
+		r = ARCHIVE_WARN;
+	}
+	archive_entry_set_mode(entry, file->mode);
+	if (archive_entry_copy_pathname_l(entry, file->pathname.s,
+	    archive_strlen(&(file->pathname)), xar->sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Pathname cannot be converted from %s to current locale.",
+		    archive_string_conversion_charset_name(xar->sconv));
+		r = ARCHIVE_WARN;
+	}
+
+
+	if (file->symlink.length > 0 &&
+	    archive_entry_copy_symlink_l(entry, file->symlink.s,
+		archive_strlen(&(file->symlink)), xar->sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Linkname cannot be converted from %s to current locale.",
+		    archive_string_conversion_charset_name(xar->sconv));
+		r = ARCHIVE_WARN;
+	}
+	/* Set proper nlink. */
+	if ((file->mode & AE_IFMT) == AE_IFDIR)
+		archive_entry_set_nlink(entry, file->subdirs + 2);
+	else
+		archive_entry_set_nlink(entry, file->nlink);
+	archive_entry_set_size(entry, file->size);
+	if (archive_strlen(&(file->hardlink)) > 0)
+		archive_entry_set_hardlink(entry, file->hardlink.s);
+	archive_entry_set_ino64(entry, file->ino64);
+	if (file->has & HAS_DEV)
+		archive_entry_set_dev(entry, file->dev);
+	if (file->has & HAS_DEVMAJOR)
+		archive_entry_set_devmajor(entry, file->devmajor);
+	if (file->has & HAS_DEVMINOR)
+		archive_entry_set_devminor(entry, file->devminor);
+	if (archive_strlen(&(file->fflags_text)) > 0)
+		archive_entry_copy_fflags_text(entry, file->fflags_text.s);
+
+	xar->entry_init = 1;
+	xar->entry_total = 0;
+	xar->entry_remaining = file->length;
+	xar->entry_size = file->size;
+	xar->entry_encoding = file->encoding;
+	xar->entry_a_sum = file->a_sum;
+	xar->entry_e_sum = file->e_sum;
+	/*
+	 * Read extended attributes.
+	 */
+	xattr = file->xattr_list;
+	while (xattr != NULL) {
+		const void *d;
+		size_t outbytes = 0;
+		size_t used = 0;
+
+		r = move_reading_point(a, xattr->offset);
+		if (r != ARCHIVE_OK)
+			break;
+		r = rd_contents_init(a, xattr->encoding,
+		    xattr->a_sum.alg, xattr->e_sum.alg);
+		if (r != ARCHIVE_OK)
+			break;
+		d = NULL;
+		r = rd_contents(a, &d, &outbytes, &used, xattr->length);
+		if (r != ARCHIVE_OK)
+			break;
+		if (outbytes != xattr->size) {
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+			    "Decompressed size error");
+			r = ARCHIVE_FATAL;
+			break;
+		}
+		r = checksum_final(a,
+		    xattr->a_sum.val, xattr->a_sum.len,
+		    xattr->e_sum.val, xattr->e_sum.len);
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+			    "Xattr checksum error");
+			r = ARCHIVE_WARN;
+			break;
+		}
+		if (xattr->name.s == NULL) {
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+			    "Xattr name error");
+			r = ARCHIVE_WARN;
+			break;
+		}
+		archive_entry_xattr_add_entry(entry,
+		    xattr->name.s, d, outbytes);
+		xattr = xattr->next;
+	}
+	if (r != ARCHIVE_OK) {
+		file_free(file);
+		return (r);
+	}
+
+	if (xar->entry_remaining > 0)
+		/* Move reading point to the beginning of current
+		 * file contents. */
+		r = move_reading_point(a, file->offset);
+	else
+		r = ARCHIVE_OK;
+
+	file_free(file);
+	return (r);
+}
+
+static int
+xar_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	struct xar *xar;
+	size_t used = 0;
+	int r;
+
+	xar = (struct xar *)(a->format->data);
+
+	if (xar->entry_unconsumed) {
+		__archive_read_consume(a, xar->entry_unconsumed);
+		xar->entry_unconsumed = 0;
+	}
+
+	if (xar->end_of_file || xar->entry_remaining <= 0) {
+		r = ARCHIVE_EOF;
+		goto abort_read_data;
+	}
+
+	if (xar->entry_init) {
+		r = rd_contents_init(a, xar->entry_encoding,
+		    xar->entry_a_sum.alg, xar->entry_e_sum.alg);
+		if (r != ARCHIVE_OK) {
+			xar->entry_remaining = 0;
+			return (r);
+		}
+		xar->entry_init = 0;
+	}
+
+	*buff = NULL;
+	r = rd_contents(a, buff, size, &used, xar->entry_remaining);
+	if (r != ARCHIVE_OK)
+		goto abort_read_data;
+
+	*offset = xar->entry_total;
+	xar->entry_total += *size;
+	xar->total += *size;
+	xar->offset += used;
+	xar->entry_remaining -= used;
+	xar->entry_unconsumed = used;
+
+	if (xar->entry_remaining == 0) {
+		if (xar->entry_total != xar->entry_size) {
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+			    "Decompressed size error");
+			r = ARCHIVE_FATAL;
+			goto abort_read_data;
+		}
+		r = checksum_final(a,
+		    xar->entry_a_sum.val, xar->entry_a_sum.len,
+		    xar->entry_e_sum.val, xar->entry_e_sum.len);
+		if (r != ARCHIVE_OK)
+			goto abort_read_data;
+	}
+
+	return (ARCHIVE_OK);
+abort_read_data:
+	*buff = NULL;
+	*size = 0;
+	*offset = xar->total;
+	return (r);
+}
+
+static int
+xar_read_data_skip(struct archive_read *a)
+{
+	struct xar *xar;
+	int64_t bytes_skipped;
+
+	xar = (struct xar *)(a->format->data);
+	if (xar->end_of_file)
+		return (ARCHIVE_EOF);
+	bytes_skipped = __archive_read_consume(a, xar->entry_remaining +
+		xar->entry_unconsumed);
+	if (bytes_skipped < 0)
+		return (ARCHIVE_FATAL);
+	xar->offset += bytes_skipped;
+	xar->entry_unconsumed = 0;
+	return (ARCHIVE_OK);
+}
+
+static int
+xar_cleanup(struct archive_read *a)
+{
+	struct xar *xar;
+	struct hdlink *hdlink;
+	int i;
+	int r;
+
+	xar = (struct xar *)(a->format->data);
+	checksum_cleanup(a);
+	r = decompression_cleanup(a);
+	hdlink = xar->hdlink_list;
+	while (hdlink != NULL) {
+		struct hdlink *next = hdlink->next;
+
+		free(hdlink);
+		hdlink = next;
+	}
+	for (i = 0; i < xar->file_queue.used; i++)
+		file_free(xar->file_queue.files[i]);
+	free(xar->file_queue.files);
+	while (xar->unknowntags != NULL) {
+		struct unknown_tag *tag;
+
+		tag = xar->unknowntags;
+		xar->unknowntags = tag->next;
+		archive_string_free(&(tag->name));
+		free(tag);
+	}
+	free(xar->outbuff);
+	free(xar);
+	a->format->data = NULL;
+	return (r);
+}
+
+static int
+move_reading_point(struct archive_read *a, uint64_t offset)
+{
+	struct xar *xar;
+
+	xar = (struct xar *)(a->format->data);
+	if (xar->offset - xar->h_base != offset) {
+		/* Seek forward to the start of file contents. */
+		int64_t step;
+
+		step = offset - (xar->offset - xar->h_base);
+		if (step > 0) {
+			step = __archive_read_consume(a, step);
+			if (step < 0)
+				return ((int)step);
+			xar->offset += step;
+		} else {
+			int64_t pos = __archive_read_seek(a, xar->h_base + offset, SEEK_SET);
+			if (pos == ARCHIVE_FAILED) {
+				archive_set_error(&(a->archive),
+				    ARCHIVE_ERRNO_MISC,
+				    "Cannot seek.");
+				return (ARCHIVE_FAILED);
+			}
+			xar->offset = pos;
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+rd_contents_init(struct archive_read *a, enum enctype encoding,
+    int a_sum_alg, int e_sum_alg)
+{
+	int r;
+
+	/* Init decompress library. */
+	if ((r = decompression_init(a, encoding)) != ARCHIVE_OK)
+		return (r);
+	/* Init checksum library. */
+	checksum_init(a, a_sum_alg, e_sum_alg);
+	return (ARCHIVE_OK);
+}
+
+static int
+rd_contents(struct archive_read *a, const void **buff, size_t *size,
+    size_t *used, uint64_t remaining)
+{
+	const unsigned char *b;
+	ssize_t bytes;
+
+	/* Get whatever bytes are immediately available. */
+	b = __archive_read_ahead(a, 1, &bytes);
+	if (bytes < 0)
+		return ((int)bytes);
+	if (bytes == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Truncated archive file");
+		return (ARCHIVE_FATAL);
+	}
+	if ((uint64_t)bytes > remaining)
+		bytes = (ssize_t)remaining;
+
+	/*
+	 * Decompress contents of file.
+	 */
+	*used = bytes;
+	if (decompress(a, buff, size, b, used) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/*
+	 * Update checksum of a compressed data and a extracted data.
+	 */
+	checksum_update(a, b, *used, *buff, *size);
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Note that this implementation does not (and should not!) obey
+ * locale settings; you cannot simply substitute strtol here, since
+ * it does obey locale.
+ */
+
+static uint64_t
+atol10(const char *p, size_t char_cnt)
+{
+	uint64_t l;
+	int digit;
+
+	if (char_cnt == 0)
+		return (0);
+
+	l = 0;
+	digit = *p - '0';
+	while (digit >= 0 && digit < 10  && char_cnt-- > 0) {
+		l = (l * 10) + digit;
+		digit = *++p - '0';
+	}
+	return (l);
+}
+
+static int64_t
+atol8(const char *p, size_t char_cnt)
+{
+	int64_t l;
+	int digit;
+
+	if (char_cnt == 0)
+		return (0);
+
+	l = 0;
+	while (char_cnt-- > 0) {
+		if (*p >= '0' && *p <= '7')
+			digit = *p - '0';
+		else
+			break;
+		p++;
+		l <<= 3;
+		l |= digit;
+	}
+	return (l);
+}
+
+static size_t
+atohex(unsigned char *b, size_t bsize, const char *p, size_t psize)
+{
+	size_t fbsize = bsize;
+
+	while (bsize && psize > 1) {
+		unsigned char x;
+
+		if (p[0] >= 'a' && p[0] <= 'z')
+			x = (p[0] - 'a' + 0x0a) << 4;
+		else if (p[0] >= 'A' && p[0] <= 'Z')
+			x = (p[0] - 'A' + 0x0a) << 4;
+		else if (p[0] >= '0' && p[0] <= '9')
+			x = (p[0] - '0') << 4;
+		else
+			return (-1);
+		if (p[1] >= 'a' && p[1] <= 'z')
+			x |= p[1] - 'a' + 0x0a;
+		else if (p[1] >= 'A' && p[1] <= 'Z')
+			x |= p[1] - 'A' + 0x0a;
+		else if (p[1] >= '0' && p[1] <= '9')
+			x |= p[1] - '0';
+		else
+			return (-1);
+		
+		*b++ = x;
+		bsize--;
+		p += 2;
+		psize -= 2;
+	}
+	return (fbsize - bsize);
+}
+
+static time_t
+time_from_tm(struct tm *t)
+{
+#if HAVE_TIMEGM
+        /* Use platform timegm() if available. */
+        return (timegm(t));
+#elif HAVE__MKGMTIME64
+        return (_mkgmtime64(t));
+#else
+        /* Else use direct calculation using POSIX assumptions. */
+        /* First, fix up tm_yday based on the year/month/day. */
+        mktime(t);
+        /* Then we can compute timegm() from first principles. */
+        return (t->tm_sec
+            + t->tm_min * 60
+            + t->tm_hour * 3600
+            + t->tm_yday * 86400
+            + (t->tm_year - 70) * 31536000
+            + ((t->tm_year - 69) / 4) * 86400
+            - ((t->tm_year - 1) / 100) * 86400
+            + ((t->tm_year + 299) / 400) * 86400);
+#endif
+}
+
+static time_t
+parse_time(const char *p, size_t n)
+{
+	struct tm tm;
+	time_t t = 0;
+	int64_t data;
+
+	memset(&tm, 0, sizeof(tm));
+	if (n != 20)
+		return (t);
+	data = atol10(p, 4);
+	if (data < 1900)
+		return (t);
+	tm.tm_year = (int)data - 1900;
+	p += 4;
+	if (*p++ != '-')
+		return (t);
+	data = atol10(p, 2);
+	if (data < 1 || data > 12)
+		return (t);
+	tm.tm_mon = (int)data -1;
+	p += 2;
+	if (*p++ != '-')
+		return (t);
+	data = atol10(p, 2);
+	if (data < 1 || data > 31)
+		return (t);
+	tm.tm_mday = (int)data;
+	p += 2;
+	if (*p++ != 'T')
+		return (t);
+	data = atol10(p, 2);
+	if (data < 0 || data > 23)
+		return (t);
+	tm.tm_hour = (int)data;
+	p += 2;
+	if (*p++ != ':')
+		return (t);
+	data = atol10(p, 2);
+	if (data < 0 || data > 59)
+		return (t);
+	tm.tm_min = (int)data;
+	p += 2;
+	if (*p++ != ':')
+		return (t);
+	data = atol10(p, 2);
+	if (data < 0 || data > 60)
+		return (t);
+	tm.tm_sec = (int)data;
+#if 0
+	p += 2;
+	if (*p != 'Z')
+		return (t);
+#endif
+
+	t = time_from_tm(&tm);
+
+	return (t);
+}
+
+static int
+heap_add_entry(struct archive_read *a,
+    struct heap_queue *heap, struct xar_file *file)
+{
+	uint64_t file_id, parent_id;
+	int hole, parent;
+
+	/* Expand our pending files list as necessary. */
+	if (heap->used >= heap->allocated) {
+		struct xar_file **new_pending_files;
+		int new_size;
+
+		if (heap->allocated < 1024)
+			new_size = 1024;
+		else
+			new_size = heap->allocated * 2;
+		/* Overflow might keep us from growing the list. */
+		if (new_size <= heap->allocated) {
+			archive_set_error(&a->archive,
+			    ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		new_pending_files = (struct xar_file **)
+		    malloc(new_size * sizeof(new_pending_files[0]));
+		if (new_pending_files == NULL) {
+			archive_set_error(&a->archive,
+			    ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		if (heap->allocated) {
+			memcpy(new_pending_files, heap->files,
+			    heap->allocated * sizeof(new_pending_files[0]));
+			free(heap->files);
+		}
+		heap->files = new_pending_files;
+		heap->allocated = new_size;
+	}
+
+	file_id = file->id;
+
+	/*
+	 * Start with hole at end, walk it up tree to find insertion point.
+	 */
+	hole = heap->used++;
+	while (hole > 0) {
+		parent = (hole - 1)/2;
+		parent_id = heap->files[parent]->id;
+		if (file_id >= parent_id) {
+			heap->files[hole] = file;
+			return (ARCHIVE_OK);
+		}
+		/* Move parent into hole <==> move hole up tree. */
+		heap->files[hole] = heap->files[parent];
+		hole = parent;
+	}
+	heap->files[0] = file;
+
+	return (ARCHIVE_OK);
+}
+
+static struct xar_file *
+heap_get_entry(struct heap_queue *heap)
+{
+	uint64_t a_id, b_id, c_id;
+	int a, b, c;
+	struct xar_file *r, *tmp;
+
+	if (heap->used < 1)
+		return (NULL);
+
+	/*
+	 * The first file in the list is the earliest; we'll return this.
+	 */
+	r = heap->files[0];
+
+	/*
+	 * Move the last item in the heap to the root of the tree
+	 */
+	heap->files[0] = heap->files[--(heap->used)];
+
+	/*
+	 * Rebalance the heap.
+	 */
+	a = 0; /* Starting element and its heap key */
+	a_id = heap->files[a]->id;
+	for (;;) {
+		b = a + a + 1; /* First child */
+		if (b >= heap->used)
+			return (r);
+		b_id = heap->files[b]->id;
+		c = b + 1; /* Use second child if it is smaller. */
+		if (c < heap->used) {
+			c_id = heap->files[c]->id;
+			if (c_id < b_id) {
+				b = c;
+				b_id = c_id;
+			}
+		}
+		if (a_id <= b_id)
+			return (r);
+		tmp = heap->files[a];
+		heap->files[a] = heap->files[b];
+		heap->files[b] = tmp;
+		a = b;
+	}
+}
+
+static int
+add_link(struct archive_read *a, struct xar *xar, struct xar_file *file)
+{
+	struct hdlink *hdlink;
+
+	for (hdlink = xar->hdlink_list; hdlink != NULL; hdlink = hdlink->next) {
+		if (hdlink->id == file->link) {
+			file->hdnext = hdlink->files;
+			hdlink->cnt++;
+			hdlink->files = file;
+			return (ARCHIVE_OK);
+		}
+	}
+	hdlink = malloc(sizeof(*hdlink));
+	if (hdlink == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	file->hdnext = NULL;
+	hdlink->id = file->link;
+	hdlink->cnt = 1;
+	hdlink->files = file;
+	hdlink->next = xar->hdlink_list;
+	xar->hdlink_list = hdlink;
+	return (ARCHIVE_OK);
+}
+
+static void
+_checksum_init(struct chksumwork *sumwrk, int sum_alg)
+{
+	sumwrk->alg = sum_alg;
+	switch (sum_alg) {
+	case CKSUM_NONE:
+		break;
+	case CKSUM_SHA1:
+		archive_sha1_init(&(sumwrk->sha1ctx));
+		break;
+	case CKSUM_MD5:
+		archive_md5_init(&(sumwrk->md5ctx));
+		break;
+	}
+}
+
+static void
+_checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
+{
+
+	switch (sumwrk->alg) {
+	case CKSUM_NONE:
+		break;
+	case CKSUM_SHA1:
+		archive_sha1_update(&(sumwrk->sha1ctx), buff, size);
+		break;
+	case CKSUM_MD5:
+		archive_md5_update(&(sumwrk->md5ctx), buff, size);
+		break;
+	}
+}
+
+static int
+_checksum_final(struct chksumwork *sumwrk, const void *val, size_t len)
+{
+	unsigned char sum[MAX_SUM_SIZE];
+	int r = ARCHIVE_OK;
+
+	switch (sumwrk->alg) {
+	case CKSUM_NONE:
+		break;
+	case CKSUM_SHA1:
+		archive_sha1_final(&(sumwrk->sha1ctx), sum);
+		if (len != SHA1_SIZE ||
+		    memcmp(val, sum, SHA1_SIZE) != 0)
+			r = ARCHIVE_FAILED;
+		break;
+	case CKSUM_MD5:
+		archive_md5_final(&(sumwrk->md5ctx), sum);
+		if (len != MD5_SIZE ||
+		    memcmp(val, sum, MD5_SIZE) != 0)
+			r = ARCHIVE_FAILED;
+		break;
+	}
+	return (r);
+}
+
+static void
+checksum_init(struct archive_read *a, int a_sum_alg, int e_sum_alg)
+{
+	struct xar *xar;
+
+	xar = (struct xar *)(a->format->data);
+	_checksum_init(&(xar->a_sumwrk), a_sum_alg);
+	_checksum_init(&(xar->e_sumwrk), e_sum_alg);
+}
+
+static void
+checksum_update(struct archive_read *a, const void *abuff, size_t asize,
+    const void *ebuff, size_t esize)
+{
+	struct xar *xar;
+
+	xar = (struct xar *)(a->format->data);
+	_checksum_update(&(xar->a_sumwrk), abuff, asize);
+	_checksum_update(&(xar->e_sumwrk), ebuff, esize);
+}
+
+static int
+checksum_final(struct archive_read *a, const void *a_sum_val,
+    size_t a_sum_len, const void *e_sum_val, size_t e_sum_len)
+{
+	struct xar *xar;
+	int r;
+
+	xar = (struct xar *)(a->format->data);
+	r = _checksum_final(&(xar->a_sumwrk), a_sum_val, a_sum_len);
+	if (r == ARCHIVE_OK)
+		r = _checksum_final(&(xar->e_sumwrk), e_sum_val, e_sum_len);
+	if (r != ARCHIVE_OK)
+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+		    "Sumcheck error");
+	return (r);
+}
+
+static int
+decompression_init(struct archive_read *a, enum enctype encoding)
+{
+	struct xar *xar;
+	const char *detail;
+	int r;
+
+	xar = (struct xar *)(a->format->data);
+	xar->rd_encoding = encoding;
+	switch (encoding) {
+	case NONE:
+		break;
+	case GZIP:
+		if (xar->stream_valid)
+			r = inflateReset(&(xar->stream));
+		else
+			r = inflateInit(&(xar->stream));
+		if (r != Z_OK) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Couldn't initialize zlib stream.");
+			return (ARCHIVE_FATAL);
+		}
+		xar->stream_valid = 1;
+		xar->stream.total_in = 0;
+		xar->stream.total_out = 0;
+		break;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	case BZIP2:
+		if (xar->bzstream_valid) {
+			BZ2_bzDecompressEnd(&(xar->bzstream));
+			xar->bzstream_valid = 0;
+		}
+		r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 0);
+		if (r == BZ_MEM_ERROR)
+			r = BZ2_bzDecompressInit(&(xar->bzstream), 0, 1);
+		if (r != BZ_OK) {
+			int err = ARCHIVE_ERRNO_MISC;
+			detail = NULL;
+			switch (r) {
+			case BZ_PARAM_ERROR:
+				detail = "invalid setup parameter";
+				break;
+			case BZ_MEM_ERROR:
+				err = ENOMEM;
+				detail = "out of memory";
+				break;
+			case BZ_CONFIG_ERROR:
+				detail = "mis-compiled library";
+				break;
+			}
+			archive_set_error(&a->archive, err,
+			    "Internal error initializing decompressor: %s",
+			    detail == NULL ? "??" : detail);
+			xar->bzstream_valid = 0;
+			return (ARCHIVE_FATAL);
+		}
+		xar->bzstream_valid = 1;
+		xar->bzstream.total_in_lo32 = 0;
+		xar->bzstream.total_in_hi32 = 0;
+		xar->bzstream.total_out_lo32 = 0;
+		xar->bzstream.total_out_hi32 = 0;
+		break;
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+#if LZMA_VERSION_MAJOR >= 5
+/* Effectively disable the limiter. */
+#define LZMA_MEMLIMIT   UINT64_MAX
+#else
+/* NOTE: This needs to check memory size which running system has. */
+#define LZMA_MEMLIMIT   (1U << 30)
+#endif
+	case XZ:
+	case LZMA:
+		if (xar->lzstream_valid) {
+			lzma_end(&(xar->lzstream));
+			xar->lzstream_valid = 0;
+		}
+		if (xar->entry_encoding == XZ)
+			r = lzma_stream_decoder(&(xar->lzstream),
+			    LZMA_MEMLIMIT,/* memlimit */
+			    LZMA_CONCATENATED);
+		else
+			r = lzma_alone_decoder(&(xar->lzstream),
+			    LZMA_MEMLIMIT);/* memlimit */
+		if (r != LZMA_OK) {
+			switch (r) {
+			case LZMA_MEM_ERROR:
+				archive_set_error(&a->archive,
+				    ENOMEM,
+				    "Internal error initializing "
+				    "compression library: "
+				    "Cannot allocate memory");
+				break;
+			case LZMA_OPTIONS_ERROR:
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Internal error initializing "
+				    "compression library: "
+				    "Invalid or unsupported options");
+				break;
+			default:
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Internal error initializing "
+				    "lzma library");
+				break;
+			}
+			return (ARCHIVE_FATAL);
+		}
+		xar->lzstream_valid = 1;
+		xar->lzstream.total_in = 0;
+		xar->lzstream.total_out = 0;
+		break;
+#endif
+	/*
+	 * Unsupported compression.
+	 */
+	default:
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
+	case BZIP2:
+#endif
+#if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA)
+	case LZMA:
+	case XZ:
+#endif
+		switch (xar->entry_encoding) {
+		case BZIP2: detail = "bzip2"; break;
+		case LZMA: detail = "lzma"; break;
+		case XZ: detail = "xz"; break;
+		default: detail = "??"; break;
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "%s compression not supported on this platform",
+		    detail);
+		return (ARCHIVE_FAILED);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+decompress(struct archive_read *a, const void **buff, size_t *outbytes,
+    const void *b, size_t *used)
+{
+	struct xar *xar;
+	void *outbuff;
+	size_t avail_in, avail_out;
+	int r;
+
+	xar = (struct xar *)(a->format->data);
+	avail_in = *used;
+	outbuff = (void *)(uintptr_t)*buff;
+	if (outbuff == NULL) {
+		if (xar->outbuff == NULL) {
+			xar->outbuff = malloc(OUTBUFF_SIZE);
+			if (xar->outbuff == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Couldn't allocate memory for out buffer");
+				return (ARCHIVE_FATAL);
+			}
+		}
+		outbuff = xar->outbuff;
+		*buff = outbuff;
+		avail_out = OUTBUFF_SIZE;
+	} else
+		avail_out = *outbytes;
+	switch (xar->rd_encoding) {
+	case GZIP:
+		xar->stream.next_in = (Bytef *)(uintptr_t)b;
+		xar->stream.avail_in = avail_in;
+		xar->stream.next_out = (unsigned char *)outbuff;
+		xar->stream.avail_out = avail_out;
+		r = inflate(&(xar->stream), 0);
+		switch (r) {
+		case Z_OK: /* Decompressor made some progress.*/
+		case Z_STREAM_END: /* Found end of stream. */
+			break;
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "File decompression failed (%d)", r);
+			return (ARCHIVE_FATAL);
+		}
+		*used = avail_in - xar->stream.avail_in;
+		*outbytes = avail_out - xar->stream.avail_out;
+		break;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	case BZIP2:
+		xar->bzstream.next_in = (char *)(uintptr_t)b;
+		xar->bzstream.avail_in = avail_in;
+		xar->bzstream.next_out = (char *)outbuff;
+		xar->bzstream.avail_out = avail_out;
+		r = BZ2_bzDecompress(&(xar->bzstream));
+		switch (r) {
+		case BZ_STREAM_END: /* Found end of stream. */
+			switch (BZ2_bzDecompressEnd(&(xar->bzstream))) {
+			case BZ_OK:
+				break;
+			default:
+				archive_set_error(&(a->archive),
+				    ARCHIVE_ERRNO_MISC,
+				    "Failed to clean up decompressor");
+				return (ARCHIVE_FATAL);
+			}
+			xar->bzstream_valid = 0;
+			/* FALLTHROUGH */
+		case BZ_OK: /* Decompressor made some progress. */
+			break;
+		default:
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "bzip decompression failed");
+			return (ARCHIVE_FATAL);
+		}
+		*used = avail_in - xar->bzstream.avail_in;
+		*outbytes = avail_out - xar->bzstream.avail_out;
+		break;
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+	case LZMA:
+	case XZ:
+		xar->lzstream.next_in = b;
+		xar->lzstream.avail_in = avail_in;
+		xar->lzstream.next_out = (unsigned char *)outbuff;
+		xar->lzstream.avail_out = avail_out;
+		r = lzma_code(&(xar->lzstream), LZMA_RUN);
+		switch (r) {
+		case LZMA_STREAM_END: /* Found end of stream. */
+			lzma_end(&(xar->lzstream));
+			xar->lzstream_valid = 0;
+			/* FALLTHROUGH */
+		case LZMA_OK: /* Decompressor made some progress. */
+			break;
+		default:
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "%s decompression failed(%d)",
+			    (xar->entry_encoding == XZ)?"xz":"lzma",
+			    r);
+			return (ARCHIVE_FATAL);
+		}
+		*used = avail_in - xar->lzstream.avail_in;
+		*outbytes = avail_out - xar->lzstream.avail_out;
+		break;
+#endif
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR)
+	case BZIP2:
+#endif
+#if !defined(HAVE_LZMA_H) || !defined(HAVE_LIBLZMA)
+	case LZMA:
+	case XZ:
+#endif
+	case NONE:
+	default:
+		if (outbuff == xar->outbuff) {
+			*buff = b;
+			*used = avail_in;
+			*outbytes = avail_in;
+		} else {
+			if (avail_out > avail_in)
+				avail_out = avail_in;
+			memcpy(outbuff, b, avail_out);
+			*used = avail_out;
+			*outbytes = avail_out;
+		}
+		break;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+decompression_cleanup(struct archive_read *a)
+{
+	struct xar *xar;
+	int r;
+
+	xar = (struct xar *)(a->format->data);
+	r = ARCHIVE_OK;
+	if (xar->stream_valid) {
+		if (inflateEnd(&(xar->stream)) != Z_OK) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "Failed to clean up zlib decompressor");
+			r = ARCHIVE_FATAL;
+		}
+	}
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	if (xar->bzstream_valid) {
+		if (BZ2_bzDecompressEnd(&(xar->bzstream)) != BZ_OK) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "Failed to clean up bzip2 decompressor");
+			r = ARCHIVE_FATAL;
+		}
+	}
+#endif
+#if defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+	if (xar->lzstream_valid)
+		lzma_end(&(xar->lzstream));
+#elif defined(HAVE_LZMA_H) && defined(HAVE_LIBLZMA)
+	if (xar->lzstream_valid) {
+		if (lzmadec_end(&(xar->lzstream)) != LZMADEC_OK) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "Failed to clean up lzmadec decompressor");
+			r = ARCHIVE_FATAL;
+		}
+	}
+#endif
+	return (r);
+}
+
+static void
+checksum_cleanup(struct archive_read *a) {
+	struct xar *xar;
+
+	xar = (struct xar *)(a->format->data);
+
+	_checksum_final(&(xar->a_sumwrk), NULL, 0);
+	_checksum_final(&(xar->e_sumwrk), NULL, 0);
+}
+
+static void
+xmlattr_cleanup(struct xmlattr_list *list)
+{
+	struct xmlattr *attr, *next;
+
+	attr = list->first;
+	while (attr != NULL) {
+		next = attr->next;
+		free(attr->name);
+		free(attr->value);
+		free(attr);
+		attr = next;
+	}
+	list->first = NULL;
+	list->last = &(list->first);
+}
+
+static int
+file_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)
+{
+	struct xar_file *file;
+	struct xmlattr *attr;
+
+	file = calloc(1, sizeof(*file));
+	if (file == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	file->parent = xar->file;
+	file->mode = 0777 | AE_IFREG;
+	file->atime =  0;
+	file->mtime = 0;
+	xar->file = file;
+	xar->xattr = NULL;
+	for (attr = list->first; attr != NULL; attr = attr->next) {
+		if (strcmp(attr->name, "id") == 0)
+			file->id = atol10(attr->value, strlen(attr->value));
+	}
+	file->nlink = 1;
+	if (heap_add_entry(a, &(xar->file_queue), file) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	return (ARCHIVE_OK);
+}
+
+static void
+file_free(struct xar_file *file)
+{
+	struct xattr *xattr;
+
+	archive_string_free(&(file->pathname));
+	archive_string_free(&(file->symlink));
+	archive_string_free(&(file->uname));
+	archive_string_free(&(file->gname));
+	archive_string_free(&(file->hardlink));
+	xattr = file->xattr_list;
+	while (xattr != NULL) {
+		struct xattr *next;
+
+		next = xattr->next;
+		xattr_free(xattr);
+		xattr = next;
+	}
+
+	free(file);
+}
+
+static int
+xattr_new(struct archive_read *a, struct xar *xar, struct xmlattr_list *list)
+{
+	struct xattr *xattr, **nx;
+	struct xmlattr *attr;
+
+	xattr = calloc(1, sizeof(*xattr));
+	if (xattr == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	xar->xattr = xattr;
+	for (attr = list->first; attr != NULL; attr = attr->next) {
+		if (strcmp(attr->name, "id") == 0)
+			xattr->id = atol10(attr->value, strlen(attr->value));
+	}
+	/* Chain to xattr list. */
+	for (nx = &(xar->file->xattr_list);
+	    *nx != NULL; nx = &((*nx)->next)) {
+		if (xattr->id < (*nx)->id)
+			break;
+	}
+	xattr->next = *nx;
+	*nx = xattr;
+
+	return (ARCHIVE_OK);
+}
+
+static void
+xattr_free(struct xattr *xattr)
+{
+	archive_string_free(&(xattr->name));
+	free(xattr);
+}
+
+static int
+getencoding(struct xmlattr_list *list)
+{
+	struct xmlattr *attr;
+	enum enctype encoding = NONE;
+
+	for (attr = list->first; attr != NULL; attr = attr->next) {
+		if (strcmp(attr->name, "style") == 0) {
+			if (strcmp(attr->value, "application/octet-stream") == 0)
+				encoding = NONE;
+			else if (strcmp(attr->value, "application/x-gzip") == 0)
+				encoding = GZIP;
+			else if (strcmp(attr->value, "application/x-bzip2") == 0)
+				encoding = BZIP2;
+			else if (strcmp(attr->value, "application/x-lzma") == 0)
+				encoding = LZMA;
+			else if (strcmp(attr->value, "application/x-xz") == 0)
+				encoding = XZ;
+		}
+	}
+	return (encoding);
+}
+
+static int
+getsumalgorithm(struct xmlattr_list *list)
+{
+	struct xmlattr *attr;
+	int alg = CKSUM_NONE;
+
+	for (attr = list->first; attr != NULL; attr = attr->next) {
+		if (strcmp(attr->name, "style") == 0) {
+			const char *v = attr->value;
+			if ((v[0] == 'S' || v[0] == 's') &&
+			    (v[1] == 'H' || v[1] == 'h') &&
+			    (v[2] == 'A' || v[2] == 'a') &&
+			    v[3] == '1' && v[4] == '\0')
+				alg = CKSUM_SHA1;
+			if ((v[0] == 'M' || v[0] == 'm') &&
+			    (v[1] == 'D' || v[1] == 'd') &&
+			    v[2] == '5' && v[3] == '\0')
+				alg = CKSUM_MD5;
+		}
+	}
+	return (alg);
+}
+
+static int
+unknowntag_start(struct archive_read *a, struct xar *xar, const char *name)
+{
+	struct unknown_tag *tag;
+
+	tag = malloc(sizeof(*tag));
+	if (tag == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	tag->next = xar->unknowntags;
+	archive_string_init(&(tag->name));
+	archive_strcpy(&(tag->name), name);
+	if (xar->unknowntags == NULL) {
+#if DEBUG
+		fprintf(stderr, "UNKNOWNTAG_START:%s\n", name);
+#endif
+		xar->xmlsts_unknown = xar->xmlsts;
+		xar->xmlsts = UNKNOWN;
+	}
+	xar->unknowntags = tag;
+	return (ARCHIVE_OK);
+}
+
+static void
+unknowntag_end(struct xar *xar, const char *name)
+{
+	struct unknown_tag *tag;
+
+	tag = xar->unknowntags;
+	if (tag == NULL || name == NULL)
+		return;
+	if (strcmp(tag->name.s, name) == 0) {
+		xar->unknowntags = tag->next;
+		archive_string_free(&(tag->name));
+		free(tag);
+		if (xar->unknowntags == NULL) {
+#if DEBUG
+			fprintf(stderr, "UNKNOWNTAG_END:%s\n", name);
+#endif
+			xar->xmlsts = xar->xmlsts_unknown;
+		}
+	}
+}
+
+static int
+xml_start(struct archive_read *a, const char *name, struct xmlattr_list *list)
+{
+	struct xar *xar;
+	struct xmlattr *attr;
+
+	xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+	fprintf(stderr, "xml_sta:[%s]\n", name);
+	for (attr = list->first; attr != NULL; attr = attr->next)
+		fprintf(stderr, "    attr:\"%s\"=\"%s\"\n",
+		    attr->name, attr->value);
+#endif
+	xar->base64text = 0;
+	switch (xar->xmlsts) {
+	case INIT:
+		if (strcmp(name, "xar") == 0)
+			xar->xmlsts = XAR;
+		else
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case XAR:
+		if (strcmp(name, "toc") == 0)
+			xar->xmlsts = TOC;
+		else
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case TOC:
+		if (strcmp(name, "creation-time") == 0)
+			xar->xmlsts = TOC_CREATION_TIME;
+		else if (strcmp(name, "checksum") == 0)
+			xar->xmlsts = TOC_CHECKSUM;
+		else if (strcmp(name, "file") == 0) {
+			if (file_new(a, xar, list) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			xar->xmlsts = TOC_FILE;
+		}
+		else
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case TOC_CHECKSUM:
+		if (strcmp(name, "offset") == 0)
+			xar->xmlsts = TOC_CHECKSUM_OFFSET;
+		else if (strcmp(name, "size") == 0)
+			xar->xmlsts = TOC_CHECKSUM_SIZE;
+		else
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case TOC_FILE:
+		if (strcmp(name, "file") == 0) {
+			if (file_new(a, xar, list) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		}
+		else if (strcmp(name, "data") == 0)
+			xar->xmlsts = FILE_DATA;
+		else if (strcmp(name, "ea") == 0) {
+			if (xattr_new(a, xar, list) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			xar->xmlsts = FILE_EA;
+		}
+		else if (strcmp(name, "ctime") == 0)
+			xar->xmlsts = FILE_CTIME;
+		else if (strcmp(name, "mtime") == 0)
+			xar->xmlsts = FILE_MTIME;
+		else if (strcmp(name, "atime") == 0)
+			xar->xmlsts = FILE_ATIME;
+		else if (strcmp(name, "group") == 0)
+			xar->xmlsts = FILE_GROUP;
+		else if (strcmp(name, "gid") == 0)
+			xar->xmlsts = FILE_GID;
+		else if (strcmp(name, "user") == 0)
+			xar->xmlsts = FILE_USER;
+		else if (strcmp(name, "uid") == 0)
+			xar->xmlsts = FILE_UID;
+		else if (strcmp(name, "mode") == 0)
+			xar->xmlsts = FILE_MODE;
+		else if (strcmp(name, "device") == 0)
+			xar->xmlsts = FILE_DEVICE;
+		else if (strcmp(name, "deviceno") == 0)
+			xar->xmlsts = FILE_DEVICENO;
+		else if (strcmp(name, "inode") == 0)
+			xar->xmlsts = FILE_INODE;
+		else if (strcmp(name, "link") == 0)
+			xar->xmlsts = FILE_LINK;
+		else if (strcmp(name, "type") == 0) {
+			xar->xmlsts = FILE_TYPE;
+			for (attr = list->first; attr != NULL;
+			    attr = attr->next) {
+				if (strcmp(attr->name, "link") != 0)
+					continue;
+				if (strcmp(attr->value, "original") == 0) {
+					xar->file->hdnext = xar->hdlink_orgs;
+					xar->hdlink_orgs = xar->file;
+				} else {
+					xar->file->link = (unsigned)atol10(attr->value,
+					    strlen(attr->value));
+					if (xar->file->link > 0)
+						if (add_link(a, xar, xar->file) != ARCHIVE_OK) {
+							return (ARCHIVE_FATAL);
+						};
+				}
+			}
+		}
+		else if (strcmp(name, "name") == 0) {
+			xar->xmlsts = FILE_NAME;
+			for (attr = list->first; attr != NULL;
+			    attr = attr->next) {
+				if (strcmp(attr->name, "enctype") == 0 &&
+				    strcmp(attr->value, "base64") == 0)
+					xar->base64text = 1;
+			}
+		}
+		else if (strcmp(name, "acl") == 0)
+			xar->xmlsts = FILE_ACL;
+		else if (strcmp(name, "flags") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		else if (strcmp(name, "ext2") == 0)
+			xar->xmlsts = FILE_EXT2;
+		else
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case FILE_DATA:
+		if (strcmp(name, "length") == 0)
+			xar->xmlsts = FILE_DATA_LENGTH;
+		else if (strcmp(name, "offset") == 0)
+			xar->xmlsts = FILE_DATA_OFFSET;
+		else if (strcmp(name, "size") == 0)
+			xar->xmlsts = FILE_DATA_SIZE;
+		else if (strcmp(name, "encoding") == 0) {
+			xar->xmlsts = FILE_DATA_ENCODING;
+			xar->file->encoding = getencoding(list);
+		}
+		else if (strcmp(name, "archived-checksum") == 0) {
+			xar->xmlsts = FILE_DATA_A_CHECKSUM;
+			xar->file->a_sum.alg = getsumalgorithm(list);
+		}
+		else if (strcmp(name, "extracted-checksum") == 0) {
+			xar->xmlsts = FILE_DATA_E_CHECKSUM;
+			xar->file->e_sum.alg = getsumalgorithm(list);
+		}
+		else if (strcmp(name, "content") == 0)
+			xar->xmlsts = FILE_DATA_CONTENT;
+		else
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case FILE_DEVICE:
+		if (strcmp(name, "major") == 0)
+			xar->xmlsts = FILE_DEVICE_MAJOR;
+		else if (strcmp(name, "minor") == 0)
+			xar->xmlsts = FILE_DEVICE_MINOR;
+		else
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case FILE_DATA_CONTENT:
+		if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		break;
+	case FILE_EA:
+		if (strcmp(name, "length") == 0)
+			xar->xmlsts = FILE_EA_LENGTH;
+		else if (strcmp(name, "offset") == 0)
+			xar->xmlsts = FILE_EA_OFFSET;
+		else if (strcmp(name, "size") == 0)
+			xar->xmlsts = FILE_EA_SIZE;
+		else if (strcmp(name, "encoding") == 0) {
+			xar->xmlsts = FILE_EA_ENCODING;
+			xar->xattr->encoding = getencoding(list);
+		} else if (strcmp(name, "archived-checksum") == 0)
+			xar->xmlsts = FILE_EA_A_CHECKSUM;
+		else if (strcmp(name, "extracted-checksum") == 0)
+			xar->xmlsts = FILE_EA_E_CHECKSUM;
+		else if (strcmp(name, "name") == 0)
+			xar->xmlsts = FILE_EA_NAME;
+		else if (strcmp(name, "fstype") == 0)
+			xar->xmlsts = FILE_EA_FSTYPE;
+		else
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case FILE_ACL:
+		if (strcmp(name, "appleextended") == 0)
+			xar->xmlsts = FILE_ACL_APPLEEXTENDED;
+		else if (strcmp(name, "default") == 0)
+			xar->xmlsts = FILE_ACL_DEFAULT;
+		else if (strcmp(name, "access") == 0)
+			xar->xmlsts = FILE_ACL_ACCESS;
+		else
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case FILE_FLAGS:
+		if (!xml_parse_file_flags(xar, name))
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case FILE_EXT2:
+		if (!xml_parse_file_ext2(xar, name))
+			if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		break;
+	case TOC_CREATION_TIME:
+	case TOC_CHECKSUM_OFFSET:
+	case TOC_CHECKSUM_SIZE:
+	case FILE_DATA_LENGTH:
+	case FILE_DATA_OFFSET:
+	case FILE_DATA_SIZE:
+	case FILE_DATA_ENCODING:
+	case FILE_DATA_A_CHECKSUM:
+	case FILE_DATA_E_CHECKSUM:
+	case FILE_EA_LENGTH:
+	case FILE_EA_OFFSET:
+	case FILE_EA_SIZE:
+	case FILE_EA_ENCODING:
+	case FILE_EA_A_CHECKSUM:
+	case FILE_EA_E_CHECKSUM:
+	case FILE_EA_NAME:
+	case FILE_EA_FSTYPE:
+	case FILE_CTIME:
+	case FILE_MTIME:
+	case FILE_ATIME:
+	case FILE_GROUP:
+	case FILE_GID:
+	case FILE_USER:
+	case FILE_UID:
+	case FILE_INODE:
+	case FILE_DEVICE_MAJOR:
+	case FILE_DEVICE_MINOR:
+	case FILE_DEVICENO:
+	case FILE_MODE:
+	case FILE_TYPE:
+	case FILE_LINK:
+	case FILE_NAME:
+	case FILE_ACL_DEFAULT:
+	case FILE_ACL_ACCESS:
+	case FILE_ACL_APPLEEXTENDED:
+	case FILE_FLAGS_USER_NODUMP:
+	case FILE_FLAGS_USER_IMMUTABLE:
+	case FILE_FLAGS_USER_APPEND:
+	case FILE_FLAGS_USER_OPAQUE:
+	case FILE_FLAGS_USER_NOUNLINK:
+	case FILE_FLAGS_SYS_ARCHIVED:
+	case FILE_FLAGS_SYS_IMMUTABLE:
+	case FILE_FLAGS_SYS_APPEND:
+	case FILE_FLAGS_SYS_NOUNLINK:
+	case FILE_FLAGS_SYS_SNAPSHOT:
+	case FILE_EXT2_SecureDeletion:
+	case FILE_EXT2_Undelete:
+	case FILE_EXT2_Compress:
+	case FILE_EXT2_Synchronous:
+	case FILE_EXT2_Immutable:
+	case FILE_EXT2_AppendOnly:
+	case FILE_EXT2_NoDump:
+	case FILE_EXT2_NoAtime:
+	case FILE_EXT2_CompDirty:
+	case FILE_EXT2_CompBlock:
+	case FILE_EXT2_NoCompBlock:
+	case FILE_EXT2_CompError:
+	case FILE_EXT2_BTree:
+	case FILE_EXT2_HashIndexed:
+	case FILE_EXT2_iMagic:
+	case FILE_EXT2_Journaled:
+	case FILE_EXT2_NoTail:
+	case FILE_EXT2_DirSync:
+	case FILE_EXT2_TopDir:
+	case FILE_EXT2_Reserved:
+	case UNKNOWN:
+		if (unknowntag_start(a, xar, name) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		break;
+	}
+	return (ARCHIVE_OK);
+}
+
+static void
+xml_end(void *userData, const char *name)
+{
+	struct archive_read *a;
+	struct xar *xar;
+
+	a = (struct archive_read *)userData;
+	xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+	fprintf(stderr, "xml_end:[%s]\n", name);
+#endif
+	switch (xar->xmlsts) {
+	case INIT:
+		break;
+	case XAR:
+		if (strcmp(name, "xar") == 0)
+			xar->xmlsts = INIT;
+		break;
+	case TOC:
+		if (strcmp(name, "toc") == 0)
+			xar->xmlsts = XAR;
+		break;
+	case TOC_CREATION_TIME:
+		if (strcmp(name, "creation-time") == 0)
+			xar->xmlsts = TOC;
+		break;
+	case TOC_CHECKSUM:
+		if (strcmp(name, "checksum") == 0)
+			xar->xmlsts = TOC;
+		break;
+	case TOC_CHECKSUM_OFFSET:
+		if (strcmp(name, "offset") == 0)
+			xar->xmlsts = TOC_CHECKSUM;
+		break;
+	case TOC_CHECKSUM_SIZE:
+		if (strcmp(name, "size") == 0)
+			xar->xmlsts = TOC_CHECKSUM;
+		break;
+	case TOC_FILE:
+		if (strcmp(name, "file") == 0) {
+			if (xar->file->parent != NULL &&
+			    ((xar->file->mode & AE_IFMT) == AE_IFDIR))
+				xar->file->parent->subdirs++;
+			xar->file = xar->file->parent;
+			if (xar->file == NULL)
+				xar->xmlsts = TOC;
+		}
+		break;
+	case FILE_DATA:
+		if (strcmp(name, "data") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_DATA_LENGTH:
+		if (strcmp(name, "length") == 0)
+			xar->xmlsts = FILE_DATA;
+		break;
+	case FILE_DATA_OFFSET:
+		if (strcmp(name, "offset") == 0)
+			xar->xmlsts = FILE_DATA;
+		break;
+	case FILE_DATA_SIZE:
+		if (strcmp(name, "size") == 0)
+			xar->xmlsts = FILE_DATA;
+		break;
+	case FILE_DATA_ENCODING:
+		if (strcmp(name, "encoding") == 0)
+			xar->xmlsts = FILE_DATA;
+		break;
+	case FILE_DATA_A_CHECKSUM:
+		if (strcmp(name, "archived-checksum") == 0)
+			xar->xmlsts = FILE_DATA;
+		break;
+	case FILE_DATA_E_CHECKSUM:
+		if (strcmp(name, "extracted-checksum") == 0)
+			xar->xmlsts = FILE_DATA;
+		break;
+	case FILE_DATA_CONTENT:
+		if (strcmp(name, "content") == 0)
+			xar->xmlsts = FILE_DATA;
+		break;
+	case FILE_EA:
+		if (strcmp(name, "ea") == 0) {
+			xar->xmlsts = TOC_FILE;
+			xar->xattr = NULL;
+		}
+		break;
+	case FILE_EA_LENGTH:
+		if (strcmp(name, "length") == 0)
+			xar->xmlsts = FILE_EA;
+		break;
+	case FILE_EA_OFFSET:
+		if (strcmp(name, "offset") == 0)
+			xar->xmlsts = FILE_EA;
+		break;
+	case FILE_EA_SIZE:
+		if (strcmp(name, "size") == 0)
+			xar->xmlsts = FILE_EA;
+		break;
+	case FILE_EA_ENCODING:
+		if (strcmp(name, "encoding") == 0)
+			xar->xmlsts = FILE_EA;
+		break;
+	case FILE_EA_A_CHECKSUM:
+		if (strcmp(name, "archived-checksum") == 0)
+			xar->xmlsts = FILE_EA;
+		break;
+	case FILE_EA_E_CHECKSUM:
+		if (strcmp(name, "extracted-checksum") == 0)
+			xar->xmlsts = FILE_EA;
+		break;
+	case FILE_EA_NAME:
+		if (strcmp(name, "name") == 0)
+			xar->xmlsts = FILE_EA;
+		break;
+	case FILE_EA_FSTYPE:
+		if (strcmp(name, "fstype") == 0)
+			xar->xmlsts = FILE_EA;
+		break;
+	case FILE_CTIME:
+		if (strcmp(name, "ctime") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_MTIME:
+		if (strcmp(name, "mtime") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_ATIME:
+		if (strcmp(name, "atime") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_GROUP:
+		if (strcmp(name, "group") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_GID:
+		if (strcmp(name, "gid") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_USER:
+		if (strcmp(name, "user") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_UID:
+		if (strcmp(name, "uid") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_MODE:
+		if (strcmp(name, "mode") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_DEVICE:
+		if (strcmp(name, "device") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_DEVICE_MAJOR:
+		if (strcmp(name, "major") == 0)
+			xar->xmlsts = FILE_DEVICE;
+		break;
+	case FILE_DEVICE_MINOR:
+		if (strcmp(name, "minor") == 0)
+			xar->xmlsts = FILE_DEVICE;
+		break;
+	case FILE_DEVICENO:
+		if (strcmp(name, "deviceno") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_INODE:
+		if (strcmp(name, "inode") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_LINK:
+		if (strcmp(name, "link") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_TYPE:
+		if (strcmp(name, "type") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_NAME:
+		if (strcmp(name, "name") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_ACL:
+		if (strcmp(name, "acl") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_ACL_DEFAULT:
+		if (strcmp(name, "default") == 0)
+			xar->xmlsts = FILE_ACL;
+		break;
+	case FILE_ACL_ACCESS:
+		if (strcmp(name, "access") == 0)
+			xar->xmlsts = FILE_ACL;
+		break;
+	case FILE_ACL_APPLEEXTENDED:
+		if (strcmp(name, "appleextended") == 0)
+			xar->xmlsts = FILE_ACL;
+		break;
+	case FILE_FLAGS:
+		if (strcmp(name, "flags") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_FLAGS_USER_NODUMP:
+		if (strcmp(name, "UserNoDump") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		break;
+	case FILE_FLAGS_USER_IMMUTABLE:
+		if (strcmp(name, "UserImmutable") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		break;
+	case FILE_FLAGS_USER_APPEND:
+		if (strcmp(name, "UserAppend") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		break;
+	case FILE_FLAGS_USER_OPAQUE:
+		if (strcmp(name, "UserOpaque") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		break;
+	case FILE_FLAGS_USER_NOUNLINK:
+		if (strcmp(name, "UserNoUnlink") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		break;
+	case FILE_FLAGS_SYS_ARCHIVED:
+		if (strcmp(name, "SystemArchived") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		break;
+	case FILE_FLAGS_SYS_IMMUTABLE:
+		if (strcmp(name, "SystemImmutable") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		break;
+	case FILE_FLAGS_SYS_APPEND:
+		if (strcmp(name, "SystemAppend") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		break;
+	case FILE_FLAGS_SYS_NOUNLINK:
+		if (strcmp(name, "SystemNoUnlink") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		break;
+	case FILE_FLAGS_SYS_SNAPSHOT:
+		if (strcmp(name, "SystemSnapshot") == 0)
+			xar->xmlsts = FILE_FLAGS;
+		break;
+	case FILE_EXT2:
+		if (strcmp(name, "ext2") == 0)
+			xar->xmlsts = TOC_FILE;
+		break;
+	case FILE_EXT2_SecureDeletion:
+		if (strcmp(name, "SecureDeletion") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_Undelete:
+		if (strcmp(name, "Undelete") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_Compress:
+		if (strcmp(name, "Compress") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_Synchronous:
+		if (strcmp(name, "Synchronous") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_Immutable:
+		if (strcmp(name, "Immutable") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_AppendOnly:
+		if (strcmp(name, "AppendOnly") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_NoDump:
+		if (strcmp(name, "NoDump") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_NoAtime:
+		if (strcmp(name, "NoAtime") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_CompDirty:
+		if (strcmp(name, "CompDirty") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_CompBlock:
+		if (strcmp(name, "CompBlock") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_NoCompBlock:
+		if (strcmp(name, "NoCompBlock") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_CompError:
+		if (strcmp(name, "CompError") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_BTree:
+		if (strcmp(name, "BTree") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_HashIndexed:
+		if (strcmp(name, "HashIndexed") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_iMagic:
+		if (strcmp(name, "iMagic") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_Journaled:
+		if (strcmp(name, "Journaled") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_NoTail:
+		if (strcmp(name, "NoTail") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_DirSync:
+		if (strcmp(name, "DirSync") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_TopDir:
+		if (strcmp(name, "TopDir") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case FILE_EXT2_Reserved:
+		if (strcmp(name, "Reserved") == 0)
+			xar->xmlsts = FILE_EXT2;
+		break;
+	case UNKNOWN:
+		unknowntag_end(xar, name);
+		break;
+	}
+}
+
+static const int base64[256] = {
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, /* 00 - 0F */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, /* 10 - 1F */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, 62, -1, -1, -1, 63, /* 20 - 2F */
+	52, 53, 54, 55, 56, 57, 58, 59,
+	60, 61, -1, -1, -1, -1, -1, -1, /* 30 - 3F */
+	-1,  0,  1,  2,  3,  4,  5,  6,
+	 7,  8,  9, 10, 11, 12, 13, 14, /* 40 - 4F */
+	15, 16, 17, 18, 19, 20, 21, 22,
+	23, 24, 25, -1, -1, -1, -1, -1, /* 50 - 5F */
+	-1, 26, 27, 28, 29, 30, 31, 32,
+	33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */
+	41, 42, 43, 44, 45, 46, 47, 48,
+	49, 50, 51, -1, -1, -1, -1, -1, /* 70 - 7F */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 8F */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, /* 90 - 9F */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, /* A0 - AF */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, /* B0 - BF */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, /* C0 - CF */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, /* D0 - DF */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, /* E0 - EF */
+	-1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, /* F0 - FF */
+};
+
+static void
+strappend_base64(struct xar *xar,
+    struct archive_string *as, const char *s, size_t l)
+{
+	unsigned char buff[256];
+	unsigned char *out;
+	const unsigned char *b;
+	size_t len;
+
+	(void)xar; /* UNUSED */
+	len = 0;
+	out = buff;
+	b = (const unsigned char *)s;
+	while (l > 0) {
+		int n = 0;
+
+		if (base64[b[0]] < 0 || base64[b[1]] < 0)
+			break;
+		n = base64[*b++] << 18;
+		n |= base64[*b++] << 12;
+		*out++ = n >> 16;
+		len++;
+		l -= 2;
+
+		if (l > 0) {
+			if (base64[*b] < 0)
+				break;
+			n |= base64[*b++] << 6;
+			*out++ = (n >> 8) & 0xFF;
+			len++;
+			--l;
+		}
+		if (l > 0) {
+			if (base64[*b] < 0)
+				break;
+			n |= base64[*b++];
+			*out++ = n & 0xFF;
+			len++;
+			--l;
+		}
+		if (len+3 >= sizeof(buff)) {
+			archive_strncat(as, (const char *)buff, len);
+			len = 0;
+			out = buff;
+		}
+	}
+	if (len > 0)
+		archive_strncat(as, (const char *)buff, len);
+}
+
+static int
+is_string(const char *known, const char *data, size_t len)
+{
+	if (strlen(known) != len)
+		return -1;
+	return memcmp(data, known, len);
+}
+
+static void
+xml_data(void *userData, const char *s, int len)
+{
+	struct archive_read *a;
+	struct xar *xar;
+
+	a = (struct archive_read *)userData;
+	xar = (struct xar *)(a->format->data);
+
+#if DEBUG
+	{
+		char buff[1024];
+		if (len > (int)(sizeof(buff)-1))
+			len = (int)(sizeof(buff)-1);
+		strncpy(buff, s, len);
+		buff[len] = 0;
+		fprintf(stderr, "\tlen=%d:\"%s\"\n", len, buff);
+	}
+#endif
+	switch (xar->xmlsts) {
+	case TOC_CHECKSUM_OFFSET:
+		xar->toc_chksum_offset = atol10(s, len);
+		break;
+	case TOC_CHECKSUM_SIZE:
+		xar->toc_chksum_size = atol10(s, len);
+		break;
+	default:
+		break;
+	}
+	if (xar->file == NULL)
+		return;
+
+	switch (xar->xmlsts) {
+	case FILE_NAME:
+		if (xar->file->parent != NULL) {
+			archive_string_concat(&(xar->file->pathname),
+			    &(xar->file->parent->pathname));
+			archive_strappend_char(&(xar->file->pathname), '/');
+		}
+		xar->file->has |= HAS_PATHNAME;
+		if (xar->base64text) {
+			strappend_base64(xar,
+			    &(xar->file->pathname), s, len);
+		} else
+			archive_strncat(&(xar->file->pathname), s, len);
+		break;
+	case FILE_LINK:
+		xar->file->has |= HAS_SYMLINK;
+		archive_strncpy(&(xar->file->symlink), s, len);
+		break;
+	case FILE_TYPE:
+		if (is_string("file", s, len) == 0 ||
+		    is_string("hardlink", s, len) == 0)
+			xar->file->mode =
+			    (xar->file->mode & ~AE_IFMT) | AE_IFREG;
+		if (is_string("directory", s, len) == 0)
+			xar->file->mode =
+			    (xar->file->mode & ~AE_IFMT) | AE_IFDIR;
+		if (is_string("symlink", s, len) == 0)
+			xar->file->mode =
+			    (xar->file->mode & ~AE_IFMT) | AE_IFLNK;
+		if (is_string("character special", s, len) == 0)
+			xar->file->mode =
+			    (xar->file->mode & ~AE_IFMT) | AE_IFCHR;
+		if (is_string("block special", s, len) == 0)
+			xar->file->mode =
+			    (xar->file->mode & ~AE_IFMT) | AE_IFBLK;
+		if (is_string("socket", s, len) == 0)
+			xar->file->mode =
+			    (xar->file->mode & ~AE_IFMT) | AE_IFSOCK;
+		if (is_string("fifo", s, len) == 0)
+			xar->file->mode =
+			    (xar->file->mode & ~AE_IFMT) | AE_IFIFO;
+		xar->file->has |= HAS_TYPE;
+		break;
+	case FILE_INODE:
+		xar->file->has |= HAS_INO;
+		xar->file->ino64 = atol10(s, len);
+		break;
+	case FILE_DEVICE_MAJOR:
+		xar->file->has |= HAS_DEVMAJOR;
+		xar->file->devmajor = (dev_t)atol10(s, len);
+		break;
+	case FILE_DEVICE_MINOR:
+		xar->file->has |= HAS_DEVMINOR;
+		xar->file->devminor = (dev_t)atol10(s, len);
+		break;
+	case FILE_DEVICENO:
+		xar->file->has |= HAS_DEV;
+		xar->file->dev = (dev_t)atol10(s, len);
+		break;
+	case FILE_MODE:
+		xar->file->has |= HAS_MODE;
+		xar->file->mode =
+		    (xar->file->mode & AE_IFMT) |
+		    ((mode_t)(atol8(s, len)) & ~AE_IFMT);
+		break;
+	case FILE_GROUP:
+		xar->file->has |= HAS_GID;
+		archive_strncpy(&(xar->file->gname), s, len);
+		break;
+	case FILE_GID:
+		xar->file->has |= HAS_GID;
+		xar->file->gid = atol10(s, len);
+		break;
+	case FILE_USER:
+		xar->file->has |= HAS_UID;
+		archive_strncpy(&(xar->file->uname), s, len);
+		break;
+	case FILE_UID:
+		xar->file->has |= HAS_UID;
+		xar->file->uid = atol10(s, len);
+		break;
+	case FILE_CTIME:
+		xar->file->has |= HAS_TIME | HAS_CTIME;
+		xar->file->ctime = parse_time(s, len);
+		break;
+	case FILE_MTIME:
+		xar->file->has |= HAS_TIME | HAS_MTIME;
+		xar->file->mtime = parse_time(s, len);
+		break;
+	case FILE_ATIME:
+		xar->file->has |= HAS_TIME | HAS_ATIME;
+		xar->file->atime = parse_time(s, len);
+		break;
+	case FILE_DATA_LENGTH:
+		xar->file->has |= HAS_DATA;
+		xar->file->length = atol10(s, len);
+		break;
+	case FILE_DATA_OFFSET:
+		xar->file->has |= HAS_DATA;
+		xar->file->offset = atol10(s, len);
+		break;
+	case FILE_DATA_SIZE:
+		xar->file->has |= HAS_DATA;
+		xar->file->size = atol10(s, len);
+		break;
+	case FILE_DATA_A_CHECKSUM:
+		xar->file->a_sum.len = atohex(xar->file->a_sum.val,
+		    sizeof(xar->file->a_sum.val), s, len);
+		break;
+	case FILE_DATA_E_CHECKSUM:
+		xar->file->e_sum.len = atohex(xar->file->e_sum.val,
+		    sizeof(xar->file->e_sum.val), s, len);
+		break;
+	case FILE_EA_LENGTH:
+		xar->file->has |= HAS_XATTR;
+		xar->xattr->length = atol10(s, len);
+		break;
+	case FILE_EA_OFFSET:
+		xar->file->has |= HAS_XATTR;
+		xar->xattr->offset = atol10(s, len);
+		break;
+	case FILE_EA_SIZE:
+		xar->file->has |= HAS_XATTR;
+		xar->xattr->size = atol10(s, len);
+		break;
+	case FILE_EA_A_CHECKSUM:
+		xar->file->has |= HAS_XATTR;
+		xar->xattr->a_sum.len = atohex(xar->xattr->a_sum.val,
+		    sizeof(xar->xattr->a_sum.val), s, len);
+		break;
+	case FILE_EA_E_CHECKSUM:
+		xar->file->has |= HAS_XATTR;
+		xar->xattr->e_sum.len = atohex(xar->xattr->e_sum.val,
+		    sizeof(xar->xattr->e_sum.val), s, len);
+		break;
+	case FILE_EA_NAME:
+		xar->file->has |= HAS_XATTR;
+		archive_strncpy(&(xar->xattr->name), s, len);
+		break;
+	case FILE_EA_FSTYPE:
+		xar->file->has |= HAS_XATTR;
+		archive_strncpy(&(xar->xattr->fstype), s, len);
+		break;
+		break;
+	case FILE_ACL_DEFAULT:
+	case FILE_ACL_ACCESS:
+	case FILE_ACL_APPLEEXTENDED:
+		xar->file->has |= HAS_ACL;
+		/* TODO */
+		break;
+	case INIT:
+	case XAR:
+	case TOC:
+	case TOC_CREATION_TIME:
+	case TOC_CHECKSUM:
+	case TOC_CHECKSUM_OFFSET:
+	case TOC_CHECKSUM_SIZE:
+	case TOC_FILE:
+	case FILE_DATA:
+	case FILE_DATA_ENCODING:
+	case FILE_DATA_CONTENT:
+	case FILE_DEVICE:
+	case FILE_EA:
+	case FILE_EA_ENCODING:
+	case FILE_ACL:
+	case FILE_FLAGS:
+	case FILE_FLAGS_USER_NODUMP:
+	case FILE_FLAGS_USER_IMMUTABLE:
+	case FILE_FLAGS_USER_APPEND:
+	case FILE_FLAGS_USER_OPAQUE:
+	case FILE_FLAGS_USER_NOUNLINK:
+	case FILE_FLAGS_SYS_ARCHIVED:
+	case FILE_FLAGS_SYS_IMMUTABLE:
+	case FILE_FLAGS_SYS_APPEND:
+	case FILE_FLAGS_SYS_NOUNLINK:
+	case FILE_FLAGS_SYS_SNAPSHOT:
+	case FILE_EXT2:
+	case FILE_EXT2_SecureDeletion:
+	case FILE_EXT2_Undelete:
+	case FILE_EXT2_Compress:
+	case FILE_EXT2_Synchronous:
+	case FILE_EXT2_Immutable:
+	case FILE_EXT2_AppendOnly:
+	case FILE_EXT2_NoDump:
+	case FILE_EXT2_NoAtime:
+	case FILE_EXT2_CompDirty:
+	case FILE_EXT2_CompBlock:
+	case FILE_EXT2_NoCompBlock:
+	case FILE_EXT2_CompError:
+	case FILE_EXT2_BTree:
+	case FILE_EXT2_HashIndexed:
+	case FILE_EXT2_iMagic:
+	case FILE_EXT2_Journaled:
+	case FILE_EXT2_NoTail:
+	case FILE_EXT2_DirSync:
+	case FILE_EXT2_TopDir:
+	case FILE_EXT2_Reserved:
+	case UNKNOWN:
+		break;
+	}
+}
+
+/*
+ * BSD file flags.
+ */
+static int
+xml_parse_file_flags(struct xar *xar, const char *name)
+{
+	const char *flag = NULL;
+
+	if (strcmp(name, "UserNoDump") == 0) {
+		xar->xmlsts = FILE_FLAGS_USER_NODUMP;
+		flag = "nodump";
+	}
+	else if (strcmp(name, "UserImmutable") == 0) {
+		xar->xmlsts = FILE_FLAGS_USER_IMMUTABLE;
+		flag = "uimmutable";
+	}
+	else if (strcmp(name, "UserAppend") == 0) {
+		xar->xmlsts = FILE_FLAGS_USER_APPEND;
+		flag = "uappend";
+	}
+	else if (strcmp(name, "UserOpaque") == 0) {
+		xar->xmlsts = FILE_FLAGS_USER_OPAQUE;
+		flag = "opaque";
+	}
+	else if (strcmp(name, "UserNoUnlink") == 0) {
+		xar->xmlsts = FILE_FLAGS_USER_NOUNLINK;
+		flag = "nouunlink";
+	}
+	else if (strcmp(name, "SystemArchived") == 0) {
+		xar->xmlsts = FILE_FLAGS_SYS_ARCHIVED;
+		flag = "archived";
+	}
+	else if (strcmp(name, "SystemImmutable") == 0) {
+		xar->xmlsts = FILE_FLAGS_SYS_IMMUTABLE;
+		flag = "simmutable";
+	}
+	else if (strcmp(name, "SystemAppend") == 0) {
+		xar->xmlsts = FILE_FLAGS_SYS_APPEND;
+		flag = "sappend";
+	}
+	else if (strcmp(name, "SystemNoUnlink") == 0) {
+		xar->xmlsts = FILE_FLAGS_SYS_NOUNLINK;
+		flag = "nosunlink";
+	}
+	else if (strcmp(name, "SystemSnapshot") == 0) {
+		xar->xmlsts = FILE_FLAGS_SYS_SNAPSHOT;
+		flag = "snapshot";
+	}
+
+	if (flag == NULL)
+		return (0);
+	xar->file->has |= HAS_FFLAGS;
+	if (archive_strlen(&(xar->file->fflags_text)) > 0)
+		archive_strappend_char(&(xar->file->fflags_text), ',');
+	archive_strcat(&(xar->file->fflags_text), flag);
+	return (1);
+}
+
+/*
+ * Linux file flags.
+ */
+static int
+xml_parse_file_ext2(struct xar *xar, const char *name)
+{
+	const char *flag = NULL;
+
+	if (strcmp(name, "SecureDeletion") == 0) {
+		xar->xmlsts = FILE_EXT2_SecureDeletion;
+		flag = "securedeletion";
+	}
+	else if (strcmp(name, "Undelete") == 0) {
+		xar->xmlsts = FILE_EXT2_Undelete;
+		flag = "nouunlink";
+	}
+	else if (strcmp(name, "Compress") == 0) {
+		xar->xmlsts = FILE_EXT2_Compress;
+		flag = "compress";
+	}
+	else if (strcmp(name, "Synchronous") == 0) {
+		xar->xmlsts = FILE_EXT2_Synchronous;
+		flag = "sync";
+	}
+	else if (strcmp(name, "Immutable") == 0) {
+		xar->xmlsts = FILE_EXT2_Immutable;
+		flag = "simmutable";
+	}
+	else if (strcmp(name, "AppendOnly") == 0) {
+		xar->xmlsts = FILE_EXT2_AppendOnly;
+		flag = "sappend";
+	}
+	else if (strcmp(name, "NoDump") == 0) {
+		xar->xmlsts = FILE_EXT2_NoDump;
+		flag = "nodump";
+	}
+	else if (strcmp(name, "NoAtime") == 0) {
+		xar->xmlsts = FILE_EXT2_NoAtime;
+		flag = "noatime";
+	}
+	else if (strcmp(name, "CompDirty") == 0) {
+		xar->xmlsts = FILE_EXT2_CompDirty;
+		flag = "compdirty";
+	}
+	else if (strcmp(name, "CompBlock") == 0) {
+		xar->xmlsts = FILE_EXT2_CompBlock;
+		flag = "comprblk";
+	}
+	else if (strcmp(name, "NoCompBlock") == 0) {
+		xar->xmlsts = FILE_EXT2_NoCompBlock;
+		flag = "nocomprblk";
+	}
+	else if (strcmp(name, "CompError") == 0) {
+		xar->xmlsts = FILE_EXT2_CompError;
+		flag = "comperr";
+	}
+	else if (strcmp(name, "BTree") == 0) {
+		xar->xmlsts = FILE_EXT2_BTree;
+		flag = "btree";
+	}
+	else if (strcmp(name, "HashIndexed") == 0) {
+		xar->xmlsts = FILE_EXT2_HashIndexed;
+		flag = "hashidx";
+	}
+	else if (strcmp(name, "iMagic") == 0) {
+		xar->xmlsts = FILE_EXT2_iMagic;
+		flag = "imagic";
+	}
+	else if (strcmp(name, "Journaled") == 0) {
+		xar->xmlsts = FILE_EXT2_Journaled;
+		flag = "journal";
+	}
+	else if (strcmp(name, "NoTail") == 0) {
+		xar->xmlsts = FILE_EXT2_NoTail;
+		flag = "notail";
+	}
+	else if (strcmp(name, "DirSync") == 0) {
+		xar->xmlsts = FILE_EXT2_DirSync;
+		flag = "dirsync";
+	}
+	else if (strcmp(name, "TopDir") == 0) {
+		xar->xmlsts = FILE_EXT2_TopDir;
+		flag = "topdir";
+	}
+	else if (strcmp(name, "Reserved") == 0) {
+		xar->xmlsts = FILE_EXT2_Reserved;
+		flag = "reserved";
+	}
+
+	if (flag == NULL)
+		return (0);
+	if (archive_strlen(&(xar->file->fflags_text)) > 0)
+		archive_strappend_char(&(xar->file->fflags_text), ',');
+	archive_strcat(&(xar->file->fflags_text), flag);
+	return (1);
+}
+
+#ifdef HAVE_LIBXML_XMLREADER_H
+
+static int
+xml2_xmlattr_setup(struct archive_read *a,
+    struct xmlattr_list *list, xmlTextReaderPtr reader)
+{
+	struct xmlattr *attr;
+	int r;
+
+	list->first = NULL;
+	list->last = &(list->first);
+	r = xmlTextReaderMoveToFirstAttribute(reader);
+	while (r == 1) {
+		attr = malloc(sizeof*(attr));
+		if (attr == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		attr->name = strdup(
+		    (const char *)xmlTextReaderConstLocalName(reader));
+		if (attr->name == NULL) {
+			free(attr);
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		attr->value = strdup(
+		    (const char *)xmlTextReaderConstValue(reader));
+		if (attr->value == NULL) {
+			free(attr->name);
+			free(attr);
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		attr->next = NULL;
+		*list->last = attr;
+		list->last = &(attr->next);
+		r = xmlTextReaderMoveToNextAttribute(reader);
+	}
+	return (r);
+}
+
+static int
+xml2_read_cb(void *context, char *buffer, int len)
+{
+	struct archive_read *a;
+	struct xar *xar;
+	const void *d;
+	size_t outbytes;
+	size_t used = 0;
+	int r;
+
+	a = (struct archive_read *)context;
+	xar = (struct xar *)(a->format->data);
+
+	if (xar->toc_remaining <= 0)
+		return (0);
+	d = buffer;
+	outbytes = len;
+	r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
+	if (r != ARCHIVE_OK)
+		return (r);
+	__archive_read_consume(a, used);
+	xar->toc_remaining -= used;
+	xar->offset += used;
+	xar->toc_total += outbytes;
+	PRINT_TOC(buffer, len);
+
+	return ((int)outbytes);
+}
+
+static int
+xml2_close_cb(void *context)
+{
+
+	(void)context; /* UNUSED */
+	return (0);
+}
+
+static void
+xml2_error_hdr(void *arg, const char *msg, xmlParserSeverities severity,
+    xmlTextReaderLocatorPtr locator)
+{
+	struct archive_read *a;
+
+	(void)locator; /* UNUSED */
+	a = (struct archive_read *)arg;
+	switch (severity) {
+	case XML_PARSER_SEVERITY_VALIDITY_WARNING:
+	case XML_PARSER_SEVERITY_WARNING:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "XML Parsing error: %s", msg);
+		break;
+	case XML_PARSER_SEVERITY_VALIDITY_ERROR:
+	case XML_PARSER_SEVERITY_ERROR:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "XML Parsing error: %s", msg);
+		break;
+	}
+}
+
+static int
+xml2_read_toc(struct archive_read *a)
+{
+	xmlTextReaderPtr reader;
+	struct xmlattr_list list;
+	int r;
+
+	reader = xmlReaderForIO(xml2_read_cb, xml2_close_cb, a, NULL, NULL, 0);
+	if (reader == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Couldn't allocate memory for xml parser");
+		return (ARCHIVE_FATAL);
+	}
+	xmlTextReaderSetErrorHandler(reader, xml2_error_hdr, a);
+
+	while ((r = xmlTextReaderRead(reader)) == 1) {
+		const char *name, *value;
+		int type, empty;
+
+		type = xmlTextReaderNodeType(reader);
+		name = (const char *)xmlTextReaderConstLocalName(reader);
+		switch (type) {
+		case XML_READER_TYPE_ELEMENT:
+			empty = xmlTextReaderIsEmptyElement(reader);
+			r = xml2_xmlattr_setup(a, &list, reader);
+			if (r == ARCHIVE_OK)
+				r = xml_start(a, name, &list);
+			xmlattr_cleanup(&list);
+			if (r != ARCHIVE_OK)
+				return (r);
+			if (empty)
+				xml_end(a, name);
+			break;
+		case XML_READER_TYPE_END_ELEMENT:
+			xml_end(a, name);
+			break;
+		case XML_READER_TYPE_TEXT:
+			value = (const char *)xmlTextReaderConstValue(reader);
+			xml_data(a, value, strlen(value));
+			break;
+		case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
+		default:
+			break;
+		}
+		if (r < 0)
+			break;
+	}
+	xmlFreeTextReader(reader);
+	xmlCleanupParser();
+
+	return ((r == 0)?ARCHIVE_OK:ARCHIVE_FATAL);
+}
+
+#elif defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H)
+
+static int
+expat_xmlattr_setup(struct archive_read *a,
+    struct xmlattr_list *list, const XML_Char **atts)
+{
+	struct xmlattr *attr;
+	char *name, *value;
+
+	list->first = NULL;
+	list->last = &(list->first);
+	if (atts == NULL)
+		return (ARCHIVE_OK);
+	while (atts[0] != NULL && atts[1] != NULL) {
+		attr = malloc(sizeof*(attr));
+		name = strdup(atts[0]);
+		value = strdup(atts[1]);
+		if (attr == NULL || name == NULL || value == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			free(attr);
+			free(name);
+			free(value);
+			return (ARCHIVE_FATAL);
+		}
+		attr->name = name;
+		attr->value = value;
+		attr->next = NULL;
+		*list->last = attr;
+		list->last = &(attr->next);
+		atts += 2;
+	}
+	return (ARCHIVE_OK);
+}
+
+static void
+expat_start_cb(void *userData, const XML_Char *name, const XML_Char **atts)
+{
+	struct expat_userData *ud = (struct expat_userData *)userData;
+	struct archive_read *a = ud->archive;
+	struct xmlattr_list list;
+	int r;
+
+	r = expat_xmlattr_setup(a, &list, atts);
+	if (r == ARCHIVE_OK)
+		r = xml_start(a, (const char *)name, &list);
+	xmlattr_cleanup(&list);
+	ud->state = r;
+}
+
+static void
+expat_end_cb(void *userData, const XML_Char *name)
+{
+	struct expat_userData *ud = (struct expat_userData *)userData;
+
+	xml_end(ud->archive, (const char *)name);
+}
+
+static void
+expat_data_cb(void *userData, const XML_Char *s, int len)
+{
+	struct expat_userData *ud = (struct expat_userData *)userData;
+
+	xml_data(ud->archive, s, len);
+}
+
+static int
+expat_read_toc(struct archive_read *a)
+{
+	struct xar *xar;
+	XML_Parser parser;
+	struct expat_userData ud;
+
+	ud.state = ARCHIVE_OK;
+	ud.archive = a;
+
+	xar = (struct xar *)(a->format->data);
+
+	/* Initialize XML Parser library. */
+	parser = XML_ParserCreate(NULL);
+	if (parser == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Couldn't allocate memory for xml parser");
+		return (ARCHIVE_FATAL);
+	}
+	XML_SetUserData(parser, &ud);
+	XML_SetElementHandler(parser, expat_start_cb, expat_end_cb);
+	XML_SetCharacterDataHandler(parser, expat_data_cb);
+	xar->xmlsts = INIT;
+
+	while (xar->toc_remaining && ud.state == ARCHIVE_OK) {
+		enum XML_Status xr;
+		const void *d;
+		size_t outbytes;
+		size_t used;
+		int r;
+
+		d = NULL;
+		r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
+		if (r != ARCHIVE_OK)
+			return (r);
+		xar->toc_remaining -= used;
+		xar->offset += used;
+		xar->toc_total += outbytes;
+		PRINT_TOC(d, outbytes);
+
+		xr = XML_Parse(parser, d, outbytes, xar->toc_remaining == 0);
+		__archive_read_consume(a, used);
+		if (xr == XML_STATUS_ERROR) {
+			XML_ParserFree(parser);
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "XML Parsing failed");
+			return (ARCHIVE_FATAL);
+		}
+	}
+	XML_ParserFree(parser);
+	return (ud.state);
+}
+#endif /* defined(HAVE_BSDXML_H) || defined(HAVE_EXPAT_H) */
+
+#endif /* Support xar format */
diff --git a/c/archive_read_support_format_zip.c b/c/archive_read_support_format_zip.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_format_zip.c
@@ -0,0 +1,4044 @@
+/*-
+ * Copyright (c) 2004-2013 Tim Kientzle
+ * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA
+ * Copyright (c) 2013 Konrad Kleine
+ * 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: head/lib/libarchive/archive_read_support_format_zip.c 201102 2009-12-28 03:11:36Z kientzle $");
+
+/*
+ * The definitive documentation of the Zip file format is:
+ *   http://www.pkware.com/documents/casestudies/APPNOTE.TXT
+ *
+ * The Info-Zip project has pioneered various extensions to better
+ * support Zip on Unix, including the 0x5455 "UT", 0x5855 "UX", 0x7855
+ * "Ux", and 0x7875 "ux" extensions for time and ownership
+ * information.
+ *
+ * History of this code: The streaming Zip reader was first added to
+ * libarchive in January 2005.  Support for seekable input sources was
+ * added in Nov 2011.  Zip64 support (including a significant code
+ * refactoring) was added in 2014.
+ */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#include <lzma.h>
+#endif
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_cryptor_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_hmac_private.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_read_private.h"
+#include "archive_ppmd8_private.h"
+
+#ifndef HAVE_ZLIB_H
+#include "archive_crc32.h"
+#endif
+
+struct zip_entry {
+	struct archive_rb_node	node;
+	struct zip_entry	*next;
+	int64_t			local_header_offset;
+	int64_t			compressed_size;
+	int64_t			uncompressed_size;
+	int64_t			gid;
+	int64_t			uid;
+	struct archive_string	rsrcname;
+	time_t			mtime;
+	time_t			atime;
+	time_t			ctime;
+	uint32_t		crc32;
+	uint16_t		mode;
+	uint16_t		zip_flags; /* From GP Flags Field */
+	unsigned char		compression;
+	unsigned char		system; /* From "version written by" */
+	unsigned char		flags; /* Our extra markers. */
+	unsigned char		decdat;/* Used for Decryption check */
+
+	/* WinZip AES encryption extra field should be available
+	 * when compression is 99. */
+	struct {
+		/* Vendor version: AE-1 - 0x0001, AE-2 - 0x0002 */
+		unsigned	vendor;
+#define AES_VENDOR_AE_1	0x0001
+#define AES_VENDOR_AE_2	0x0002
+		/* AES encryption strength:
+		 * 1 - 128 bits, 2 - 192 bits, 2 - 256 bits. */
+		unsigned	strength;
+		/* Actual compression method. */
+		unsigned char	compression;
+	}			aes_extra;
+};
+
+struct trad_enc_ctx {
+	uint32_t	keys[3];
+};
+
+/* Bits used in zip_flags. */
+#define ZIP_ENCRYPTED	(1 << 0)
+#define ZIP_LENGTH_AT_END	(1 << 3)
+#define ZIP_STRONG_ENCRYPTED	(1 << 6)
+#define ZIP_UTF8_NAME	(1 << 11)
+/* See "7.2 Single Password Symmetric Encryption Method"
+   in http://www.pkware.com/documents/casestudies/APPNOTE.TXT */
+#define ZIP_CENTRAL_DIRECTORY_ENCRYPTED	(1 << 13)
+
+/* Bits used in flags. */
+#define LA_USED_ZIP64	(1 << 0)
+#define LA_FROM_CENTRAL_DIRECTORY (1 << 1)
+
+/*
+ * See "WinZip - AES Encryption Information"
+ *     http://www.winzip.com/aes_info.htm
+ */
+/* Value used in compression method. */
+#define WINZIP_AES_ENCRYPTION	99
+/* Authentication code size. */
+#define AUTH_CODE_SIZE	10
+/**/
+#define MAX_DERIVED_KEY_BUF_SIZE	(AES_MAX_KEY_SIZE * 2 + 2)
+
+struct zip {
+	/* Structural information about the archive. */
+	struct archive_string	format_name;
+	int64_t			central_directory_offset;
+	size_t			central_directory_entries_total;
+	size_t			central_directory_entries_on_this_disk;
+	int			has_encrypted_entries;
+
+	/* List of entries (seekable Zip only) */
+	struct zip_entry	*zip_entries;
+	struct archive_rb_tree	tree;
+	struct archive_rb_tree	tree_rsrc;
+
+	/* Bytes read but not yet consumed via __archive_read_consume() */
+	size_t			unconsumed;
+
+	/* Information about entry we're currently reading. */
+	struct zip_entry	*entry;
+	int64_t			entry_bytes_remaining;
+
+	/* These count the number of bytes actually read for the entry. */
+	int64_t			entry_compressed_bytes_read;
+	int64_t			entry_uncompressed_bytes_read;
+
+	/* Running CRC32 of the decompressed data */
+	unsigned long		entry_crc32;
+	unsigned long		(*crc32func)(unsigned long, const void *,
+				    size_t);
+	char			ignore_crc32;
+
+	/* Flags to mark progress of decompression. */
+	char			decompress_init;
+	char			end_of_entry;
+
+	unsigned char 		*uncompressed_buffer;
+	size_t 			uncompressed_buffer_size;
+
+#ifdef HAVE_ZLIB_H
+	z_stream		stream;
+	char			stream_valid;
+#endif
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+	lzma_stream		zipx_lzma_stream;
+	char            zipx_lzma_valid;
+#endif
+
+#ifdef HAVE_BZLIB_H
+	bz_stream		bzstream;
+	char            bzstream_valid;
+#endif
+
+	IByteIn			zipx_ppmd_stream;
+	ssize_t			zipx_ppmd_read_compressed;
+	CPpmd8			ppmd8;
+	char			ppmd8_valid;
+	char			ppmd8_stream_failed;
+
+	struct archive_string_conv *sconv;
+	struct archive_string_conv *sconv_default;
+	struct archive_string_conv *sconv_utf8;
+	int			init_default_conversion;
+	int			process_mac_extensions;
+
+	char			init_decryption;
+
+	/* Decryption buffer. */
+	/*
+	 * The decrypted data starts at decrypted_ptr and
+	 * extends for decrypted_bytes_remaining.  Decryption
+	 * adds new data to the end of this block, data is returned
+	 * to clients from the beginning.  When the block hits the
+	 * end of decrypted_buffer, it has to be shuffled back to
+	 * the beginning of the buffer.
+	 */
+	unsigned char 		*decrypted_buffer;
+	unsigned char 		*decrypted_ptr;
+	size_t 			decrypted_buffer_size;
+	size_t 			decrypted_bytes_remaining;
+	size_t 			decrypted_unconsumed_bytes;
+
+	/* Traditional PKWARE decryption. */
+	struct trad_enc_ctx	tctx;
+	char			tctx_valid;
+
+	/* WinZip AES decryption. */
+	/* Contexts used for AES decryption. */
+	archive_crypto_ctx	cctx;
+	char			cctx_valid;
+	archive_hmac_sha1_ctx	hctx;
+	char			hctx_valid;
+
+	/* Strong encryption's decryption header information. */
+	unsigned		iv_size;
+	unsigned		alg_id;
+	unsigned		bit_len;
+	unsigned		flags;
+	unsigned		erd_size;
+	unsigned		v_size;
+	unsigned		v_crc32;
+	uint8_t			*iv;
+	uint8_t			*erd;
+	uint8_t			*v_data;
+};
+
+/* Many systems define min or MIN, but not all. */
+#define	zipmin(a,b) ((a) < (b) ? (a) : (b))
+
+/* This function is used by Ppmd8_DecodeSymbol during decompression of Ppmd8
+ * streams inside ZIP files. It has 2 purposes: one is to fetch the next
+ * compressed byte from the stream, second one is to increase the counter how
+ * many compressed bytes were read. */
+static Byte
+ppmd_read(void* p) {
+	/* Get the handle to current decompression context. */
+	struct archive_read *a = ((IByteIn*)p)->a;
+	struct zip *zip = (struct zip*) a->format->data;
+	ssize_t bytes_avail = 0;
+
+	/* Fetch next byte. */
+	const uint8_t* data = __archive_read_ahead(a, 1, &bytes_avail);
+	if(bytes_avail < 1) {
+		zip->ppmd8_stream_failed = 1;
+		return 0;
+	}
+
+	__archive_read_consume(a, 1);
+
+	/* Increment the counter. */
+	++zip->zipx_ppmd_read_compressed;
+
+	/* Return the next compressed byte. */
+	return data[0];
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+  Traditional PKWARE Decryption functions.
+ */
+
+static void
+trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c)
+{
+	uint8_t t;
+#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL)
+
+	ctx->keys[0] = CRC32(ctx->keys[0], c);
+	ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1;
+	t = (ctx->keys[1] >> 24) & 0xff;
+	ctx->keys[2] = CRC32(ctx->keys[2], t);
+#undef CRC32
+}
+
+static uint8_t
+trad_enc_decrypt_byte(struct trad_enc_ctx *ctx)
+{
+	unsigned temp = ctx->keys[2] | 2;
+	return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;
+}
+
+static void
+trad_enc_decrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in,
+    size_t in_len, uint8_t *out, size_t out_len)
+{
+	unsigned i, max;
+
+	max = (unsigned)((in_len < out_len)? in_len: out_len);
+
+	for (i = 0; i < max; i++) {
+		uint8_t t = in[i] ^ trad_enc_decrypt_byte(ctx);
+		out[i] = t;
+		trad_enc_update_keys(ctx, t);
+	}
+}
+
+static int
+trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len,
+    const uint8_t *key, size_t key_len, uint8_t *crcchk)
+{
+	uint8_t header[12];
+
+	if (key_len < 12) {
+		*crcchk = 0xff;
+		return -1;
+	}
+
+	ctx->keys[0] = 305419896L;
+	ctx->keys[1] = 591751049L;
+	ctx->keys[2] = 878082192L;
+
+	for (;pw_len; --pw_len)
+		trad_enc_update_keys(ctx, *pw++);
+
+	trad_enc_decrypt_update(ctx, key, 12, header, 12);
+	/* Return the last byte for CRC check. */
+	*crcchk = header[11];
+	return 0;
+}
+
+#if 0
+static void
+crypt_derive_key_sha1(const void *p, int size, unsigned char *key,
+    int key_size)
+{
+#define MD_SIZE 20
+	archive_sha1_ctx ctx;
+	unsigned char md1[MD_SIZE];
+	unsigned char md2[MD_SIZE * 2];
+	unsigned char mkb[64];
+	int i;
+
+	archive_sha1_init(&ctx);
+	archive_sha1_update(&ctx, p, size);
+	archive_sha1_final(&ctx, md1);
+
+	memset(mkb, 0x36, sizeof(mkb));
+	for (i = 0; i < MD_SIZE; i++)
+		mkb[i] ^= md1[i];
+	archive_sha1_init(&ctx);
+	archive_sha1_update(&ctx, mkb, sizeof(mkb));
+	archive_sha1_final(&ctx, md2);
+
+	memset(mkb, 0x5C, sizeof(mkb));
+	for (i = 0; i < MD_SIZE; i++)
+		mkb[i] ^= md1[i];
+	archive_sha1_init(&ctx);
+	archive_sha1_update(&ctx, mkb, sizeof(mkb));
+	archive_sha1_final(&ctx, md2 + MD_SIZE);
+
+	if (key_size > 32)
+		key_size = 32;
+	memcpy(key, md2, key_size);
+#undef MD_SIZE
+}
+#endif
+
+/*
+ * Common code for streaming or seeking modes.
+ *
+ * Includes code to read local file headers, decompress data
+ * from entry bodies, and common API.
+ */
+
+static unsigned long
+real_crc32(unsigned long crc, const void *buff, size_t len)
+{
+	return crc32(crc, buff, (unsigned int)len);
+}
+
+/* Used by "ignorecrc32" option to speed up tests. */
+static unsigned long
+fake_crc32(unsigned long crc, const void *buff, size_t len)
+{
+	(void)crc; /* UNUSED */
+	(void)buff; /* UNUSED */
+	(void)len; /* UNUSED */
+	return 0;
+}
+
+static const struct {
+	int id;
+	const char * name;
+} compression_methods[] = {
+	{0, "uncompressed"}, /* The file is stored (no compression) */
+	{1, "shrinking"}, /* The file is Shrunk */
+	{2, "reduced-1"}, /* The file is Reduced with compression factor 1 */
+	{3, "reduced-2"}, /* The file is Reduced with compression factor 2 */
+	{4, "reduced-3"}, /* The file is Reduced with compression factor 3 */
+	{5, "reduced-4"}, /* The file is Reduced with compression factor 4 */
+	{6, "imploded"},  /* The file is Imploded */
+	{7, "reserved"},  /* Reserved for Tokenizing compression algorithm */
+	{8, "deflation"}, /* The file is Deflated */
+	{9, "deflation-64-bit"}, /* Enhanced Deflating using Deflate64(tm) */
+	{10, "ibm-terse"},/* PKWARE Data Compression Library Imploding
+			   * (old IBM TERSE) */
+	{11, "reserved"}, /* Reserved by PKWARE */
+	{12, "bzip"},     /* File is compressed using BZIP2 algorithm */
+	{13, "reserved"}, /* Reserved by PKWARE */
+	{14, "lzma"},     /* LZMA (EFS) */
+	{15, "reserved"}, /* Reserved by PKWARE */
+	{16, "reserved"}, /* Reserved by PKWARE */
+	{17, "reserved"}, /* Reserved by PKWARE */
+	{18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */
+	{19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */
+	{95, "xz"},       /* XZ compressed data */
+	{96, "jpeg"},     /* JPEG compressed data */
+	{97, "wav-pack"}, /* WavPack compressed data */
+	{98, "ppmd-1"},   /* PPMd version I, Rev 1 */
+	{99, "aes"}       /* WinZip AES encryption  */
+};
+
+static const char *
+compression_name(const int compression)
+{
+	static const int num_compression_methods =
+		sizeof(compression_methods)/sizeof(compression_methods[0]);
+	int i=0;
+
+	while(compression >= 0 && i < num_compression_methods) {
+		if (compression_methods[i].id == compression)
+			return compression_methods[i].name;
+		i++;
+	}
+	return "??";
+}
+
+/* Convert an MSDOS-style date/time into Unix-style time. */
+static time_t
+zip_time(const char *p)
+{
+	int msTime, msDate;
+	struct tm ts;
+
+	msTime = (0xff & (unsigned)p[0]) + 256 * (0xff & (unsigned)p[1]);
+	msDate = (0xff & (unsigned)p[2]) + 256 * (0xff & (unsigned)p[3]);
+
+	memset(&ts, 0, sizeof(ts));
+	ts.tm_year = ((msDate >> 9) & 0x7f) + 80; /* Years since 1900. */
+	ts.tm_mon = ((msDate >> 5) & 0x0f) - 1; /* Month number. */
+	ts.tm_mday = msDate & 0x1f; /* Day of month. */
+	ts.tm_hour = (msTime >> 11) & 0x1f;
+	ts.tm_min = (msTime >> 5) & 0x3f;
+	ts.tm_sec = (msTime << 1) & 0x3e;
+	ts.tm_isdst = -1;
+	return mktime(&ts);
+}
+
+/*
+ * The extra data is stored as a list of
+ *	id1+size1+data1 + id2+size2+data2 ...
+ *  triplets.  id and size are 2 bytes each.
+ */
+static int
+process_extra(struct archive_read *a, struct archive_entry *entry,
+     const char *p, size_t extra_length, struct zip_entry* zip_entry)
+{
+	unsigned offset = 0;
+	struct zip *zip = (struct zip *)(a->format->data);
+
+	if (extra_length == 0) {
+		return ARCHIVE_OK;
+	}
+
+	if (extra_length < 4) {
+		size_t i = 0;
+		/* Some ZIP files may have trailing 0 bytes. Let's check they
+		 * are all 0 and ignore them instead of returning an error.
+		 *
+		 * This is not technically correct, but some ZIP files look
+		 * like this and other tools support those files - so let's
+		 * also  support them.
+		 */
+		for (; i < extra_length; i++) {
+			if (p[i] != 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Too-small extra data: "
+				    "Need at least 4 bytes, "
+				    "but only found %d bytes",
+				    (int)extra_length);
+				return ARCHIVE_FAILED;
+			}
+		}
+
+		return ARCHIVE_OK;
+	}
+
+	while (offset <= extra_length - 4) {
+		unsigned short headerid = archive_le16dec(p + offset);
+		unsigned short datasize = archive_le16dec(p + offset + 2);
+
+		offset += 4;
+		if (offset + datasize > extra_length) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT, "Extra data overflow: "
+			    "Need %d bytes but only found %d bytes",
+			    (int)datasize, (int)(extra_length - offset));
+			return ARCHIVE_FAILED;
+		}
+#ifdef DEBUG
+		fprintf(stderr, "Header id 0x%04x, length %d\n",
+		    headerid, datasize);
+#endif
+		switch (headerid) {
+		case 0x0001:
+			/* Zip64 extended information extra field. */
+			zip_entry->flags |= LA_USED_ZIP64;
+			if (zip_entry->uncompressed_size == 0xffffffff) {
+				uint64_t t = 0;
+				if (datasize < 8
+				    || (t = archive_le64dec(p + offset)) >
+				    INT64_MAX) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_FILE_FORMAT,
+					    "Malformed 64-bit "
+					    "uncompressed size");
+					return ARCHIVE_FAILED;
+				}
+				zip_entry->uncompressed_size = t;
+				offset += 8;
+				datasize -= 8;
+			}
+			if (zip_entry->compressed_size == 0xffffffff) {
+				uint64_t t = 0;
+				if (datasize < 8
+				    || (t = archive_le64dec(p + offset)) >
+				    INT64_MAX) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_FILE_FORMAT,
+					    "Malformed 64-bit "
+					    "compressed size");
+					return ARCHIVE_FAILED;
+				}
+				zip_entry->compressed_size = t;
+				offset += 8;
+				datasize -= 8;
+			}
+			if (zip_entry->local_header_offset == 0xffffffff) {
+				uint64_t t = 0;
+				if (datasize < 8
+				    || (t = archive_le64dec(p + offset)) >
+				    INT64_MAX) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_FILE_FORMAT,
+					    "Malformed 64-bit "
+					    "local header offset");
+					return ARCHIVE_FAILED;
+				}
+				zip_entry->local_header_offset = t;
+				offset += 8;
+				datasize -= 8;
+			}
+			/* archive_le32dec(p + offset) gives disk
+			 * on which file starts, but we don't handle
+			 * multi-volume Zip files. */
+			break;
+#ifdef DEBUG
+		case 0x0017:
+		{
+			/* Strong encryption field. */
+			if (archive_le16dec(p + offset) == 2) {
+				unsigned algId =
+					archive_le16dec(p + offset + 2);
+				unsigned bitLen =
+					archive_le16dec(p + offset + 4);
+				int	 flags =
+					archive_le16dec(p + offset + 6);
+				fprintf(stderr, "algId=0x%04x, bitLen=%u, "
+				    "flgas=%d\n", algId, bitLen,flags);
+			}
+			break;
+		}
+#endif
+		case 0x5455:
+		{
+			/* Extended time field "UT". */
+			int flags;
+			if (datasize == 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Incomplete extended time field");
+				return ARCHIVE_FAILED;
+			}
+			flags = p[offset];
+			offset++;
+			datasize--;
+			/* Flag bits indicate which dates are present. */
+			if (flags & 0x01)
+			{
+#ifdef DEBUG
+				fprintf(stderr, "mtime: %lld -> %d\n",
+				    (long long)zip_entry->mtime,
+				    archive_le32dec(p + offset));
+#endif
+				if (datasize < 4)
+					break;
+				zip_entry->mtime = archive_le32dec(p + offset);
+				offset += 4;
+				datasize -= 4;
+			}
+			if (flags & 0x02)
+			{
+				if (datasize < 4)
+					break;
+				zip_entry->atime = archive_le32dec(p + offset);
+				offset += 4;
+				datasize -= 4;
+			}
+			if (flags & 0x04)
+			{
+				if (datasize < 4)
+					break;
+				zip_entry->ctime = archive_le32dec(p + offset);
+				offset += 4;
+				datasize -= 4;
+			}
+			break;
+		}
+		case 0x5855:
+		{
+			/* Info-ZIP Unix Extra Field (old version) "UX". */
+			if (datasize >= 8) {
+				zip_entry->atime = archive_le32dec(p + offset);
+				zip_entry->mtime =
+				    archive_le32dec(p + offset + 4);
+			}
+			if (datasize >= 12) {
+				zip_entry->uid =
+				    archive_le16dec(p + offset + 8);
+				zip_entry->gid =
+				    archive_le16dec(p + offset + 10);
+			}
+			break;
+		}
+		case 0x6c78:
+		{
+			/* Experimental 'xl' field */
+			/*
+			 * Introduced Dec 2013 to provide a way to
+			 * include external file attributes (and other
+			 * fields that ordinarily appear only in
+			 * central directory) in local file header.
+			 * This provides file type and permission
+			 * information necessary to support full
+			 * streaming extraction.  Currently being
+			 * discussed with other Zip developers
+			 * ... subject to change.
+			 *
+			 * Format:
+			 *  The field starts with a bitmap that specifies
+			 *  which additional fields are included.  The
+			 *  bitmap is variable length and can be extended in
+			 *  the future.
+			 *
+			 *  n bytes - feature bitmap: first byte has low-order
+			 *    7 bits.  If high-order bit is set, a subsequent
+			 *    byte holds the next 7 bits, etc.
+			 *
+			 *  if bitmap & 1, 2 byte "version made by"
+			 *  if bitmap & 2, 2 byte "internal file attributes"
+			 *  if bitmap & 4, 4 byte "external file attributes"
+			 *  if bitmap & 8, 2 byte comment length + n byte
+			 *  comment
+			 */
+			int bitmap, bitmap_last;
+
+			if (datasize < 1)
+				break;
+			bitmap_last = bitmap = 0xff & p[offset];
+			offset += 1;
+			datasize -= 1;
+
+			/* We only support first 7 bits of bitmap; skip rest. */
+			while ((bitmap_last & 0x80) != 0
+			    && datasize >= 1) {
+				bitmap_last = p[offset];
+				offset += 1;
+				datasize -= 1;
+			}
+
+			if (bitmap & 1) {
+				/* 2 byte "version made by" */
+				if (datasize < 2)
+					break;
+				zip_entry->system
+				    = archive_le16dec(p + offset) >> 8;
+				offset += 2;
+				datasize -= 2;
+			}
+			if (bitmap & 2) {
+				/* 2 byte "internal file attributes" */
+				uint32_t internal_attributes;
+				if (datasize < 2)
+					break;
+				internal_attributes
+				    = archive_le16dec(p + offset);
+				/* Not used by libarchive at present. */
+				(void)internal_attributes; /* UNUSED */
+				offset += 2;
+				datasize -= 2;
+			}
+			if (bitmap & 4) {
+				/* 4 byte "external file attributes" */
+				uint32_t external_attributes;
+				if (datasize < 4)
+					break;
+				external_attributes
+				    = archive_le32dec(p + offset);
+				if (zip_entry->system == 3) {
+					zip_entry->mode
+					    = external_attributes >> 16;
+				} else if (zip_entry->system == 0) {
+					// Interpret MSDOS directory bit
+					if (0x10 == (external_attributes &
+					    0x10)) {
+						zip_entry->mode =
+						    AE_IFDIR | 0775;
+					} else {
+						zip_entry->mode =
+						    AE_IFREG | 0664;
+					}
+					if (0x01 == (external_attributes &
+					    0x01)) {
+						/* Read-only bit;
+						 * strip write permissions */
+						zip_entry->mode &= 0555;
+					}
+				} else {
+					zip_entry->mode = 0;
+				}
+				offset += 4;
+				datasize -= 4;
+			}
+			if (bitmap & 8) {
+				/* 2 byte comment length + comment */
+				uint32_t comment_length;
+				if (datasize < 2)
+					break;
+				comment_length
+				    = archive_le16dec(p + offset);
+				offset += 2;
+				datasize -= 2;
+
+				if (datasize < comment_length)
+					break;
+				/* Comment is not supported by libarchive */
+				offset += comment_length;
+				datasize -= comment_length;
+			}
+			break;
+		}
+		case 0x7075:
+		{
+			/* Info-ZIP Unicode Path Extra Field. */
+			if (datasize < 5 || entry == NULL)
+				break;
+			offset += 5;
+			datasize -= 5;
+
+			/* The path name in this field is always encoded
+			 * in UTF-8. */
+			if (zip->sconv_utf8 == NULL) {
+				zip->sconv_utf8 =
+					archive_string_conversion_from_charset(
+					&a->archive, "UTF-8", 1);
+				/* If the converter from UTF-8 is not
+				 * available, then the path name from the main
+				 * field will more likely be correct. */
+				if (zip->sconv_utf8 == NULL)
+					break;
+			}
+
+			/* Make sure the CRC32 of the filename matches. */
+			if (!zip->ignore_crc32) {
+				const char *cp = archive_entry_pathname(entry);
+				if (cp) {
+					unsigned long file_crc =
+					    zip->crc32func(0, cp, strlen(cp));
+					unsigned long utf_crc =
+					    archive_le32dec(p + offset - 4);
+					if (file_crc != utf_crc) {
+#ifdef DEBUG
+						fprintf(stderr,
+						    "CRC filename mismatch; "
+						    "CDE is %lx, but UTF8 "
+						    "is outdated with %lx\n",
+						    file_crc, utf_crc);
+#endif
+						break;
+					}
+				}
+			}
+
+			if (archive_entry_copy_pathname_l(entry,
+			    p + offset, datasize, zip->sconv_utf8) != 0) {
+				/* Ignore the error, and fallback to the path
+				 * name from the main field. */
+#ifdef DEBUG
+				fprintf(stderr, "Failed to read the ZIP "
+				    "0x7075 extra field path.\n");
+#endif
+			}
+			break;
+		}
+		case 0x7855:
+			/* Info-ZIP Unix Extra Field (type 2) "Ux". */
+#ifdef DEBUG
+			fprintf(stderr, "uid %d gid %d\n",
+			    archive_le16dec(p + offset),
+			    archive_le16dec(p + offset + 2));
+#endif
+			if (datasize >= 2)
+				zip_entry->uid = archive_le16dec(p + offset);
+			if (datasize >= 4)
+				zip_entry->gid =
+				    archive_le16dec(p + offset + 2);
+			break;
+		case 0x7875:
+		{
+			/* Info-Zip Unix Extra Field (type 3) "ux". */
+			int uidsize = 0, gidsize = 0;
+
+			/* TODO: support arbitrary uidsize/gidsize. */
+			if (datasize >= 1 && p[offset] == 1) {/* version=1 */
+				if (datasize >= 4) {
+					/* get a uid size. */
+					uidsize = 0xff & (int)p[offset+1];
+					if (uidsize == 2)
+						zip_entry->uid =
+						    archive_le16dec(
+						        p + offset + 2);
+					else if (uidsize == 4 && datasize >= 6)
+						zip_entry->uid =
+						    archive_le32dec(
+						        p + offset + 2);
+				}
+				if (datasize >= (2 + uidsize + 3)) {
+					/* get a gid size. */
+					gidsize = 0xff &
+					    (int)p[offset+2+uidsize];
+					if (gidsize == 2)
+						zip_entry->gid =
+						    archive_le16dec(
+						        p+offset+2+uidsize+1);
+					else if (gidsize == 4 &&
+					    datasize >= (2 + uidsize + 5))
+						zip_entry->gid =
+						    archive_le32dec(
+						        p+offset+2+uidsize+1);
+				}
+			}
+			break;
+		}
+		case 0x9901:
+			/* WinZip AES extra data field. */
+			if (datasize < 6) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Incomplete AES field");
+				return ARCHIVE_FAILED;
+			}
+			if (p[offset + 2] == 'A' && p[offset + 3] == 'E') {
+				/* Vendor version. */
+				zip_entry->aes_extra.vendor =
+				    archive_le16dec(p + offset);
+				/* AES encryption strength. */
+				zip_entry->aes_extra.strength = p[offset + 4];
+				/* Actual compression method. */
+				zip_entry->aes_extra.compression =
+				    p[offset + 5];
+			}
+			break;
+		default:
+			break;
+		}
+		offset += datasize;
+	}
+	return ARCHIVE_OK;
+}
+
+/*
+ * Assumes file pointer is at beginning of local file header.
+ */
+static int
+zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
+    struct zip *zip)
+{
+	const char *p;
+	const void *h;
+	const wchar_t *wp;
+	const char *cp;
+	size_t len, filename_length, extra_length;
+	struct archive_string_conv *sconv;
+	struct zip_entry *zip_entry = zip->entry;
+	struct zip_entry zip_entry_central_dir;
+	int ret = ARCHIVE_OK;
+	char version;
+
+	/* Save a copy of the original for consistency checks. */
+	zip_entry_central_dir = *zip_entry;
+
+	zip->decompress_init = 0;
+	zip->end_of_entry = 0;
+	zip->entry_uncompressed_bytes_read = 0;
+	zip->entry_compressed_bytes_read = 0;
+	zip->entry_crc32 = zip->crc32func(0, NULL, 0);
+
+	/* Setup default conversion. */
+	if (zip->sconv == NULL && !zip->init_default_conversion) {
+		zip->sconv_default =
+		    archive_string_default_conversion_for_read(&(a->archive));
+		zip->init_default_conversion = 1;
+	}
+
+	if ((p = __archive_read_ahead(a, 30, NULL)) == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated ZIP file header");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (memcmp(p, "PK\003\004", 4) != 0) {
+		archive_set_error(&a->archive, -1, "Damaged Zip archive");
+		return ARCHIVE_FATAL;
+	}
+	version = p[4];
+	zip_entry->system = p[5];
+	zip_entry->zip_flags = archive_le16dec(p + 6);
+	if (zip_entry->zip_flags & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)) {
+		zip->has_encrypted_entries = 1;
+		archive_entry_set_is_data_encrypted(entry, 1);
+		if (zip_entry->zip_flags & ZIP_CENTRAL_DIRECTORY_ENCRYPTED &&
+			zip_entry->zip_flags & ZIP_ENCRYPTED &&
+			zip_entry->zip_flags & ZIP_STRONG_ENCRYPTED) {
+			archive_entry_set_is_metadata_encrypted(entry, 1);
+			return ARCHIVE_FATAL;
+		}
+	}
+	zip->init_decryption = (zip_entry->zip_flags & ZIP_ENCRYPTED);
+	zip_entry->compression = (char)archive_le16dec(p + 8);
+	zip_entry->mtime = zip_time(p + 10);
+	zip_entry->crc32 = archive_le32dec(p + 14);
+	if (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+		zip_entry->decdat = p[11];
+	else
+		zip_entry->decdat = p[17];
+	zip_entry->compressed_size = archive_le32dec(p + 18);
+	zip_entry->uncompressed_size = archive_le32dec(p + 22);
+	filename_length = archive_le16dec(p + 26);
+	extra_length = archive_le16dec(p + 28);
+
+	__archive_read_consume(a, 30);
+
+	/* Read the filename. */
+	if ((h = __archive_read_ahead(a, filename_length, NULL)) == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated ZIP file header");
+		return (ARCHIVE_FATAL);
+	}
+	if (zip_entry->zip_flags & ZIP_UTF8_NAME) {
+		/* The filename is stored to be UTF-8. */
+		if (zip->sconv_utf8 == NULL) {
+			zip->sconv_utf8 =
+			    archive_string_conversion_from_charset(
+				&a->archive, "UTF-8", 1);
+			if (zip->sconv_utf8 == NULL)
+				return (ARCHIVE_FATAL);
+		}
+		sconv = zip->sconv_utf8;
+	} else if (zip->sconv != NULL)
+		sconv = zip->sconv;
+	else
+		sconv = zip->sconv_default;
+
+	if (archive_entry_copy_pathname_l(entry,
+	    h, filename_length, sconv) != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Pathname cannot be converted "
+		    "from %s to current locale.",
+		    archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+	}
+	__archive_read_consume(a, filename_length);
+
+	/* Read the extra data. */
+	if ((h = __archive_read_ahead(a, extra_length, NULL)) == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated ZIP file header");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (ARCHIVE_OK != process_extra(a, entry, h, extra_length,
+	    zip_entry)) {
+		return ARCHIVE_FATAL;
+	}
+	__archive_read_consume(a, extra_length);
+
+	/* Work around a bug in Info-Zip: When reading from a pipe, it
+	 * stats the pipe instead of synthesizing a file entry. */
+	if ((zip_entry->mode & AE_IFMT) == AE_IFIFO) {
+		zip_entry->mode &= ~ AE_IFMT;
+		zip_entry->mode |= AE_IFREG;
+	}
+
+	/* If the mode is totally empty, set some sane default. */
+	if (zip_entry->mode == 0) {
+		zip_entry->mode |= 0664;
+	}
+
+	/* Windows archivers sometimes use backslash as the directory
+	 * separator. Normalize to slash. */
+	if (zip_entry->system == 0 &&
+	    (wp = archive_entry_pathname_w(entry)) != NULL) {
+		if (wcschr(wp, L'/') == NULL && wcschr(wp, L'\\') != NULL) {
+			size_t i;
+			struct archive_wstring s;
+			archive_string_init(&s);
+			archive_wstrcpy(&s, wp);
+			for (i = 0; i < archive_strlen(&s); i++) {
+				if (s.s[i] == '\\')
+					s.s[i] = '/';
+			}
+			archive_entry_copy_pathname_w(entry, s.s);
+			archive_wstring_free(&s);
+		}
+	}
+
+	/* Make sure that entries with a trailing '/' are marked as directories
+	 * even if the External File Attributes contains bogus values.  If this
+	 * is not a directory and there is no type, assume a regular file. */
+	if ((zip_entry->mode & AE_IFMT) != AE_IFDIR) {
+		int has_slash;
+
+		wp = archive_entry_pathname_w(entry);
+		if (wp != NULL) {
+			len = wcslen(wp);
+			has_slash = len > 0 && wp[len - 1] == L'/';
+		} else {
+			cp = archive_entry_pathname(entry);
+			len = (cp != NULL)?strlen(cp):0;
+			has_slash = len > 0 && cp[len - 1] == '/';
+		}
+		/* Correct file type as needed. */
+		if (has_slash) {
+			zip_entry->mode &= ~AE_IFMT;
+			zip_entry->mode |= AE_IFDIR;
+			zip_entry->mode |= 0111;
+		} else if ((zip_entry->mode & AE_IFMT) == 0) {
+			zip_entry->mode |= AE_IFREG;
+		}
+	}
+
+	/* Make sure directories end in '/' */
+	if ((zip_entry->mode & AE_IFMT) == AE_IFDIR) {
+		wp = archive_entry_pathname_w(entry);
+		if (wp != NULL) {
+			len = wcslen(wp);
+			if (len > 0 && wp[len - 1] != L'/') {
+				struct archive_wstring s;
+				archive_string_init(&s);
+				archive_wstrcat(&s, wp);
+				archive_wstrappend_wchar(&s, L'/');
+				archive_entry_copy_pathname_w(entry, s.s);
+				archive_wstring_free(&s);
+			}
+		} else {
+			cp = archive_entry_pathname(entry);
+			len = (cp != NULL)?strlen(cp):0;
+			if (len > 0 && cp[len - 1] != '/') {
+				struct archive_string s;
+				archive_string_init(&s);
+				archive_strcat(&s, cp);
+				archive_strappend_char(&s, '/');
+				archive_entry_set_pathname(entry, s.s);
+				archive_string_free(&s);
+			}
+		}
+	}
+
+	if (zip_entry->flags & LA_FROM_CENTRAL_DIRECTORY) {
+		/* If this came from the central dir, its size info
+		 * is definitive, so ignore the length-at-end flag. */
+		zip_entry->zip_flags &= ~ZIP_LENGTH_AT_END;
+		/* If local header is missing a value, use the one from
+		   the central directory.  If both have it, warn about
+		   mismatches. */
+		if (zip_entry->crc32 == 0) {
+			zip_entry->crc32 = zip_entry_central_dir.crc32;
+		} else if (!zip->ignore_crc32
+		    && zip_entry->crc32 != zip_entry_central_dir.crc32) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Inconsistent CRC32 values");
+			ret = ARCHIVE_WARN;
+		}
+		if (zip_entry->compressed_size == 0) {
+			zip_entry->compressed_size
+			    = zip_entry_central_dir.compressed_size;
+		} else if (zip_entry->compressed_size
+		    != zip_entry_central_dir.compressed_size) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Inconsistent compressed size: "
+			    "%jd in central directory, %jd in local header",
+			    (intmax_t)zip_entry_central_dir.compressed_size,
+			    (intmax_t)zip_entry->compressed_size);
+			ret = ARCHIVE_WARN;
+		}
+		if (zip_entry->uncompressed_size == 0) {
+			zip_entry->uncompressed_size
+			    = zip_entry_central_dir.uncompressed_size;
+		} else if (zip_entry->uncompressed_size
+		    != zip_entry_central_dir.uncompressed_size) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Inconsistent uncompressed size: "
+			    "%jd in central directory, %jd in local header",
+			    (intmax_t)zip_entry_central_dir.uncompressed_size,
+			    (intmax_t)zip_entry->uncompressed_size);
+			ret = ARCHIVE_WARN;
+		}
+	}
+
+	/* Populate some additional entry fields: */
+	archive_entry_set_mode(entry, zip_entry->mode);
+	archive_entry_set_uid(entry, zip_entry->uid);
+	archive_entry_set_gid(entry, zip_entry->gid);
+	archive_entry_set_mtime(entry, zip_entry->mtime, 0);
+	archive_entry_set_ctime(entry, zip_entry->ctime, 0);
+	archive_entry_set_atime(entry, zip_entry->atime, 0);
+
+	if ((zip->entry->mode & AE_IFMT) == AE_IFLNK) {
+		size_t linkname_length;
+
+		if (zip_entry->compressed_size > 64 * 1024) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Zip file with oversized link entry");
+			return ARCHIVE_FATAL;
+		}
+
+		linkname_length = (size_t)zip_entry->compressed_size;
+
+		archive_entry_set_size(entry, 0);
+		p = __archive_read_ahead(a, linkname_length, NULL);
+		if (p == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Truncated Zip file");
+			return ARCHIVE_FATAL;
+		}
+
+		sconv = zip->sconv;
+		if (sconv == NULL && (zip->entry->zip_flags & ZIP_UTF8_NAME))
+			sconv = zip->sconv_utf8;
+		if (sconv == NULL)
+			sconv = zip->sconv_default;
+		if (archive_entry_copy_symlink_l(entry, p, linkname_length,
+		    sconv) != 0) {
+			if (errno != ENOMEM && sconv == zip->sconv_utf8 &&
+			    (zip->entry->zip_flags & ZIP_UTF8_NAME))
+			    archive_entry_copy_symlink_l(entry, p,
+				linkname_length, NULL);
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Symlink");
+				return (ARCHIVE_FATAL);
+			}
+			/*
+			 * Since there is no character-set regulation for
+			 * symlink name, do not report the conversion error
+			 * in an automatic conversion.
+			 */
+			if (sconv != zip->sconv_utf8 ||
+			    (zip->entry->zip_flags & ZIP_UTF8_NAME) == 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Symlink cannot be converted "
+				    "from %s to current locale.",
+				    archive_string_conversion_charset_name(
+					sconv));
+				ret = ARCHIVE_WARN;
+			}
+		}
+		zip_entry->uncompressed_size = zip_entry->compressed_size = 0;
+
+		if (__archive_read_consume(a, linkname_length) < 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Read error skipping symlink target name");
+			return ARCHIVE_FATAL;
+		}
+	} else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+	    || zip_entry->uncompressed_size > 0) {
+		/* Set the size only if it's meaningful. */
+		archive_entry_set_size(entry, zip_entry->uncompressed_size);
+	}
+	zip->entry_bytes_remaining = zip_entry->compressed_size;
+
+	/* If there's no body, force read_data() to return EOF immediately. */
+	if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+	    && zip->entry_bytes_remaining < 1)
+		zip->end_of_entry = 1;
+
+	/* Set up a more descriptive format name. */
+        archive_string_empty(&zip->format_name);
+	archive_string_sprintf(&zip->format_name, "ZIP %d.%d (%s)",
+	    version / 10, version % 10,
+	    compression_name(zip->entry->compression));
+	a->archive.archive_format_name = zip->format_name.s;
+
+	return (ret);
+}
+
+static int
+check_authentication_code(struct archive_read *a, const void *_p)
+{
+	struct zip *zip = (struct zip *)(a->format->data);
+
+	/* Check authentication code. */
+	if (zip->hctx_valid) {
+		const void *p;
+		uint8_t hmac[20];
+		size_t hmac_len = 20;
+		int cmp;
+
+		archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len);
+		if (_p == NULL) {
+			/* Read authentication code. */
+			p = __archive_read_ahead(a, AUTH_CODE_SIZE, NULL);
+			if (p == NULL) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Truncated ZIP file data");
+				return (ARCHIVE_FATAL);
+			}
+		} else {
+			p = _p;
+		}
+		cmp = memcmp(hmac, p, AUTH_CODE_SIZE);
+		__archive_read_consume(a, AUTH_CODE_SIZE);
+		if (cmp != 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "ZIP bad Authentication code");
+			return (ARCHIVE_WARN);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Read "uncompressed" data.  There are three cases:
+ *  1) We know the size of the data.  This is always true for the
+ * seeking reader (we've examined the Central Directory already).
+ *  2) ZIP_LENGTH_AT_END was set, but only the CRC was deferred.
+ * Info-ZIP seems to do this; we know the size but have to grab
+ * the CRC from the data descriptor afterwards.
+ *  3) We're streaming and ZIP_LENGTH_AT_END was specified and
+ * we have no size information.  In this case, we can do pretty
+ * well by watching for the data descriptor record.  The data
+ * descriptor is 16 bytes and includes a computed CRC that should
+ * provide a strong check.
+ *
+ * TODO: Technically, the PK\007\010 signature is optional.
+ * In the original spec, the data descriptor contained CRC
+ * and size fields but had no leading signature.  In practice,
+ * newer writers seem to provide the signature pretty consistently.
+ *
+ * For uncompressed data, the PK\007\010 marker seems essential
+ * to be sure we've actually seen the end of the entry.
+ *
+ * Returns ARCHIVE_OK if successful, ARCHIVE_FATAL otherwise, sets
+ * zip->end_of_entry if it consumes all of the data.
+ */
+static int
+zip_read_data_none(struct archive_read *a, const void **_buff,
+    size_t *size, int64_t *offset)
+{
+	struct zip *zip;
+	const char *buff;
+	ssize_t bytes_avail;
+	int r;
+
+	(void)offset; /* UNUSED */
+
+	zip = (struct zip *)(a->format->data);
+
+	if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) {
+		const char *p;
+		ssize_t grabbing_bytes = 24;
+
+		if (zip->hctx_valid)
+			grabbing_bytes += AUTH_CODE_SIZE;
+		/* Grab at least 24 bytes. */
+		buff = __archive_read_ahead(a, grabbing_bytes, &bytes_avail);
+		if (bytes_avail < grabbing_bytes) {
+			/* Zip archives have end-of-archive markers
+			   that are longer than this, so a failure to get at
+			   least 24 bytes really does indicate a truncated
+			   file. */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated ZIP file data");
+			return (ARCHIVE_FATAL);
+		}
+		/* Check for a complete PK\007\010 signature, followed
+		 * by the correct 4-byte CRC. */
+		p = buff;
+		if (zip->hctx_valid)
+			p += AUTH_CODE_SIZE;
+		if (p[0] == 'P' && p[1] == 'K'
+		    && p[2] == '\007' && p[3] == '\010'
+		    && (archive_le32dec(p + 4) == zip->entry_crc32
+			|| zip->ignore_crc32
+			|| (zip->hctx_valid
+			 && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) {
+			if (zip->entry->flags & LA_USED_ZIP64) {
+				uint64_t compressed, uncompressed;
+				zip->entry->crc32 = archive_le32dec(p + 4);
+				compressed = archive_le64dec(p + 8);
+				uncompressed = archive_le64dec(p + 16);
+				if (compressed > INT64_MAX || uncompressed >
+				    INT64_MAX) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_FILE_FORMAT,
+					    "Overflow of 64-bit file sizes");
+					return ARCHIVE_FAILED;
+				}
+				zip->entry->compressed_size = compressed;
+				zip->entry->uncompressed_size = uncompressed;
+				zip->unconsumed = 24;
+			} else {
+				zip->entry->crc32 = archive_le32dec(p + 4);
+				zip->entry->compressed_size =
+					archive_le32dec(p + 8);
+				zip->entry->uncompressed_size =
+					archive_le32dec(p + 12);
+				zip->unconsumed = 16;
+			}
+			if (zip->hctx_valid) {
+				r = check_authentication_code(a, buff);
+				if (r != ARCHIVE_OK)
+					return (r);
+			}
+			zip->end_of_entry = 1;
+			return (ARCHIVE_OK);
+		}
+		/* If not at EOF, ensure we consume at least one byte. */
+		++p;
+
+		/* Scan forward until we see where a PK\007\010 signature
+		 * might be. */
+		/* Return bytes up until that point.  On the next call,
+		 * the code above will verify the data descriptor. */
+		while (p < buff + bytes_avail - 4) {
+			if (p[3] == 'P') { p += 3; }
+			else if (p[3] == 'K') { p += 2; }
+			else if (p[3] == '\007') { p += 1; }
+			else if (p[3] == '\010' && p[2] == '\007'
+			    && p[1] == 'K' && p[0] == 'P') {
+				if (zip->hctx_valid)
+					p -= AUTH_CODE_SIZE;
+				break;
+			} else { p += 4; }
+		}
+		bytes_avail = p - buff;
+	} else {
+		if (zip->entry_bytes_remaining == 0) {
+			zip->end_of_entry = 1;
+			if (zip->hctx_valid) {
+				r = check_authentication_code(a, NULL);
+				if (r != ARCHIVE_OK)
+					return (r);
+			}
+			return (ARCHIVE_OK);
+		}
+		/* Grab a bunch of bytes. */
+		buff = __archive_read_ahead(a, 1, &bytes_avail);
+		if (bytes_avail <= 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated ZIP file data");
+			return (ARCHIVE_FATAL);
+		}
+		if (bytes_avail > zip->entry_bytes_remaining)
+			bytes_avail = (ssize_t)zip->entry_bytes_remaining;
+	}
+	if (zip->tctx_valid || zip->cctx_valid) {
+		size_t dec_size = bytes_avail;
+
+		if (dec_size > zip->decrypted_buffer_size)
+			dec_size = zip->decrypted_buffer_size;
+		if (zip->tctx_valid) {
+			trad_enc_decrypt_update(&zip->tctx,
+			    (const uint8_t *)buff, dec_size,
+			    zip->decrypted_buffer, dec_size);
+		} else {
+			size_t dsize = dec_size;
+			archive_hmac_sha1_update(&zip->hctx,
+			    (const uint8_t *)buff, dec_size);
+			archive_decrypto_aes_ctr_update(&zip->cctx,
+			    (const uint8_t *)buff, dec_size,
+			    zip->decrypted_buffer, &dsize);
+		}
+		bytes_avail = dec_size;
+		buff = (const char *)zip->decrypted_buffer;
+	}
+	*size = bytes_avail;
+	zip->entry_bytes_remaining -= bytes_avail;
+	zip->entry_uncompressed_bytes_read += bytes_avail;
+	zip->entry_compressed_bytes_read += bytes_avail;
+	zip->unconsumed += bytes_avail;
+	*_buff = buff;
+	return (ARCHIVE_OK);
+}
+
+static int
+consume_optional_marker(struct archive_read *a, struct zip *zip)
+{
+	if (zip->end_of_entry && (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
+		const char *p;
+
+		if (NULL == (p = __archive_read_ahead(a, 24, NULL))) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated ZIP end-of-file record");
+			return (ARCHIVE_FATAL);
+		}
+		/* Consume the optional PK\007\010 marker. */
+		if (p[0] == 'P' && p[1] == 'K' &&
+		    p[2] == '\007' && p[3] == '\010') {
+			p += 4;
+			zip->unconsumed = 4;
+		}
+		if (zip->entry->flags & LA_USED_ZIP64) {
+			uint64_t compressed, uncompressed;
+			zip->entry->crc32 = archive_le32dec(p);
+			compressed = archive_le64dec(p + 4);
+			uncompressed = archive_le64dec(p + 12);
+			if (compressed > INT64_MAX ||
+			    uncompressed > INT64_MAX) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Overflow of 64-bit file sizes");
+				return ARCHIVE_FAILED;
+			}
+			zip->entry->compressed_size = compressed;
+			zip->entry->uncompressed_size = uncompressed;
+			zip->unconsumed += 20;
+		} else {
+			zip->entry->crc32 = archive_le32dec(p);
+			zip->entry->compressed_size = archive_le32dec(p + 4);
+			zip->entry->uncompressed_size = archive_le32dec(p + 8);
+			zip->unconsumed += 12;
+		}
+	}
+
+    return (ARCHIVE_OK);
+}
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+static int
+zipx_xz_init(struct archive_read *a, struct zip *zip)
+{
+	lzma_ret r;
+
+	if(zip->zipx_lzma_valid) {
+		lzma_end(&zip->zipx_lzma_stream);
+		zip->zipx_lzma_valid = 0;
+	}
+
+	memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream));
+	r = lzma_stream_decoder(&zip->zipx_lzma_stream, UINT64_MAX, 0);
+	if (r != LZMA_OK) {
+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+		    "xz initialization failed(%d)",
+		    r);
+
+		return (ARCHIVE_FAILED);
+	}
+
+	zip->zipx_lzma_valid = 1;
+
+	free(zip->uncompressed_buffer);
+
+	zip->uncompressed_buffer_size = 256 * 1024;
+	zip->uncompressed_buffer =
+	    (uint8_t*) malloc(zip->uncompressed_buffer_size);
+	if (zip->uncompressed_buffer == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "No memory for xz decompression");
+		    return (ARCHIVE_FATAL);
+	}
+
+	zip->decompress_init = 1;
+	return (ARCHIVE_OK);
+}
+
+static int
+zipx_lzma_alone_init(struct archive_read *a, struct zip *zip)
+{
+	lzma_ret r;
+	const uint8_t* p;
+
+#pragma pack(push)
+#pragma pack(1)
+	struct _alone_header {
+	    uint8_t bytes[5];
+	    uint64_t uncompressed_size;
+	} alone_header;
+#pragma pack(pop)
+
+	if(zip->zipx_lzma_valid) {
+		lzma_end(&zip->zipx_lzma_stream);
+		zip->zipx_lzma_valid = 0;
+	}
+
+	/* To unpack ZIPX's "LZMA" (id 14) stream we can use standard liblzma
+	 * that is a part of XZ Utils. The stream format stored inside ZIPX
+	 * file is a modified "lzma alone" file format, that was used by the
+	 * `lzma` utility which was later deprecated in favour of `xz` utility. 	 * Since those formats are nearly the same, we can use a standard
+	 * "lzma alone" decoder from XZ Utils. */
+
+	memset(&zip->zipx_lzma_stream, 0, sizeof(zip->zipx_lzma_stream));
+	r = lzma_alone_decoder(&zip->zipx_lzma_stream, UINT64_MAX);
+	if (r != LZMA_OK) {
+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+		    "lzma initialization failed(%d)", r);
+
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Flag the cleanup function that we want our lzma-related structures
+	 * to be freed later. */
+	zip->zipx_lzma_valid = 1;
+
+	/* The "lzma alone" file format and the stream format inside ZIPx are
+	 * almost the same. Here's an example of a structure of "lzma alone"
+	 * format:
+	 *
+	 * $ cat /bin/ls | lzma | xxd | head -n 1
+	 * 00000000: 5d00 0080 00ff ffff ffff ffff ff00 2814
+	 *
+	 *    5 bytes        8 bytes        n bytes
+	 * <lzma_params><uncompressed_size><data...>
+	 *
+	 * lzma_params is a 5-byte blob that has to be decoded to extract
+	 * parameters of this LZMA stream. The uncompressed_size field is an
+	 * uint64_t value that contains information about the size of the
+	 * uncompressed file, or UINT64_MAX if this value is unknown.
+	 * The <data...> part is the actual lzma-compressed data stream.
+	 *
+	 * Now here's the structure of the stream inside the ZIPX file:
+	 *
+	 * $ cat stream_inside_zipx | xxd | head -n 1
+	 * 00000000: 0914 0500 5d00 8000 0000 2814 .... ....
+	 *
+	 *  2byte   2byte    5 bytes     n bytes
+	 * <magic1><magic2><lzma_params><data...>
+	 *
+	 * This means that the ZIPX file contains an additional magic1 and
+	 * magic2 headers, the lzma_params field contains the same parameter
+	 * set as in the "lzma alone" format, and the <data...> field is the
+	 * same as in the "lzma alone" format as well. Note that also the zipx
+	 * format is missing the uncompressed_size field.
+	 *
+	 * So, in order to use the "lzma alone" decoder for the zipx lzma
+	 * stream, we simply need to shuffle around some fields, prepare a new
+	 * lzma alone header, feed it into lzma alone decoder so it will
+	 * initialize itself properly, and then we can start feeding normal
+	 * zipx lzma stream into the decoder.
+	 */
+
+	/* Read magic1,magic2,lzma_params from the ZIPX stream. */
+	if((p = __archive_read_ahead(a, 9, NULL)) == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated lzma data");
+		return (ARCHIVE_FATAL);
+	}
+
+	if(p[2] != 0x05 || p[3] != 0x00) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Invalid lzma data");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Prepare an lzma alone header: copy the lzma_params blob into
+	 * a proper place into the lzma alone header. */
+	memcpy(&alone_header.bytes[0], p + 4, 5);
+
+	/* Initialize the 'uncompressed size' field to unknown; we'll manually
+	 * monitor how many bytes there are still to be uncompressed. */
+	alone_header.uncompressed_size = UINT64_MAX;
+
+	if(!zip->uncompressed_buffer) {
+		zip->uncompressed_buffer_size = 256 * 1024;
+		zip->uncompressed_buffer =
+			(uint8_t*) malloc(zip->uncompressed_buffer_size);
+
+		if (zip->uncompressed_buffer == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "No memory for lzma decompression");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	zip->zipx_lzma_stream.next_in = (void*) &alone_header;
+	zip->zipx_lzma_stream.avail_in = sizeof(alone_header);
+	zip->zipx_lzma_stream.total_in = 0;
+	zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
+	zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
+	zip->zipx_lzma_stream.total_out = 0;
+
+	/* Feed only the header into the lzma alone decoder. This will
+	 * effectively initialize the decoder, and will not produce any
+	 * output bytes yet. */
+	r = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
+	if (r != LZMA_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "lzma stream initialization error");
+		return ARCHIVE_FATAL;
+	}
+
+	/* We've already consumed some bytes, so take this into account. */
+	__archive_read_consume(a, 9);
+	zip->entry_bytes_remaining -= 9;
+	zip->entry_compressed_bytes_read += 9;
+
+	zip->decompress_init = 1;
+	return (ARCHIVE_OK);
+}
+
+static int
+zip_read_data_zipx_xz(struct archive_read *a, const void **buff,
+	size_t *size, int64_t *offset)
+{
+	struct zip* zip = (struct zip *)(a->format->data);
+	int ret;
+	lzma_ret lz_ret;
+	const void* compressed_buf;
+	ssize_t bytes_avail, in_bytes, to_consume = 0;
+
+	(void) offset; /* UNUSED */
+
+	/* Initialize decompressor if not yet initialized. */
+	if (!zip->decompress_init) {
+		ret = zipx_xz_init(a, zip);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+	}
+
+	compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
+	if (bytes_avail < 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated xz file body");
+		return (ARCHIVE_FATAL);
+	}
+
+	in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+	zip->zipx_lzma_stream.next_in = compressed_buf;
+	zip->zipx_lzma_stream.avail_in = in_bytes;
+	zip->zipx_lzma_stream.total_in = 0;
+	zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
+	zip->zipx_lzma_stream.avail_out = zip->uncompressed_buffer_size;
+	zip->zipx_lzma_stream.total_out = 0;
+
+	/* Perform the decompression. */
+	lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
+	switch(lz_ret) {
+		case LZMA_DATA_ERROR:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "xz data error (error %d)", (int) lz_ret);
+			return (ARCHIVE_FATAL);
+
+		case LZMA_NO_CHECK:
+		case LZMA_OK:
+			break;
+
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "xz unknown error %d", (int) lz_ret);
+			return (ARCHIVE_FATAL);
+
+		case LZMA_STREAM_END:
+			lzma_end(&zip->zipx_lzma_stream);
+			zip->zipx_lzma_valid = 0;
+
+			if((int64_t) zip->zipx_lzma_stream.total_in !=
+			    zip->entry_bytes_remaining)
+			{
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "xz premature end of stream");
+				return (ARCHIVE_FATAL);
+			}
+
+			zip->end_of_entry = 1;
+			break;
+	}
+
+	to_consume = zip->zipx_lzma_stream.total_in;
+
+	__archive_read_consume(a, to_consume);
+	zip->entry_bytes_remaining -= to_consume;
+	zip->entry_compressed_bytes_read += to_consume;
+	zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;
+
+	*size = zip->zipx_lzma_stream.total_out;
+	*buff = zip->uncompressed_buffer;
+
+	ret = consume_optional_marker(a, zip);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
+    size_t *size, int64_t *offset)
+{
+	struct zip* zip = (struct zip *)(a->format->data);
+	int ret;
+	lzma_ret lz_ret;
+	const void* compressed_buf;
+	ssize_t bytes_avail, in_bytes, to_consume;
+
+	(void) offset; /* UNUSED */
+
+	/* Initialize decompressor if not yet initialized. */
+	if (!zip->decompress_init) {
+		ret = zipx_lzma_alone_init(a, zip);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+	}
+
+	/* Fetch more compressed data. The same note as in deflate handler
+	 * applies here as well:
+	 *
+	 * Note: '1' here is a performance optimization. Recall that the
+	 * decompression layer returns a count of available bytes; asking for
+	 * more than that forces the decompressor to combine reads by copying
+	 * data.
+	 */
+	compressed_buf = __archive_read_ahead(a, 1, &bytes_avail);
+	if (bytes_avail < 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated lzma file body");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Set decompressor parameters. */
+	in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+
+	zip->zipx_lzma_stream.next_in = compressed_buf;
+	zip->zipx_lzma_stream.avail_in = in_bytes;
+	zip->zipx_lzma_stream.total_in = 0;
+	zip->zipx_lzma_stream.next_out = zip->uncompressed_buffer;
+	zip->zipx_lzma_stream.avail_out =
+		/* These lzma_alone streams lack end of stream marker, so let's
+		 * make sure the unpacker won't try to unpack more than it's
+		 * supposed to. */
+		zipmin((int64_t) zip->uncompressed_buffer_size,
+		    zip->entry->uncompressed_size -
+		    zip->entry_uncompressed_bytes_read);
+	zip->zipx_lzma_stream.total_out = 0;
+
+	/* Perform the decompression. */
+	lz_ret = lzma_code(&zip->zipx_lzma_stream, LZMA_RUN);
+	switch(lz_ret) {
+		case LZMA_DATA_ERROR:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "lzma data error (error %d)", (int) lz_ret);
+			return (ARCHIVE_FATAL);
+
+		/* This case is optional in lzma alone format. It can happen,
+		 * but most of the files don't have it. (GitHub #1257) */
+		case LZMA_STREAM_END:
+			lzma_end(&zip->zipx_lzma_stream);
+			zip->zipx_lzma_valid = 0;
+			if((int64_t) zip->zipx_lzma_stream.total_in !=
+			    zip->entry_bytes_remaining)
+			{
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "lzma alone premature end of stream");
+				return (ARCHIVE_FATAL);
+			}
+
+			zip->end_of_entry = 1;
+			break;
+
+		case LZMA_OK:
+			break;
+
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "lzma unknown error %d", (int) lz_ret);
+			return (ARCHIVE_FATAL);
+	}
+
+	to_consume = zip->zipx_lzma_stream.total_in;
+
+	/* Update pointers. */
+	__archive_read_consume(a, to_consume);
+	zip->entry_bytes_remaining -= to_consume;
+	zip->entry_compressed_bytes_read += to_consume;
+	zip->entry_uncompressed_bytes_read += zip->zipx_lzma_stream.total_out;
+
+	if(zip->entry_bytes_remaining == 0) {
+		zip->end_of_entry = 1;
+	}
+
+	/* Return values. */
+	*size = zip->zipx_lzma_stream.total_out;
+	*buff = zip->uncompressed_buffer;
+
+	/* Behave the same way as during deflate decompression. */
+	ret = consume_optional_marker(a, zip);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+
+	/* Free lzma decoder handle because we'll no longer need it. */
+	if(zip->end_of_entry) {
+		lzma_end(&zip->zipx_lzma_stream);
+		zip->zipx_lzma_valid = 0;
+	}
+
+	/* If we're here, then we're good! */
+	return (ARCHIVE_OK);
+}
+#endif /* HAVE_LZMA_H && HAVE_LIBLZMA */
+
+static int
+zipx_ppmd8_init(struct archive_read *a, struct zip *zip)
+{
+	const void* p;
+	uint32_t val;
+	uint32_t order;
+	uint32_t mem;
+	uint32_t restore_method;
+
+	/* Remove previous decompression context if it exists. */
+	if(zip->ppmd8_valid) {
+		__archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
+		zip->ppmd8_valid = 0;
+	}
+
+	/* Create a new decompression context. */
+	__archive_ppmd8_functions.Ppmd8_Construct(&zip->ppmd8);
+	zip->ppmd8_stream_failed = 0;
+
+	/* Setup function pointers required by Ppmd8 decompressor. The
+	 * 'ppmd_read' function will feed new bytes to the decompressor,
+	 * and will increment the 'zip->zipx_ppmd_read_compressed' counter. */
+	zip->ppmd8.Stream.In = &zip->zipx_ppmd_stream;
+	zip->zipx_ppmd_stream.a = a;
+	zip->zipx_ppmd_stream.Read = &ppmd_read;
+
+	/* Reset number of read bytes to 0. */
+	zip->zipx_ppmd_read_compressed = 0;
+
+	/* Read Ppmd8 header (2 bytes). */
+	p = __archive_read_ahead(a, 2, NULL);
+	if(!p) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated file data in PPMd8 stream");
+		return (ARCHIVE_FATAL);
+	}
+	__archive_read_consume(a, 2);
+
+	/* Decode the stream's compression parameters. */
+	val = archive_le16dec(p);
+	order = (val & 15) + 1;
+	mem = ((val >> 4) & 0xff) + 1;
+	restore_method = (val >> 12);
+
+	if(order < 2 || restore_method > 2) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Invalid parameter set in PPMd8 stream (order=%d, "
+		    "restore=%d)", order, restore_method);
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Allocate the memory needed to properly decompress the file. */
+	if(!__archive_ppmd8_functions.Ppmd8_Alloc(&zip->ppmd8, mem << 20)) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Unable to allocate memory for PPMd8 stream: %d bytes",
+		    mem << 20);
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Signal the cleanup function to release Ppmd8 context in the
+	 * cleanup phase. */
+	zip->ppmd8_valid = 1;
+
+	/* Perform further Ppmd8 initialization. */
+	if(!__archive_ppmd8_functions.Ppmd8_RangeDec_Init(&zip->ppmd8)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "PPMd8 stream range decoder initialization error");
+		return (ARCHIVE_FATAL);
+	}
+
+	__archive_ppmd8_functions.Ppmd8_Init(&zip->ppmd8, order,
+	    restore_method);
+
+	/* Allocate the buffer that will hold uncompressed data. */
+	free(zip->uncompressed_buffer);
+
+	zip->uncompressed_buffer_size = 256 * 1024;
+	zip->uncompressed_buffer =
+	    (uint8_t*) malloc(zip->uncompressed_buffer_size);
+
+	if(zip->uncompressed_buffer == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "No memory for PPMd8 decompression");
+		return ARCHIVE_FATAL;
+	}
+
+	/* Ppmd8 initialization is done. */
+	zip->decompress_init = 1;
+
+	/* We've already read 2 bytes in the output stream. Additionally,
+	 * Ppmd8 initialization code could read some data as well. So we
+	 * are advancing the stream by 2 bytes plus whatever number of
+	 * bytes Ppmd8 init function used. */
+	zip->entry_compressed_bytes_read += 2 + zip->zipx_ppmd_read_compressed;
+
+	return ARCHIVE_OK;
+}
+
+static int
+zip_read_data_zipx_ppmd(struct archive_read *a, const void **buff,
+    size_t *size, int64_t *offset)
+{
+	struct zip* zip = (struct zip *)(a->format->data);
+	int ret;
+	size_t consumed_bytes = 0;
+	ssize_t bytes_avail = 0;
+
+	(void) offset; /* UNUSED */
+
+	/* If we're here for the first time, initialize Ppmd8 decompression
+	 * context first. */
+	if(!zip->decompress_init) {
+		ret = zipx_ppmd8_init(a, zip);
+		if(ret != ARCHIVE_OK)
+			return ret;
+	}
+
+	/* Fetch for more data. We're reading 1 byte here, but libarchive
+	 * should prefetch more bytes. */
+	(void) __archive_read_ahead(a, 1, &bytes_avail);
+	if(bytes_avail < 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated PPMd8 file body");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* This counter will be updated inside ppmd_read(), which at one
+	 * point will be called by Ppmd8_DecodeSymbol. */
+	zip->zipx_ppmd_read_compressed = 0;
+
+	/* Decompression loop. */
+	do {
+		int sym = __archive_ppmd8_functions.Ppmd8_DecodeSymbol(
+		    &zip->ppmd8);
+		if(sym < 0) {
+			zip->end_of_entry = 1;
+			break;
+		}
+
+		/* This field is set by ppmd_read() when there was no more data
+		 * to be read. */
+		if(zip->ppmd8_stream_failed) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated PPMd8 file body");
+			return (ARCHIVE_FATAL);
+		}
+
+		zip->uncompressed_buffer[consumed_bytes] = (uint8_t) sym;
+		++consumed_bytes;
+	} while(consumed_bytes < zip->uncompressed_buffer_size);
+
+	/* Update pointers for libarchive. */
+	*buff = zip->uncompressed_buffer;
+	*size = consumed_bytes;
+
+	/* Update pointers so we can continue decompression in another call. */
+	zip->entry_bytes_remaining -= zip->zipx_ppmd_read_compressed;
+	zip->entry_compressed_bytes_read += zip->zipx_ppmd_read_compressed;
+	zip->entry_uncompressed_bytes_read += consumed_bytes;
+
+	/* If we're at the end of stream, deinitialize Ppmd8 context. */
+	if(zip->end_of_entry) {
+		__archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
+		zip->ppmd8_valid = 0;
+	}
+
+	/* Seek for optional marker, same way as in each zip entry. */
+	ret = consume_optional_marker(a, zip);
+	if (ret != ARCHIVE_OK)
+		return ret;
+
+	return ARCHIVE_OK;
+}
+
+#ifdef HAVE_BZLIB_H
+static int
+zipx_bzip2_init(struct archive_read *a, struct zip *zip)
+{
+	int r;
+
+	/* Deallocate already existing BZ2 decompression context if it
+	 * exists. */
+	if(zip->bzstream_valid) {
+		BZ2_bzDecompressEnd(&zip->bzstream);
+		zip->bzstream_valid = 0;
+	}
+
+	/* Allocate a new BZ2 decompression context. */
+	memset(&zip->bzstream, 0, sizeof(bz_stream));
+	r = BZ2_bzDecompressInit(&zip->bzstream, 0, 1);
+	if(r != BZ_OK) {
+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+		    "bzip2 initialization failed(%d)",
+		    r);
+
+		return ARCHIVE_FAILED;
+	}
+
+	/* Mark the bzstream field to be released in cleanup phase. */
+	zip->bzstream_valid = 1;
+
+	/* (Re)allocate the buffer that will contain decompressed bytes. */
+	free(zip->uncompressed_buffer);
+
+	zip->uncompressed_buffer_size = 256 * 1024;
+	zip->uncompressed_buffer =
+	    (uint8_t*) malloc(zip->uncompressed_buffer_size);
+	if (zip->uncompressed_buffer == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "No memory for bzip2 decompression");
+		    return ARCHIVE_FATAL;
+	}
+
+	/* Initialization done. */
+	zip->decompress_init = 1;
+	return ARCHIVE_OK;
+}
+
+static int
+zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
+    size_t *size, int64_t *offset)
+{
+	struct zip *zip = (struct zip *)(a->format->data);
+	ssize_t bytes_avail = 0, in_bytes, to_consume;
+	const void *compressed_buff;
+	int r;
+	uint64_t total_out;
+
+	(void) offset; /* UNUSED */
+
+	/* Initialize decompression context if we're here for the first time. */
+	if(!zip->decompress_init) {
+		r = zipx_bzip2_init(a, zip);
+		if(r != ARCHIVE_OK)
+			return r;
+	}
+
+	/* Fetch more compressed bytes. */
+	compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
+	if(bytes_avail < 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated bzip2 file body");
+		return (ARCHIVE_FATAL);
+	}
+
+	in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+	if(in_bytes < 1) {
+		/* libbz2 doesn't complain when caller feeds avail_in == 0.
+		 * It will actually return success in this case, which is
+		 * undesirable. This is why we need to make this check
+		 * manually. */
+
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated bzip2 file body");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Setup buffer boundaries. */
+	zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;
+	zip->bzstream.avail_in = in_bytes;
+	zip->bzstream.total_in_hi32 = 0;
+	zip->bzstream.total_in_lo32 = 0;
+	zip->bzstream.next_out = (char*) zip->uncompressed_buffer;
+	zip->bzstream.avail_out = zip->uncompressed_buffer_size;
+	zip->bzstream.total_out_hi32 = 0;
+	zip->bzstream.total_out_lo32 = 0;
+
+	/* Perform the decompression. */
+	r = BZ2_bzDecompress(&zip->bzstream);
+	switch(r) {
+		case BZ_STREAM_END:
+			/* If we're at the end of the stream, deinitialize the
+			 * decompression context now. */
+			switch(BZ2_bzDecompressEnd(&zip->bzstream)) {
+				case BZ_OK:
+					break;
+				default:
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_MISC,
+					    "Failed to clean up bzip2 "
+					    "decompressor");
+					return ARCHIVE_FATAL;
+			}
+
+			zip->end_of_entry = 1;
+			break;
+		case BZ_OK:
+			/* The decompressor has successfully decoded this
+			 * chunk of data, but more data is still in queue. */
+			break;
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "bzip2 decompression failed");
+			return ARCHIVE_FATAL;
+	}
+
+	/* Update the pointers so decompressor can continue decoding. */
+	to_consume = zip->bzstream.total_in_lo32;
+	__archive_read_consume(a, to_consume);
+
+	total_out = ((uint64_t) zip->bzstream.total_out_hi32 << 32) +
+	    zip->bzstream.total_out_lo32;
+
+	zip->entry_bytes_remaining -= to_consume;
+	zip->entry_compressed_bytes_read += to_consume;
+	zip->entry_uncompressed_bytes_read += total_out;
+
+	/* Give libarchive its due. */
+	*size = total_out;
+	*buff = zip->uncompressed_buffer;
+
+	/* Seek for optional marker, like in other entries. */
+	r = consume_optional_marker(a, zip);
+	if(r != ARCHIVE_OK)
+		return r;
+
+	return ARCHIVE_OK;
+}
+
+#endif
+
+#ifdef HAVE_ZLIB_H
+static int
+zip_deflate_init(struct archive_read *a, struct zip *zip)
+{
+	int r;
+
+	/* If we haven't yet read any data, initialize the decompressor. */
+	if (!zip->decompress_init) {
+		if (zip->stream_valid)
+			r = inflateReset(&zip->stream);
+		else
+			r = inflateInit2(&zip->stream,
+			    -15 /* Don't check for zlib header */);
+		if (r != Z_OK) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Can't initialize ZIP decompression.");
+			return (ARCHIVE_FATAL);
+		}
+		/* Stream structure has been set up. */
+		zip->stream_valid = 1;
+		/* We've initialized decompression for this stream. */
+		zip->decompress_init = 1;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+zip_read_data_deflate(struct archive_read *a, const void **buff,
+    size_t *size, int64_t *offset)
+{
+	struct zip *zip;
+	ssize_t bytes_avail;
+	const void *compressed_buff, *sp;
+	int r;
+
+	(void)offset; /* UNUSED */
+
+	zip = (struct zip *)(a->format->data);
+
+	/* If the buffer hasn't been allocated, allocate it now. */
+	if (zip->uncompressed_buffer == NULL) {
+		zip->uncompressed_buffer_size = 256 * 1024;
+		zip->uncompressed_buffer
+		    = (unsigned char *)malloc(zip->uncompressed_buffer_size);
+		if (zip->uncompressed_buffer == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "No memory for ZIP decompression");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	r = zip_deflate_init(a, zip);
+	if (r != ARCHIVE_OK)
+		return (r);
+
+	/*
+	 * Note: '1' here is a performance optimization.
+	 * Recall that the decompression layer returns a count of
+	 * available bytes; asking for more than that forces the
+	 * decompressor to combine reads by copying data.
+	 */
+	compressed_buff = sp = __archive_read_ahead(a, 1, &bytes_avail);
+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+	    && bytes_avail > zip->entry_bytes_remaining) {
+		bytes_avail = (ssize_t)zip->entry_bytes_remaining;
+	}
+	if (bytes_avail < 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated ZIP file body");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (zip->tctx_valid || zip->cctx_valid) {
+		if (zip->decrypted_bytes_remaining < (size_t)bytes_avail) {
+			size_t buff_remaining =
+			    (zip->decrypted_buffer +
+			    zip->decrypted_buffer_size)
+			    - (zip->decrypted_ptr +
+			    zip->decrypted_bytes_remaining);
+
+			if (buff_remaining > (size_t)bytes_avail)
+				buff_remaining = (size_t)bytes_avail;
+
+			if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END) &&
+			      zip->entry_bytes_remaining > 0) {
+				if ((int64_t)(zip->decrypted_bytes_remaining
+				    + buff_remaining)
+				      > zip->entry_bytes_remaining) {
+					if (zip->entry_bytes_remaining <
+					    (int64_t)zip->decrypted_bytes_remaining)
+						buff_remaining = 0;
+					else
+						buff_remaining =
+						    (size_t)zip->entry_bytes_remaining
+						    - zip->decrypted_bytes_remaining;
+				}
+			}
+			if (buff_remaining > 0) {
+				if (zip->tctx_valid) {
+					trad_enc_decrypt_update(&zip->tctx,
+					    compressed_buff, buff_remaining,
+					    zip->decrypted_ptr
+					      + zip->decrypted_bytes_remaining,
+					    buff_remaining);
+				} else {
+					size_t dsize = buff_remaining;
+					archive_decrypto_aes_ctr_update(
+					    &zip->cctx,
+					    compressed_buff, buff_remaining,
+					    zip->decrypted_ptr
+					      + zip->decrypted_bytes_remaining,
+					    &dsize);
+				}
+				zip->decrypted_bytes_remaining +=
+				    buff_remaining;
+			}
+		}
+		bytes_avail = zip->decrypted_bytes_remaining;
+		compressed_buff = (const char *)zip->decrypted_ptr;
+	}
+
+	/*
+	 * 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'.
+	 */
+	zip->stream.next_in = (Bytef *)(uintptr_t)(const void *)compressed_buff;
+	zip->stream.avail_in = (uInt)bytes_avail;
+	zip->stream.total_in = 0;
+	zip->stream.next_out = zip->uncompressed_buffer;
+	zip->stream.avail_out = (uInt)zip->uncompressed_buffer_size;
+	zip->stream.total_out = 0;
+
+	r = inflate(&zip->stream, 0);
+	switch (r) {
+	case Z_OK:
+		break;
+	case Z_STREAM_END:
+		zip->end_of_entry = 1;
+		break;
+	case Z_MEM_ERROR:
+		archive_set_error(&a->archive, ENOMEM,
+		    "Out of memory for ZIP decompression");
+		return (ARCHIVE_FATAL);
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "ZIP decompression failed (%d)", r);
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Consume as much as the compressor actually used. */
+	bytes_avail = zip->stream.total_in;
+	if (zip->tctx_valid || zip->cctx_valid) {
+		zip->decrypted_bytes_remaining -= bytes_avail;
+		if (zip->decrypted_bytes_remaining == 0)
+			zip->decrypted_ptr = zip->decrypted_buffer;
+		else
+			zip->decrypted_ptr += bytes_avail;
+	}
+	/* Calculate compressed data as much as we used.*/
+	if (zip->hctx_valid)
+		archive_hmac_sha1_update(&zip->hctx, sp, bytes_avail);
+	__archive_read_consume(a, bytes_avail);
+	zip->entry_bytes_remaining -= bytes_avail;
+	zip->entry_compressed_bytes_read += bytes_avail;
+
+	*size = zip->stream.total_out;
+	zip->entry_uncompressed_bytes_read += zip->stream.total_out;
+	*buff = zip->uncompressed_buffer;
+
+	if (zip->end_of_entry && zip->hctx_valid) {
+		r = check_authentication_code(a, NULL);
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	r = consume_optional_marker(a, zip);
+	if (r != ARCHIVE_OK)
+		return (r);
+
+	return (ARCHIVE_OK);
+}
+#endif
+
+static int
+read_decryption_header(struct archive_read *a)
+{
+	struct zip *zip = (struct zip *)(a->format->data);
+	const char *p;
+	unsigned int remaining_size;
+	unsigned int ts;
+
+	/*
+	 * Read an initialization vector data field.
+	 */
+	p = __archive_read_ahead(a, 2, NULL);
+	if (p == NULL)
+		goto truncated;
+	ts = zip->iv_size;
+	zip->iv_size = archive_le16dec(p);
+	__archive_read_consume(a, 2);
+	if (ts < zip->iv_size) {
+		free(zip->iv);
+		zip->iv = NULL;
+	}
+	p = __archive_read_ahead(a, zip->iv_size, NULL);
+	if (p == NULL)
+		goto truncated;
+	if (zip->iv == NULL) {
+		zip->iv = malloc(zip->iv_size);
+		if (zip->iv == NULL)
+			goto nomem;
+	}
+	memcpy(zip->iv, p, zip->iv_size);
+	__archive_read_consume(a, zip->iv_size);
+
+	/*
+	 * Read a size of remaining decryption header field.
+	 */
+	p = __archive_read_ahead(a, 14, NULL);
+	if (p == NULL)
+		goto truncated;
+	remaining_size = archive_le32dec(p);
+	if (remaining_size < 16 || remaining_size > (1 << 18))
+		goto corrupted;
+
+	/* Check if format version is supported. */
+	if (archive_le16dec(p+4) != 3) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unsupported encryption format version: %u",
+		    archive_le16dec(p+4));
+		return (ARCHIVE_FAILED);
+	}
+
+	/*
+	 * Read an encryption algorithm field.
+	 */
+	zip->alg_id = archive_le16dec(p+6);
+	switch (zip->alg_id) {
+	case 0x6601:/* DES */
+	case 0x6602:/* RC2 */
+	case 0x6603:/* 3DES 168 */
+	case 0x6609:/* 3DES 112 */
+	case 0x660E:/* AES 128 */
+	case 0x660F:/* AES 192 */
+	case 0x6610:/* AES 256 */
+	case 0x6702:/* RC2 (version >= 5.2) */
+	case 0x6720:/* Blowfish */
+	case 0x6721:/* Twofish */
+	case 0x6801:/* RC4 */
+		/* Supported encryption algorithm. */
+		break;
+	default:
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unknown encryption algorithm: %u", zip->alg_id);
+		return (ARCHIVE_FAILED);
+	}
+
+	/*
+	 * Read a bit length field.
+	 */
+	zip->bit_len = archive_le16dec(p+8);
+
+	/*
+	 * Read a flags field.
+	 */
+	zip->flags = archive_le16dec(p+10);
+	switch (zip->flags & 0xf000) {
+	case 0x0001: /* Password is required to decrypt. */
+	case 0x0002: /* Certificates only. */
+	case 0x0003: /* Password or certificate required to decrypt. */
+		break;
+	default:
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unknown encryption flag: %u", zip->flags);
+		return (ARCHIVE_FAILED);
+	}
+	if ((zip->flags & 0xf000) == 0 ||
+	    (zip->flags & 0xf000) == 0x4000) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unknown encryption flag: %u", zip->flags);
+		return (ARCHIVE_FAILED);
+	}
+
+	/*
+	 * Read an encrypted random data field.
+	 */
+	ts = zip->erd_size;
+	zip->erd_size = archive_le16dec(p+12);
+	__archive_read_consume(a, 14);
+	if ((zip->erd_size & 0xf) != 0 ||
+	    (zip->erd_size + 16) > remaining_size ||
+	    (zip->erd_size + 16) < zip->erd_size)
+		goto corrupted;
+
+	if (ts < zip->erd_size) {
+		free(zip->erd);
+		zip->erd = NULL;
+	}
+	p = __archive_read_ahead(a, zip->erd_size, NULL);
+	if (p == NULL)
+		goto truncated;
+	if (zip->erd == NULL) {
+		zip->erd = malloc(zip->erd_size);
+		if (zip->erd == NULL)
+			goto nomem;
+	}
+	memcpy(zip->erd, p, zip->erd_size);
+	__archive_read_consume(a, zip->erd_size);
+
+	/*
+	 * Read a reserved data field.
+	 */
+	p = __archive_read_ahead(a, 4, NULL);
+	if (p == NULL)
+		goto truncated;
+	/* Reserved data size should be zero. */
+	if (archive_le32dec(p) != 0)
+		goto corrupted;
+	__archive_read_consume(a, 4);
+
+	/*
+	 * Read a password validation data field.
+	 */
+	p = __archive_read_ahead(a, 2, NULL);
+	if (p == NULL)
+		goto truncated;
+	ts = zip->v_size;
+	zip->v_size = archive_le16dec(p);
+	__archive_read_consume(a, 2);
+	if ((zip->v_size & 0x0f) != 0 ||
+	    (zip->erd_size + zip->v_size + 16) > remaining_size ||
+	    (zip->erd_size + zip->v_size + 16) < (zip->erd_size + zip->v_size))
+		goto corrupted;
+	if (ts < zip->v_size) {
+		free(zip->v_data);
+		zip->v_data = NULL;
+	}
+	p = __archive_read_ahead(a, zip->v_size, NULL);
+	if (p == NULL)
+		goto truncated;
+	if (zip->v_data == NULL) {
+		zip->v_data = malloc(zip->v_size);
+		if (zip->v_data == NULL)
+			goto nomem;
+	}
+	memcpy(zip->v_data, p, zip->v_size);
+	__archive_read_consume(a, zip->v_size);
+
+	p = __archive_read_ahead(a, 4, NULL);
+	if (p == NULL)
+		goto truncated;
+	zip->v_crc32 = archive_le32dec(p);
+	__archive_read_consume(a, 4);
+
+	/*return (ARCHIVE_OK);
+	 * This is not fully implemented yet.*/
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Encrypted file is unsupported");
+	return (ARCHIVE_FAILED);
+truncated:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Truncated ZIP file data");
+	return (ARCHIVE_FATAL);
+corrupted:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Corrupted ZIP file data");
+	return (ARCHIVE_FATAL);
+nomem:
+	archive_set_error(&a->archive, ENOMEM,
+	    "No memory for ZIP decryption");
+	return (ARCHIVE_FATAL);
+}
+
+static int
+zip_alloc_decryption_buffer(struct archive_read *a)
+{
+	struct zip *zip = (struct zip *)(a->format->data);
+	size_t bs = 256 * 1024;
+
+	if (zip->decrypted_buffer == NULL) {
+		zip->decrypted_buffer_size = bs;
+		zip->decrypted_buffer = malloc(bs);
+		if (zip->decrypted_buffer == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "No memory for ZIP decryption");
+			return (ARCHIVE_FATAL);
+		}
+	}
+	zip->decrypted_ptr = zip->decrypted_buffer;
+	return (ARCHIVE_OK);
+}
+
+static int
+init_traditional_PKWARE_decryption(struct archive_read *a)
+{
+	struct zip *zip = (struct zip *)(a->format->data);
+	const void *p;
+	int retry;
+	int r;
+
+	if (zip->tctx_valid)
+		return (ARCHIVE_OK);
+
+	/*
+	   Read the 12 bytes encryption header stored at
+	   the start of the data area.
+	 */
+#define ENC_HEADER_SIZE	12
+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+	    && zip->entry_bytes_remaining < ENC_HEADER_SIZE) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated Zip encrypted body: only %jd bytes available",
+		    (intmax_t)zip->entry_bytes_remaining);
+		return (ARCHIVE_FATAL);
+	}
+
+	p = __archive_read_ahead(a, ENC_HEADER_SIZE, NULL);
+	if (p == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated ZIP file data");
+		return (ARCHIVE_FATAL);
+	}
+
+	for (retry = 0;; retry++) {
+		const char *passphrase;
+		uint8_t crcchk;
+
+		passphrase = __archive_read_next_passphrase(a);
+		if (passphrase == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    (retry > 0)?
+				"Incorrect passphrase":
+				"Passphrase required for this entry");
+			return (ARCHIVE_FAILED);
+		}
+
+		/*
+		 * Initialize ctx for Traditional PKWARE Decryption.
+		 */
+		r = trad_enc_init(&zip->tctx, passphrase, strlen(passphrase),
+			p, ENC_HEADER_SIZE, &crcchk);
+		if (r == 0 && crcchk == zip->entry->decdat)
+			break;/* The passphrase is OK. */
+		if (retry > 10000) {
+			/* Avoid infinity loop. */
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Too many incorrect passphrases");
+			return (ARCHIVE_FAILED);
+		}
+	}
+
+	__archive_read_consume(a, ENC_HEADER_SIZE);
+	zip->tctx_valid = 1;
+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)) {
+	    zip->entry_bytes_remaining -= ENC_HEADER_SIZE;
+	}
+	/*zip->entry_uncompressed_bytes_read += ENC_HEADER_SIZE;*/
+	zip->entry_compressed_bytes_read += ENC_HEADER_SIZE;
+	zip->decrypted_bytes_remaining = 0;
+
+	return (zip_alloc_decryption_buffer(a));
+#undef ENC_HEADER_SIZE
+}
+
+static int
+init_WinZip_AES_decryption(struct archive_read *a)
+{
+	struct zip *zip = (struct zip *)(a->format->data);
+	const void *p;
+	const uint8_t *pv;
+	size_t key_len, salt_len;
+	uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];
+	int retry;
+	int r;
+
+	if (zip->cctx_valid || zip->hctx_valid)
+		return (ARCHIVE_OK);
+
+	switch (zip->entry->aes_extra.strength) {
+	case 1: salt_len = 8;  key_len = 16; break;
+	case 2: salt_len = 12; key_len = 24; break;
+	case 3: salt_len = 16; key_len = 32; break;
+	default: goto corrupted;
+	}
+	p = __archive_read_ahead(a, salt_len + 2, NULL);
+	if (p == NULL)
+		goto truncated;
+
+	for (retry = 0;; retry++) {
+		const char *passphrase;
+
+		passphrase = __archive_read_next_passphrase(a);
+		if (passphrase == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    (retry > 0)?
+				"Incorrect passphrase":
+				"Passphrase required for this entry");
+			return (ARCHIVE_FAILED);
+		}
+		memset(derived_key, 0, sizeof(derived_key));
+		r = archive_pbkdf2_sha1(passphrase, strlen(passphrase),
+		    p, salt_len, 1000, derived_key, key_len * 2 + 2);
+		if (r != 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Decryption is unsupported due to lack of "
+			    "crypto library");
+			return (ARCHIVE_FAILED);
+		}
+
+		/* Check password verification value. */
+		pv = ((const uint8_t *)p) + salt_len;
+		if (derived_key[key_len * 2] == pv[0] &&
+		    derived_key[key_len * 2 + 1] == pv[1])
+			break;/* The passphrase is OK. */
+		if (retry > 10000) {
+			/* Avoid infinity loop. */
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Too many incorrect passphrases");
+			return (ARCHIVE_FAILED);
+		}
+	}
+
+	r = archive_decrypto_aes_ctr_init(&zip->cctx, derived_key, key_len);
+	if (r != 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Decryption is unsupported due to lack of crypto library");
+		return (ARCHIVE_FAILED);
+	}
+	r = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len, key_len);
+	if (r != 0) {
+		archive_decrypto_aes_ctr_release(&zip->cctx);
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to initialize HMAC-SHA1");
+		return (ARCHIVE_FAILED);
+	}
+	zip->cctx_valid = zip->hctx_valid = 1;
+	__archive_read_consume(a, salt_len + 2);
+	zip->entry_bytes_remaining -= salt_len + 2 + AUTH_CODE_SIZE;
+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+	    && zip->entry_bytes_remaining < 0)
+		goto corrupted;
+	zip->entry_compressed_bytes_read += salt_len + 2 + AUTH_CODE_SIZE;
+	zip->decrypted_bytes_remaining = 0;
+
+	zip->entry->compression = zip->entry->aes_extra.compression;
+	return (zip_alloc_decryption_buffer(a));
+
+truncated:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Truncated ZIP file data");
+	return (ARCHIVE_FATAL);
+corrupted:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Corrupted ZIP file data");
+	return (ARCHIVE_FATAL);
+}
+
+static int
+archive_read_format_zip_read_data(struct archive_read *a,
+    const void **buff, size_t *size, int64_t *offset)
+{
+	int r;
+	struct zip *zip = (struct zip *)(a->format->data);
+
+	if (zip->has_encrypted_entries ==
+			ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+		zip->has_encrypted_entries = 0;
+	}
+
+	*offset = zip->entry_uncompressed_bytes_read;
+	*size = 0;
+	*buff = NULL;
+
+	/* If we hit end-of-entry last time, return ARCHIVE_EOF. */
+	if (zip->end_of_entry)
+		return (ARCHIVE_EOF);
+
+	/* Return EOF immediately if this is a non-regular file. */
+	if (AE_IFREG != (zip->entry->mode & AE_IFMT))
+		return (ARCHIVE_EOF);
+
+	__archive_read_consume(a, zip->unconsumed);
+	zip->unconsumed = 0;
+
+	if (zip->init_decryption) {
+		zip->has_encrypted_entries = 1;
+		if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED)
+			r = read_decryption_header(a);
+		else if (zip->entry->compression == WINZIP_AES_ENCRYPTION)
+			r = init_WinZip_AES_decryption(a);
+		else
+			r = init_traditional_PKWARE_decryption(a);
+		if (r != ARCHIVE_OK)
+			return (r);
+		zip->init_decryption = 0;
+	}
+
+	switch(zip->entry->compression) {
+	case 0:  /* No compression. */
+		r =  zip_read_data_none(a, buff, size, offset);
+		break;
+#ifdef HAVE_BZLIB_H
+	case 12: /* ZIPx bzip2 compression. */
+		r = zip_read_data_zipx_bzip2(a, buff, size, offset);
+		break;
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+	case 14: /* ZIPx LZMA compression. */
+		r = zip_read_data_zipx_lzma_alone(a, buff, size, offset);
+		break;
+	case 95: /* ZIPx XZ compression. */
+		r = zip_read_data_zipx_xz(a, buff, size, offset);
+		break;
+#endif
+	/* PPMd support is built-in, so we don't need any #if guards. */
+	case 98: /* ZIPx PPMd compression. */
+		r = zip_read_data_zipx_ppmd(a, buff, size, offset);
+		break;
+
+#ifdef HAVE_ZLIB_H
+	case 8: /* Deflate compression. */
+		r =  zip_read_data_deflate(a, buff, size, offset);
+		break;
+#endif
+	default: /* Unsupported compression. */
+		/* Return a warning. */
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unsupported ZIP compression method (%d: %s)",
+		    zip->entry->compression, compression_name(zip->entry->compression));
+		/* We can't decompress this entry, but we will
+		 * be able to skip() it and try the next entry. */
+		return (ARCHIVE_FAILED);
+		break;
+	}
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Update checksum */
+	if (*size)
+		zip->entry_crc32 = zip->crc32func(zip->entry_crc32, *buff,
+		    (unsigned)*size);
+	/* If we hit the end, swallow any end-of-data marker. */
+	if (zip->end_of_entry) {
+		/* Check file size, CRC against these values. */
+		if (zip->entry->compressed_size !=
+		    zip->entry_compressed_bytes_read) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "ZIP compressed data is wrong size "
+			    "(read %jd, expected %jd)",
+			    (intmax_t)zip->entry_compressed_bytes_read,
+			    (intmax_t)zip->entry->compressed_size);
+			return (ARCHIVE_WARN);
+		}
+		/* Size field only stores the lower 32 bits of the actual
+		 * size. */
+		if ((zip->entry->uncompressed_size & UINT32_MAX)
+		    != (zip->entry_uncompressed_bytes_read & UINT32_MAX)) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "ZIP uncompressed data is wrong size "
+			    "(read %jd, expected %jd)\n",
+			    (intmax_t)zip->entry_uncompressed_bytes_read,
+			    (intmax_t)zip->entry->uncompressed_size);
+			return (ARCHIVE_WARN);
+		}
+		/* Check computed CRC against header */
+		if ((!zip->hctx_valid ||
+		      zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) &&
+		   zip->entry->crc32 != zip->entry_crc32
+		    && !zip->ignore_crc32) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "ZIP bad CRC: 0x%lx should be 0x%lx",
+			    (unsigned long)zip->entry_crc32,
+			    (unsigned long)zip->entry->crc32);
+			return (ARCHIVE_WARN);
+		}
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_zip_cleanup(struct archive_read *a)
+{
+	struct zip *zip;
+	struct zip_entry *zip_entry, *next_zip_entry;
+
+	zip = (struct zip *)(a->format->data);
+
+#ifdef HAVE_ZLIB_H
+	if (zip->stream_valid)
+		inflateEnd(&zip->stream);
+#endif
+
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+    if (zip->zipx_lzma_valid) {
+		lzma_end(&zip->zipx_lzma_stream);
+	}
+#endif
+
+#ifdef HAVE_BZLIB_H
+	if (zip->bzstream_valid) {
+		BZ2_bzDecompressEnd(&zip->bzstream);
+	}
+#endif
+
+	free(zip->uncompressed_buffer);
+
+	if (zip->ppmd8_valid)
+		__archive_ppmd8_functions.Ppmd8_Free(&zip->ppmd8);
+
+	if (zip->zip_entries) {
+		zip_entry = zip->zip_entries;
+		while (zip_entry != NULL) {
+			next_zip_entry = zip_entry->next;
+			archive_string_free(&zip_entry->rsrcname);
+			free(zip_entry);
+			zip_entry = next_zip_entry;
+		}
+	}
+	free(zip->decrypted_buffer);
+	if (zip->cctx_valid)
+		archive_decrypto_aes_ctr_release(&zip->cctx);
+	if (zip->hctx_valid)
+		archive_hmac_sha1_cleanup(&zip->hctx);
+	free(zip->iv);
+	free(zip->erd);
+	free(zip->v_data);
+	archive_string_free(&zip->format_name);
+	free(zip);
+	(a->format->data) = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_read_format_zip_has_encrypted_entries(struct archive_read *_a)
+{
+	if (_a && _a->format) {
+		struct zip * zip = (struct zip *)_a->format->data;
+		if (zip) {
+			return zip->has_encrypted_entries;
+		}
+	}
+	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+static int
+archive_read_format_zip_options(struct archive_read *a,
+    const char *key, const char *val)
+{
+	struct zip *zip;
+	int ret = ARCHIVE_FAILED;
+
+	zip = (struct zip *)(a->format->data);
+	if (strcmp(key, "compat-2x")  == 0) {
+		/* Handle filenames as libarchive 2.x */
+		zip->init_default_conversion = (val != NULL) ? 1 : 0;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "zip: hdrcharset option needs a character-set name"
+			);
+		else {
+			zip->sconv = archive_string_conversion_from_charset(
+			    &a->archive, val, 0);
+			if (zip->sconv != NULL) {
+				if (strcmp(val, "UTF-8") == 0)
+					zip->sconv_utf8 = zip->sconv;
+				ret = ARCHIVE_OK;
+			} else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	} else if (strcmp(key, "ignorecrc32") == 0) {
+		/* Mostly useful for testing. */
+		if (val == NULL || val[0] == 0) {
+			zip->crc32func = real_crc32;
+			zip->ignore_crc32 = 0;
+		} else {
+			zip->crc32func = fake_crc32;
+			zip->ignore_crc32 = 1;
+		}
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "mac-ext") == 0) {
+		zip->process_mac_extensions = (val != NULL && val[0] != 0);
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+int
+archive_read_support_format_zip(struct archive *a)
+{
+	int r;
+	r = archive_read_support_format_zip_streamable(a);
+	if (r != ARCHIVE_OK)
+		return r;
+	return (archive_read_support_format_zip_seekable(a));
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Streaming-mode support
+ */
+
+
+static int
+archive_read_support_format_zip_capabilities_streamable(struct archive_read * a)
+{
+	(void)a; /* UNUSED */
+	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
+		ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+static int
+archive_read_format_zip_streamable_bid(struct archive_read *a, int best_bid)
+{
+	const char *p;
+
+	(void)best_bid; /* UNUSED */
+
+	if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
+		return (-1);
+
+	/*
+	 * Bid of 29 here comes from:
+	 *  + 16 bits for "PK",
+	 *  + next 16-bit field has 6 options so contributes
+	 *    about 16 - log_2(6) ~= 16 - 2.6 ~= 13 bits
+	 *
+	 * So we've effectively verified ~29 total bits of check data.
+	 */
+	if (p[0] == 'P' && p[1] == 'K') {
+		if ((p[2] == '\001' && p[3] == '\002')
+		    || (p[2] == '\003' && p[3] == '\004')
+		    || (p[2] == '\005' && p[3] == '\006')
+		    || (p[2] == '\006' && p[3] == '\006')
+		    || (p[2] == '\007' && p[3] == '\010')
+		    || (p[2] == '0' && p[3] == '0'))
+			return (29);
+	}
+
+	/* TODO: It's worth looking ahead a little bit for a valid
+	 * PK signature.  In particular, that would make it possible
+	 * to read some UUEncoded SFX files or SFX files coming from
+	 * a network socket. */
+
+	return (0);
+}
+
+static int
+archive_read_format_zip_streamable_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct zip *zip;
+
+	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+	if (a->archive.archive_format_name == NULL)
+		a->archive.archive_format_name = "ZIP";
+
+	zip = (struct zip *)(a->format->data);
+
+	/*
+	 * It should be sufficient to call archive_read_next_header() for
+	 * a reader to determine if an entry is encrypted or not. If the
+	 * encryption of an entry is only detectable when calling
+	 * archive_read_data(), so be it. We'll do the same check there
+	 * as well.
+	 */
+	if (zip->has_encrypted_entries ==
+			ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW)
+		zip->has_encrypted_entries = 0;
+
+	/* Make sure we have a zip_entry structure to use. */
+	if (zip->zip_entries == NULL) {
+		zip->zip_entries = malloc(sizeof(struct zip_entry));
+		if (zip->zip_entries == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Out  of memory");
+			return ARCHIVE_FATAL;
+		}
+	}
+	zip->entry = zip->zip_entries;
+	memset(zip->entry, 0, sizeof(struct zip_entry));
+
+	if (zip->cctx_valid)
+		archive_decrypto_aes_ctr_release(&zip->cctx);
+	if (zip->hctx_valid)
+		archive_hmac_sha1_cleanup(&zip->hctx);
+	zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
+	__archive_read_reset_passphrase(a);
+
+	/* Search ahead for the next local file header. */
+	__archive_read_consume(a, zip->unconsumed);
+	zip->unconsumed = 0;
+	for (;;) {
+		int64_t skipped = 0;
+		const char *p, *end;
+		ssize_t bytes;
+
+		p = __archive_read_ahead(a, 4, &bytes);
+		if (p == NULL)
+			return (ARCHIVE_FATAL);
+		end = p + bytes;
+
+		while (p + 4 <= end) {
+			if (p[0] == 'P' && p[1] == 'K') {
+				if (p[2] == '\003' && p[3] == '\004') {
+					/* Regular file entry. */
+					__archive_read_consume(a, skipped);
+					return zip_read_local_file_header(a,
+					    entry, zip);
+				}
+
+                              /*
+                               * TODO: We cannot restore permissions
+                               * based only on the local file headers.
+                               * Consider scanning the central
+                               * directory and returning additional
+                               * entries for at least directories.
+                               * This would allow us to properly set
+                               * directory permissions.
+			       *
+			       * This won't help us fix symlinks
+			       * and may not help with regular file
+			       * permissions, either.  <sigh>
+                               */
+                              if (p[2] == '\001' && p[3] == '\002') {
+                                      return (ARCHIVE_EOF);
+                              }
+
+                              /* End of central directory?  Must be an
+                               * empty archive. */
+                              if ((p[2] == '\005' && p[3] == '\006')
+                                  || (p[2] == '\006' && p[3] == '\006'))
+                                      return (ARCHIVE_EOF);
+			}
+			++p;
+			++skipped;
+		}
+		__archive_read_consume(a, skipped);
+	}
+}
+
+static int
+archive_read_format_zip_read_data_skip_streamable(struct archive_read *a)
+{
+	struct zip *zip;
+	int64_t bytes_skipped;
+
+	zip = (struct zip *)(a->format->data);
+	bytes_skipped = __archive_read_consume(a, zip->unconsumed);
+	zip->unconsumed = 0;
+	if (bytes_skipped < 0)
+		return (ARCHIVE_FATAL);
+
+	/* If we've already read to end of data, we're done. */
+	if (zip->end_of_entry)
+		return (ARCHIVE_OK);
+
+	/* So we know we're streaming... */
+	if (0 == (zip->entry->zip_flags & ZIP_LENGTH_AT_END)
+	    || zip->entry->compressed_size > 0) {
+		/* We know the compressed length, so we can just skip. */
+		bytes_skipped = __archive_read_consume(a,
+					zip->entry_bytes_remaining);
+		if (bytes_skipped < 0)
+			return (ARCHIVE_FATAL);
+		return (ARCHIVE_OK);
+	}
+
+	if (zip->init_decryption) {
+		int r;
+
+		zip->has_encrypted_entries = 1;
+		if (zip->entry->zip_flags & ZIP_STRONG_ENCRYPTED)
+			r = read_decryption_header(a);
+		else if (zip->entry->compression == WINZIP_AES_ENCRYPTION)
+			r = init_WinZip_AES_decryption(a);
+		else
+			r = init_traditional_PKWARE_decryption(a);
+		if (r != ARCHIVE_OK)
+			return (r);
+		zip->init_decryption = 0;
+	}
+
+	/* We're streaming and we don't know the length. */
+	/* If the body is compressed and we know the format, we can
+	 * find an exact end-of-entry by decompressing it. */
+	switch (zip->entry->compression) {
+#ifdef HAVE_ZLIB_H
+	case 8: /* Deflate compression. */
+		while (!zip->end_of_entry) {
+			int64_t offset = 0;
+			const void *buff = NULL;
+			size_t size = 0;
+			int r;
+			r =  zip_read_data_deflate(a, &buff, &size, &offset);
+			if (r != ARCHIVE_OK)
+				return (r);
+		}
+		return ARCHIVE_OK;
+#endif
+	default: /* Uncompressed or unknown. */
+		/* Scan for a PK\007\010 signature. */
+		for (;;) {
+			const char *p, *buff;
+			ssize_t bytes_avail;
+			buff = __archive_read_ahead(a, 16, &bytes_avail);
+			if (bytes_avail < 16) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Truncated ZIP file data");
+				return (ARCHIVE_FATAL);
+			}
+			p = buff;
+			while (p <= buff + bytes_avail - 16) {
+				if (p[3] == 'P') { p += 3; }
+				else if (p[3] == 'K') { p += 2; }
+				else if (p[3] == '\007') { p += 1; }
+				else if (p[3] == '\010' && p[2] == '\007'
+				    && p[1] == 'K' && p[0] == 'P') {
+					if (zip->entry->flags & LA_USED_ZIP64)
+						__archive_read_consume(a,
+						    p - buff + 24);
+					else
+						__archive_read_consume(a,
+						    p - buff + 16);
+					return ARCHIVE_OK;
+				} else { p += 4; }
+			}
+			__archive_read_consume(a, p - buff);
+		}
+	}
+}
+
+int
+archive_read_support_format_zip_streamable(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct zip *zip;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_zip");
+
+	zip = (struct zip *)calloc(1, sizeof(*zip));
+	if (zip == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate zip data");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Streamable reader doesn't support mac extensions. */
+	zip->process_mac_extensions = 0;
+
+	/*
+	 * Until enough data has been read, we cannot tell about
+	 * any encrypted entries yet.
+	 */
+	zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+	zip->crc32func = real_crc32;
+
+	r = __archive_read_register_format(a,
+	    zip,
+	    "zip",
+	    archive_read_format_zip_streamable_bid,
+	    archive_read_format_zip_options,
+	    archive_read_format_zip_streamable_read_header,
+	    archive_read_format_zip_read_data,
+	    archive_read_format_zip_read_data_skip_streamable,
+	    NULL,
+	    archive_read_format_zip_cleanup,
+	    archive_read_support_format_zip_capabilities_streamable,
+	    archive_read_format_zip_has_encrypted_entries);
+
+	if (r != ARCHIVE_OK)
+		free(zip);
+	return (ARCHIVE_OK);
+}
+
+/* ------------------------------------------------------------------------ */
+
+/*
+ * Seeking-mode support
+ */
+
+static int
+archive_read_support_format_zip_capabilities_seekable(struct archive_read * a)
+{
+	(void)a; /* UNUSED */
+	return (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA |
+		ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+/*
+ * TODO: This is a performance sink because it forces the read core to
+ * drop buffered data from the start of file, which will then have to
+ * be re-read again if this bidder loses.
+ *
+ * We workaround this a little by passing in the best bid so far so
+ * that later bidders can do nothing if they know they'll never
+ * outbid.  But we can certainly do better...
+ */
+static int
+read_eocd(struct zip *zip, const char *p, int64_t current_offset)
+{
+	/* Sanity-check the EOCD we've found. */
+
+	/* This must be the first volume. */
+	if (archive_le16dec(p + 4) != 0)
+		return 0;
+	/* Central directory must be on this volume. */
+	if (archive_le16dec(p + 4) != archive_le16dec(p + 6))
+		return 0;
+	/* All central directory entries must be on this volume. */
+	if (archive_le16dec(p + 10) != archive_le16dec(p + 8))
+		return 0;
+	/* Central directory can't extend beyond start of EOCD record. */
+	if (archive_le32dec(p + 16) + archive_le32dec(p + 12)
+	    > current_offset)
+		return 0;
+
+	/* Save the central directory location for later use. */
+	zip->central_directory_offset = archive_le32dec(p + 16);
+
+	/* This is just a tiny bit higher than the maximum
+	   returned by the streaming Zip bidder.  This ensures
+	   that the more accurate seeking Zip parser wins
+	   whenever seek is available. */
+	return 32;
+}
+
+/*
+ * Examine Zip64 EOCD locator:  If it's valid, store the information
+ * from it.
+ */
+static int
+read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p)
+{
+	int64_t eocd64_offset;
+	int64_t eocd64_size;
+
+	/* Sanity-check the locator record. */
+
+	/* Central dir must be on first volume. */
+	if (archive_le32dec(p + 4) != 0)
+		return 0;
+	/* Must be only a single volume. */
+	if (archive_le32dec(p + 16) != 1)
+		return 0;
+
+	/* Find the Zip64 EOCD record. */
+	eocd64_offset = archive_le64dec(p + 8);
+	if (__archive_read_seek(a, eocd64_offset, SEEK_SET) < 0)
+		return 0;
+	if ((p = __archive_read_ahead(a, 56, NULL)) == NULL)
+		return 0;
+	/* Make sure we can read all of it. */
+	eocd64_size = archive_le64dec(p + 4) + 12;
+	if (eocd64_size < 56 || eocd64_size > 16384)
+		return 0;
+	if ((p = __archive_read_ahead(a, (size_t)eocd64_size, NULL)) == NULL)
+		return 0;
+
+	/* Sanity-check the EOCD64 */
+	if (archive_le32dec(p + 16) != 0) /* Must be disk #0 */
+		return 0;
+	if (archive_le32dec(p + 20) != 0) /* CD must be on disk #0 */
+		return 0;
+	/* CD can't be split. */
+	if (archive_le64dec(p + 24) != archive_le64dec(p + 32))
+		return 0;
+
+	/* Save the central directory offset for later use. */
+	zip->central_directory_offset = archive_le64dec(p + 48);
+
+	return 32;
+}
+
+static int
+archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid)
+{
+	struct zip *zip = (struct zip *)a->format->data;
+	int64_t file_size, current_offset;
+	const char *p;
+	int i, tail;
+
+	/* If someone has already bid more than 32, then avoid
+	   trashing the look-ahead buffers with a seek. */
+	if (best_bid > 32)
+		return (-1);
+
+	file_size = __archive_read_seek(a, 0, SEEK_END);
+	if (file_size <= 0)
+		return 0;
+
+	/* Search last 16k of file for end-of-central-directory
+	 * record (which starts with PK\005\006) */
+	tail = (int)zipmin(1024 * 16, file_size);
+	current_offset = __archive_read_seek(a, -tail, SEEK_END);
+	if (current_offset < 0)
+		return 0;
+	if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL)
+		return 0;
+	/* Boyer-Moore search backwards from the end, since we want
+	 * to match the last EOCD in the file (there can be more than
+	 * one if there is an uncompressed Zip archive as a member
+	 * within this Zip archive). */
+	for (i = tail - 22; i > 0;) {
+		switch (p[i]) {
+		case 'P':
+			if (memcmp(p + i, "PK\005\006", 4) == 0) {
+				int ret = read_eocd(zip, p + i,
+				    current_offset + i);
+				/* Zip64 EOCD locator precedes
+				 * regular EOCD if present. */
+				if (i >= 20 && memcmp(p + i - 20, "PK\006\007", 4) == 0) {
+					int ret_zip64 = read_zip64_eocd(a, zip, p + i - 20);
+					if (ret_zip64 > ret)
+						ret = ret_zip64;
+				}
+				return (ret);
+			}
+			i -= 4;
+			break;
+		case 'K': i -= 1; break;
+		case 005: i -= 2; break;
+		case 006: i -= 3; break;
+		default: i -= 4; break;
+		}
+	}
+	return 0;
+}
+
+/* The red-black trees are only used in seeking mode to manage
+ * the in-memory copy of the central directory. */
+
+static int
+cmp_node(const struct archive_rb_node *n1, const struct archive_rb_node *n2)
+{
+	const struct zip_entry *e1 = (const struct zip_entry *)n1;
+	const struct zip_entry *e2 = (const struct zip_entry *)n2;
+
+	if (e1->local_header_offset > e2->local_header_offset)
+		return -1;
+	if (e1->local_header_offset < e2->local_header_offset)
+		return 1;
+	return 0;
+}
+
+static int
+cmp_key(const struct archive_rb_node *n, const void *key)
+{
+	/* This function won't be called */
+	(void)n; /* UNUSED */
+	(void)key; /* UNUSED */
+	return 1;
+}
+
+static const struct archive_rb_tree_ops rb_ops = {
+	&cmp_node, &cmp_key
+};
+
+static int
+rsrc_cmp_node(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	const struct zip_entry *e1 = (const struct zip_entry *)n1;
+	const struct zip_entry *e2 = (const struct zip_entry *)n2;
+
+	return (strcmp(e2->rsrcname.s, e1->rsrcname.s));
+}
+
+static int
+rsrc_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+	const struct zip_entry *e = (const struct zip_entry *)n;
+	return (strcmp((const char *)key, e->rsrcname.s));
+}
+
+static const struct archive_rb_tree_ops rb_rsrc_ops = {
+	&rsrc_cmp_node, &rsrc_cmp_key
+};
+
+static const char *
+rsrc_basename(const char *name, size_t name_length)
+{
+	const char *s, *r;
+
+	r = s = name;
+	for (;;) {
+		s = memchr(s, '/', name_length - (s - name));
+		if (s == NULL)
+			break;
+		r = ++s;
+	}
+	return (r);
+}
+
+static void
+expose_parent_dirs(struct zip *zip, const char *name, size_t name_length)
+{
+	struct archive_string str;
+	struct zip_entry *dir;
+	char *s;
+
+	archive_string_init(&str);
+	archive_strncpy(&str, name, name_length);
+	for (;;) {
+		s = strrchr(str.s, '/');
+		if (s == NULL)
+			break;
+		*s = '\0';
+		/* Transfer the parent directory from zip->tree_rsrc RB
+		 * tree to zip->tree RB tree to expose. */
+		dir = (struct zip_entry *)
+		    __archive_rb_tree_find_node(&zip->tree_rsrc, str.s);
+		if (dir == NULL)
+			break;
+		__archive_rb_tree_remove_node(&zip->tree_rsrc, &dir->node);
+		archive_string_free(&dir->rsrcname);
+		__archive_rb_tree_insert_node(&zip->tree, &dir->node);
+	}
+	archive_string_free(&str);
+}
+
+static int
+slurp_central_directory(struct archive_read *a, struct archive_entry* entry,
+    struct zip *zip)
+{
+	ssize_t i;
+	unsigned found;
+	int64_t correction;
+	ssize_t bytes_avail;
+	const char *p;
+
+	/*
+	 * Find the start of the central directory.  The end-of-CD
+	 * record has our starting point, but there are lots of
+	 * Zip archives which have had other data prepended to the
+	 * file, which makes the recorded offsets all too small.
+	 * So we search forward from the specified offset until we
+	 * find the real start of the central directory.  Then we
+	 * know the correction we need to apply to account for leading
+	 * padding.
+	 */
+	if (__archive_read_seek(a, zip->central_directory_offset, SEEK_SET) < 0)
+		return ARCHIVE_FATAL;
+
+	found = 0;
+	while (!found) {
+		if ((p = __archive_read_ahead(a, 20, &bytes_avail)) == NULL)
+			return ARCHIVE_FATAL;
+		for (found = 0, i = 0; !found && i < bytes_avail - 4;) {
+			switch (p[i + 3]) {
+			case 'P': i += 3; break;
+			case 'K': i += 2; break;
+			case 001: i += 1; break;
+			case 002:
+				if (memcmp(p + i, "PK\001\002", 4) == 0) {
+					p += i;
+					found = 1;
+				} else
+					i += 4;
+				break;
+			case 005: i += 1; break;
+			case 006:
+				if (memcmp(p + i, "PK\005\006", 4) == 0) {
+					p += i;
+					found = 1;
+				} else if (memcmp(p + i, "PK\006\006", 4) == 0) {
+					p += i;
+					found = 1;
+				} else
+					i += 1;
+				break;
+			default: i += 4; break;
+			}
+		}
+		__archive_read_consume(a, i);
+	}
+	correction = archive_filter_bytes(&a->archive, 0)
+			- zip->central_directory_offset;
+
+	__archive_rb_tree_init(&zip->tree, &rb_ops);
+	__archive_rb_tree_init(&zip->tree_rsrc, &rb_rsrc_ops);
+
+	zip->central_directory_entries_total = 0;
+	while (1) {
+		struct zip_entry *zip_entry;
+		size_t filename_length, extra_length, comment_length;
+		uint32_t external_attributes;
+		const char *name, *r;
+
+		if ((p = __archive_read_ahead(a, 4, NULL)) == NULL)
+			return ARCHIVE_FATAL;
+		if (memcmp(p, "PK\006\006", 4) == 0
+		    || memcmp(p, "PK\005\006", 4) == 0) {
+			break;
+		} else if (memcmp(p, "PK\001\002", 4) != 0) {
+			archive_set_error(&a->archive,
+			    -1, "Invalid central directory signature");
+			return ARCHIVE_FATAL;
+		}
+		if ((p = __archive_read_ahead(a, 46, NULL)) == NULL)
+			return ARCHIVE_FATAL;
+
+		zip_entry = calloc(1, sizeof(struct zip_entry));
+		if (zip_entry == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+				"Can't allocate zip entry");
+			return ARCHIVE_FATAL;
+		}
+		zip_entry->next = zip->zip_entries;
+		zip_entry->flags |= LA_FROM_CENTRAL_DIRECTORY;
+		zip->zip_entries = zip_entry;
+		zip->central_directory_entries_total++;
+
+		/* version = p[4]; */
+		zip_entry->system = p[5];
+		/* version_required = archive_le16dec(p + 6); */
+		zip_entry->zip_flags = archive_le16dec(p + 8);
+		if (zip_entry->zip_flags
+		      & (ZIP_ENCRYPTED | ZIP_STRONG_ENCRYPTED)){
+			zip->has_encrypted_entries = 1;
+		}
+		zip_entry->compression = (char)archive_le16dec(p + 10);
+		zip_entry->mtime = zip_time(p + 12);
+		zip_entry->crc32 = archive_le32dec(p + 16);
+		if (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+			zip_entry->decdat = p[13];
+		else
+			zip_entry->decdat = p[19];
+		zip_entry->compressed_size = archive_le32dec(p + 20);
+		zip_entry->uncompressed_size = archive_le32dec(p + 24);
+		filename_length = archive_le16dec(p + 28);
+		extra_length = archive_le16dec(p + 30);
+		comment_length = archive_le16dec(p + 32);
+		/* disk_start = archive_le16dec(p + 34);
+		 *   Better be zero.
+		 * internal_attributes = archive_le16dec(p + 36);
+		 *   text bit */
+		external_attributes = archive_le32dec(p + 38);
+		zip_entry->local_header_offset =
+		    archive_le32dec(p + 42) + correction;
+
+		/* If we can't guess the mode, leave it zero here;
+		   when we read the local file header we might get
+		   more information. */
+		if (zip_entry->system == 3) {
+			zip_entry->mode = external_attributes >> 16;
+		} else if (zip_entry->system == 0) {
+			// Interpret MSDOS directory bit
+			if (0x10 == (external_attributes & 0x10)) {
+				zip_entry->mode = AE_IFDIR | 0775;
+			} else {
+				zip_entry->mode = AE_IFREG | 0664;
+			}
+			if (0x01 == (external_attributes & 0x01)) {
+				// Read-only bit; strip write permissions
+				zip_entry->mode &= 0555;
+			}
+		} else {
+			zip_entry->mode = 0;
+		}
+
+		/* We're done with the regular data; get the filename and
+		 * extra data. */
+		__archive_read_consume(a, 46);
+		p = __archive_read_ahead(a, filename_length + extra_length,
+			NULL);
+		if (p == NULL) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated ZIP file header");
+			return ARCHIVE_FATAL;
+		}
+		if (ARCHIVE_OK != process_extra(a, entry, p + filename_length,
+		    extra_length, zip_entry)) {
+			return ARCHIVE_FATAL;
+		}
+
+		/*
+		 * Mac resource fork files are stored under the
+		 * "__MACOSX/" directory, so we should check if
+		 * it is.
+		 */
+		if (!zip->process_mac_extensions) {
+			/* Treat every entry as a regular entry. */
+			__archive_rb_tree_insert_node(&zip->tree,
+			    &zip_entry->node);
+		} else {
+			name = p;
+			r = rsrc_basename(name, filename_length);
+			if (filename_length >= 9 &&
+			    strncmp("__MACOSX/", name, 9) == 0) {
+				/* If this file is not a resource fork nor
+				 * a directory. We should treat it as a non
+				 * resource fork file to expose it. */
+				if (name[filename_length-1] != '/' &&
+				    (r - name < 3 || r[0] != '.' ||
+				     r[1] != '_')) {
+					__archive_rb_tree_insert_node(
+					    &zip->tree, &zip_entry->node);
+					/* Expose its parent directories. */
+					expose_parent_dirs(zip, name,
+					    filename_length);
+				} else {
+					/* This file is a resource fork file or
+					 * a directory. */
+					archive_strncpy(&(zip_entry->rsrcname),
+					     name, filename_length);
+					__archive_rb_tree_insert_node(
+					    &zip->tree_rsrc, &zip_entry->node);
+				}
+			} else {
+				/* Generate resource fork name to find its
+				 * resource file at zip->tree_rsrc. */
+				archive_strcpy(&(zip_entry->rsrcname),
+				    "__MACOSX/");
+				archive_strncat(&(zip_entry->rsrcname),
+				    name, r - name);
+				archive_strcat(&(zip_entry->rsrcname), "._");
+				archive_strncat(&(zip_entry->rsrcname),
+				    name + (r - name),
+				    filename_length - (r - name));
+				/* Register an entry to RB tree to sort it by
+				 * file offset. */
+				__archive_rb_tree_insert_node(&zip->tree,
+				    &zip_entry->node);
+			}
+		}
+
+		/* Skip the comment too ... */
+		__archive_read_consume(a,
+		    filename_length + extra_length + comment_length);
+	}
+
+	return ARCHIVE_OK;
+}
+
+static ssize_t
+zip_get_local_file_header_size(struct archive_read *a, size_t extra)
+{
+	const char *p;
+	ssize_t filename_length, extra_length;
+
+	if ((p = __archive_read_ahead(a, extra + 30, NULL)) == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Truncated ZIP file header");
+		return (ARCHIVE_WARN);
+	}
+	p += extra;
+
+	if (memcmp(p, "PK\003\004", 4) != 0) {
+		archive_set_error(&a->archive, -1, "Damaged Zip archive");
+		return ARCHIVE_WARN;
+	}
+	filename_length = archive_le16dec(p + 26);
+	extra_length = archive_le16dec(p + 28);
+
+	return (30 + filename_length + extra_length);
+}
+
+static int
+zip_read_mac_metadata(struct archive_read *a, struct archive_entry *entry,
+    struct zip_entry *rsrc)
+{
+	struct zip *zip = (struct zip *)a->format->data;
+	unsigned char *metadata, *mp;
+	int64_t offset = archive_filter_bytes(&a->archive, 0);
+	size_t remaining_bytes, metadata_bytes;
+	ssize_t hsize;
+	int ret = ARCHIVE_OK, eof;
+
+	switch(rsrc->compression) {
+	case 0:  /* No compression. */
+		if (rsrc->uncompressed_size != rsrc->compressed_size) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Malformed OS X metadata entry: "
+			    "inconsistent size");
+			return (ARCHIVE_FATAL);
+		}
+#ifdef HAVE_ZLIB_H
+	case 8: /* Deflate compression. */
+#endif
+		break;
+	default: /* Unsupported compression. */
+		/* Return a warning. */
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Unsupported ZIP compression method (%s)",
+		    compression_name(rsrc->compression));
+		/* We can't decompress this entry, but we will
+		 * be able to skip() it and try the next entry. */
+		return (ARCHIVE_WARN);
+	}
+
+	if (rsrc->uncompressed_size > (4 * 1024 * 1024)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Mac metadata is too large: %jd > 4M bytes",
+		    (intmax_t)rsrc->uncompressed_size);
+		return (ARCHIVE_WARN);
+	}
+	if (rsrc->compressed_size > (4 * 1024 * 1024)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Mac metadata is too large: %jd > 4M bytes",
+		    (intmax_t)rsrc->compressed_size);
+		return (ARCHIVE_WARN);
+	}
+
+	metadata = malloc((size_t)rsrc->uncompressed_size);
+	if (metadata == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for Mac metadata");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (offset < rsrc->local_header_offset)
+		__archive_read_consume(a, rsrc->local_header_offset - offset);
+	else if (offset != rsrc->local_header_offset) {
+		__archive_read_seek(a, rsrc->local_header_offset, SEEK_SET);
+	}
+
+	hsize = zip_get_local_file_header_size(a, 0);
+	__archive_read_consume(a, hsize);
+
+	remaining_bytes = (size_t)rsrc->compressed_size;
+	metadata_bytes = (size_t)rsrc->uncompressed_size;
+	mp = metadata;
+	eof = 0;
+	while (!eof && remaining_bytes) {
+		const unsigned char *p;
+		ssize_t bytes_avail;
+		size_t bytes_used;
+
+		p = __archive_read_ahead(a, 1, &bytes_avail);
+		if (p == NULL) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated ZIP file header");
+			ret = ARCHIVE_WARN;
+			goto exit_mac_metadata;
+		}
+		if ((size_t)bytes_avail > remaining_bytes)
+			bytes_avail = remaining_bytes;
+		switch(rsrc->compression) {
+		case 0:  /* No compression. */
+			if ((size_t)bytes_avail > metadata_bytes)
+				bytes_avail = metadata_bytes;
+			memcpy(mp, p, bytes_avail);
+			bytes_used = (size_t)bytes_avail;
+			metadata_bytes -= bytes_used;
+			mp += bytes_used;
+			if (metadata_bytes == 0)
+				eof = 1;
+			break;
+#ifdef HAVE_ZLIB_H
+		case 8: /* Deflate compression. */
+		{
+			int r;
+
+			ret = zip_deflate_init(a, zip);
+			if (ret != ARCHIVE_OK)
+				goto exit_mac_metadata;
+			zip->stream.next_in =
+			    (Bytef *)(uintptr_t)(const void *)p;
+			zip->stream.avail_in = (uInt)bytes_avail;
+			zip->stream.total_in = 0;
+			zip->stream.next_out = mp;
+			zip->stream.avail_out = (uInt)metadata_bytes;
+			zip->stream.total_out = 0;
+
+			r = inflate(&zip->stream, 0);
+			switch (r) {
+			case Z_OK:
+				break;
+			case Z_STREAM_END:
+				eof = 1;
+				break;
+			case Z_MEM_ERROR:
+				archive_set_error(&a->archive, ENOMEM,
+				    "Out of memory for ZIP decompression");
+				ret = ARCHIVE_FATAL;
+				goto exit_mac_metadata;
+			default:
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "ZIP decompression failed (%d)", r);
+				ret = ARCHIVE_FATAL;
+				goto exit_mac_metadata;
+			}
+			bytes_used = zip->stream.total_in;
+			metadata_bytes -= zip->stream.total_out;
+			mp += zip->stream.total_out;
+			break;
+		}
+#endif
+		default:
+			bytes_used = 0;
+			break;
+		}
+		__archive_read_consume(a, bytes_used);
+		remaining_bytes -= bytes_used;
+	}
+	archive_entry_copy_mac_metadata(entry, metadata,
+	    (size_t)rsrc->uncompressed_size - metadata_bytes);
+
+exit_mac_metadata:
+	__archive_read_seek(a, offset, SEEK_SET);
+	zip->decompress_init = 0;
+	free(metadata);
+	return (ret);
+}
+
+static int
+archive_read_format_zip_seekable_read_header(struct archive_read *a,
+	struct archive_entry *entry)
+{
+	struct zip *zip = (struct zip *)a->format->data;
+	struct zip_entry *rsrc;
+	int64_t offset;
+	int r, ret = ARCHIVE_OK;
+
+	/*
+	 * It should be sufficient to call archive_read_next_header() for
+	 * a reader to determine if an entry is encrypted or not. If the
+	 * encryption of an entry is only detectable when calling
+	 * archive_read_data(), so be it. We'll do the same check there
+	 * as well.
+	 */
+	if (zip->has_encrypted_entries ==
+			ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW)
+		zip->has_encrypted_entries = 0;
+
+	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+	if (a->archive.archive_format_name == NULL)
+		a->archive.archive_format_name = "ZIP";
+
+	if (zip->zip_entries == NULL) {
+		r = slurp_central_directory(a, entry, zip);
+		if (r != ARCHIVE_OK)
+			return r;
+		/* Get first entry whose local header offset is lower than
+		 * other entries in the archive file. */
+		zip->entry =
+		    (struct zip_entry *)ARCHIVE_RB_TREE_MIN(&zip->tree);
+	} else if (zip->entry != NULL) {
+		/* Get next entry in local header offset order. */
+		zip->entry = (struct zip_entry *)__archive_rb_tree_iterate(
+		    &zip->tree, &zip->entry->node, ARCHIVE_RB_DIR_RIGHT);
+	}
+
+	if (zip->entry == NULL)
+		return ARCHIVE_EOF;
+
+	if (zip->entry->rsrcname.s)
+		rsrc = (struct zip_entry *)__archive_rb_tree_find_node(
+		    &zip->tree_rsrc, zip->entry->rsrcname.s);
+	else
+		rsrc = NULL;
+
+	if (zip->cctx_valid)
+		archive_decrypto_aes_ctr_release(&zip->cctx);
+	if (zip->hctx_valid)
+		archive_hmac_sha1_cleanup(&zip->hctx);
+	zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
+	__archive_read_reset_passphrase(a);
+
+	/* File entries are sorted by the header offset, we should mostly
+	 * use __archive_read_consume to advance a read point to avoid
+	 * redundant data reading.  */
+	offset = archive_filter_bytes(&a->archive, 0);
+	if (offset < zip->entry->local_header_offset)
+		__archive_read_consume(a,
+		    zip->entry->local_header_offset - offset);
+	else if (offset != zip->entry->local_header_offset) {
+		__archive_read_seek(a, zip->entry->local_header_offset,
+		    SEEK_SET);
+	}
+	zip->unconsumed = 0;
+	r = zip_read_local_file_header(a, entry, zip);
+	if (r != ARCHIVE_OK)
+		return r;
+	if (rsrc) {
+		int ret2 = zip_read_mac_metadata(a, entry, rsrc);
+		if (ret2 < ret)
+			ret = ret2;
+	}
+	return (ret);
+}
+
+/*
+ * We're going to seek for the next header anyway, so we don't
+ * need to bother doing anything here.
+ */
+static int
+archive_read_format_zip_read_data_skip_seekable(struct archive_read *a)
+{
+	struct zip *zip;
+	zip = (struct zip *)(a->format->data);
+
+	zip->unconsumed = 0;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_read_support_format_zip_seekable(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+	struct zip *zip;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_format_zip_seekable");
+
+	zip = (struct zip *)calloc(1, sizeof(*zip));
+	if (zip == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate zip data");
+		return (ARCHIVE_FATAL);
+	}
+
+#ifdef HAVE_COPYFILE_H
+	/* Set this by default on Mac OS. */
+	zip->process_mac_extensions = 1;
+#endif
+
+	/*
+	 * Until enough data has been read, we cannot tell about
+	 * any encrypted entries yet.
+	 */
+	zip->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+	zip->crc32func = real_crc32;
+
+	r = __archive_read_register_format(a,
+	    zip,
+	    "zip",
+	    archive_read_format_zip_seekable_bid,
+	    archive_read_format_zip_options,
+	    archive_read_format_zip_seekable_read_header,
+	    archive_read_format_zip_read_data,
+	    archive_read_format_zip_read_data_skip_seekable,
+	    NULL,
+	    archive_read_format_zip_cleanup,
+	    archive_read_support_format_zip_capabilities_seekable,
+	    archive_read_format_zip_has_encrypted_entries);
+
+	if (r != ARCHIVE_OK)
+		free(zip);
+	return (ARCHIVE_OK);
+}
+
+/*# vim:set noet:*/
diff --git a/c/archive_string.c b/c/archive_string.c
new file mode 100644
--- /dev/null
+++ b/c/archive_string.c
@@ -0,0 +1,4224 @@
+/*-
+ * Copyright (c) 2003-2011 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_string.c 201095 2009-12-28 02:33:22Z kientzle $");
+
+/*
+ * Basic resizable string support, to simplify manipulating arbitrary-sized
+ * strings while minimizing heap activity.
+ *
+ * In particular, the buffer used by a string object is only grown, it
+ * never shrinks, so you can clear and reuse the same string object
+ * without incurring additional memory allocations.
+ */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_ICONV_H
+#include <iconv.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#ifdef HAVE_LOCALCHARSET_H
+#include <localcharset.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windows.h>
+#include <locale.h>
+#endif
+
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_string_composition.h"
+
+#if !defined(HAVE_WMEMCPY) && !defined(wmemcpy)
+#define wmemcpy(a,b,i)  (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+#if !defined(HAVE_WMEMMOVE) && !defined(wmemmove)
+#define wmemmove(a,b,i)  (wchar_t *)memmove((a), (b), (i) * sizeof(wchar_t))
+#endif
+
+#undef max
+#define max(a, b)       ((a)>(b)?(a):(b))
+
+struct archive_string_conv {
+	struct archive_string_conv	*next;
+	char				*from_charset;
+	char				*to_charset;
+	unsigned			 from_cp;
+	unsigned			 to_cp;
+	/* Set 1 if from_charset and to_charset are the same. */
+	int				 same;
+	int				 flag;
+#define SCONV_TO_CHARSET	1	/* MBS is being converted to specified
+					 * charset. */
+#define SCONV_FROM_CHARSET	(1<<1)	/* MBS is being converted from
+					 * specified charset. */
+#define SCONV_BEST_EFFORT 	(1<<2)	/* Copy at least ASCII code. */
+#define SCONV_WIN_CP	 	(1<<3)	/* Use Windows API for converting
+					 * MBS. */
+#define SCONV_UTF8_LIBARCHIVE_2 (1<<4)	/* Incorrect UTF-8 made by libarchive
+					 * 2.x in the wrong assumption. */
+#define SCONV_NORMALIZATION_C	(1<<6)	/* Need normalization to be Form C.
+					 * Before UTF-8 characters are actually
+					 * processed. */
+#define SCONV_NORMALIZATION_D	(1<<7)	/* Need normalization to be Form D.
+					 * Before UTF-8 characters are actually
+					 * processed.
+					 * Currently this only for MAC OS X. */
+#define SCONV_TO_UTF8		(1<<8)	/* "to charset" side is UTF-8. */
+#define SCONV_FROM_UTF8		(1<<9)	/* "from charset" side is UTF-8. */
+#define SCONV_TO_UTF16BE 	(1<<10)	/* "to charset" side is UTF-16BE. */
+#define SCONV_FROM_UTF16BE 	(1<<11)	/* "from charset" side is UTF-16BE. */
+#define SCONV_TO_UTF16LE 	(1<<12)	/* "to charset" side is UTF-16LE. */
+#define SCONV_FROM_UTF16LE 	(1<<13)	/* "from charset" side is UTF-16LE. */
+#define SCONV_TO_UTF16		(SCONV_TO_UTF16BE | SCONV_TO_UTF16LE)
+#define SCONV_FROM_UTF16	(SCONV_FROM_UTF16BE | SCONV_FROM_UTF16LE)
+
+#if HAVE_ICONV
+	iconv_t				 cd;
+	iconv_t				 cd_w;/* Use at archive_mstring on
+				 	       * Windows. */
+#endif
+	/* A temporary buffer for normalization. */
+	struct archive_string		 utftmp;
+	int (*converter[2])(struct archive_string *, const void *, size_t,
+	    struct archive_string_conv *);
+	int				 nconverter;
+};
+
+#define CP_C_LOCALE	0	/* "C" locale only for this file. */
+#define CP_UTF16LE	1200
+#define CP_UTF16BE	1201
+
+#define IS_HIGH_SURROGATE_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDBFF)
+#define IS_LOW_SURROGATE_LA(uc)	 ((uc) >= 0xDC00 && (uc) <= 0xDFFF)
+#define IS_SURROGATE_PAIR_LA(uc) ((uc) >= 0xD800 && (uc) <= 0xDFFF)
+#define UNICODE_MAX		0x10FFFF
+#define UNICODE_R_CHAR		0xFFFD	/* Replacement character. */
+/* Set U+FFFD(Replacement character) in UTF-8. */
+static const char utf8_replacement_char[] = {0xef, 0xbf, 0xbd};
+
+static struct archive_string_conv *find_sconv_object(struct archive *,
+	const char *, const char *);
+static void add_sconv_object(struct archive *, struct archive_string_conv *);
+static struct archive_string_conv *create_sconv_object(const char *,
+	const char *, unsigned, int);
+static void free_sconv_object(struct archive_string_conv *);
+static struct archive_string_conv *get_sconv_object(struct archive *,
+	const char *, const char *, int);
+static unsigned make_codepage_from_charset(const char *);
+static unsigned get_current_codepage(void);
+static unsigned get_current_oemcp(void);
+static size_t mbsnbytes(const void *, size_t);
+static size_t utf16nbytes(const void *, size_t);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+static int archive_wstring_append_from_mbs_in_codepage(
+    struct archive_wstring *, const char *, size_t,
+    struct archive_string_conv *);
+static int archive_string_append_from_wcs_in_codepage(struct archive_string *,
+    const wchar_t *, size_t, struct archive_string_conv *);
+static int is_big_endian(void);
+static int strncat_in_codepage(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int win_strncat_from_utf16be(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int win_strncat_from_utf16le(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int win_strncat_to_utf16be(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int win_strncat_to_utf16le(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+#endif
+static int best_effort_strncat_from_utf16be(struct archive_string *,
+    const void *, size_t, struct archive_string_conv *);
+static int best_effort_strncat_from_utf16le(struct archive_string *,
+    const void *, size_t, struct archive_string_conv *);
+static int best_effort_strncat_to_utf16be(struct archive_string *,
+    const void *, size_t, struct archive_string_conv *);
+static int best_effort_strncat_to_utf16le(struct archive_string *,
+    const void *, size_t, struct archive_string_conv *);
+#if defined(HAVE_ICONV)
+static int iconv_strncat_in_locale(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+#endif
+static int best_effort_strncat_in_locale(struct archive_string *,
+    const void *, size_t, struct archive_string_conv *);
+static int _utf8_to_unicode(uint32_t *, const char *, size_t);
+static int utf8_to_unicode(uint32_t *, const char *, size_t);
+static inline uint32_t combine_surrogate_pair(uint32_t, uint32_t);
+static int cesu8_to_unicode(uint32_t *, const char *, size_t);
+static size_t unicode_to_utf8(char *, size_t, uint32_t);
+static int utf16_to_unicode(uint32_t *, const char *, size_t, int);
+static size_t unicode_to_utf16be(char *, size_t, uint32_t);
+static size_t unicode_to_utf16le(char *, size_t, uint32_t);
+static int strncat_from_utf8_libarchive2(struct archive_string *,
+    const void *, size_t, struct archive_string_conv *);
+static int strncat_from_utf8_to_utf8(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int archive_string_normalize_C(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int archive_string_normalize_D(struct archive_string *, const void *,
+    size_t, struct archive_string_conv *);
+static int archive_string_append_unicode(struct archive_string *,
+    const void *, size_t, struct archive_string_conv *);
+
+static struct archive_string *
+archive_string_append(struct archive_string *as, const char *p, size_t s)
+{
+	if (archive_string_ensure(as, as->length + s + 1) == NULL)
+		return (NULL);
+	if (s)
+		memmove(as->s + as->length, p, s);
+	as->length += s;
+	as->s[as->length] = 0;
+	return (as);
+}
+
+static struct archive_wstring *
+archive_wstring_append(struct archive_wstring *as, const wchar_t *p, size_t s)
+{
+	if (archive_wstring_ensure(as, as->length + s + 1) == NULL)
+		return (NULL);
+	if (s)
+		wmemmove(as->s + as->length, p, s);
+	as->length += s;
+	as->s[as->length] = 0;
+	return (as);
+}
+
+struct archive_string *
+archive_array_append(struct archive_string *as, const char *p, size_t s)
+{
+	return archive_string_append(as, p, s);
+}
+
+void
+archive_string_concat(struct archive_string *dest, struct archive_string *src)
+{
+	if (archive_string_append(dest, src->s, src->length) == NULL)
+		__archive_errx(1, "Out of memory");
+}
+
+void
+archive_wstring_concat(struct archive_wstring *dest,
+    struct archive_wstring *src)
+{
+	if (archive_wstring_append(dest, src->s, src->length) == NULL)
+		__archive_errx(1, "Out of memory");
+}
+
+void
+archive_string_free(struct archive_string *as)
+{
+	as->length = 0;
+	as->buffer_length = 0;
+	free(as->s);
+	as->s = NULL;
+}
+
+void
+archive_wstring_free(struct archive_wstring *as)
+{
+	as->length = 0;
+	as->buffer_length = 0;
+	free(as->s);
+	as->s = NULL;
+}
+
+struct archive_wstring *
+archive_wstring_ensure(struct archive_wstring *as, size_t s)
+{
+	return (struct archive_wstring *)
+		archive_string_ensure((struct archive_string *)as,
+					s * sizeof(wchar_t));
+}
+
+/* Returns NULL on any allocation failure. */
+struct archive_string *
+archive_string_ensure(struct archive_string *as, size_t s)
+{
+	char *p;
+	size_t new_length;
+
+	/* If buffer is already big enough, don't reallocate. */
+	if (as->s && (s <= as->buffer_length))
+		return (as);
+
+	/*
+	 * Growing the buffer at least exponentially ensures that
+	 * append operations are always linear in the number of
+	 * characters appended.  Using a smaller growth rate for
+	 * larger buffers reduces memory waste somewhat at the cost of
+	 * a larger constant factor.
+	 */
+	if (as->buffer_length < 32)
+		/* Start with a minimum 32-character buffer. */
+		new_length = 32;
+	else if (as->buffer_length < 8192)
+		/* Buffers under 8k are doubled for speed. */
+		new_length = as->buffer_length + as->buffer_length;
+	else {
+		/* Buffers 8k and over grow by at least 25% each time. */
+		new_length = as->buffer_length + as->buffer_length / 4;
+		/* Be safe: If size wraps, fail. */
+		if (new_length < as->buffer_length) {
+			/* On failure, wipe the string and return NULL. */
+			archive_string_free(as);
+			errno = ENOMEM;/* Make sure errno has ENOMEM. */
+			return (NULL);
+		}
+	}
+	/*
+	 * The computation above is a lower limit to how much we'll
+	 * grow the buffer.  In any case, we have to grow it enough to
+	 * hold the request.
+	 */
+	if (new_length < s)
+		new_length = s;
+	/* Now we can reallocate the buffer. */
+	p = (char *)realloc(as->s, new_length);
+	if (p == NULL) {
+		/* On failure, wipe the string and return NULL. */
+		archive_string_free(as);
+		errno = ENOMEM;/* Make sure errno has ENOMEM. */
+		return (NULL);
+	}
+
+	as->s = p;
+	as->buffer_length = new_length;
+	return (as);
+}
+
+/*
+ * TODO: See if there's a way to avoid scanning
+ * the source string twice.  Then test to see
+ * if it actually helps (remember that we're almost
+ * always called with pretty short arguments, so
+ * such an optimization might not help).
+ */
+struct archive_string *
+archive_strncat(struct archive_string *as, const void *_p, size_t n)
+{
+	size_t s;
+	const char *p, *pp;
+
+	p = (const char *)_p;
+
+	/* Like strlen(p), except won't examine positions beyond p[n]. */
+	s = 0;
+	pp = p;
+	while (s < n && *pp) {
+		pp++;
+		s++;
+	}
+	if ((as = archive_string_append(as, p, s)) == NULL)
+		__archive_errx(1, "Out of memory");
+	return (as);
+}
+
+struct archive_wstring *
+archive_wstrncat(struct archive_wstring *as, const wchar_t *p, size_t n)
+{
+	size_t s;
+	const wchar_t *pp;
+
+	/* Like strlen(p), except won't examine positions beyond p[n]. */
+	s = 0;
+	pp = p;
+	while (s < n && *pp) {
+		pp++;
+		s++;
+	}
+	if ((as = archive_wstring_append(as, p, s)) == NULL)
+		__archive_errx(1, "Out of memory");
+	return (as);
+}
+
+struct archive_string *
+archive_strcat(struct archive_string *as, const void *p)
+{
+	/* strcat is just strncat without an effective limit. 
+	 * Assert that we'll never get called with a source
+	 * string over 16MB.
+	 * TODO: Review all uses of strcat in the source
+	 * and try to replace them with strncat().
+	 */
+	return archive_strncat(as, p, 0x1000000);
+}
+
+struct archive_wstring *
+archive_wstrcat(struct archive_wstring *as, const wchar_t *p)
+{
+	/* Ditto. */
+	return archive_wstrncat(as, p, 0x1000000);
+}
+
+struct archive_string *
+archive_strappend_char(struct archive_string *as, char c)
+{
+	if ((as = archive_string_append(as, &c, 1)) == NULL)
+		__archive_errx(1, "Out of memory");
+	return (as);
+}
+
+struct archive_wstring *
+archive_wstrappend_wchar(struct archive_wstring *as, wchar_t c)
+{
+	if ((as = archive_wstring_append(as, &c, 1)) == NULL)
+		__archive_errx(1, "Out of memory");
+	return (as);
+}
+
+/*
+ * Get the "current character set" name to use with iconv.
+ * On FreeBSD, the empty character set name "" chooses
+ * the correct character encoding for the current locale,
+ * so this isn't necessary.
+ * But iconv on Mac OS 10.6 doesn't seem to handle this correctly;
+ * on that system, we have to explicitly call nl_langinfo()
+ * to get the right name.  Not sure about other platforms.
+ *
+ * NOTE: GNU libiconv does not recognize the character-set name
+ * which some platform nl_langinfo(CODESET) returns, so we should
+ * use locale_charset() instead of nl_langinfo(CODESET) for GNU libiconv.
+ */
+static const char *
+default_iconv_charset(const char *charset) {
+	if (charset != NULL && charset[0] != '\0')
+		return charset;
+#if HAVE_LOCALE_CHARSET && !defined(__APPLE__)
+	/* locale_charset() is broken on Mac OS */
+	return locale_charset();
+#elif HAVE_NL_LANGINFO
+	return nl_langinfo(CODESET);
+#else
+	return "";
+#endif
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Convert MBS to WCS.
+ * Note: returns -1 if conversion fails.
+ */
+int
+archive_wstring_append_from_mbs(struct archive_wstring *dest,
+    const char *p, size_t len)
+{
+	return archive_wstring_append_from_mbs_in_codepage(dest, p, len, NULL);
+}
+
+static int
+archive_wstring_append_from_mbs_in_codepage(struct archive_wstring *dest,
+    const char *s, size_t length, struct archive_string_conv *sc)
+{
+	int count, ret = 0;
+	UINT from_cp;
+
+	if (sc != NULL)
+		from_cp = sc->from_cp;
+	else
+		from_cp = get_current_codepage();
+
+	if (from_cp == CP_C_LOCALE) {
+		/*
+		 * "C" locale special processing.
+		 */
+		wchar_t *ws;
+		const unsigned char *mp;
+
+		if (NULL == archive_wstring_ensure(dest,
+		    dest->length + length + 1))
+			return (-1);
+
+		ws = dest->s + dest->length;
+		mp = (const unsigned char *)s;
+		count = 0;
+		while (count < (int)length && *mp) {
+			*ws++ = (wchar_t)*mp++;
+			count++;
+		}
+	} else if (sc != NULL &&
+	    (sc->flag & (SCONV_NORMALIZATION_C | SCONV_NORMALIZATION_D))) {
+		/*
+		 * Normalize UTF-8 and UTF-16BE and convert it directly
+		 * to UTF-16 as wchar_t.
+		 */
+		struct archive_string u16;
+		int saved_flag = sc->flag;/* save current flag. */
+
+		if (is_big_endian())
+			sc->flag |= SCONV_TO_UTF16BE;
+		else
+			sc->flag |= SCONV_TO_UTF16LE;
+
+		if (sc->flag & SCONV_FROM_UTF16) {
+			/*
+			 *  UTF-16BE/LE NFD ===> UTF-16 NFC
+			 *  UTF-16BE/LE NFC ===> UTF-16 NFD
+			 */
+			count = (int)utf16nbytes(s, length);
+		} else {
+			/*
+			 *  UTF-8 NFD ===> UTF-16 NFC
+			 *  UTF-8 NFC ===> UTF-16 NFD
+			 */
+			count = (int)mbsnbytes(s, length);
+		}
+		u16.s = (char *)dest->s;
+		u16.length = dest->length << 1;;
+		u16.buffer_length = dest->buffer_length;
+		if (sc->flag & SCONV_NORMALIZATION_C)
+			ret = archive_string_normalize_C(&u16, s, count, sc);
+		else
+			ret = archive_string_normalize_D(&u16, s, count, sc);
+		dest->s = (wchar_t *)u16.s;
+		dest->length = u16.length >> 1;
+		dest->buffer_length = u16.buffer_length;
+		sc->flag = saved_flag;/* restore the saved flag. */
+		return (ret);
+	} else if (sc != NULL && (sc->flag & SCONV_FROM_UTF16)) {
+		count = (int)utf16nbytes(s, length);
+		count >>= 1; /* to be WCS length */
+		/* Allocate memory for WCS. */
+		if (NULL == archive_wstring_ensure(dest,
+		    dest->length + count + 1))
+			return (-1);
+		wmemcpy(dest->s + dest->length, (const wchar_t *)s, count);
+		if ((sc->flag & SCONV_FROM_UTF16BE) && !is_big_endian()) {
+			uint16_t *u16 = (uint16_t *)(dest->s + dest->length);
+			int b;
+			for (b = 0; b < count; b++) {
+				uint16_t val = archive_le16dec(u16+b);
+				archive_be16enc(u16+b, val);
+			}
+		} else if ((sc->flag & SCONV_FROM_UTF16LE) && is_big_endian()) {
+			uint16_t *u16 = (uint16_t *)(dest->s + dest->length);
+			int b;
+			for (b = 0; b < count; b++) {
+				uint16_t val = archive_be16dec(u16+b);
+				archive_le16enc(u16+b, val);
+			}
+		}
+	} else {
+		DWORD mbflag;
+		size_t buffsize;
+
+		if (sc == NULL)
+			mbflag = 0;
+		else if (sc->flag & SCONV_FROM_CHARSET) {
+			/* Do not trust the length which comes from
+			 * an archive file. */
+			length = mbsnbytes(s, length);
+			mbflag = 0;
+		} else
+			mbflag = MB_PRECOMPOSED;
+
+		buffsize = dest->length + length + 1;
+		do {
+			/* Allocate memory for WCS. */
+			if (NULL == archive_wstring_ensure(dest, buffsize))
+				return (-1);
+			/* Convert MBS to WCS. */
+			count = MultiByteToWideChar(from_cp,
+			    mbflag, s, (int)length, dest->s + dest->length,
+			    (int)(dest->buffer_length >> 1) -1);
+			if (count == 0 &&
+			    GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+				/* Expand the WCS buffer. */
+				buffsize = dest->buffer_length << 1;
+				continue;
+			}
+			if (count == 0 && length != 0)
+				ret = -1;
+			break;
+		} while (1);
+	}
+	dest->length += count;
+	dest->s[dest->length] = L'\0';
+	return (ret);
+}
+
+#else
+
+/*
+ * Convert MBS to WCS.
+ * Note: returns -1 if conversion fails.
+ */
+int
+archive_wstring_append_from_mbs(struct archive_wstring *dest,
+    const char *p, size_t len)
+{
+	size_t r;
+	int ret_val = 0;
+	/*
+	 * No single byte will be more than one wide character,
+	 * so this length estimate will always be big enough.
+	 */
+	// size_t wcs_length = len;
+	size_t mbs_length = len;
+	const char *mbs = p;
+	wchar_t *wcs;
+#if HAVE_MBRTOWC
+	mbstate_t shift_state;
+
+	memset(&shift_state, 0, sizeof(shift_state));
+#endif
+	/*
+	 * As we decided to have wcs_length == mbs_length == len
+	 * we can use len here instead of wcs_length
+	 */
+	if (NULL == archive_wstring_ensure(dest, dest->length + len + 1))
+		return (-1);
+	wcs = dest->s + dest->length;
+	/*
+	 * We cannot use mbsrtowcs/mbstowcs here because those may convert
+	 * extra MBS when strlen(p) > len and one wide character consists of
+	 * multi bytes.
+	 */
+	while (*mbs && mbs_length > 0) {
+		/*
+		 * The buffer we allocated is always big enough.
+		 * Keep this code path in a comment if we decide to choose
+		 * smaller wcs_length in the future
+		 */
+/*
+		if (wcs_length == 0) {
+			dest->length = wcs - dest->s;
+			dest->s[dest->length] = L'\0';
+			wcs_length = mbs_length;
+			if (NULL == archive_wstring_ensure(dest,
+			    dest->length + wcs_length + 1))
+				return (-1);
+			wcs = dest->s + dest->length;
+		}
+*/
+#if HAVE_MBRTOWC
+		r = mbrtowc(wcs, mbs, mbs_length, &shift_state);
+#else
+		r = mbtowc(wcs, mbs, mbs_length);
+#endif
+		if (r == (size_t)-1 || r == (size_t)-2) {
+			ret_val = -1;
+			break;
+		}
+		if (r == 0 || r > mbs_length)
+			break;
+		wcs++;
+		// wcs_length--;
+		mbs += r;
+		mbs_length -= r;
+	}
+	dest->length = wcs - dest->s;
+	dest->s[dest->length] = L'\0';
+	return (ret_val);
+}
+
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * WCS ==> MBS.
+ * Note: returns -1 if conversion fails.
+ *
+ * Win32 builds use WideCharToMultiByte from the Windows API.
+ * (Maybe Cygwin should too?  WideCharToMultiByte will know a
+ * lot more about local character encodings than the wcrtomb()
+ * wrapper is going to know.)
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+    const wchar_t *w, size_t len)
+{
+	return archive_string_append_from_wcs_in_codepage(as, w, len, NULL);
+}
+
+static int
+archive_string_append_from_wcs_in_codepage(struct archive_string *as,
+    const wchar_t *ws, size_t len, struct archive_string_conv *sc)
+{
+	BOOL defchar_used, *dp;
+	int count, ret = 0;
+	UINT to_cp;
+	int wslen = (int)len;
+
+	if (sc != NULL)
+		to_cp = sc->to_cp;
+	else
+		to_cp = get_current_codepage();
+
+	if (to_cp == CP_C_LOCALE) {
+		/*
+		 * "C" locale special processing.
+		 */
+		const wchar_t *wp = ws;
+		char *p;
+
+		if (NULL == archive_string_ensure(as,
+		    as->length + wslen +1))
+			return (-1);
+		p = as->s + as->length;
+		count = 0;
+		defchar_used = 0;
+		while (count < wslen && *wp) {
+			if (*wp > 255) {
+				*p++ = '?';
+				wp++;
+				defchar_used = 1;
+			} else
+				*p++ = (char)*wp++;
+			count++;
+		}
+	} else if (sc != NULL && (sc->flag & SCONV_TO_UTF16)) {
+		uint16_t *u16;
+
+		if (NULL ==
+		    archive_string_ensure(as, as->length + len * 2 + 2))
+			return (-1);
+		u16 = (uint16_t *)(as->s + as->length);
+		count = 0;
+		defchar_used = 0;
+		if (sc->flag & SCONV_TO_UTF16BE) {
+			while (count < (int)len && *ws) {
+				archive_be16enc(u16+count, *ws);
+				ws++;
+				count++;
+			}
+		} else {
+			while (count < (int)len && *ws) {
+				archive_le16enc(u16+count, *ws);
+				ws++;
+				count++;
+			}
+		}
+		count <<= 1; /* to be byte size */
+	} else {
+		/* Make sure the MBS buffer has plenty to set. */
+		if (NULL ==
+		    archive_string_ensure(as, as->length + len * 2 + 1))
+			return (-1);
+		do {
+			defchar_used = 0;
+			if (to_cp == CP_UTF8 || sc == NULL)
+				dp = NULL;
+			else
+				dp = &defchar_used;
+			count = WideCharToMultiByte(to_cp, 0, ws, wslen,
+			    as->s + as->length,
+			    (int)as->buffer_length - as->length - 1, NULL, dp);
+			if (count == 0 &&
+			    GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+				/* Expand the MBS buffer and retry. */
+				if (NULL == archive_string_ensure(as,
+					as->buffer_length + len))
+					return (-1);
+				continue;
+			}
+			if (count == 0)
+				ret = -1;
+			break;
+		} while (1);
+	}
+	as->length += count;
+	as->s[as->length] = '\0';
+	return (defchar_used?-1:ret);
+}
+
+#elif defined(HAVE_WCTOMB) || defined(HAVE_WCRTOMB)
+
+/*
+ * Translates a wide character string into current locale character set
+ * and appends to the archive_string.  Note: returns -1 if conversion
+ * fails.
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+    const wchar_t *w, size_t len)
+{
+	/* We cannot use the standard wcstombs() here because it
+	 * cannot tell us how big the output buffer should be.  So
+	 * I've built a loop around wcrtomb() or wctomb() that
+	 * converts a character at a time and resizes the string as
+	 * needed.  We prefer wcrtomb() when it's available because
+	 * it's thread-safe. */
+	int n, ret_val = 0;
+	char *p;
+	char *end;
+#if HAVE_WCRTOMB
+	mbstate_t shift_state;
+
+	memset(&shift_state, 0, sizeof(shift_state));
+#else
+	/* Clear the shift state before starting. */
+	wctomb(NULL, L'\0');
+#endif
+	/*
+	 * Allocate buffer for MBS.
+	 * We need this allocation here since it is possible that
+	 * as->s is still NULL.
+	 */
+	if (archive_string_ensure(as, as->length + len + 1) == NULL)
+		return (-1);
+
+	p = as->s + as->length;
+	end = as->s + as->buffer_length - MB_CUR_MAX -1;
+	while (*w != L'\0' && len > 0) {
+		if (p >= end) {
+			as->length = p - as->s;
+			as->s[as->length] = '\0';
+			/* Re-allocate buffer for MBS. */
+			if (archive_string_ensure(as,
+			    as->length + max(len * 2,
+			    (size_t)MB_CUR_MAX) + 1) == NULL)
+				return (-1);
+			p = as->s + as->length;
+			end = as->s + as->buffer_length - MB_CUR_MAX -1;
+		}
+#if HAVE_WCRTOMB
+		n = wcrtomb(p, *w++, &shift_state);
+#else
+		n = wctomb(p, *w++);
+#endif
+		if (n == -1) {
+			if (errno == EILSEQ) {
+				/* Skip an illegal wide char. */
+				*p++ = '?';
+				ret_val = -1;
+			} else {
+				ret_val = -1;
+				break;
+			}
+		} else
+			p += n;
+		len--;
+	}
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	return (ret_val);
+}
+
+#else /* HAVE_WCTOMB || HAVE_WCRTOMB */
+
+/*
+ * TODO: Test if __STDC_ISO_10646__ is defined.
+ * Non-Windows uses ISO C wcrtomb() or wctomb() to perform the conversion
+ * one character at a time.  If a non-Windows platform doesn't have
+ * either of these, fall back to the built-in UTF8 conversion.
+ */
+int
+archive_string_append_from_wcs(struct archive_string *as,
+    const wchar_t *w, size_t len)
+{
+	(void)as;/* UNUSED */
+	(void)w;/* UNUSED */
+	(void)len;/* UNUSED */
+	errno = ENOSYS;
+	return (-1);
+}
+
+#endif /* HAVE_WCTOMB || HAVE_WCRTOMB */
+
+/*
+ * Find a string conversion object by a pair of 'from' charset name
+ * and 'to' charset name from an archive object.
+ * Return NULL if not found.
+ */
+static struct archive_string_conv *
+find_sconv_object(struct archive *a, const char *fc, const char *tc)
+{
+	struct archive_string_conv *sc; 
+
+	if (a == NULL)
+		return (NULL);
+
+	for (sc = a->sconv; sc != NULL; sc = sc->next) {
+		if (strcmp(sc->from_charset, fc) == 0 &&
+		    strcmp(sc->to_charset, tc) == 0)
+			break;
+	}
+	return (sc);
+}
+
+/*
+ * Register a string object to an archive object.
+ */
+static void
+add_sconv_object(struct archive *a, struct archive_string_conv *sc)
+{
+	struct archive_string_conv **psc; 
+
+	/* Add a new sconv to sconv list. */
+	psc = &(a->sconv);
+	while (*psc != NULL)
+		psc = &((*psc)->next);
+	*psc = sc;
+}
+
+static void
+add_converter(struct archive_string_conv *sc, int (*converter)
+    (struct archive_string *, const void *, size_t,
+     struct archive_string_conv *))
+{
+	if (sc == NULL || sc->nconverter >= 2)
+		__archive_errx(1, "Programming error");
+	sc->converter[sc->nconverter++] = converter;
+}
+
+static void
+setup_converter(struct archive_string_conv *sc)
+{
+
+	/* Reset. */
+	sc->nconverter = 0;
+
+	/*
+	 * Perform special sequence for the incorrect UTF-8 filenames
+	 * made by libarchive2.x.
+	 */
+	if (sc->flag & SCONV_UTF8_LIBARCHIVE_2) {
+		add_converter(sc, strncat_from_utf8_libarchive2);
+		return;
+	}
+
+	/*
+	 * Convert a string to UTF-16BE/LE.
+	 */
+	if (sc->flag & SCONV_TO_UTF16) {
+		/*
+		 * If the current locale is UTF-8, we can translate
+		 * a UTF-8 string into a UTF-16BE string.
+		 */
+		if (sc->flag & SCONV_FROM_UTF8) {
+			add_converter(sc, archive_string_append_unicode);
+			return;
+		}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		if (sc->flag & SCONV_WIN_CP) {
+			if (sc->flag & SCONV_TO_UTF16BE)
+				add_converter(sc, win_strncat_to_utf16be);
+			else
+				add_converter(sc, win_strncat_to_utf16le);
+			return;
+		}
+#endif
+
+#if defined(HAVE_ICONV)
+		if (sc->cd != (iconv_t)-1) {
+			add_converter(sc, iconv_strncat_in_locale);
+			return;
+		}
+#endif
+
+		if (sc->flag & SCONV_BEST_EFFORT) {
+			if (sc->flag & SCONV_TO_UTF16BE)
+				add_converter(sc,
+					best_effort_strncat_to_utf16be);
+			else
+				add_converter(sc,
+					best_effort_strncat_to_utf16le);
+		} else
+			/* Make sure we have no converter. */
+			sc->nconverter = 0;
+		return;
+	}
+
+	/*
+	 * Convert a string from UTF-16BE/LE.
+	 */
+	if (sc->flag & SCONV_FROM_UTF16) {
+		/*
+		 * At least we should normalize a UTF-16BE string.
+		 */
+		if (sc->flag & SCONV_NORMALIZATION_D)
+			add_converter(sc,archive_string_normalize_D);
+		else if (sc->flag & SCONV_NORMALIZATION_C)
+			add_converter(sc, archive_string_normalize_C);
+
+		if (sc->flag & SCONV_TO_UTF8) {
+			/*
+			 * If the current locale is UTF-8, we can translate
+			 * a UTF-16BE/LE string into a UTF-8 string directly.
+			 */
+			if (!(sc->flag &
+			    (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))
+				add_converter(sc,
+				    archive_string_append_unicode);
+			return;
+		}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		if (sc->flag & SCONV_WIN_CP) {
+			if (sc->flag & SCONV_FROM_UTF16BE)
+				add_converter(sc, win_strncat_from_utf16be);
+			else
+				add_converter(sc, win_strncat_from_utf16le);
+			return;
+		}
+#endif
+
+#if defined(HAVE_ICONV)
+		if (sc->cd != (iconv_t)-1) {
+			add_converter(sc, iconv_strncat_in_locale);
+			return;
+		}
+#endif
+
+		if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))
+		    == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16BE))
+			add_converter(sc, best_effort_strncat_from_utf16be);
+		else if ((sc->flag & (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))
+		    == (SCONV_BEST_EFFORT | SCONV_FROM_UTF16LE))
+			add_converter(sc, best_effort_strncat_from_utf16le);
+		else
+			/* Make sure we have no converter. */
+			sc->nconverter = 0;
+		return;
+	}
+
+	if (sc->flag & SCONV_FROM_UTF8) {
+		/*
+		 * At least we should normalize a UTF-8 string.
+		 */
+		if (sc->flag & SCONV_NORMALIZATION_D)
+			add_converter(sc,archive_string_normalize_D);
+		else if (sc->flag & SCONV_NORMALIZATION_C)
+			add_converter(sc, archive_string_normalize_C);
+
+		/*
+		 * Copy UTF-8 string with a check of CESU-8.
+		 * Apparently, iconv does not check surrogate pairs in UTF-8
+		 * when both from-charset and to-charset are UTF-8, and then
+		 * we use our UTF-8 copy code.
+		 */
+		if (sc->flag & SCONV_TO_UTF8) {
+			/*
+			 * If the current locale is UTF-8, we can translate
+			 * a UTF-16BE string into a UTF-8 string directly.
+			 */
+			if (!(sc->flag &
+			    (SCONV_NORMALIZATION_D |SCONV_NORMALIZATION_C)))
+				add_converter(sc, strncat_from_utf8_to_utf8);
+			return;
+		}
+	}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/*
+	 * On Windows we can use Windows API for a string conversion.
+	 */
+	if (sc->flag & SCONV_WIN_CP) {
+		add_converter(sc, strncat_in_codepage);
+		return;
+	}
+#endif
+
+#if HAVE_ICONV
+	if (sc->cd != (iconv_t)-1) {
+		add_converter(sc, iconv_strncat_in_locale);
+		/*
+		 * iconv generally does not support UTF-8-MAC and so
+		 * we have to the output of iconv from NFC to NFD if
+		 * need.
+		 */
+		if ((sc->flag & SCONV_FROM_CHARSET) &&
+		    (sc->flag & SCONV_TO_UTF8)) {
+			if (sc->flag & SCONV_NORMALIZATION_D)
+				add_converter(sc, archive_string_normalize_D);
+		}
+		return;
+	}
+#endif
+
+	/*
+	 * Try conversion in the best effort or no conversion.
+	 */
+	if ((sc->flag & SCONV_BEST_EFFORT) || sc->same)
+		add_converter(sc, best_effort_strncat_in_locale);
+	else
+		/* Make sure we have no converter. */
+		sc->nconverter = 0;
+}
+
+/*
+ * Return canonicalized charset-name but this supports just UTF-8, UTF-16BE
+ * and CP932 which are referenced in create_sconv_object().
+ */
+static const char *
+canonical_charset_name(const char *charset)
+{
+	char cs[16];
+	char *p;
+	const char *s;
+
+	if (charset == NULL || charset[0] == '\0'
+	    || strlen(charset) > 15)
+		return (charset);
+
+	/* Copy name to uppercase. */
+	p = cs;
+	s = charset;
+	while (*s) {
+		char c = *s++;
+		if (c >= 'a' && c <= 'z')
+			c -= 'a' - 'A';
+		*p++ = c;
+	}
+	*p++ = '\0';
+
+	if (strcmp(cs, "UTF-8") == 0 ||
+	    strcmp(cs, "UTF8") == 0)
+		return ("UTF-8");
+	if (strcmp(cs, "UTF-16BE") == 0 ||
+	    strcmp(cs, "UTF16BE") == 0)
+		return ("UTF-16BE");
+	if (strcmp(cs, "UTF-16LE") == 0 ||
+	    strcmp(cs, "UTF16LE") == 0)
+		return ("UTF-16LE");
+	if (strcmp(cs, "CP932") == 0)
+		return ("CP932");
+	return (charset);
+}
+
+/*
+ * Create a string conversion object.
+ */
+static struct archive_string_conv *
+create_sconv_object(const char *fc, const char *tc,
+    unsigned current_codepage, int flag)
+{
+	struct archive_string_conv *sc; 
+
+	sc = calloc(1, sizeof(*sc));
+	if (sc == NULL)
+		return (NULL);
+	sc->next = NULL;
+	sc->from_charset = strdup(fc);
+	if (sc->from_charset == NULL) {
+		free(sc);
+		return (NULL);
+	}
+	sc->to_charset = strdup(tc);
+	if (sc->to_charset == NULL) {
+		free(sc->from_charset);
+		free(sc);
+		return (NULL);
+	}
+	archive_string_init(&sc->utftmp);
+
+	if (flag & SCONV_TO_CHARSET) {
+		/*
+		 * Convert characters from the current locale charset to
+		 * a specified charset.
+		 */
+		sc->from_cp = current_codepage;
+		sc->to_cp = make_codepage_from_charset(tc);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		if (IsValidCodePage(sc->to_cp))
+			flag |= SCONV_WIN_CP;
+#endif
+	} else if (flag & SCONV_FROM_CHARSET) {
+		/*
+		 * Convert characters from a specified charset to
+		 * the current locale charset.
+		 */
+		sc->to_cp = current_codepage;
+		sc->from_cp = make_codepage_from_charset(fc);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		if (IsValidCodePage(sc->from_cp))
+			flag |= SCONV_WIN_CP;
+#endif
+	}
+
+	/*
+	 * Check if "from charset" and "to charset" are the same.
+	 */
+	if (strcmp(fc, tc) == 0 ||
+	    (sc->from_cp != (unsigned)-1 && sc->from_cp == sc->to_cp))
+		sc->same = 1;
+	else
+		sc->same = 0;
+
+	/*
+	 * Mark if "from charset" or "to charset" are UTF-8 or UTF-16BE/LE.
+	 */
+	if (strcmp(tc, "UTF-8") == 0)
+		flag |= SCONV_TO_UTF8;
+	else if (strcmp(tc, "UTF-16BE") == 0)
+		flag |= SCONV_TO_UTF16BE;
+	else if (strcmp(tc, "UTF-16LE") == 0)
+		flag |= SCONV_TO_UTF16LE;
+	if (strcmp(fc, "UTF-8") == 0)
+		flag |= SCONV_FROM_UTF8;
+	else if (strcmp(fc, "UTF-16BE") == 0)
+		flag |= SCONV_FROM_UTF16BE;
+	else if (strcmp(fc, "UTF-16LE") == 0)
+		flag |= SCONV_FROM_UTF16LE;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	if (sc->to_cp == CP_UTF8)
+		flag |= SCONV_TO_UTF8;
+	else if (sc->to_cp == CP_UTF16BE)
+		flag |= SCONV_TO_UTF16BE | SCONV_WIN_CP;
+	else if (sc->to_cp == CP_UTF16LE)
+		flag |= SCONV_TO_UTF16LE | SCONV_WIN_CP;
+	if (sc->from_cp == CP_UTF8)
+		flag |= SCONV_FROM_UTF8;
+	else if (sc->from_cp == CP_UTF16BE)
+		flag |= SCONV_FROM_UTF16BE | SCONV_WIN_CP;
+	else if (sc->from_cp == CP_UTF16LE)
+		flag |= SCONV_FROM_UTF16LE | SCONV_WIN_CP;
+#endif
+
+	/*
+	 * Set a flag for Unicode NFD. Usually iconv cannot correctly
+	 * handle it. So we have to translate NFD characters to NFC ones
+	 * ourselves before iconv handles. Another reason is to prevent
+	 * that the same sight of two filenames, one is NFC and other
+	 * is NFD, would be in its directory.
+	 * On Mac OS X, although its filesystem layer automatically
+	 * convert filenames to NFD, it would be useful for filename
+	 * comparing to find out the same filenames that we normalize
+	 * that to be NFD ourselves.
+	 */
+	if ((flag & SCONV_FROM_CHARSET) &&
+	    (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8))) {
+#if defined(__APPLE__)
+		if (flag & SCONV_TO_UTF8)
+			flag |= SCONV_NORMALIZATION_D;
+		else
+#endif
+			flag |= SCONV_NORMALIZATION_C;
+	}
+#if defined(__APPLE__)
+	/*
+	 * In case writing an archive file, make sure that a filename
+	 * going to be passed to iconv is a Unicode NFC string since
+	 * a filename in HFS Plus filesystem is a Unicode NFD one and
+	 * iconv cannot handle it with "UTF-8" charset. It is simpler
+	 * than a use of "UTF-8-MAC" charset.
+	 */
+	if ((flag & SCONV_TO_CHARSET) &&
+	    (flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
+	    !(flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8)))
+		flag |= SCONV_NORMALIZATION_C;
+	/*
+	 * In case reading an archive file. make sure that a filename
+	 * will be passed to users is a Unicode NFD string in order to
+	 * correctly compare the filename with other one which comes
+	 * from HFS Plus filesystem.
+	 */
+	if ((flag & SCONV_FROM_CHARSET) &&
+	   !(flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
+	    (flag & SCONV_TO_UTF8))
+		flag |= SCONV_NORMALIZATION_D;
+#endif
+
+#if defined(HAVE_ICONV)
+	sc->cd_w = (iconv_t)-1;
+	/*
+	 * Create an iconv object.
+	 */
+	if (((flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) &&
+	    (flag & (SCONV_FROM_UTF8 | SCONV_FROM_UTF16))) ||
+	    (flag & SCONV_WIN_CP)) {
+		/* This case we won't use iconv. */
+		sc->cd = (iconv_t)-1;
+	} else {
+		sc->cd = iconv_open(tc, fc);
+		if (sc->cd == (iconv_t)-1 && (sc->flag & SCONV_BEST_EFFORT)) {
+			/*
+			 * Unfortunately, all of iconv implements do support
+			 * "CP932" character-set, so we should use "SJIS"
+			 * instead if iconv_open failed.
+			 */
+			if (strcmp(tc, "CP932") == 0)
+				sc->cd = iconv_open("SJIS", fc);
+			else if (strcmp(fc, "CP932") == 0)
+				sc->cd = iconv_open(tc, "SJIS");
+		}
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		/*
+		 * archive_mstring on Windows directly convert multi-bytes
+		 * into archive_wstring in order not to depend on locale
+		 * so that you can do a I18N programming. This will be
+		 * used only in archive_mstring_copy_mbs_len_l so far.
+		 */
+		if (flag & SCONV_FROM_CHARSET) {
+			sc->cd_w = iconv_open("UTF-8", fc);
+			if (sc->cd_w == (iconv_t)-1 &&
+			    (sc->flag & SCONV_BEST_EFFORT)) {
+				if (strcmp(fc, "CP932") == 0)
+					sc->cd_w = iconv_open("UTF-8", "SJIS");
+			}
+		}
+#endif /* _WIN32 && !__CYGWIN__ */
+	}
+#endif	/* HAVE_ICONV */
+
+	sc->flag = flag;
+
+	/*
+	 * Set up converters.
+	 */
+	setup_converter(sc);
+
+	return (sc);
+}
+
+/*
+ * Free a string conversion object.
+ */
+static void
+free_sconv_object(struct archive_string_conv *sc)
+{
+	free(sc->from_charset);
+	free(sc->to_charset);
+	archive_string_free(&sc->utftmp);
+#if HAVE_ICONV
+	if (sc->cd != (iconv_t)-1)
+		iconv_close(sc->cd);
+	if (sc->cd_w != (iconv_t)-1)
+		iconv_close(sc->cd_w);
+#endif
+	free(sc);
+}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+static unsigned
+my_atoi(const char *p)
+{
+	unsigned cp;
+
+	cp = 0;
+	while (*p) {
+		if (*p >= '0' && *p <= '9')
+			cp = cp * 10 + (*p - '0');
+		else
+			return (-1);
+		p++;
+	}
+	return (cp);
+}
+
+/*
+ * Translate Charset name (as used by iconv) into CodePage (as used by Windows)
+ * Return -1 if failed.
+ *
+ * Note: This translation code may be insufficient.
+ */
+static struct charset {
+	const char *name;
+	unsigned cp;
+} charsets[] = {
+	/* MUST BE SORTED! */
+	{"ASCII", 1252},
+	{"ASMO-708", 708},
+	{"BIG5", 950},
+	{"CHINESE", 936},
+	{"CP367", 1252},
+	{"CP819", 1252},
+	{"CP1025", 21025},
+	{"DOS-720", 720},
+	{"DOS-862", 862},
+	{"EUC-CN", 51936},
+	{"EUC-JP", 51932},
+	{"EUC-KR", 949},
+	{"EUCCN", 51936},
+	{"EUCJP", 51932},
+	{"EUCKR", 949},
+	{"GB18030", 54936},
+	{"GB2312", 936},
+	{"HEBREW", 1255},
+	{"HZ-GB-2312", 52936},
+	{"IBM273", 20273},
+	{"IBM277", 20277},
+	{"IBM278", 20278},
+	{"IBM280", 20280},
+	{"IBM284", 20284},
+	{"IBM285", 20285},
+	{"IBM290", 20290},
+	{"IBM297", 20297},
+	{"IBM367", 1252},
+	{"IBM420", 20420},
+	{"IBM423", 20423},
+	{"IBM424", 20424},
+	{"IBM819", 1252},
+	{"IBM871", 20871},
+	{"IBM880", 20880},
+	{"IBM905", 20905},
+	{"IBM924", 20924},
+	{"ISO-8859-1", 28591},
+	{"ISO-8859-13", 28603},
+	{"ISO-8859-15", 28605},
+	{"ISO-8859-2", 28592},
+	{"ISO-8859-3", 28593},
+	{"ISO-8859-4", 28594},
+	{"ISO-8859-5", 28595},
+	{"ISO-8859-6", 28596},
+	{"ISO-8859-7", 28597},
+	{"ISO-8859-8", 28598},
+	{"ISO-8859-9", 28599},
+	{"ISO8859-1", 28591},
+	{"ISO8859-13", 28603},
+	{"ISO8859-15", 28605},
+	{"ISO8859-2", 28592},
+	{"ISO8859-3", 28593},
+	{"ISO8859-4", 28594},
+	{"ISO8859-5", 28595},
+	{"ISO8859-6", 28596},
+	{"ISO8859-7", 28597},
+	{"ISO8859-8", 28598},
+	{"ISO8859-9", 28599},
+	{"JOHAB", 1361},
+	{"KOI8-R", 20866},
+	{"KOI8-U", 21866},
+	{"KS_C_5601-1987", 949},
+	{"LATIN1", 1252},
+	{"LATIN2", 28592},
+	{"MACINTOSH", 10000},
+	{"SHIFT-JIS", 932},
+	{"SHIFT_JIS", 932},
+	{"SJIS", 932},
+	{"US", 1252},
+	{"US-ASCII", 1252},
+	{"UTF-16", 1200},
+	{"UTF-16BE", 1201},
+	{"UTF-16LE", 1200},
+	{"UTF-8", CP_UTF8},
+	{"X-EUROPA", 29001},
+	{"X-MAC-ARABIC", 10004},
+	{"X-MAC-CE", 10029},
+	{"X-MAC-CHINESEIMP", 10008},
+	{"X-MAC-CHINESETRAD", 10002},
+	{"X-MAC-CROATIAN", 10082},
+	{"X-MAC-CYRILLIC", 10007},
+	{"X-MAC-GREEK", 10006},
+	{"X-MAC-HEBREW", 10005},
+	{"X-MAC-ICELANDIC", 10079},
+	{"X-MAC-JAPANESE", 10001},
+	{"X-MAC-KOREAN", 10003},
+	{"X-MAC-ROMANIAN", 10010},
+	{"X-MAC-THAI", 10021},
+	{"X-MAC-TURKISH", 10081},
+	{"X-MAC-UKRAINIAN", 10017},
+};
+static unsigned
+make_codepage_from_charset(const char *charset)
+{
+	char cs[16];
+	char *p;
+	unsigned cp;
+	int a, b;
+
+	if (charset == NULL || strlen(charset) > 15)
+		return -1;
+
+	/* Copy name to uppercase. */
+	p = cs;
+	while (*charset) {
+		char c = *charset++;
+		if (c >= 'a' && c <= 'z')
+			c -= 'a' - 'A';
+		*p++ = c;
+	}
+	*p++ = '\0';
+	cp = -1;
+
+	/* Look it up in the table first, so that we can easily
+	 * override CP367, which we map to 1252 instead of 367. */
+	a = 0;
+	b = sizeof(charsets)/sizeof(charsets[0]);
+	while (b > a) {
+		int c = (b + a) / 2;
+		int r = strcmp(charsets[c].name, cs);
+		if (r < 0)
+			a = c + 1;
+		else if (r > 0)
+			b = c;
+		else
+			return charsets[c].cp;
+	}
+
+	/* If it's not in the table, try to parse it. */
+	switch (*cs) {
+	case 'C':
+		if (cs[1] == 'P' && cs[2] >= '0' && cs[2] <= '9') {
+			cp = my_atoi(cs + 2);
+		} else if (strcmp(cs, "CP_ACP") == 0)
+			cp = get_current_codepage();
+		else if (strcmp(cs, "CP_OEMCP") == 0)
+			cp = get_current_oemcp();
+		break;
+	case 'I':
+		if (cs[1] == 'B' && cs[2] == 'M' &&
+		    cs[3] >= '0' && cs[3] <= '9') {
+			cp = my_atoi(cs + 3);
+		}
+		break;
+	case 'W':
+		if (strncmp(cs, "WINDOWS-", 8) == 0) {
+			cp = my_atoi(cs + 8);
+			if (cp != 874 && (cp < 1250 || cp > 1258))
+				cp = -1;/* This may invalid code. */
+		}
+		break;
+	}
+	return (cp);
+}
+
+/*
+ * Return ANSI Code Page of current locale set by setlocale().
+ */
+static unsigned
+get_current_codepage(void)
+{
+	char *locale, *p;
+	unsigned cp;
+
+	locale = setlocale(LC_CTYPE, NULL);
+	if (locale == NULL)
+		return (GetACP());
+	if (locale[0] == 'C' && locale[1] == '\0')
+		return (CP_C_LOCALE);
+	p = strrchr(locale, '.');
+	if (p == NULL)
+		return (GetACP());
+	if (strcmp(p+1, "utf8") == 0)
+		return CP_UTF8;
+	cp = my_atoi(p+1);
+	if ((int)cp <= 0)
+		return (GetACP());
+	return (cp);
+}
+
+/*
+ * Translation table between Locale Name and ACP/OEMCP.
+ */
+static struct {
+	unsigned acp;
+	unsigned ocp;
+	const char *locale;
+} acp_ocp_map[] = {
+	{  950,  950, "Chinese_Taiwan" },
+	{  936,  936, "Chinese_People's Republic of China" },
+	{  950,  950, "Chinese_Taiwan" },
+	{ 1250,  852, "Czech_Czech Republic" },
+	{ 1252,  850, "Danish_Denmark" },
+	{ 1252,  850, "Dutch_Netherlands" },
+	{ 1252,  850, "Dutch_Belgium" },
+	{ 1252,  437, "English_United States" },
+	{ 1252,  850, "English_Australia" },
+	{ 1252,  850, "English_Canada" },
+	{ 1252,  850, "English_New Zealand" },
+	{ 1252,  850, "English_United Kingdom" },
+	{ 1252,  437, "English_United States" },
+	{ 1252,  850, "Finnish_Finland" },
+	{ 1252,  850, "French_France" },
+	{ 1252,  850, "French_Belgium" },
+	{ 1252,  850, "French_Canada" },
+	{ 1252,  850, "French_Switzerland" },
+	{ 1252,  850, "German_Germany" },
+	{ 1252,  850, "German_Austria" },
+	{ 1252,  850, "German_Switzerland" },
+	{ 1253,  737, "Greek_Greece" },
+	{ 1250,  852, "Hungarian_Hungary" },
+	{ 1252,  850, "Icelandic_Iceland" },
+	{ 1252,  850, "Italian_Italy" },
+	{ 1252,  850, "Italian_Switzerland" },
+	{  932,  932, "Japanese_Japan" },
+	{  949,  949, "Korean_Korea" },
+	{ 1252,  850, "Norwegian (BokmOl)_Norway" },
+	{ 1252,  850, "Norwegian (BokmOl)_Norway" },
+	{ 1252,  850, "Norwegian-Nynorsk_Norway" },
+	{ 1250,  852, "Polish_Poland" },
+	{ 1252,  850, "Portuguese_Portugal" },
+	{ 1252,  850, "Portuguese_Brazil" },
+	{ 1251,  866, "Russian_Russia" },
+	{ 1250,  852, "Slovak_Slovakia" },
+	{ 1252,  850, "Spanish_Spain" },
+	{ 1252,  850, "Spanish_Mexico" },
+	{ 1252,  850, "Spanish_Spain" },
+	{ 1252,  850, "Swedish_Sweden" },
+	{ 1254,  857, "Turkish_Turkey" },
+	{ 0, 0, NULL}
+};
+
+/*
+ * Return OEM Code Page of current locale set by setlocale().
+ */
+static unsigned
+get_current_oemcp(void)
+{
+	int i;
+	char *locale, *p;
+	size_t len;
+
+	locale = setlocale(LC_CTYPE, NULL);
+	if (locale == NULL)
+		return (GetOEMCP());
+	if (locale[0] == 'C' && locale[1] == '\0')
+		return (CP_C_LOCALE);
+
+	p = strrchr(locale, '.');
+	if (p == NULL)
+		return (GetOEMCP());
+	len = p - locale;
+	for (i = 0; acp_ocp_map[i].acp; i++) {
+		if (strncmp(acp_ocp_map[i].locale, locale, len) == 0)
+			return (acp_ocp_map[i].ocp);
+	}
+	return (GetOEMCP());
+}
+#else
+
+/*
+ * POSIX platform does not use CodePage.
+ */
+
+static unsigned
+get_current_codepage(void)
+{
+	return (-1);/* Unknown */
+}
+static unsigned
+make_codepage_from_charset(const char *charset)
+{
+	(void)charset; /* UNUSED */
+	return (-1);/* Unknown */
+}
+static unsigned
+get_current_oemcp(void)
+{
+	return (-1);/* Unknown */
+}
+
+#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
+
+/*
+ * Return a string conversion object.
+ */
+static struct archive_string_conv *
+get_sconv_object(struct archive *a, const char *fc, const char *tc, int flag)
+{
+	struct archive_string_conv *sc;
+	unsigned current_codepage;
+
+	/* Check if we have made the sconv object. */
+	sc = find_sconv_object(a, fc, tc);
+	if (sc != NULL)
+		return (sc);
+
+	if (a == NULL)
+		current_codepage = get_current_codepage();
+	else
+		current_codepage = a->current_codepage;
+
+	sc = create_sconv_object(canonical_charset_name(fc),
+	    canonical_charset_name(tc), current_codepage, flag);
+	if (sc == NULL) {
+		if (a != NULL)
+			archive_set_error(a, ENOMEM,
+			    "Could not allocate memory for "
+			    "a string conversion object");
+		return (NULL);
+	}
+
+	/*
+	 * If there is no converter for current string conversion object,
+	 * we cannot handle this conversion.
+	 */
+	if (sc->nconverter == 0) {
+		if (a != NULL) {
+#if HAVE_ICONV
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "iconv_open failed : Cannot handle ``%s''",
+			    (flag & SCONV_TO_CHARSET)?tc:fc);
+#else
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "A character-set conversion not fully supported "
+			    "on this platform");
+#endif
+		}
+		/* Failed; free a sconv object. */
+		free_sconv_object(sc);
+		return (NULL);
+	}
+
+	/*
+	 * Success!
+	 */
+	if (a != NULL)
+		add_sconv_object(a, sc);
+	return (sc);
+}
+
+static const char *
+get_current_charset(struct archive *a)
+{
+	const char *cur_charset;
+
+	if (a == NULL)
+		cur_charset = default_iconv_charset("");
+	else {
+		cur_charset = default_iconv_charset(a->current_code);
+		if (a->current_code == NULL) {
+			a->current_code = strdup(cur_charset);
+			a->current_codepage = get_current_codepage();
+			a->current_oemcp = get_current_oemcp();
+		}
+	}
+	return (cur_charset);
+}
+
+/*
+ * Make and Return a string conversion object.
+ * Return NULL if the platform does not support the specified conversion
+ * and best_effort is 0.
+ * If best_effort is set, A string conversion object must be returned
+ * unless memory allocation for the object fails, but the conversion
+ * might fail when non-ASCII code is found.
+ */
+struct archive_string_conv *
+archive_string_conversion_to_charset(struct archive *a, const char *charset,
+    int best_effort)
+{
+	int flag = SCONV_TO_CHARSET;
+
+	if (best_effort)
+		flag |= SCONV_BEST_EFFORT;
+	return (get_sconv_object(a, get_current_charset(a), charset, flag));
+}
+
+struct archive_string_conv *
+archive_string_conversion_from_charset(struct archive *a, const char *charset,
+    int best_effort)
+{
+	int flag = SCONV_FROM_CHARSET;
+
+	if (best_effort)
+		flag |= SCONV_BEST_EFFORT;
+	return (get_sconv_object(a, charset, get_current_charset(a), flag));
+}
+
+/*
+ * archive_string_default_conversion_*_archive() are provided for Windows
+ * platform because other archiver application use CP_OEMCP for
+ * MultiByteToWideChar() and WideCharToMultiByte() for the filenames
+ * in tar or zip files. But mbstowcs/wcstombs(CRT) usually use CP_ACP
+ * unless you use setlocale(LC_ALL, ".OCP")(specify CP_OEMCP).
+ * So we should make a string conversion between CP_ACP and CP_OEMCP
+ * for compatibility.
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *a)
+{
+	const char *cur_charset = get_current_charset(a);
+	char oemcp[16];
+
+	/* NOTE: a check of cur_charset is unneeded but we need
+	 * that get_current_charset() has been surely called at
+	 * this time whatever C compiler optimized. */
+	if (cur_charset != NULL &&
+	    (a->current_codepage == CP_C_LOCALE ||
+	     a->current_codepage == a->current_oemcp))
+		return (NULL);/* no conversion. */
+
+	_snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
+	/* Make sure a null termination must be set. */
+	oemcp[sizeof(oemcp)-1] = '\0';
+	return (get_sconv_object(a, oemcp, cur_charset,
+	    SCONV_FROM_CHARSET));
+}
+
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *a)
+{
+	const char *cur_charset = get_current_charset(a);
+	char oemcp[16];
+
+	/* NOTE: a check of cur_charset is unneeded but we need
+	 * that get_current_charset() has been surely called at
+	 * this time whatever C compiler optimized. */
+	if (cur_charset != NULL &&
+	    (a->current_codepage == CP_C_LOCALE ||
+	     a->current_codepage == a->current_oemcp))
+		return (NULL);/* no conversion. */
+
+	_snprintf(oemcp, sizeof(oemcp)-1, "CP%d", a->current_oemcp);
+	/* Make sure a null termination must be set. */
+	oemcp[sizeof(oemcp)-1] = '\0';
+	return (get_sconv_object(a, cur_charset, oemcp,
+	    SCONV_TO_CHARSET));
+}
+#else
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *a)
+{
+	(void)a; /* UNUSED */
+	return (NULL);
+}
+
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *a)
+{
+	(void)a; /* UNUSED */
+	return (NULL);
+}
+#endif
+
+/*
+ * Dispose of all character conversion objects in the archive object.
+ */
+void
+archive_string_conversion_free(struct archive *a)
+{
+	struct archive_string_conv *sc; 
+	struct archive_string_conv *sc_next; 
+
+	for (sc = a->sconv; sc != NULL; sc = sc_next) {
+		sc_next = sc->next;
+		free_sconv_object(sc);
+	}
+	a->sconv = NULL;
+	free(a->current_code);
+	a->current_code = NULL;
+}
+
+/*
+ * Return a conversion charset name.
+ */
+const char *
+archive_string_conversion_charset_name(struct archive_string_conv *sc)
+{
+	if (sc->flag & SCONV_TO_CHARSET)
+		return (sc->to_charset);
+	else
+		return (sc->from_charset);
+}
+
+/*
+ * Change the behavior of a string conversion.
+ */
+void
+archive_string_conversion_set_opt(struct archive_string_conv *sc, int opt)
+{
+	switch (opt) {
+	/*
+	 * A filename in UTF-8 was made with libarchive 2.x in a wrong
+	 * assumption that wchar_t was Unicode.
+	 * This option enables simulating the assumption in order to read
+	 * that filename correctly.
+	 */
+	case SCONV_SET_OPT_UTF8_LIBARCHIVE2X:
+#if (defined(_WIN32) && !defined(__CYGWIN__)) \
+	 || defined(__STDC_ISO_10646__) || defined(__APPLE__)
+		/*
+		 * Nothing to do for it since wchar_t on these platforms
+		 * is really Unicode.
+		 */
+		(void)sc; /* UNUSED */
+#else
+		if ((sc->flag & SCONV_UTF8_LIBARCHIVE_2) == 0) {
+			sc->flag |= SCONV_UTF8_LIBARCHIVE_2;
+			/* Set up string converters. */
+			setup_converter(sc);
+		}
+#endif
+		break;
+	case SCONV_SET_OPT_NORMALIZATION_C:
+		if ((sc->flag & SCONV_NORMALIZATION_C) == 0) {
+			sc->flag |= SCONV_NORMALIZATION_C;
+			sc->flag &= ~SCONV_NORMALIZATION_D;
+			/* Set up string converters. */
+			setup_converter(sc);
+		}
+		break;
+	case SCONV_SET_OPT_NORMALIZATION_D:
+#if defined(HAVE_ICONV)
+		/*
+		 * If iconv will take the string, do not change the
+		 * setting of the normalization.
+		 */
+		if (!(sc->flag & SCONV_WIN_CP) &&
+		     (sc->flag & (SCONV_FROM_UTF16 | SCONV_FROM_UTF8)) &&
+		    !(sc->flag & (SCONV_TO_UTF16 | SCONV_TO_UTF8)))
+			break;
+#endif
+		if ((sc->flag & SCONV_NORMALIZATION_D) == 0) {
+			sc->flag |= SCONV_NORMALIZATION_D;
+			sc->flag &= ~SCONV_NORMALIZATION_C;
+			/* Set up string converters. */
+			setup_converter(sc);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ *
+ * Copy one archive_string to another in locale conversion.
+ *
+ *	archive_strncat_l();
+ *	archive_strncpy_l();
+ *
+ */
+
+static size_t
+mbsnbytes(const void *_p, size_t n)
+{
+	size_t s;
+	const char *p, *pp;
+
+	if (_p == NULL)
+		return (0);
+	p = (const char *)_p;
+
+	/* Like strlen(p), except won't examine positions beyond p[n]. */
+	s = 0;
+	pp = p;
+	while (s < n && *pp) {
+		pp++;
+		s++;
+	}
+	return (s);
+}
+
+static size_t
+utf16nbytes(const void *_p, size_t n)
+{
+	size_t s;
+	const char *p, *pp;
+
+	if (_p == NULL)
+		return (0);
+	p = (const char *)_p;
+
+	/* Like strlen(p), except won't examine positions beyond p[n]. */
+	s = 0;
+	pp = p;
+	n >>= 1;
+	while (s < n && (pp[0] || pp[1])) {
+		pp += 2;
+		s++;
+	}
+	return (s<<1);
+}
+
+int
+archive_strncpy_l(struct archive_string *as, const void *_p, size_t n,
+    struct archive_string_conv *sc)
+{
+	as->length = 0;
+	return (archive_strncat_l(as, _p, n, sc));
+}
+
+int
+archive_strncat_l(struct archive_string *as, const void *_p, size_t n,
+    struct archive_string_conv *sc)
+{
+	const void *s;
+	size_t length = 0;
+	int i, r = 0, r2;
+
+	if (_p != NULL && n > 0) {
+		if (sc != NULL && (sc->flag & SCONV_FROM_UTF16))
+			length = utf16nbytes(_p, n);
+		else
+			length = mbsnbytes(_p, n);
+	}
+
+	/* We must allocate memory even if there is no data for conversion
+	 * or copy. This simulates archive_string_append behavior. */
+	if (length == 0) {
+		int tn = 1;
+		if (sc != NULL && (sc->flag & SCONV_TO_UTF16))
+			tn = 2;
+		if (archive_string_ensure(as, as->length + tn) == NULL)
+			return (-1);
+		as->s[as->length] = 0;
+		if (tn == 2)
+			as->s[as->length+1] = 0;
+		return (0);
+	}
+
+	/*
+	 * If sc is NULL, we just make a copy.
+	 */
+	if (sc == NULL) {
+		if (archive_string_append(as, _p, length) == NULL)
+			return (-1);/* No memory */
+		return (0);
+	}
+
+	s = _p;
+	i = 0;
+	if (sc->nconverter > 1) {
+		sc->utftmp.length = 0;
+		r2 = sc->converter[0](&(sc->utftmp), s, length, sc);
+		if (r2 != 0 && errno == ENOMEM)
+			return (r2);
+		if (r > r2)
+			r = r2;
+		s = sc->utftmp.s;
+		length = sc->utftmp.length;
+		++i;
+	}
+	r2 = sc->converter[i](as, s, length, sc);
+	if (r > r2)
+		r = r2;
+	return (r);
+}
+
+#if HAVE_ICONV
+
+/*
+ * Return -1 if conversion fails.
+ */
+static int
+iconv_strncat_in_locale(struct archive_string *as, const void *_p,
+    size_t length, struct archive_string_conv *sc)
+{
+	ICONV_CONST char *itp;
+	size_t remaining;
+	iconv_t cd;
+	char *outp;
+	size_t avail, bs;
+	int return_value = 0; /* success */
+	int to_size, from_size;
+
+	if (sc->flag & SCONV_TO_UTF16)
+		to_size = 2;
+	else
+		to_size = 1;
+	if (sc->flag & SCONV_FROM_UTF16)
+		from_size = 2;
+	else
+		from_size = 1;
+
+	if (archive_string_ensure(as, as->length + length*2+to_size) == NULL)
+		return (-1);
+
+	cd = sc->cd;
+	itp = (char *)(uintptr_t)_p;
+	remaining = length;
+	outp = as->s + as->length;
+	avail = as->buffer_length - as->length - to_size;
+	while (remaining >= (size_t)from_size) {
+		size_t result = iconv(cd, &itp, &remaining, &outp, &avail);
+
+		if (result != (size_t)-1)
+			break; /* Conversion completed. */
+
+		if (errno == EILSEQ || errno == EINVAL) {
+			/*
+		 	 * If an output charset is UTF-8 or UTF-16BE/LE,
+			 * unknown character should be U+FFFD
+			 * (replacement character).
+			 */
+			if (sc->flag & (SCONV_TO_UTF8 | SCONV_TO_UTF16)) {
+				size_t rbytes;
+				if (sc->flag & SCONV_TO_UTF8)
+					rbytes = sizeof(utf8_replacement_char);
+				else
+					rbytes = 2;
+
+				if (avail < rbytes) {
+					as->length = outp - as->s;
+					bs = as->buffer_length +
+					    (remaining * to_size) + rbytes;
+					if (NULL ==
+					    archive_string_ensure(as, bs))
+						return (-1);
+					outp = as->s + as->length;
+					avail = as->buffer_length
+					    - as->length - to_size;
+				}
+				if (sc->flag & SCONV_TO_UTF8)
+					memcpy(outp, utf8_replacement_char, sizeof(utf8_replacement_char));
+				else if (sc->flag & SCONV_TO_UTF16BE)
+					archive_be16enc(outp, UNICODE_R_CHAR);
+				else
+					archive_le16enc(outp, UNICODE_R_CHAR);
+				outp += rbytes;
+				avail -= rbytes;
+			} else {
+				/* Skip the illegal input bytes. */
+				*outp++ = '?';
+				avail--;
+			}
+			itp += from_size;
+			remaining -= from_size;
+			return_value = -1; /* failure */
+		} else {
+			/* E2BIG no output buffer,
+			 * Increase an output buffer.  */
+			as->length = outp - as->s;
+			bs = as->buffer_length + remaining * 2;
+			if (NULL == archive_string_ensure(as, bs))
+				return (-1);
+			outp = as->s + as->length;
+			avail = as->buffer_length - as->length - to_size;
+		}
+	}
+	as->length = outp - as->s;
+	as->s[as->length] = 0;
+	if (to_size == 2)
+		as->s[as->length+1] = 0;
+	return (return_value);
+}
+
+#endif /* HAVE_ICONV */
+
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Translate a string from a some CodePage to an another CodePage by
+ * Windows APIs, and copy the result. Return -1 if conversion fails.
+ */
+static int
+strncat_in_codepage(struct archive_string *as,
+    const void *_p, size_t length, struct archive_string_conv *sc)
+{
+	const char *s = (const char *)_p;
+	struct archive_wstring aws;
+	size_t l;
+	int r, saved_flag;
+
+	archive_string_init(&aws);
+	saved_flag = sc->flag;
+	sc->flag &= ~(SCONV_NORMALIZATION_D | SCONV_NORMALIZATION_C);
+	r = archive_wstring_append_from_mbs_in_codepage(&aws, s, length, sc);
+	sc->flag = saved_flag;
+	if (r != 0) {
+		archive_wstring_free(&aws);
+		if (errno != ENOMEM)
+			archive_string_append(as, s, length);
+		return (-1);
+	}
+
+	l = as->length;
+	r = archive_string_append_from_wcs_in_codepage(
+	    as, aws.s, aws.length, sc);
+	if (r != 0 && errno != ENOMEM && l == as->length)
+		archive_string_append(as, s, length);
+	archive_wstring_free(&aws);
+	return (r);
+}
+
+/*
+ * Test whether MBS ==> WCS is okay.
+ */
+static int
+invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)
+{
+	const char *p = (const char *)_p;
+	unsigned codepage;
+	DWORD mbflag = MB_ERR_INVALID_CHARS;
+
+	if (sc->flag & SCONV_FROM_CHARSET)
+		codepage = sc->to_cp;
+	else
+		codepage = sc->from_cp;
+
+	if (codepage == CP_C_LOCALE)
+		return (0);
+	if (codepage != CP_UTF8)
+		mbflag |= MB_PRECOMPOSED;
+
+	if (MultiByteToWideChar(codepage, mbflag, p, (int)n, NULL, 0) == 0)
+		return (-1); /* Invalid */
+	return (0); /* Okay */
+}
+
+#else
+
+/*
+ * Test whether MBS ==> WCS is okay.
+ */
+static int
+invalid_mbs(const void *_p, size_t n, struct archive_string_conv *sc)
+{
+	const char *p = (const char *)_p;
+	size_t r;
+
+#if HAVE_MBRTOWC
+	mbstate_t shift_state;
+
+	memset(&shift_state, 0, sizeof(shift_state));
+#else
+	/* Clear the shift state before starting. */
+	mbtowc(NULL, NULL, 0);
+#endif
+	while (n) {
+		wchar_t wc;
+
+#if HAVE_MBRTOWC
+		r = mbrtowc(&wc, p, n, &shift_state);
+#else
+		r = mbtowc(&wc, p, n);
+#endif
+		if (r == (size_t)-1 || r == (size_t)-2)
+			return (-1);/* Invalid. */
+		if (r == 0)
+			break;
+		p += r;
+		n -= r;
+	}
+	(void)sc; /* UNUSED */
+	return (0); /* All Okey. */
+}
+
+#endif /* defined(_WIN32) && !defined(__CYGWIN__) */
+
+/*
+ * Basically returns -1 because we cannot make a conversion of charset
+ * without iconv but in some cases this would return 0.
+ * Returns 0 if all copied characters are ASCII.
+ * Returns 0 if both from-locale and to-locale are the same and those
+ * can be WCS with no error.
+ */
+static int
+best_effort_strncat_in_locale(struct archive_string *as, const void *_p,
+    size_t length, struct archive_string_conv *sc)
+{
+	size_t remaining;
+	const uint8_t *itp;
+	int return_value = 0; /* success */
+
+	/*
+	 * If both from-locale and to-locale is the same, this makes a copy.
+	 * And then this checks all copied MBS can be WCS if so returns 0.
+	 */
+	if (sc->same) {
+		if (archive_string_append(as, _p, length) == NULL)
+			return (-1);/* No memory */
+		return (invalid_mbs(_p, length, sc));
+	}
+
+	/*
+	 * If a character is ASCII, this just copies it. If not, this
+	 * assigns '?' character instead but in UTF-8 locale this assigns
+	 * byte sequence 0xEF 0xBD 0xBD, which are code point U+FFFD,
+	 * a Replacement Character in Unicode.
+	 */
+
+	remaining = length;
+	itp = (const uint8_t *)_p;
+	while (*itp && remaining > 0) {
+		if (*itp > 127) {
+			// Non-ASCII: Substitute with suitable replacement
+			if (sc->flag & SCONV_TO_UTF8) {
+				if (archive_string_append(as, utf8_replacement_char, sizeof(utf8_replacement_char)) == NULL) {
+					__archive_errx(1, "Out of memory");
+				}
+			} else {
+				archive_strappend_char(as, '?');
+			}
+			return_value = -1;
+		} else {
+			archive_strappend_char(as, *itp);
+		}
+		++itp;
+	}
+	return (return_value);
+}
+
+
+/*
+ * Unicode conversion functions.
+ *   - UTF-8 <===> UTF-8 in removing surrogate pairs.
+ *   - UTF-8 NFD ===> UTF-8 NFC in removing surrogate pairs.
+ *   - UTF-8 made by libarchive 2.x ===> UTF-8.
+ *   - UTF-16BE <===> UTF-8.
+ *
+ */
+
+/*
+ * Utility to convert a single UTF-8 sequence.
+ *
+ * Usually return used bytes, return used byte in negative value when
+ * a unicode character is replaced with U+FFFD.
+ * See also http://unicode.org/review/pr-121.html Public Review Issue #121
+ * Recommended Practice for Replacement Characters.
+ */
+static int
+_utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	static const char utf8_count[256] = {
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 00 - 0F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 10 - 1F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20 - 2F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30 - 3F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40 - 4F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 50 - 5F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60 - 6F */
+		 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 70 - 7F */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 80 - 8F */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 90 - 9F */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* A0 - AF */
+		 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* B0 - BF */
+		 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* C0 - CF */
+		 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,/* D0 - DF */
+		 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,/* E0 - EF */
+		 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* F0 - FF */
+	};
+	int ch, i;
+	int cnt;
+	uint32_t wc;
+
+	/* Sanity check. */
+	if (n == 0)
+		return (0);
+	/*
+	 * Decode 1-4 bytes depending on the value of the first byte.
+	 */
+	ch = (unsigned char)*s;
+	if (ch == 0)
+		return (0); /* Standard:  return 0 for end-of-string. */
+	cnt = utf8_count[ch];
+
+	/* Invalid sequence or there are not plenty bytes. */
+	if ((int)n < cnt) {
+		cnt = (int)n;
+		for (i = 1; i < cnt; i++) {
+			if ((s[i] & 0xc0) != 0x80) {
+				cnt = i;
+				break;
+			}
+		}
+		goto invalid_sequence;
+	}
+
+	/* Make a Unicode code point from a single UTF-8 sequence. */
+	switch (cnt) {
+	case 1:	/* 1 byte sequence. */
+		*pwc = ch & 0x7f;
+		return (cnt);
+	case 2:	/* 2 bytes sequence. */
+		if ((s[1] & 0xc0) != 0x80) {
+			cnt = 1;
+			goto invalid_sequence;
+		}
+		*pwc = ((ch & 0x1f) << 6) | (s[1] & 0x3f);
+		return (cnt);
+	case 3:	/* 3 bytes sequence. */
+		if ((s[1] & 0xc0) != 0x80) {
+			cnt = 1;
+			goto invalid_sequence;
+		}
+		if ((s[2] & 0xc0) != 0x80) {
+			cnt = 2;
+			goto invalid_sequence;
+		}
+		wc = ((ch & 0x0f) << 12)
+		    | ((s[1] & 0x3f) << 6)
+		    | (s[2] & 0x3f);
+		if (wc < 0x800)
+			goto invalid_sequence;/* Overlong sequence. */
+		break;
+	case 4:	/* 4 bytes sequence. */
+		if ((s[1] & 0xc0) != 0x80) {
+			cnt = 1;
+			goto invalid_sequence;
+		}
+		if ((s[2] & 0xc0) != 0x80) {
+			cnt = 2;
+			goto invalid_sequence;
+		}
+		if ((s[3] & 0xc0) != 0x80) {
+			cnt = 3;
+			goto invalid_sequence;
+		}
+		wc = ((ch & 0x07) << 18)
+		    | ((s[1] & 0x3f) << 12)
+		    | ((s[2] & 0x3f) << 6)
+		    | (s[3] & 0x3f);
+		if (wc < 0x10000)
+			goto invalid_sequence;/* Overlong sequence. */
+		break;
+	default: /* Others are all invalid sequence. */
+		if (ch == 0xc0 || ch == 0xc1)
+			cnt = 2;
+		else if (ch >= 0xf5 && ch <= 0xf7)
+			cnt = 4;
+		else if (ch >= 0xf8 && ch <= 0xfb)
+			cnt = 5;
+		else if (ch == 0xfc || ch == 0xfd)
+			cnt = 6;
+		else
+			cnt = 1;
+		if ((int)n < cnt)
+			cnt = (int)n;
+		for (i = 1; i < cnt; i++) {
+			if ((s[i] & 0xc0) != 0x80) {
+				cnt = i;
+				break;
+			}
+		}
+		goto invalid_sequence;
+	}
+
+	/* The code point larger than 0x10FFFF is not legal
+	 * Unicode values. */
+	if (wc > UNICODE_MAX)
+		goto invalid_sequence;
+	/* Correctly gets a Unicode, returns used bytes. */
+	*pwc = wc;
+	return (cnt);
+invalid_sequence:
+	*pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */
+	return (cnt * -1);
+}
+
+static int
+utf8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	int cnt;
+
+	cnt = _utf8_to_unicode(pwc, s, n);
+	/* Any of Surrogate pair is not legal Unicode values. */
+	if (cnt == 3 && IS_SURROGATE_PAIR_LA(*pwc))
+		return (-3);
+	return (cnt);
+}
+
+static inline uint32_t
+combine_surrogate_pair(uint32_t uc, uint32_t uc2)
+{
+	uc -= 0xD800;
+	uc *= 0x400;
+	uc += uc2 - 0xDC00;
+	uc += 0x10000;
+	return (uc);
+}
+
+/*
+ * Convert a single UTF-8/CESU-8 sequence to a Unicode code point in
+ * removing surrogate pairs.
+ *
+ * CESU-8: The Compatibility Encoding Scheme for UTF-16.
+ *
+ * Usually return used bytes, return used byte in negative value when
+ * a unicode character is replaced with U+FFFD.
+ */
+static int
+cesu8_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	uint32_t wc = 0;
+	int cnt;
+
+	cnt = _utf8_to_unicode(&wc, s, n);
+	if (cnt == 3 && IS_HIGH_SURROGATE_LA(wc)) {
+		uint32_t wc2 = 0;
+		if (n - 3 < 3) {
+			/* Invalid byte sequence. */
+			goto invalid_sequence;
+		}
+		cnt = _utf8_to_unicode(&wc2, s+3, n-3);
+		if (cnt != 3 || !IS_LOW_SURROGATE_LA(wc2)) {
+			/* Invalid byte sequence. */
+			goto invalid_sequence;
+		}
+		wc = combine_surrogate_pair(wc, wc2);
+		cnt = 6;
+	} else if (cnt == 3 && IS_LOW_SURROGATE_LA(wc)) {
+		/* Invalid byte sequence. */
+		goto invalid_sequence;
+	}
+	*pwc = wc;
+	return (cnt);
+invalid_sequence:
+	*pwc = UNICODE_R_CHAR;/* set the Replacement Character instead. */
+	if (cnt > 0)
+		cnt *= -1;
+	return (cnt);
+}
+
+/*
+ * Convert a Unicode code point to a single UTF-8 sequence.
+ *
+ * NOTE:This function does not check if the Unicode is legal or not.
+ * Please you definitely check it before calling this.
+ */
+static size_t
+unicode_to_utf8(char *p, size_t remaining, uint32_t uc)
+{
+	char *_p = p;
+
+	/* Invalid Unicode char maps to Replacement character */
+	if (uc > UNICODE_MAX)
+		uc = UNICODE_R_CHAR;
+	/* Translate code point to UTF8 */
+	if (uc <= 0x7f) {
+		if (remaining == 0)
+			return (0);
+		*p++ = (char)uc;
+	} else if (uc <= 0x7ff) {
+		if (remaining < 2)
+			return (0);
+		*p++ = 0xc0 | ((uc >> 6) & 0x1f);
+		*p++ = 0x80 | (uc & 0x3f);
+	} else if (uc <= 0xffff) {
+		if (remaining < 3)
+			return (0);
+		*p++ = 0xe0 | ((uc >> 12) & 0x0f);
+		*p++ = 0x80 | ((uc >> 6) & 0x3f);
+		*p++ = 0x80 | (uc & 0x3f);
+	} else {
+		if (remaining < 4)
+			return (0);
+		*p++ = 0xf0 | ((uc >> 18) & 0x07);
+		*p++ = 0x80 | ((uc >> 12) & 0x3f);
+		*p++ = 0x80 | ((uc >> 6) & 0x3f);
+		*p++ = 0x80 | (uc & 0x3f);
+	}
+	return (p - _p);
+}
+
+static int
+utf16be_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	return (utf16_to_unicode(pwc, s, n, 1));
+}
+
+static int
+utf16le_to_unicode(uint32_t *pwc, const char *s, size_t n)
+{
+	return (utf16_to_unicode(pwc, s, n, 0));
+}
+
+static int
+utf16_to_unicode(uint32_t *pwc, const char *s, size_t n, int be)
+{
+	const char *utf16 = s;
+	unsigned uc;
+
+	if (n == 0)
+		return (0);
+	if (n == 1) {
+		/* set the Replacement Character instead. */
+		*pwc = UNICODE_R_CHAR;
+		return (-1);
+	}
+
+	if (be)
+		uc = archive_be16dec(utf16);
+	else
+		uc = archive_le16dec(utf16);
+	utf16 += 2;
+		
+	/* If this is a surrogate pair, assemble the full code point.*/
+	if (IS_HIGH_SURROGATE_LA(uc)) {
+		unsigned uc2;
+
+		if (n >= 4) {
+			if (be)
+				uc2 = archive_be16dec(utf16);
+			else
+				uc2 = archive_le16dec(utf16);
+		} else
+			uc2 = 0;
+		if (IS_LOW_SURROGATE_LA(uc2)) {
+			uc = combine_surrogate_pair(uc, uc2);
+			utf16 += 2;
+		} else {
+	 		/* Undescribed code point should be U+FFFD
+		 	* (replacement character). */
+			*pwc = UNICODE_R_CHAR;
+			return (-2);
+		}
+	}
+
+	/*
+	 * Surrogate pair values(0xd800 through 0xdfff) are only
+	 * used by UTF-16, so, after above calculation, the code
+	 * must not be surrogate values, and Unicode has no codes
+	 * larger than 0x10ffff. Thus, those are not legal Unicode
+	 * values.
+	 */
+	if (IS_SURROGATE_PAIR_LA(uc) || uc > UNICODE_MAX) {
+	 	/* Undescribed code point should be U+FFFD
+	 	* (replacement character). */
+		*pwc = UNICODE_R_CHAR;
+		return (((int)(utf16 - s)) * -1);
+	}
+	*pwc = uc;
+	return ((int)(utf16 - s));
+}
+
+static size_t
+unicode_to_utf16be(char *p, size_t remaining, uint32_t uc)
+{
+	char *utf16 = p;
+
+	if (uc > 0xffff) {
+		/* We have a code point that won't fit into a
+		 * wchar_t; convert it to a surrogate pair. */
+		if (remaining < 4)
+			return (0);
+		uc -= 0x10000;
+		archive_be16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);
+		archive_be16enc(utf16+2, (uc & 0x3ff) + 0xDC00);
+		return (4);
+	} else {
+		if (remaining < 2)
+			return (0);
+		archive_be16enc(utf16, uc);
+		return (2);
+	}
+}
+
+static size_t
+unicode_to_utf16le(char *p, size_t remaining, uint32_t uc)
+{
+	char *utf16 = p;
+
+	if (uc > 0xffff) {
+		/* We have a code point that won't fit into a
+		 * wchar_t; convert it to a surrogate pair. */
+		if (remaining < 4)
+			return (0);
+		uc -= 0x10000;
+		archive_le16enc(utf16, ((uc >> 10) & 0x3ff) + 0xD800);
+		archive_le16enc(utf16+2, (uc & 0x3ff) + 0xDC00);
+		return (4);
+	} else {
+		if (remaining < 2)
+			return (0);
+		archive_le16enc(utf16, uc);
+		return (2);
+	}
+}
+
+/*
+ * Copy UTF-8 string in checking surrogate pair.
+ * If any surrogate pair are found, it would be canonicalized.
+ */
+static int
+strncat_from_utf8_to_utf8(struct archive_string *as, const void *_p,
+    size_t len, struct archive_string_conv *sc)
+{
+	const char *s;
+	char *p, *endp;
+	int n, ret = 0;
+
+	(void)sc; /* UNUSED */
+
+	if (archive_string_ensure(as, as->length + len + 1) == NULL)
+		return (-1);
+
+	s = (const char *)_p;
+	p = as->s + as->length;
+	endp = as->s + as->buffer_length -1;
+	do {
+		uint32_t uc;
+		const char *ss = s;
+		size_t w;
+
+		/*
+		 * Forward byte sequence until a conversion of that is needed.
+		 */
+		while ((n = utf8_to_unicode(&uc, s, len)) > 0) {
+			s += n;
+			len -= n;
+		}
+		if (ss < s) {
+			if (p + (s - ss) > endp) {
+				as->length = p - as->s;
+				if (archive_string_ensure(as,
+				    as->buffer_length + len + 1) == NULL)
+					return (-1);
+				p = as->s + as->length;
+				endp = as->s + as->buffer_length -1;
+			}
+
+			memcpy(p, ss, s - ss);
+			p += s - ss;
+		}
+
+		/*
+		 * If n is negative, current byte sequence needs a replacement.
+		 */
+		if (n < 0) {
+			if (n == -3 && IS_SURROGATE_PAIR_LA(uc)) {
+				/* Current byte sequence may be CESU-8. */
+				n = cesu8_to_unicode(&uc, s, len);
+			}
+			if (n < 0) {
+				ret = -1;
+				n *= -1;/* Use a replaced unicode character. */
+			}
+
+			/* Rebuild UTF-8 byte sequence. */
+			while ((w = unicode_to_utf8(p, endp - p, uc)) == 0) {
+				as->length = p - as->s;
+				if (archive_string_ensure(as,
+				    as->buffer_length + len + 1) == NULL)
+					return (-1);
+				p = as->s + as->length;
+				endp = as->s + as->buffer_length -1;
+			}
+			p += w;
+			s += n;
+			len -= n;
+		}
+	} while (n > 0);
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	return (ret);
+}
+
+static int
+archive_string_append_unicode(struct archive_string *as, const void *_p,
+    size_t len, struct archive_string_conv *sc)
+{
+	const char *s;
+	char *p, *endp;
+	uint32_t uc;
+	size_t w;
+	int n, ret = 0, ts, tm;
+	int (*parse)(uint32_t *, const char *, size_t);
+	size_t (*unparse)(char *, size_t, uint32_t);
+
+	if (sc->flag & SCONV_TO_UTF16BE) {
+		unparse = unicode_to_utf16be;
+		ts = 2;
+	} else if (sc->flag & SCONV_TO_UTF16LE) {
+		unparse = unicode_to_utf16le;
+		ts = 2;
+	} else if (sc->flag & SCONV_TO_UTF8) {
+		unparse = unicode_to_utf8;
+		ts = 1;
+	} else {
+		/*
+		 * This case is going to be converted to another
+		 * character-set through iconv.
+		 */
+		if (sc->flag & SCONV_FROM_UTF16BE) {
+			unparse = unicode_to_utf16be;
+			ts = 2;
+		} else if (sc->flag & SCONV_FROM_UTF16LE) {
+			unparse = unicode_to_utf16le;
+			ts = 2;
+		} else {
+			unparse = unicode_to_utf8;
+			ts = 1;
+		}
+	}
+
+	if (sc->flag & SCONV_FROM_UTF16BE) {
+		parse = utf16be_to_unicode;
+		tm = 1;
+	} else if (sc->flag & SCONV_FROM_UTF16LE) {
+		parse = utf16le_to_unicode;
+		tm = 1;
+	} else {
+		parse = cesu8_to_unicode;
+		tm = ts;
+	}
+
+	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+		return (-1);
+
+	s = (const char *)_p;
+	p = as->s + as->length;
+	endp = as->s + as->buffer_length - ts;
+	while ((n = parse(&uc, s, len)) != 0) {
+		if (n < 0) {
+			/* Use a replaced unicode character. */
+			n *= -1;
+			ret = -1;
+		}
+		s += n;
+		len -= n;
+		while ((w = unparse(p, endp - p, uc)) == 0) {
+			/* There is not enough output buffer so
+			 * we have to expand it. */
+			as->length = p - as->s;
+			if (archive_string_ensure(as,
+			    as->buffer_length + len * tm + ts) == NULL)
+				return (-1);
+			p = as->s + as->length;
+			endp = as->s + as->buffer_length - ts;
+		}
+		p += w;
+	}
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	if (ts == 2)
+		as->s[as->length+1] = '\0';
+	return (ret);
+}
+
+/*
+ * Following Constants for Hangul compositions this information comes from
+ * Unicode Standard Annex #15  http://unicode.org/reports/tr15/
+ */
+#define HC_SBASE	0xAC00
+#define HC_LBASE	0x1100
+#define HC_VBASE	0x1161
+#define HC_TBASE	0x11A7
+#define HC_LCOUNT	19
+#define HC_VCOUNT	21
+#define HC_TCOUNT	28
+#define HC_NCOUNT	(HC_VCOUNT * HC_TCOUNT)
+#define HC_SCOUNT	(HC_LCOUNT * HC_NCOUNT)
+
+static uint32_t
+get_nfc(uint32_t uc, uint32_t uc2)
+{
+	int t, b;
+
+	t = 0;
+	b = sizeof(u_composition_table)/sizeof(u_composition_table[0]) -1;
+	while (b >= t) {
+		int m = (t + b) / 2;
+		if (u_composition_table[m].cp1 < uc)
+			t = m + 1;
+		else if (u_composition_table[m].cp1 > uc)
+			b = m - 1;
+		else if (u_composition_table[m].cp2 < uc2)
+			t = m + 1;
+		else if (u_composition_table[m].cp2 > uc2)
+			b = m - 1;
+		else
+			return (u_composition_table[m].nfc);
+	}
+	return (0);
+}
+
+#define FDC_MAX 10	/* The maximum number of Following Decomposable
+			 * Characters. */
+
+/*
+ * Update first code point.
+ */
+#define UPDATE_UC(new_uc)	do {		\
+	uc = new_uc;				\
+	ucptr = NULL;				\
+} while (0)
+
+/*
+ * Replace first code point with second code point.
+ */
+#define REPLACE_UC_WITH_UC2() do {		\
+	uc = uc2;				\
+	ucptr = uc2ptr;				\
+	n = n2;					\
+} while (0)
+
+#define EXPAND_BUFFER() do {			\
+	as->length = p - as->s;			\
+	if (archive_string_ensure(as,		\
+	    as->buffer_length + len * tm + ts) == NULL)\
+		return (-1);			\
+	p = as->s + as->length;			\
+	endp = as->s + as->buffer_length - ts;	\
+} while (0)
+
+#define UNPARSE(p, endp, uc)	do {		\
+	while ((w = unparse(p, (endp) - (p), uc)) == 0) {\
+		EXPAND_BUFFER();		\
+	}					\
+	p += w;					\
+} while (0)
+
+/*
+ * Write first code point.
+ * If the code point has not be changed from its original code,
+ * this just copies it from its original buffer pointer.
+ * If not, this converts it to UTF-8 byte sequence and copies it.
+ */
+#define WRITE_UC()	do {			\
+	if (ucptr) {				\
+		if (p + n > endp)		\
+			EXPAND_BUFFER();	\
+		switch (n) {			\
+		case 4:				\
+			*p++ = *ucptr++;	\
+			/* FALL THROUGH */	\
+		case 3:				\
+			*p++ = *ucptr++;	\
+			/* FALL THROUGH */	\
+		case 2:				\
+			*p++ = *ucptr++;	\
+			/* FALL THROUGH */	\
+		case 1:				\
+			*p++ = *ucptr;		\
+			break;			\
+		}				\
+		ucptr = NULL;			\
+	} else {				\
+		UNPARSE(p, endp, uc);		\
+	}					\
+} while (0)
+
+/*
+ * Collect following decomposable code points.
+ */
+#define COLLECT_CPS(start)	do {		\
+	int _i;					\
+	for (_i = start; _i < FDC_MAX ; _i++) {	\
+		nx = parse(&ucx[_i], s, len);	\
+		if (nx <= 0)			\
+			break;			\
+		cx = CCC(ucx[_i]);		\
+		if (cl >= cx && cl != 228 && cx != 228)\
+			break;			\
+		s += nx;			\
+		len -= nx;			\
+		cl = cx;			\
+		ccx[_i] = cx;			\
+	}					\
+	if (_i >= FDC_MAX) {			\
+		ret = -1;			\
+		ucx_size = FDC_MAX;		\
+	} else					\
+		ucx_size = _i;			\
+} while (0)
+
+/*
+ * Normalize UTF-8/UTF-16BE characters to Form C and copy the result.
+ *
+ * TODO: Convert composition exclusions, which are never converted
+ * from NFC,NFD,NFKC and NFKD, to Form C.
+ */
+static int
+archive_string_normalize_C(struct archive_string *as, const void *_p,
+    size_t len, struct archive_string_conv *sc)
+{
+	const char *s = (const char *)_p;
+	char *p, *endp;
+	uint32_t uc, uc2;
+	size_t w;
+	int always_replace, n, n2, ret = 0, spair, ts, tm;
+	int (*parse)(uint32_t *, const char *, size_t);
+	size_t (*unparse)(char *, size_t, uint32_t);
+
+	always_replace = 1;
+	ts = 1;/* text size. */
+	if (sc->flag & SCONV_TO_UTF16BE) {
+		unparse = unicode_to_utf16be;
+		ts = 2;
+		if (sc->flag & SCONV_FROM_UTF16BE)
+			always_replace = 0;
+	} else if (sc->flag & SCONV_TO_UTF16LE) {
+		unparse = unicode_to_utf16le;
+		ts = 2;
+		if (sc->flag & SCONV_FROM_UTF16LE)
+			always_replace = 0;
+	} else if (sc->flag & SCONV_TO_UTF8) {
+		unparse = unicode_to_utf8;
+		if (sc->flag & SCONV_FROM_UTF8)
+			always_replace = 0;
+	} else {
+		/*
+		 * This case is going to be converted to another
+		 * character-set through iconv.
+		 */
+		always_replace = 0;
+		if (sc->flag & SCONV_FROM_UTF16BE) {
+			unparse = unicode_to_utf16be;
+			ts = 2;
+		} else if (sc->flag & SCONV_FROM_UTF16LE) {
+			unparse = unicode_to_utf16le;
+			ts = 2;
+		} else {
+			unparse = unicode_to_utf8;
+		}
+	}
+
+	if (sc->flag & SCONV_FROM_UTF16BE) {
+		parse = utf16be_to_unicode;
+		tm = 1;
+		spair = 4;/* surrogate pair size in UTF-16. */
+	} else if (sc->flag & SCONV_FROM_UTF16LE) {
+		parse = utf16le_to_unicode;
+		tm = 1;
+		spair = 4;/* surrogate pair size in UTF-16. */
+	} else {
+		parse = cesu8_to_unicode;
+		tm = ts;
+		spair = 6;/* surrogate pair size in UTF-8. */
+	}
+
+	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+		return (-1);
+
+	p = as->s + as->length;
+	endp = as->s + as->buffer_length - ts;
+	while ((n = parse(&uc, s, len)) != 0) {
+		const char *ucptr, *uc2ptr;
+
+		if (n < 0) {
+			/* Use a replaced unicode character. */
+			UNPARSE(p, endp, uc);
+			s += n*-1;
+			len -= n*-1;
+			ret = -1;
+			continue;
+		} else if (n == spair || always_replace)
+			/* uc is converted from a surrogate pair.
+			 * this should be treated as a changed code. */
+			ucptr = NULL;
+		else
+			ucptr = s;
+		s += n;
+		len -= n;
+
+		/* Read second code point. */
+		while ((n2 = parse(&uc2, s, len)) > 0) {
+			uint32_t ucx[FDC_MAX];
+			int ccx[FDC_MAX];
+			int cl, cx, i, nx, ucx_size;
+			int LIndex,SIndex;
+			uint32_t nfc;
+
+			if (n2 == spair || always_replace)
+				/* uc2 is converted from a surrogate pair.
+			 	 * this should be treated as a changed code. */
+				uc2ptr = NULL;
+			else
+				uc2ptr = s;
+			s += n2;
+			len -= n2;
+
+			/*
+			 * If current second code point is out of decomposable
+			 * code points, finding compositions is unneeded.
+			 */
+			if (!IS_DECOMPOSABLE_BLOCK(uc2)) {
+				WRITE_UC();
+				REPLACE_UC_WITH_UC2();
+				continue;
+			}
+
+			/*
+			 * Try to combine current code points.
+			 */
+			/*
+			 * We have to combine Hangul characters according to
+			 * http://uniicode.org/reports/tr15/#Hangul
+			 */
+			if (0 <= (LIndex = uc - HC_LBASE) &&
+			    LIndex < HC_LCOUNT) {
+				/*
+				 * Hangul Composition.
+				 * 1. Two current code points are L and V.
+				 */
+				int VIndex = uc2 - HC_VBASE;
+				if (0 <= VIndex && VIndex < HC_VCOUNT) {
+					/* Make syllable of form LV. */
+					UPDATE_UC(HC_SBASE +
+					    (LIndex * HC_VCOUNT + VIndex) *
+					     HC_TCOUNT);
+				} else {
+					WRITE_UC();
+					REPLACE_UC_WITH_UC2();
+				}
+				continue;
+			} else if (0 <= (SIndex = uc - HC_SBASE) &&
+			    SIndex < HC_SCOUNT && (SIndex % HC_TCOUNT) == 0) {
+				/*
+				 * Hangul Composition.
+				 * 2. Two current code points are LV and T.
+				 */
+				int TIndex = uc2 - HC_TBASE;
+				if (0 < TIndex && TIndex < HC_TCOUNT) {
+					/* Make syllable of form LVT. */
+					UPDATE_UC(uc + TIndex);
+				} else {
+					WRITE_UC();
+					REPLACE_UC_WITH_UC2();
+				}
+				continue;
+			} else if ((nfc = get_nfc(uc, uc2)) != 0) {
+				/* A composition to current code points
+				 * is found. */
+				UPDATE_UC(nfc);
+				continue;
+			} else if ((cl = CCC(uc2)) == 0) {
+				/* Clearly 'uc2' the second code point is not
+				 * a decomposable code. */
+				WRITE_UC();
+				REPLACE_UC_WITH_UC2();
+				continue;
+			}
+
+			/*
+			 * Collect following decomposable code points.
+			 */
+			cx = 0;
+			ucx[0] = uc2;
+			ccx[0] = cl;
+			COLLECT_CPS(1);
+
+			/*
+			 * Find a composed code in the collected code points.
+			 */
+			i = 1;
+			while (i < ucx_size) {
+				int j;
+
+				if ((nfc = get_nfc(uc, ucx[i])) == 0) {
+					i++;
+					continue;
+				}
+
+				/*
+				 * nfc is composed of uc and ucx[i].
+				 */
+				UPDATE_UC(nfc);
+
+				/*
+				 * Remove ucx[i] by shifting
+				 * following code points.
+				 */
+				for (j = i; j+1 < ucx_size; j++) {
+					ucx[j] = ucx[j+1];
+					ccx[j] = ccx[j+1];
+				}
+				ucx_size --;
+
+				/*
+				 * Collect following code points blocked
+				 * by ucx[i] the removed code point.
+				 */
+				if (ucx_size > 0 && i == ucx_size &&
+				    nx > 0 && cx == cl) {
+					cl =  ccx[ucx_size-1];
+					COLLECT_CPS(ucx_size);
+				}
+				/*
+				 * Restart finding a composed code with
+				 * the updated uc from the top of the
+				 * collected code points.
+				 */
+				i = 0;
+			}
+
+			/*
+			 * Apparently the current code points are not
+			 * decomposed characters or already composed.
+			 */
+			WRITE_UC();
+			for (i = 0; i < ucx_size; i++)
+				UNPARSE(p, endp, ucx[i]);
+
+			/*
+			 * Flush out remaining canonical combining characters.
+			 */
+			if (nx > 0 && cx == cl && len > 0) {
+				while ((nx = parse(&ucx[0], s, len))
+				    > 0) {
+					cx = CCC(ucx[0]);
+					if (cl > cx)
+						break;
+					s += nx;
+					len -= nx;
+					cl = cx;
+					UNPARSE(p, endp, ucx[0]);
+				}
+			}
+			break;
+		}
+		if (n2 < 0) {
+			WRITE_UC();
+			/* Use a replaced unicode character. */
+			UNPARSE(p, endp, uc2);
+			s += n2*-1;
+			len -= n2*-1;
+			ret = -1;
+			continue;
+		} else if (n2 == 0) {
+			WRITE_UC();
+			break;
+		}
+	}
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	if (ts == 2)
+		as->s[as->length+1] = '\0';
+	return (ret);
+}
+
+static int
+get_nfd(uint32_t *cp1, uint32_t *cp2, uint32_t uc)
+{
+	int t, b;
+
+	/*
+	 * These are not converted to NFD on Mac OS.
+	 */
+	if ((uc >= 0x2000 && uc <= 0x2FFF) ||
+	    (uc >= 0xF900 && uc <= 0xFAFF) ||
+	    (uc >= 0x2F800 && uc <= 0x2FAFF))
+		return (0);
+	/*
+	 * Those code points are not converted to NFD on Mac OS.
+	 * I do not know the reason because it is undocumented.
+	 *   NFC        NFD
+	 *   1109A  ==> 11099 110BA
+	 *   1109C  ==> 1109B 110BA
+	 *   110AB  ==> 110A5 110BA
+	 */
+	if (uc == 0x1109A || uc == 0x1109C || uc == 0x110AB)
+		return (0);
+
+	t = 0;
+	b = sizeof(u_decomposition_table)/sizeof(u_decomposition_table[0]) -1;
+	while (b >= t) {
+		int m = (t + b) / 2;
+		if (u_decomposition_table[m].nfc < uc)
+			t = m + 1;
+		else if (u_decomposition_table[m].nfc > uc)
+			b = m - 1;
+		else {
+			*cp1 = u_decomposition_table[m].cp1;
+			*cp2 = u_decomposition_table[m].cp2;
+			return (1);
+		}
+	}
+	return (0);
+}
+
+#define REPLACE_UC_WITH(cp) do {		\
+	uc = cp;				\
+	ucptr = NULL;				\
+} while (0)
+
+/*
+ * Normalize UTF-8 characters to Form D and copy the result.
+ */
+static int
+archive_string_normalize_D(struct archive_string *as, const void *_p,
+    size_t len, struct archive_string_conv *sc)
+{
+	const char *s = (const char *)_p;
+	char *p, *endp;
+	uint32_t uc, uc2;
+	size_t w;
+	int always_replace, n, n2, ret = 0, spair, ts, tm;
+	int (*parse)(uint32_t *, const char *, size_t);
+	size_t (*unparse)(char *, size_t, uint32_t);
+
+	always_replace = 1;
+	ts = 1;/* text size. */
+	if (sc->flag & SCONV_TO_UTF16BE) {
+		unparse = unicode_to_utf16be;
+		ts = 2;
+		if (sc->flag & SCONV_FROM_UTF16BE)
+			always_replace = 0;
+	} else if (sc->flag & SCONV_TO_UTF16LE) {
+		unparse = unicode_to_utf16le;
+		ts = 2;
+		if (sc->flag & SCONV_FROM_UTF16LE)
+			always_replace = 0;
+	} else if (sc->flag & SCONV_TO_UTF8) {
+		unparse = unicode_to_utf8;
+		if (sc->flag & SCONV_FROM_UTF8)
+			always_replace = 0;
+	} else {
+		/*
+		 * This case is going to be converted to another
+		 * character-set through iconv.
+		 */
+		always_replace = 0;
+		if (sc->flag & SCONV_FROM_UTF16BE) {
+			unparse = unicode_to_utf16be;
+			ts = 2;
+		} else if (sc->flag & SCONV_FROM_UTF16LE) {
+			unparse = unicode_to_utf16le;
+			ts = 2;
+		} else {
+			unparse = unicode_to_utf8;
+		}
+	}
+
+	if (sc->flag & SCONV_FROM_UTF16BE) {
+		parse = utf16be_to_unicode;
+		tm = 1;
+		spair = 4;/* surrogate pair size in UTF-16. */
+	} else if (sc->flag & SCONV_FROM_UTF16LE) {
+		parse = utf16le_to_unicode;
+		tm = 1;
+		spair = 4;/* surrogate pair size in UTF-16. */
+	} else {
+		parse = cesu8_to_unicode;
+		tm = ts;
+		spair = 6;/* surrogate pair size in UTF-8. */
+	}
+
+	if (archive_string_ensure(as, as->length + len * tm + ts) == NULL)
+		return (-1);
+
+	p = as->s + as->length;
+	endp = as->s + as->buffer_length - ts;
+	while ((n = parse(&uc, s, len)) != 0) {
+		const char *ucptr;
+		uint32_t cp1, cp2;
+		int SIndex;
+		struct {
+			uint32_t uc;
+			int ccc;
+		} fdc[FDC_MAX];
+		int fdi, fdj;
+		int ccc;
+
+check_first_code:
+		if (n < 0) {
+			/* Use a replaced unicode character. */
+			UNPARSE(p, endp, uc);
+			s += n*-1;
+			len -= n*-1;
+			ret = -1;
+			continue;
+		} else if (n == spair || always_replace)
+			/* uc is converted from a surrogate pair.
+			 * this should be treated as a changed code. */
+			ucptr = NULL;
+		else
+			ucptr = s;
+		s += n;
+		len -= n;
+
+		/* Hangul Decomposition. */
+		if ((SIndex = uc - HC_SBASE) >= 0 && SIndex < HC_SCOUNT) {
+			int L = HC_LBASE + SIndex / HC_NCOUNT;
+			int V = HC_VBASE + (SIndex % HC_NCOUNT) / HC_TCOUNT;
+			int T = HC_TBASE + SIndex % HC_TCOUNT;
+
+			REPLACE_UC_WITH(L);
+			WRITE_UC();
+			REPLACE_UC_WITH(V);
+			WRITE_UC();
+			if (T != HC_TBASE) {
+				REPLACE_UC_WITH(T);
+				WRITE_UC();
+			}
+			continue;
+		}
+		if (IS_DECOMPOSABLE_BLOCK(uc) && CCC(uc) != 0) {
+			WRITE_UC();
+			continue;
+		}
+
+		fdi = 0;
+		while (get_nfd(&cp1, &cp2, uc) && fdi < FDC_MAX) {
+			int k;
+
+			for (k = fdi; k > 0; k--)
+				fdc[k] = fdc[k-1];
+			fdc[0].ccc = CCC(cp2);
+			fdc[0].uc = cp2;
+			fdi++;
+			REPLACE_UC_WITH(cp1);
+		}
+
+		/* Read following code points. */
+		while ((n2 = parse(&uc2, s, len)) > 0 &&
+		    (ccc = CCC(uc2)) != 0 && fdi < FDC_MAX) {
+			int j, k;
+
+			s += n2;
+			len -= n2;
+			for (j = 0; j < fdi; j++) {
+				if (fdc[j].ccc > ccc)
+					break;
+			}
+			if (j < fdi) {
+				for (k = fdi; k > j; k--)
+					fdc[k] = fdc[k-1];
+				fdc[j].ccc = ccc;
+				fdc[j].uc = uc2;
+			} else {
+				fdc[fdi].ccc = ccc;
+				fdc[fdi].uc = uc2;
+			}
+			fdi++;
+		}
+
+		WRITE_UC();
+		for (fdj = 0; fdj < fdi; fdj++) {
+			REPLACE_UC_WITH(fdc[fdj].uc);
+			WRITE_UC();
+		}
+
+		if (n2 == 0)
+			break;
+		REPLACE_UC_WITH(uc2);
+		n = n2;
+		goto check_first_code;
+	}
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	if (ts == 2)
+		as->s[as->length+1] = '\0';
+	return (ret);
+}
+
+/*
+ * libarchive 2.x made incorrect UTF-8 strings in the wrong assumption
+ * that WCS is Unicode. It is true for several platforms but some are false.
+ * And then people who did not use UTF-8 locale on the non Unicode WCS
+ * platform and made a tar file with libarchive(mostly bsdtar) 2.x. Those
+ * now cannot get right filename from libarchive 3.x and later since we
+ * fixed the wrong assumption and it is incompatible to older its versions.
+ * So we provide special option, "compat-2x.x", for resolving it.
+ * That option enable the string conversion of libarchive 2.x.
+ *
+ * Translates the wrong UTF-8 string made by libarchive 2.x into current
+ * locale character set and appends to the archive_string.
+ * Note: returns -1 if conversion fails.
+ */
+static int
+strncat_from_utf8_libarchive2(struct archive_string *as,
+    const void *_p, size_t len, struct archive_string_conv *sc)
+{
+	const char *s;
+	int n;
+	char *p;
+	char *end;
+	uint32_t unicode;
+#if HAVE_WCRTOMB
+	mbstate_t shift_state;
+
+	memset(&shift_state, 0, sizeof(shift_state));
+#else
+	/* Clear the shift state before starting. */
+	wctomb(NULL, L'\0');
+#endif
+	(void)sc; /* UNUSED */
+	/*
+	 * Allocate buffer for MBS.
+	 * We need this allocation here since it is possible that
+	 * as->s is still NULL.
+	 */
+	if (archive_string_ensure(as, as->length + len + 1) == NULL)
+		return (-1);
+
+	s = (const char *)_p;
+	p = as->s + as->length;
+	end = as->s + as->buffer_length - MB_CUR_MAX -1;
+	while ((n = _utf8_to_unicode(&unicode, s, len)) != 0) {
+		wchar_t wc;
+
+		if (p >= end) {
+			as->length = p - as->s;
+			/* Re-allocate buffer for MBS. */
+			if (archive_string_ensure(as,
+			    as->length + max(len * 2,
+			    (size_t)MB_CUR_MAX) + 1) == NULL)
+				return (-1);
+			p = as->s + as->length;
+			end = as->s + as->buffer_length - MB_CUR_MAX -1;
+		}
+
+		/*
+		 * As libarchive 2.x, translates the UTF-8 characters into
+		 * wide-characters in the assumption that WCS is Unicode.
+		 */
+		if (n < 0) {
+			n *= -1;
+			wc = L'?';
+		} else
+			wc = (wchar_t)unicode;
+
+		s += n;
+		len -= n;
+		/*
+		 * Translates the wide-character into the current locale MBS.
+		 */
+#if HAVE_WCRTOMB
+		n = (int)wcrtomb(p, wc, &shift_state);
+#else
+		n = (int)wctomb(p, wc);
+#endif
+		if (n == -1)
+			return (-1);
+		p += n;
+	}
+	as->length = p - as->s;
+	as->s[as->length] = '\0';
+	return (0);
+}
+
+
+/*
+ * Conversion functions between current locale dependent MBS and UTF-16BE.
+ *   strncat_from_utf16be() : UTF-16BE --> MBS
+ *   strncat_to_utf16be()   : MBS --> UTF16BE
+ */
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Convert a UTF-16BE/LE string to current locale and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+win_strncat_from_utf16(struct archive_string *as, const void *_p, size_t bytes,
+    struct archive_string_conv *sc, int be)
+{
+	struct archive_string tmp;
+	const char *u16;
+	int ll;
+	BOOL defchar;
+	char *mbs;
+	size_t mbs_size, b;
+	int ret = 0;
+
+	bytes &= ~1;
+	if (archive_string_ensure(as, as->length + bytes +1) == NULL)
+		return (-1);
+
+	mbs = as->s + as->length;
+	mbs_size = as->buffer_length - as->length -1;
+
+	if (sc->to_cp == CP_C_LOCALE) {
+		/*
+		 * "C" locale special process.
+		 */
+		u16 = _p;
+		ll = 0;
+		for (b = 0; b < bytes; b += 2) {
+			uint16_t val;
+			if (be)
+				val = archive_be16dec(u16+b);
+			else
+				val = archive_le16dec(u16+b);
+			if (val > 255) {
+				*mbs++ = '?';
+				ret = -1;
+			} else
+				*mbs++ = (char)(val&0xff);
+			ll++;
+		}
+		as->length += ll;
+		as->s[as->length] = '\0';
+		return (ret);
+	}
+
+	archive_string_init(&tmp);
+	if (be) {
+		if (is_big_endian()) {
+			u16 = _p;
+		} else {
+			if (archive_string_ensure(&tmp, bytes+2) == NULL)
+				return (-1);
+			memcpy(tmp.s, _p, bytes);
+			for (b = 0; b < bytes; b += 2) {
+				uint16_t val = archive_be16dec(tmp.s+b);
+				archive_le16enc(tmp.s+b, val);
+			}
+			u16 = tmp.s;
+		}
+	} else {
+		if (!is_big_endian()) {
+			u16 = _p;
+		} else {
+			if (archive_string_ensure(&tmp, bytes+2) == NULL)
+				return (-1);
+			memcpy(tmp.s, _p, bytes);
+			for (b = 0; b < bytes; b += 2) {
+				uint16_t val = archive_le16dec(tmp.s+b);
+				archive_be16enc(tmp.s+b, val);
+			}
+			u16 = tmp.s;
+		}
+	}
+
+	do {
+		defchar = 0;
+		ll = WideCharToMultiByte(sc->to_cp, 0,
+		    (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size,
+			NULL, &defchar);
+		/* Exit loop if we succeeded */
+		if (ll != 0 ||
+		    GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+			break;
+		}
+		/* Else expand buffer and loop to try again. */
+		ll = WideCharToMultiByte(sc->to_cp, 0,
+		    (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL);
+		if (archive_string_ensure(as, ll +1) == NULL)
+			return (-1);
+		mbs = as->s + as->length;
+		mbs_size = as->buffer_length - as->length -1;
+	} while (1);
+	archive_string_free(&tmp);
+	as->length += ll;
+	as->s[as->length] = '\0';
+	if (ll == 0 || defchar)
+		ret = -1;
+	return (ret);
+}
+
+static int
+win_strncat_from_utf16be(struct archive_string *as, const void *_p,
+    size_t bytes, struct archive_string_conv *sc)
+{
+	return (win_strncat_from_utf16(as, _p, bytes, sc, 1));
+}
+
+static int
+win_strncat_from_utf16le(struct archive_string *as, const void *_p,
+    size_t bytes, struct archive_string_conv *sc)
+{
+	return (win_strncat_from_utf16(as, _p, bytes, sc, 0));
+}
+
+static int
+is_big_endian(void)
+{
+	uint16_t d = 1;
+
+	return (archive_be16dec(&d) == 1);
+}
+
+/*
+ * Convert a current locale string to UTF-16BE/LE and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+win_strncat_to_utf16(struct archive_string *as16, const void *_p,
+    size_t length, struct archive_string_conv *sc, int bigendian)
+{
+	const char *s = (const char *)_p;
+	char *u16;
+	size_t count, avail;
+
+	if (archive_string_ensure(as16,
+	    as16->length + (length + 1) * 2) == NULL)
+		return (-1);
+
+	u16 = as16->s + as16->length;
+	avail = as16->buffer_length - 2;
+	if (sc->from_cp == CP_C_LOCALE) {
+		/*
+		 * "C" locale special process.
+		 */
+		count = 0;
+		while (count < length && *s) {
+			if (bigendian)
+				archive_be16enc(u16, *s);
+			else
+				archive_le16enc(u16, *s);
+			u16 += 2;
+			s++;
+			count++;
+		}
+		as16->length += count << 1;
+		as16->s[as16->length] = 0;
+		as16->s[as16->length+1] = 0;
+		return (0);
+	}
+	do {
+		count = MultiByteToWideChar(sc->from_cp,
+		    MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1);
+		/* Exit loop if we succeeded */
+		if (count != 0 ||
+		    GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+			break;
+		}
+		/* Expand buffer and try again */
+		count = MultiByteToWideChar(sc->from_cp,
+		    MB_PRECOMPOSED, s, (int)length, NULL, 0);
+		if (archive_string_ensure(as16, (count +1) * 2)
+		    == NULL)
+			return (-1);
+		u16 = as16->s + as16->length;
+		avail = as16->buffer_length - 2;
+	} while (1);
+	as16->length += count * 2;
+	as16->s[as16->length] = 0;
+	as16->s[as16->length+1] = 0;
+	if (count == 0)
+		return (-1);
+
+	if (is_big_endian()) {
+		if (!bigendian) {
+			while (count > 0) {
+				uint16_t v = archive_be16dec(u16);
+				archive_le16enc(u16, v);
+				u16 += 2;
+				count--;
+			}
+		}
+	} else {
+		if (bigendian) {
+			while (count > 0) {
+				uint16_t v = archive_le16dec(u16);
+				archive_be16enc(u16, v);
+				u16 += 2;
+				count--;
+			}
+		}
+	}
+	return (0);
+}
+
+static int
+win_strncat_to_utf16be(struct archive_string *as16, const void *_p,
+    size_t length, struct archive_string_conv *sc)
+{
+	return (win_strncat_to_utf16(as16, _p, length, sc, 1));
+}
+
+static int
+win_strncat_to_utf16le(struct archive_string *as16, const void *_p,
+    size_t length, struct archive_string_conv *sc)
+{
+	return (win_strncat_to_utf16(as16, _p, length, sc, 0));
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
+
+/*
+ * Do the best effort for conversions.
+ * We cannot handle UTF-16BE character-set without such iconv,
+ * but there is a chance if a string consists just ASCII code or
+ * a current locale is UTF-8.
+ */
+
+/*
+ * Convert a UTF-16BE string to current locale and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+best_effort_strncat_from_utf16(struct archive_string *as, const void *_p,
+    size_t bytes, struct archive_string_conv *sc, int be)
+{
+	const char *utf16 = (const char *)_p;
+	char *mbs;
+	uint32_t uc;
+	int n, ret;
+
+	(void)sc; /* UNUSED */
+	/*
+	 * Other case, we should do the best effort.
+	 * If all character are ASCII(<0x7f), we can convert it.
+	 * if not , we set a alternative character and return -1.
+	 */
+	ret = 0;
+	if (archive_string_ensure(as, as->length + bytes +1) == NULL)
+		return (-1);
+	mbs = as->s + as->length;
+
+	while ((n = utf16_to_unicode(&uc, utf16, bytes, be)) != 0) {
+		if (n < 0) {
+			n *= -1;
+			ret =  -1;
+		}
+		bytes -= n;
+		utf16 += n;
+
+		if (uc > 127) {
+			/* We cannot handle it. */
+			*mbs++ = '?';
+			ret =  -1;
+		} else
+			*mbs++ = (char)uc;
+	}
+	as->length = mbs - as->s;
+	as->s[as->length] = '\0';
+	return (ret);
+}
+
+static int
+best_effort_strncat_from_utf16be(struct archive_string *as, const void *_p,
+    size_t bytes, struct archive_string_conv *sc)
+{
+	return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 1));
+}
+
+static int
+best_effort_strncat_from_utf16le(struct archive_string *as, const void *_p,
+    size_t bytes, struct archive_string_conv *sc)
+{
+	return (best_effort_strncat_from_utf16(as, _p, bytes, sc, 0));
+}
+
+/*
+ * Convert a current locale string to UTF-16BE/LE and copy the result.
+ * Return -1 if conversion fails.
+ */
+static int
+best_effort_strncat_to_utf16(struct archive_string *as16, const void *_p,
+    size_t length, struct archive_string_conv *sc, int bigendian)
+{
+	const char *s = (const char *)_p;
+	char *utf16;
+	size_t remaining;
+	int ret;
+
+	(void)sc; /* UNUSED */
+	/*
+	 * Other case, we should do the best effort.
+	 * If all character are ASCII(<0x7f), we can convert it.
+	 * if not , we set a alternative character and return -1.
+	 */
+	ret = 0;
+	remaining = length;
+
+	if (archive_string_ensure(as16,
+	    as16->length + (length + 1) * 2) == NULL)
+		return (-1);
+
+	utf16 = as16->s + as16->length;
+	while (remaining--) {
+		unsigned c = *s++;
+		if (c > 127) {
+			/* We cannot handle it. */
+			c = UNICODE_R_CHAR;
+			ret = -1;
+		}
+		if (bigendian)
+			archive_be16enc(utf16, c);
+		else
+			archive_le16enc(utf16, c);
+		utf16 += 2;
+	}
+	as16->length = utf16 - as16->s;
+	as16->s[as16->length] = 0;
+	as16->s[as16->length+1] = 0;
+	return (ret);
+}
+
+static int
+best_effort_strncat_to_utf16be(struct archive_string *as16, const void *_p,
+    size_t length, struct archive_string_conv *sc)
+{
+	return (best_effort_strncat_to_utf16(as16, _p, length, sc, 1));
+}
+
+static int
+best_effort_strncat_to_utf16le(struct archive_string *as16, const void *_p,
+    size_t length, struct archive_string_conv *sc)
+{
+	return (best_effort_strncat_to_utf16(as16, _p, length, sc, 0));
+}
+
+
+/*
+ * Multistring operations.
+ */
+
+void
+archive_mstring_clean(struct archive_mstring *aes)
+{
+	archive_wstring_free(&(aes->aes_wcs));
+	archive_string_free(&(aes->aes_mbs));
+	archive_string_free(&(aes->aes_utf8));
+	archive_string_free(&(aes->aes_mbs_in_locale));
+	aes->aes_set = 0;
+}
+
+void
+archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src)
+{
+	dest->aes_set = src->aes_set;
+	archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));
+	archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));
+	archive_wstring_copy(&(dest->aes_wcs), &(src->aes_wcs));
+}
+
+int
+archive_mstring_get_utf8(struct archive *a, struct archive_mstring *aes,
+  const char **p)
+{
+	struct archive_string_conv *sc;
+	int r;
+
+	/* If we already have a UTF8 form, return that immediately. */
+	if (aes->aes_set & AES_SET_UTF8) {
+		*p = aes->aes_utf8.s;
+		return (0);
+	}
+
+	*p = NULL;
+	if (aes->aes_set & AES_SET_MBS) {
+		sc = archive_string_conversion_to_charset(a, "UTF-8", 1);
+		if (sc == NULL)
+			return (-1);/* Couldn't allocate memory for sc. */
+		r = archive_strncpy_l(&(aes->aes_utf8), aes->aes_mbs.s,
+		    aes->aes_mbs.length, sc);
+		if (a == NULL)
+			free_sconv_object(sc);
+		if (r == 0) {
+			aes->aes_set |= AES_SET_UTF8;
+			*p = aes->aes_utf8.s;
+			return (0);/* success. */
+		} else
+			return (-1);/* failure. */
+	}
+	return (0);/* success. */
+}
+
+int
+archive_mstring_get_mbs(struct archive *a, struct archive_mstring *aes,
+    const char **p)
+{
+	int r, ret = 0;
+
+	(void)a; /* UNUSED */
+	/* If we already have an MBS form, return that immediately. */
+	if (aes->aes_set & AES_SET_MBS) {
+		*p = aes->aes_mbs.s;
+		return (ret);
+	}
+
+	*p = NULL;
+	/* If there's a WCS form, try converting with the native locale. */
+	if (aes->aes_set & AES_SET_WCS) {
+		archive_string_empty(&(aes->aes_mbs));
+		r = archive_string_append_from_wcs(&(aes->aes_mbs),
+		    aes->aes_wcs.s, aes->aes_wcs.length);
+		*p = aes->aes_mbs.s;
+		if (r == 0) {
+			aes->aes_set |= AES_SET_MBS;
+			return (ret);
+		} else
+			ret = -1;
+	}
+
+	/*
+	 * Only a UTF-8 form cannot avail because its conversion already
+	 * failed at archive_mstring_update_utf8().
+	 */
+	return (ret);
+}
+
+int
+archive_mstring_get_wcs(struct archive *a, struct archive_mstring *aes,
+    const wchar_t **wp)
+{
+	int r, ret = 0;
+
+	(void)a;/* UNUSED */
+	/* Return WCS form if we already have it. */
+	if (aes->aes_set & AES_SET_WCS) {
+		*wp = aes->aes_wcs.s;
+		return (ret);
+	}
+
+	*wp = NULL;
+	/* Try converting MBS to WCS using native locale. */
+	if (aes->aes_set & AES_SET_MBS) {
+		archive_wstring_empty(&(aes->aes_wcs));
+		r = archive_wstring_append_from_mbs(&(aes->aes_wcs),
+		    aes->aes_mbs.s, aes->aes_mbs.length);
+		if (r == 0) {
+			aes->aes_set |= AES_SET_WCS;
+			*wp = aes->aes_wcs.s;
+		} else
+			ret = -1;/* failure. */
+	}
+	return (ret);
+}
+
+int
+archive_mstring_get_mbs_l(struct archive_mstring *aes,
+    const char **p, size_t *length, struct archive_string_conv *sc)
+{
+	int r, ret = 0;
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/*
+	 * Internationalization programming on Windows must use Wide
+	 * characters because Windows platform cannot make locale UTF-8.
+	 */
+	if (sc != NULL && (aes->aes_set & AES_SET_WCS) != 0) {
+		archive_string_empty(&(aes->aes_mbs_in_locale));
+		r = archive_string_append_from_wcs_in_codepage(
+		    &(aes->aes_mbs_in_locale), aes->aes_wcs.s,
+		    aes->aes_wcs.length, sc);
+		if (r == 0) {
+			*p = aes->aes_mbs_in_locale.s;
+			if (length != NULL)
+				*length = aes->aes_mbs_in_locale.length;
+			return (0);
+		} else if (errno == ENOMEM)
+			return (-1);
+		else
+			ret = -1;
+	}
+#endif
+
+	/* If there is not an MBS form but is a WCS form, try converting
+	 * with the native locale to be used for translating it to specified
+	 * character-set. */
+	if ((aes->aes_set & AES_SET_MBS) == 0 &&
+	    (aes->aes_set & AES_SET_WCS) != 0) {
+		archive_string_empty(&(aes->aes_mbs));
+		r = archive_string_append_from_wcs(&(aes->aes_mbs),
+		    aes->aes_wcs.s, aes->aes_wcs.length);
+		if (r == 0)
+			aes->aes_set |= AES_SET_MBS;
+		else if (errno == ENOMEM)
+			return (-1);
+		else
+			ret = -1;
+	}
+	/* If we already have an MBS form, use it to be translated to
+	 * specified character-set. */
+	if (aes->aes_set & AES_SET_MBS) {
+		if (sc == NULL) {
+			/* Conversion is unneeded. */
+			*p = aes->aes_mbs.s;
+			if (length != NULL)
+				*length = aes->aes_mbs.length;
+			return (0);
+		}
+		ret = archive_strncpy_l(&(aes->aes_mbs_in_locale),
+		    aes->aes_mbs.s, aes->aes_mbs.length, sc);
+		*p = aes->aes_mbs_in_locale.s;
+		if (length != NULL)
+			*length = aes->aes_mbs_in_locale.length;
+	} else {
+		*p = NULL;
+		if (length != NULL)
+			*length = 0;
+	}
+	return (ret);
+}
+
+int
+archive_mstring_copy_mbs(struct archive_mstring *aes, const char *mbs)
+{
+	if (mbs == NULL) {
+		aes->aes_set = 0;
+		return (0);
+	}
+	return (archive_mstring_copy_mbs_len(aes, mbs, strlen(mbs)));
+}
+
+int
+archive_mstring_copy_mbs_len(struct archive_mstring *aes, const char *mbs,
+    size_t len)
+{
+	if (mbs == NULL) {
+		aes->aes_set = 0;
+		return (0);
+	}
+	aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
+	archive_strncpy(&(aes->aes_mbs), mbs, len);
+	archive_string_empty(&(aes->aes_utf8));
+	archive_wstring_empty(&(aes->aes_wcs));
+	return (0);
+}
+
+int
+archive_mstring_copy_wcs(struct archive_mstring *aes, const wchar_t *wcs)
+{
+	return archive_mstring_copy_wcs_len(aes, wcs,
+				wcs == NULL ? 0 : wcslen(wcs));
+}
+
+int
+archive_mstring_copy_utf8(struct archive_mstring *aes, const char *utf8)
+{
+  if (utf8 == NULL) {
+    aes->aes_set = 0;
+    return (0);
+  }
+  aes->aes_set = AES_SET_UTF8;
+  archive_string_empty(&(aes->aes_mbs));
+  archive_string_empty(&(aes->aes_wcs));
+  archive_strncpy(&(aes->aes_utf8), utf8, strlen(utf8));
+  return (int)strlen(utf8);
+}
+
+int
+archive_mstring_copy_wcs_len(struct archive_mstring *aes, const wchar_t *wcs,
+    size_t len)
+{
+	if (wcs == NULL) {
+		aes->aes_set = 0;
+		return (0);
+	}
+	aes->aes_set = AES_SET_WCS; /* Only WCS form set. */
+	archive_string_empty(&(aes->aes_mbs));
+	archive_string_empty(&(aes->aes_utf8));
+	archive_wstrncpy(&(aes->aes_wcs), wcs, len);
+	return (0);
+}
+
+int
+archive_mstring_copy_mbs_len_l(struct archive_mstring *aes,
+    const char *mbs, size_t len, struct archive_string_conv *sc)
+{
+	int r;
+
+	if (mbs == NULL) {
+		aes->aes_set = 0;
+		return (0);
+	}
+	archive_string_empty(&(aes->aes_mbs));
+	archive_wstring_empty(&(aes->aes_wcs));
+	archive_string_empty(&(aes->aes_utf8));
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/*
+	 * Internationalization programming on Windows must use Wide
+	 * characters because Windows platform cannot make locale UTF-8.
+	 */
+	if (sc == NULL) {
+		if (archive_string_append(&(aes->aes_mbs),
+			mbs, mbsnbytes(mbs, len)) == NULL) {
+			aes->aes_set = 0;
+			r = -1;
+		} else {
+			aes->aes_set = AES_SET_MBS;
+			r = 0;
+		}
+#if defined(HAVE_ICONV)
+	} else if (sc != NULL && sc->cd_w != (iconv_t)-1) {
+		/*
+		 * This case happens only when MultiByteToWideChar() cannot
+		 * handle sc->from_cp, and we have to iconv in order to
+		 * translate character-set to wchar_t,UTF-16.
+		 */
+		iconv_t cd = sc->cd;
+		unsigned from_cp;
+		int flag;
+
+		/*
+		 * Translate multi-bytes from some character-set to UTF-8.
+		 */ 
+		sc->cd = sc->cd_w;
+		r = archive_strncpy_l(&(aes->aes_utf8), mbs, len, sc);
+		sc->cd = cd;
+		if (r != 0) {
+			aes->aes_set = 0;
+			return (r);
+		}
+		aes->aes_set = AES_SET_UTF8;
+
+		/*
+		 * Append the UTF-8 string into wstring.
+		 */ 
+		flag = sc->flag;
+		sc->flag &= ~(SCONV_NORMALIZATION_C
+				| SCONV_TO_UTF16| SCONV_FROM_UTF16);
+		from_cp = sc->from_cp;
+		sc->from_cp = CP_UTF8;
+		r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs),
+			aes->aes_utf8.s, aes->aes_utf8.length, sc);
+		sc->flag = flag;
+		sc->from_cp = from_cp;
+		if (r == 0)
+			aes->aes_set |= AES_SET_WCS;
+#endif
+	} else {
+		r = archive_wstring_append_from_mbs_in_codepage(
+		    &(aes->aes_wcs), mbs, len, sc);
+		if (r == 0)
+			aes->aes_set = AES_SET_WCS;
+		else
+			aes->aes_set = 0;
+	}
+#else
+	r = archive_strncpy_l(&(aes->aes_mbs), mbs, len, sc);
+	if (r == 0)
+		aes->aes_set = AES_SET_MBS; /* Only MBS form is set now. */
+	else
+		aes->aes_set = 0;
+#endif
+	return (r);
+}
+
+/*
+ * The 'update' form tries to proactively update all forms of
+ * this string (WCS and MBS) and returns an error if any of
+ * them fail.  This is used by the 'pax' handler, for instance,
+ * to detect and report character-conversion failures early while
+ * still allowing clients to get potentially useful values from
+ * the more tolerant lazy conversions.  (get_mbs and get_wcs will
+ * strive to give the user something useful, so you can get hopefully
+ * usable values even if some of the character conversions are failing.)
+ */
+int
+archive_mstring_update_utf8(struct archive *a, struct archive_mstring *aes,
+    const char *utf8)
+{
+	struct archive_string_conv *sc;
+	int r;
+
+	if (utf8 == NULL) {
+		aes->aes_set = 0;
+		return (0); /* Succeeded in clearing everything. */
+	}
+
+	/* Save the UTF8 string. */
+	archive_strcpy(&(aes->aes_utf8), utf8);
+
+	/* Empty the mbs and wcs strings. */
+	archive_string_empty(&(aes->aes_mbs));
+	archive_wstring_empty(&(aes->aes_wcs));
+
+	aes->aes_set = AES_SET_UTF8;	/* Only UTF8 is set now. */
+
+	/* Try converting UTF-8 to MBS, return false on failure. */
+	sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
+	if (sc == NULL)
+		return (-1);/* Couldn't allocate memory for sc. */
+	r = archive_strcpy_l(&(aes->aes_mbs), utf8, sc);
+	if (a == NULL)
+		free_sconv_object(sc);
+	if (r != 0)
+		return (-1);
+	aes->aes_set = AES_SET_UTF8 | AES_SET_MBS; /* Both UTF8 and MBS set. */
+
+	/* Try converting MBS to WCS, return false on failure. */
+	if (archive_wstring_append_from_mbs(&(aes->aes_wcs), aes->aes_mbs.s,
+	    aes->aes_mbs.length))
+		return (-1);
+	aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
+
+	/* All conversions succeeded. */
+	return (0);
+}
diff --git a/c/archive_string.h b/c/archive_string.h
new file mode 100644
--- /dev/null
+++ b/c/archive_string.h
@@ -0,0 +1,243 @@
+/*-
+ * Copyright (c) 2003-2010 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: head/lib/libarchive/archive_string.h 201092 2009-12-28 02:26:06Z kientzle $
+ *
+ */
+
+#ifndef ARCHIVE_STRING_H_INCLUDED
+#define ARCHIVE_STRING_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>  /* required for wchar_t on some systems */
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_WCHAR_H
+#include <wchar.h>
+#endif
+
+#include "archive.h"
+
+/*
+ * Basic resizable/reusable string support similar to Java's "StringBuffer."
+ *
+ * Unlike sbuf(9), the buffers here are fully reusable and track the
+ * length throughout.
+ */
+
+struct archive_string {
+	char	*s;  /* Pointer to the storage */
+	size_t	 length; /* Length of 's' in characters */
+	size_t	 buffer_length; /* Length of malloc-ed storage in bytes. */
+};
+
+struct archive_wstring {
+	wchar_t	*s;  /* Pointer to the storage */
+	size_t	 length; /* Length of 's' in characters */
+	size_t	 buffer_length; /* Length of malloc-ed storage in bytes. */
+};
+
+struct archive_string_conv;
+
+/* Initialize an archive_string object on the stack or elsewhere. */
+#define	archive_string_init(a)	\
+	do { (a)->s = NULL; (a)->length = 0; (a)->buffer_length = 0; } while(0)
+
+/* Append a C char to an archive_string, resizing as necessary. */
+struct archive_string *
+archive_strappend_char(struct archive_string *, char);
+
+/* Ditto for a wchar_t and an archive_wstring. */
+struct archive_wstring *
+archive_wstrappend_wchar(struct archive_wstring *, wchar_t);
+
+/* Append a raw array to an archive_string, resizing as necessary */
+struct archive_string *
+archive_array_append(struct archive_string *, const char *, size_t);
+
+/* Convert a Unicode string to current locale and append the result. */
+/* Returns -1 if conversion fails. */
+int
+archive_string_append_from_wcs(struct archive_string *, const wchar_t *, size_t);
+
+
+/* Create a string conversion object.
+ * Return NULL and set a error message if the conversion is not supported
+ * on the platform. */
+struct archive_string_conv *
+archive_string_conversion_to_charset(struct archive *, const char *, int);
+struct archive_string_conv *
+archive_string_conversion_from_charset(struct archive *, const char *, int);
+/* Create the default string conversion object for reading/writing an archive.
+ * Return NULL if the conversion is unneeded.
+ * Note: On non Windows platform this always returns NULL.
+ */
+struct archive_string_conv *
+archive_string_default_conversion_for_read(struct archive *);
+struct archive_string_conv *
+archive_string_default_conversion_for_write(struct archive *);
+/* Dispose of a string conversion object. */
+void
+archive_string_conversion_free(struct archive *);
+const char *
+archive_string_conversion_charset_name(struct archive_string_conv *);
+void
+archive_string_conversion_set_opt(struct archive_string_conv *, int);
+#define SCONV_SET_OPT_UTF8_LIBARCHIVE2X	1
+#define SCONV_SET_OPT_NORMALIZATION_C	2
+#define SCONV_SET_OPT_NORMALIZATION_D	4
+
+
+/* Copy one archive_string to another in locale conversion.
+ * Return -1 if conversion fails. */
+int
+archive_strncpy_l(struct archive_string *, const void *, size_t,
+    struct archive_string_conv *);
+
+/* Copy one archive_string to another in locale conversion.
+ * Return -1 if conversion fails. */
+int
+archive_strncat_l(struct archive_string *, const void *, size_t,
+    struct archive_string_conv *);
+
+
+/* Copy one archive_string to another */
+#define	archive_string_copy(dest, src) \
+	((dest)->length = 0, archive_string_concat((dest), (src)))
+#define	archive_wstring_copy(dest, src) \
+	((dest)->length = 0, archive_wstring_concat((dest), (src)))
+
+/* Concatenate one archive_string to another */
+void archive_string_concat(struct archive_string *dest, struct archive_string *src);
+void archive_wstring_concat(struct archive_wstring *dest, struct archive_wstring *src);
+
+/* Ensure that the underlying buffer is at least as large as the request. */
+struct archive_string *
+archive_string_ensure(struct archive_string *, size_t);
+struct archive_wstring *
+archive_wstring_ensure(struct archive_wstring *, size_t);
+
+/* Append C string, which may lack trailing \0. */
+/* The source is declared void * here because this gets used with
+ * "signed char *", "unsigned char *" and "char *" arguments.
+ * Declaring it "char *" as with some of the other functions just
+ * leads to a lot of extra casts. */
+struct archive_string *
+archive_strncat(struct archive_string *, const void *, size_t);
+struct archive_wstring *
+archive_wstrncat(struct archive_wstring *, const wchar_t *, size_t);
+
+/* Append a C string to an archive_string, resizing as necessary. */
+struct archive_string *
+archive_strcat(struct archive_string *, const void *);
+struct archive_wstring *
+archive_wstrcat(struct archive_wstring *, const wchar_t *);
+
+/* Copy a C string to an archive_string, resizing as necessary. */
+#define	archive_strcpy(as,p) \
+	archive_strncpy((as), (p), ((p) == NULL ? 0 : strlen(p)))
+#define	archive_wstrcpy(as,p) \
+	archive_wstrncpy((as), (p), ((p) == NULL ? 0 : wcslen(p)))
+#define	archive_strcpy_l(as,p,lo) \
+	archive_strncpy_l((as), (p), ((p) == NULL ? 0 : strlen(p)), (lo))
+
+/* Copy a C string to an archive_string with limit, resizing as necessary. */
+#define	archive_strncpy(as,p,l) \
+	((as)->length=0, archive_strncat((as), (p), (l)))
+#define	archive_wstrncpy(as,p,l) \
+	((as)->length = 0, archive_wstrncat((as), (p), (l)))
+
+/* Return length of string. */
+#define	archive_strlen(a) ((a)->length)
+
+/* Set string length to zero. */
+#define	archive_string_empty(a) ((a)->length = 0)
+#define	archive_wstring_empty(a) ((a)->length = 0)
+
+/* Release any allocated storage resources. */
+void	archive_string_free(struct archive_string *);
+void	archive_wstring_free(struct archive_wstring *);
+
+/* Like 'vsprintf', but resizes the underlying string as necessary. */
+/* Note: This only implements a small subset of standard printf functionality. */
+void	archive_string_vsprintf(struct archive_string *, const char *,
+	    va_list) __LA_PRINTF(2, 0);
+void	archive_string_sprintf(struct archive_string *, const char *, ...)
+	    __LA_PRINTF(2, 3);
+
+/* Translates from MBS to Unicode. */
+/* Returns non-zero if conversion failed in any way. */
+int archive_wstring_append_from_mbs(struct archive_wstring *dest,
+    const char *, size_t);
+
+
+/* A "multistring" can hold Unicode, UTF8, or MBS versions of
+ * the string.  If you set and read the same version, no translation
+ * is done.  If you set and read different versions, the library
+ * will attempt to transparently convert.
+ */
+struct archive_mstring {
+	struct archive_string aes_mbs;
+	struct archive_string aes_utf8;
+	struct archive_wstring aes_wcs;
+	struct archive_string aes_mbs_in_locale;
+	/* Bitmap of which of the above are valid.  Because we're lazy
+	 * about malloc-ing and reusing the underlying storage, we
+	 * can't rely on NULL pointers to indicate whether a string
+	 * has been set. */
+	int aes_set;
+#define	AES_SET_MBS 1
+#define	AES_SET_UTF8 2
+#define	AES_SET_WCS 4
+};
+
+void	archive_mstring_clean(struct archive_mstring *);
+void	archive_mstring_copy(struct archive_mstring *dest, struct archive_mstring *src);
+int archive_mstring_get_mbs(struct archive *, struct archive_mstring *, const char **);
+int archive_mstring_get_utf8(struct archive *, struct archive_mstring *, const char **);
+int archive_mstring_get_wcs(struct archive *, struct archive_mstring *, const wchar_t **);
+int	archive_mstring_get_mbs_l(struct archive_mstring *, const char **,
+	    size_t *, struct archive_string_conv *);
+int	archive_mstring_copy_mbs(struct archive_mstring *, const char *mbs);
+int	archive_mstring_copy_mbs_len(struct archive_mstring *, const char *mbs,
+	    size_t);
+int	archive_mstring_copy_utf8(struct archive_mstring *, const char *utf8);
+int	archive_mstring_copy_wcs(struct archive_mstring *, const wchar_t *wcs);
+int	archive_mstring_copy_wcs_len(struct archive_mstring *,
+	    const wchar_t *wcs, size_t);
+int	archive_mstring_copy_mbs_len_l(struct archive_mstring *,
+	    const char *mbs, size_t, struct archive_string_conv *);
+int     archive_mstring_update_utf8(struct archive *, struct archive_mstring *aes, const char *utf8);
+
+
+#endif
diff --git a/c/archive_string_composition.h b/c/archive_string_composition.h
new file mode 100644
--- /dev/null
+++ b/c/archive_string_composition.h
@@ -0,0 +1,2292 @@
+/*-
+ * Copyright (c) 2011-2012 libarchive Project
+ * 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$
+ *
+ */
+
+/*
+ * ATTENTION!
+ *  This file is generated by build/utils/gen_archive_string_composition_h.sh
+ *  from http://unicode.org/Public/6.0.0/ucd/UnicodeData.txt
+ *
+ *  See also http://unicode.org/report/tr15/
+ */
+
+#ifndef ARCHIVE_STRING_COMPOSITION_H_INCLUDED
+#define ARCHIVE_STRING_COMPOSITION_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+struct unicode_composition_table {
+	uint32_t cp1;
+	uint32_t cp2;
+	uint32_t nfc;
+};
+
+static const struct unicode_composition_table u_composition_table[] = {
+	{ 0x0003C , 0x00338 , 0x0226E },
+	{ 0x0003D , 0x00338 , 0x02260 },
+	{ 0x0003E , 0x00338 , 0x0226F },
+	{ 0x00041 , 0x00300 , 0x000C0 },
+	{ 0x00041 , 0x00301 , 0x000C1 },
+	{ 0x00041 , 0x00302 , 0x000C2 },
+	{ 0x00041 , 0x00303 , 0x000C3 },
+	{ 0x00041 , 0x00304 , 0x00100 },
+	{ 0x00041 , 0x00306 , 0x00102 },
+	{ 0x00041 , 0x00307 , 0x00226 },
+	{ 0x00041 , 0x00308 , 0x000C4 },
+	{ 0x00041 , 0x00309 , 0x01EA2 },
+	{ 0x00041 , 0x0030A , 0x000C5 },
+	{ 0x00041 , 0x0030C , 0x001CD },
+	{ 0x00041 , 0x0030F , 0x00200 },
+	{ 0x00041 , 0x00311 , 0x00202 },
+	{ 0x00041 , 0x00323 , 0x01EA0 },
+	{ 0x00041 , 0x00325 , 0x01E00 },
+	{ 0x00041 , 0x00328 , 0x00104 },
+	{ 0x00042 , 0x00307 , 0x01E02 },
+	{ 0x00042 , 0x00323 , 0x01E04 },
+	{ 0x00042 , 0x00331 , 0x01E06 },
+	{ 0x00043 , 0x00301 , 0x00106 },
+	{ 0x00043 , 0x00302 , 0x00108 },
+	{ 0x00043 , 0x00307 , 0x0010A },
+	{ 0x00043 , 0x0030C , 0x0010C },
+	{ 0x00043 , 0x00327 , 0x000C7 },
+	{ 0x00044 , 0x00307 , 0x01E0A },
+	{ 0x00044 , 0x0030C , 0x0010E },
+	{ 0x00044 , 0x00323 , 0x01E0C },
+	{ 0x00044 , 0x00327 , 0x01E10 },
+	{ 0x00044 , 0x0032D , 0x01E12 },
+	{ 0x00044 , 0x00331 , 0x01E0E },
+	{ 0x00045 , 0x00300 , 0x000C8 },
+	{ 0x00045 , 0x00301 , 0x000C9 },
+	{ 0x00045 , 0x00302 , 0x000CA },
+	{ 0x00045 , 0x00303 , 0x01EBC },
+	{ 0x00045 , 0x00304 , 0x00112 },
+	{ 0x00045 , 0x00306 , 0x00114 },
+	{ 0x00045 , 0x00307 , 0x00116 },
+	{ 0x00045 , 0x00308 , 0x000CB },
+	{ 0x00045 , 0x00309 , 0x01EBA },
+	{ 0x00045 , 0x0030C , 0x0011A },
+	{ 0x00045 , 0x0030F , 0x00204 },
+	{ 0x00045 , 0x00311 , 0x00206 },
+	{ 0x00045 , 0x00323 , 0x01EB8 },
+	{ 0x00045 , 0x00327 , 0x00228 },
+	{ 0x00045 , 0x00328 , 0x00118 },
+	{ 0x00045 , 0x0032D , 0x01E18 },
+	{ 0x00045 , 0x00330 , 0x01E1A },
+	{ 0x00046 , 0x00307 , 0x01E1E },
+	{ 0x00047 , 0x00301 , 0x001F4 },
+	{ 0x00047 , 0x00302 , 0x0011C },
+	{ 0x00047 , 0x00304 , 0x01E20 },
+	{ 0x00047 , 0x00306 , 0x0011E },
+	{ 0x00047 , 0x00307 , 0x00120 },
+	{ 0x00047 , 0x0030C , 0x001E6 },
+	{ 0x00047 , 0x00327 , 0x00122 },
+	{ 0x00048 , 0x00302 , 0x00124 },
+	{ 0x00048 , 0x00307 , 0x01E22 },
+	{ 0x00048 , 0x00308 , 0x01E26 },
+	{ 0x00048 , 0x0030C , 0x0021E },
+	{ 0x00048 , 0x00323 , 0x01E24 },
+	{ 0x00048 , 0x00327 , 0x01E28 },
+	{ 0x00048 , 0x0032E , 0x01E2A },
+	{ 0x00049 , 0x00300 , 0x000CC },
+	{ 0x00049 , 0x00301 , 0x000CD },
+	{ 0x00049 , 0x00302 , 0x000CE },
+	{ 0x00049 , 0x00303 , 0x00128 },
+	{ 0x00049 , 0x00304 , 0x0012A },
+	{ 0x00049 , 0x00306 , 0x0012C },
+	{ 0x00049 , 0x00307 , 0x00130 },
+	{ 0x00049 , 0x00308 , 0x000CF },
+	{ 0x00049 , 0x00309 , 0x01EC8 },
+	{ 0x00049 , 0x0030C , 0x001CF },
+	{ 0x00049 , 0x0030F , 0x00208 },
+	{ 0x00049 , 0x00311 , 0x0020A },
+	{ 0x00049 , 0x00323 , 0x01ECA },
+	{ 0x00049 , 0x00328 , 0x0012E },
+	{ 0x00049 , 0x00330 , 0x01E2C },
+	{ 0x0004A , 0x00302 , 0x00134 },
+	{ 0x0004B , 0x00301 , 0x01E30 },
+	{ 0x0004B , 0x0030C , 0x001E8 },
+	{ 0x0004B , 0x00323 , 0x01E32 },
+	{ 0x0004B , 0x00327 , 0x00136 },
+	{ 0x0004B , 0x00331 , 0x01E34 },
+	{ 0x0004C , 0x00301 , 0x00139 },
+	{ 0x0004C , 0x0030C , 0x0013D },
+	{ 0x0004C , 0x00323 , 0x01E36 },
+	{ 0x0004C , 0x00327 , 0x0013B },
+	{ 0x0004C , 0x0032D , 0x01E3C },
+	{ 0x0004C , 0x00331 , 0x01E3A },
+	{ 0x0004D , 0x00301 , 0x01E3E },
+	{ 0x0004D , 0x00307 , 0x01E40 },
+	{ 0x0004D , 0x00323 , 0x01E42 },
+	{ 0x0004E , 0x00300 , 0x001F8 },
+	{ 0x0004E , 0x00301 , 0x00143 },
+	{ 0x0004E , 0x00303 , 0x000D1 },
+	{ 0x0004E , 0x00307 , 0x01E44 },
+	{ 0x0004E , 0x0030C , 0x00147 },
+	{ 0x0004E , 0x00323 , 0x01E46 },
+	{ 0x0004E , 0x00327 , 0x00145 },
+	{ 0x0004E , 0x0032D , 0x01E4A },
+	{ 0x0004E , 0x00331 , 0x01E48 },
+	{ 0x0004F , 0x00300 , 0x000D2 },
+	{ 0x0004F , 0x00301 , 0x000D3 },
+	{ 0x0004F , 0x00302 , 0x000D4 },
+	{ 0x0004F , 0x00303 , 0x000D5 },
+	{ 0x0004F , 0x00304 , 0x0014C },
+	{ 0x0004F , 0x00306 , 0x0014E },
+	{ 0x0004F , 0x00307 , 0x0022E },
+	{ 0x0004F , 0x00308 , 0x000D6 },
+	{ 0x0004F , 0x00309 , 0x01ECE },
+	{ 0x0004F , 0x0030B , 0x00150 },
+	{ 0x0004F , 0x0030C , 0x001D1 },
+	{ 0x0004F , 0x0030F , 0x0020C },
+	{ 0x0004F , 0x00311 , 0x0020E },
+	{ 0x0004F , 0x0031B , 0x001A0 },
+	{ 0x0004F , 0x00323 , 0x01ECC },
+	{ 0x0004F , 0x00328 , 0x001EA },
+	{ 0x00050 , 0x00301 , 0x01E54 },
+	{ 0x00050 , 0x00307 , 0x01E56 },
+	{ 0x00052 , 0x00301 , 0x00154 },
+	{ 0x00052 , 0x00307 , 0x01E58 },
+	{ 0x00052 , 0x0030C , 0x00158 },
+	{ 0x00052 , 0x0030F , 0x00210 },
+	{ 0x00052 , 0x00311 , 0x00212 },
+	{ 0x00052 , 0x00323 , 0x01E5A },
+	{ 0x00052 , 0x00327 , 0x00156 },
+	{ 0x00052 , 0x00331 , 0x01E5E },
+	{ 0x00053 , 0x00301 , 0x0015A },
+	{ 0x00053 , 0x00302 , 0x0015C },
+	{ 0x00053 , 0x00307 , 0x01E60 },
+	{ 0x00053 , 0x0030C , 0x00160 },
+	{ 0x00053 , 0x00323 , 0x01E62 },
+	{ 0x00053 , 0x00326 , 0x00218 },
+	{ 0x00053 , 0x00327 , 0x0015E },
+	{ 0x00054 , 0x00307 , 0x01E6A },
+	{ 0x00054 , 0x0030C , 0x00164 },
+	{ 0x00054 , 0x00323 , 0x01E6C },
+	{ 0x00054 , 0x00326 , 0x0021A },
+	{ 0x00054 , 0x00327 , 0x00162 },
+	{ 0x00054 , 0x0032D , 0x01E70 },
+	{ 0x00054 , 0x00331 , 0x01E6E },
+	{ 0x00055 , 0x00300 , 0x000D9 },
+	{ 0x00055 , 0x00301 , 0x000DA },
+	{ 0x00055 , 0x00302 , 0x000DB },
+	{ 0x00055 , 0x00303 , 0x00168 },
+	{ 0x00055 , 0x00304 , 0x0016A },
+	{ 0x00055 , 0x00306 , 0x0016C },
+	{ 0x00055 , 0x00308 , 0x000DC },
+	{ 0x00055 , 0x00309 , 0x01EE6 },
+	{ 0x00055 , 0x0030A , 0x0016E },
+	{ 0x00055 , 0x0030B , 0x00170 },
+	{ 0x00055 , 0x0030C , 0x001D3 },
+	{ 0x00055 , 0x0030F , 0x00214 },
+	{ 0x00055 , 0x00311 , 0x00216 },
+	{ 0x00055 , 0x0031B , 0x001AF },
+	{ 0x00055 , 0x00323 , 0x01EE4 },
+	{ 0x00055 , 0x00324 , 0x01E72 },
+	{ 0x00055 , 0x00328 , 0x00172 },
+	{ 0x00055 , 0x0032D , 0x01E76 },
+	{ 0x00055 , 0x00330 , 0x01E74 },
+	{ 0x00056 , 0x00303 , 0x01E7C },
+	{ 0x00056 , 0x00323 , 0x01E7E },
+	{ 0x00057 , 0x00300 , 0x01E80 },
+	{ 0x00057 , 0x00301 , 0x01E82 },
+	{ 0x00057 , 0x00302 , 0x00174 },
+	{ 0x00057 , 0x00307 , 0x01E86 },
+	{ 0x00057 , 0x00308 , 0x01E84 },
+	{ 0x00057 , 0x00323 , 0x01E88 },
+	{ 0x00058 , 0x00307 , 0x01E8A },
+	{ 0x00058 , 0x00308 , 0x01E8C },
+	{ 0x00059 , 0x00300 , 0x01EF2 },
+	{ 0x00059 , 0x00301 , 0x000DD },
+	{ 0x00059 , 0x00302 , 0x00176 },
+	{ 0x00059 , 0x00303 , 0x01EF8 },
+	{ 0x00059 , 0x00304 , 0x00232 },
+	{ 0x00059 , 0x00307 , 0x01E8E },
+	{ 0x00059 , 0x00308 , 0x00178 },
+	{ 0x00059 , 0x00309 , 0x01EF6 },
+	{ 0x00059 , 0x00323 , 0x01EF4 },
+	{ 0x0005A , 0x00301 , 0x00179 },
+	{ 0x0005A , 0x00302 , 0x01E90 },
+	{ 0x0005A , 0x00307 , 0x0017B },
+	{ 0x0005A , 0x0030C , 0x0017D },
+	{ 0x0005A , 0x00323 , 0x01E92 },
+	{ 0x0005A , 0x00331 , 0x01E94 },
+	{ 0x00061 , 0x00300 , 0x000E0 },
+	{ 0x00061 , 0x00301 , 0x000E1 },
+	{ 0x00061 , 0x00302 , 0x000E2 },
+	{ 0x00061 , 0x00303 , 0x000E3 },
+	{ 0x00061 , 0x00304 , 0x00101 },
+	{ 0x00061 , 0x00306 , 0x00103 },
+	{ 0x00061 , 0x00307 , 0x00227 },
+	{ 0x00061 , 0x00308 , 0x000E4 },
+	{ 0x00061 , 0x00309 , 0x01EA3 },
+	{ 0x00061 , 0x0030A , 0x000E5 },
+	{ 0x00061 , 0x0030C , 0x001CE },
+	{ 0x00061 , 0x0030F , 0x00201 },
+	{ 0x00061 , 0x00311 , 0x00203 },
+	{ 0x00061 , 0x00323 , 0x01EA1 },
+	{ 0x00061 , 0x00325 , 0x01E01 },
+	{ 0x00061 , 0x00328 , 0x00105 },
+	{ 0x00062 , 0x00307 , 0x01E03 },
+	{ 0x00062 , 0x00323 , 0x01E05 },
+	{ 0x00062 , 0x00331 , 0x01E07 },
+	{ 0x00063 , 0x00301 , 0x00107 },
+	{ 0x00063 , 0x00302 , 0x00109 },
+	{ 0x00063 , 0x00307 , 0x0010B },
+	{ 0x00063 , 0x0030C , 0x0010D },
+	{ 0x00063 , 0x00327 , 0x000E7 },
+	{ 0x00064 , 0x00307 , 0x01E0B },
+	{ 0x00064 , 0x0030C , 0x0010F },
+	{ 0x00064 , 0x00323 , 0x01E0D },
+	{ 0x00064 , 0x00327 , 0x01E11 },
+	{ 0x00064 , 0x0032D , 0x01E13 },
+	{ 0x00064 , 0x00331 , 0x01E0F },
+	{ 0x00065 , 0x00300 , 0x000E8 },
+	{ 0x00065 , 0x00301 , 0x000E9 },
+	{ 0x00065 , 0x00302 , 0x000EA },
+	{ 0x00065 , 0x00303 , 0x01EBD },
+	{ 0x00065 , 0x00304 , 0x00113 },
+	{ 0x00065 , 0x00306 , 0x00115 },
+	{ 0x00065 , 0x00307 , 0x00117 },
+	{ 0x00065 , 0x00308 , 0x000EB },
+	{ 0x00065 , 0x00309 , 0x01EBB },
+	{ 0x00065 , 0x0030C , 0x0011B },
+	{ 0x00065 , 0x0030F , 0x00205 },
+	{ 0x00065 , 0x00311 , 0x00207 },
+	{ 0x00065 , 0x00323 , 0x01EB9 },
+	{ 0x00065 , 0x00327 , 0x00229 },
+	{ 0x00065 , 0x00328 , 0x00119 },
+	{ 0x00065 , 0x0032D , 0x01E19 },
+	{ 0x00065 , 0x00330 , 0x01E1B },
+	{ 0x00066 , 0x00307 , 0x01E1F },
+	{ 0x00067 , 0x00301 , 0x001F5 },
+	{ 0x00067 , 0x00302 , 0x0011D },
+	{ 0x00067 , 0x00304 , 0x01E21 },
+	{ 0x00067 , 0x00306 , 0x0011F },
+	{ 0x00067 , 0x00307 , 0x00121 },
+	{ 0x00067 , 0x0030C , 0x001E7 },
+	{ 0x00067 , 0x00327 , 0x00123 },
+	{ 0x00068 , 0x00302 , 0x00125 },
+	{ 0x00068 , 0x00307 , 0x01E23 },
+	{ 0x00068 , 0x00308 , 0x01E27 },
+	{ 0x00068 , 0x0030C , 0x0021F },
+	{ 0x00068 , 0x00323 , 0x01E25 },
+	{ 0x00068 , 0x00327 , 0x01E29 },
+	{ 0x00068 , 0x0032E , 0x01E2B },
+	{ 0x00068 , 0x00331 , 0x01E96 },
+	{ 0x00069 , 0x00300 , 0x000EC },
+	{ 0x00069 , 0x00301 , 0x000ED },
+	{ 0x00069 , 0x00302 , 0x000EE },
+	{ 0x00069 , 0x00303 , 0x00129 },
+	{ 0x00069 , 0x00304 , 0x0012B },
+	{ 0x00069 , 0x00306 , 0x0012D },
+	{ 0x00069 , 0x00308 , 0x000EF },
+	{ 0x00069 , 0x00309 , 0x01EC9 },
+	{ 0x00069 , 0x0030C , 0x001D0 },
+	{ 0x00069 , 0x0030F , 0x00209 },
+	{ 0x00069 , 0x00311 , 0x0020B },
+	{ 0x00069 , 0x00323 , 0x01ECB },
+	{ 0x00069 , 0x00328 , 0x0012F },
+	{ 0x00069 , 0x00330 , 0x01E2D },
+	{ 0x0006A , 0x00302 , 0x00135 },
+	{ 0x0006A , 0x0030C , 0x001F0 },
+	{ 0x0006B , 0x00301 , 0x01E31 },
+	{ 0x0006B , 0x0030C , 0x001E9 },
+	{ 0x0006B , 0x00323 , 0x01E33 },
+	{ 0x0006B , 0x00327 , 0x00137 },
+	{ 0x0006B , 0x00331 , 0x01E35 },
+	{ 0x0006C , 0x00301 , 0x0013A },
+	{ 0x0006C , 0x0030C , 0x0013E },
+	{ 0x0006C , 0x00323 , 0x01E37 },
+	{ 0x0006C , 0x00327 , 0x0013C },
+	{ 0x0006C , 0x0032D , 0x01E3D },
+	{ 0x0006C , 0x00331 , 0x01E3B },
+	{ 0x0006D , 0x00301 , 0x01E3F },
+	{ 0x0006D , 0x00307 , 0x01E41 },
+	{ 0x0006D , 0x00323 , 0x01E43 },
+	{ 0x0006E , 0x00300 , 0x001F9 },
+	{ 0x0006E , 0x00301 , 0x00144 },
+	{ 0x0006E , 0x00303 , 0x000F1 },
+	{ 0x0006E , 0x00307 , 0x01E45 },
+	{ 0x0006E , 0x0030C , 0x00148 },
+	{ 0x0006E , 0x00323 , 0x01E47 },
+	{ 0x0006E , 0x00327 , 0x00146 },
+	{ 0x0006E , 0x0032D , 0x01E4B },
+	{ 0x0006E , 0x00331 , 0x01E49 },
+	{ 0x0006F , 0x00300 , 0x000F2 },
+	{ 0x0006F , 0x00301 , 0x000F3 },
+	{ 0x0006F , 0x00302 , 0x000F4 },
+	{ 0x0006F , 0x00303 , 0x000F5 },
+	{ 0x0006F , 0x00304 , 0x0014D },
+	{ 0x0006F , 0x00306 , 0x0014F },
+	{ 0x0006F , 0x00307 , 0x0022F },
+	{ 0x0006F , 0x00308 , 0x000F6 },
+	{ 0x0006F , 0x00309 , 0x01ECF },
+	{ 0x0006F , 0x0030B , 0x00151 },
+	{ 0x0006F , 0x0030C , 0x001D2 },
+	{ 0x0006F , 0x0030F , 0x0020D },
+	{ 0x0006F , 0x00311 , 0x0020F },
+	{ 0x0006F , 0x0031B , 0x001A1 },
+	{ 0x0006F , 0x00323 , 0x01ECD },
+	{ 0x0006F , 0x00328 , 0x001EB },
+	{ 0x00070 , 0x00301 , 0x01E55 },
+	{ 0x00070 , 0x00307 , 0x01E57 },
+	{ 0x00072 , 0x00301 , 0x00155 },
+	{ 0x00072 , 0x00307 , 0x01E59 },
+	{ 0x00072 , 0x0030C , 0x00159 },
+	{ 0x00072 , 0x0030F , 0x00211 },
+	{ 0x00072 , 0x00311 , 0x00213 },
+	{ 0x00072 , 0x00323 , 0x01E5B },
+	{ 0x00072 , 0x00327 , 0x00157 },
+	{ 0x00072 , 0x00331 , 0x01E5F },
+	{ 0x00073 , 0x00301 , 0x0015B },
+	{ 0x00073 , 0x00302 , 0x0015D },
+	{ 0x00073 , 0x00307 , 0x01E61 },
+	{ 0x00073 , 0x0030C , 0x00161 },
+	{ 0x00073 , 0x00323 , 0x01E63 },
+	{ 0x00073 , 0x00326 , 0x00219 },
+	{ 0x00073 , 0x00327 , 0x0015F },
+	{ 0x00074 , 0x00307 , 0x01E6B },
+	{ 0x00074 , 0x00308 , 0x01E97 },
+	{ 0x00074 , 0x0030C , 0x00165 },
+	{ 0x00074 , 0x00323 , 0x01E6D },
+	{ 0x00074 , 0x00326 , 0x0021B },
+	{ 0x00074 , 0x00327 , 0x00163 },
+	{ 0x00074 , 0x0032D , 0x01E71 },
+	{ 0x00074 , 0x00331 , 0x01E6F },
+	{ 0x00075 , 0x00300 , 0x000F9 },
+	{ 0x00075 , 0x00301 , 0x000FA },
+	{ 0x00075 , 0x00302 , 0x000FB },
+	{ 0x00075 , 0x00303 , 0x00169 },
+	{ 0x00075 , 0x00304 , 0x0016B },
+	{ 0x00075 , 0x00306 , 0x0016D },
+	{ 0x00075 , 0x00308 , 0x000FC },
+	{ 0x00075 , 0x00309 , 0x01EE7 },
+	{ 0x00075 , 0x0030A , 0x0016F },
+	{ 0x00075 , 0x0030B , 0x00171 },
+	{ 0x00075 , 0x0030C , 0x001D4 },
+	{ 0x00075 , 0x0030F , 0x00215 },
+	{ 0x00075 , 0x00311 , 0x00217 },
+	{ 0x00075 , 0x0031B , 0x001B0 },
+	{ 0x00075 , 0x00323 , 0x01EE5 },
+	{ 0x00075 , 0x00324 , 0x01E73 },
+	{ 0x00075 , 0x00328 , 0x00173 },
+	{ 0x00075 , 0x0032D , 0x01E77 },
+	{ 0x00075 , 0x00330 , 0x01E75 },
+	{ 0x00076 , 0x00303 , 0x01E7D },
+	{ 0x00076 , 0x00323 , 0x01E7F },
+	{ 0x00077 , 0x00300 , 0x01E81 },
+	{ 0x00077 , 0x00301 , 0x01E83 },
+	{ 0x00077 , 0x00302 , 0x00175 },
+	{ 0x00077 , 0x00307 , 0x01E87 },
+	{ 0x00077 , 0x00308 , 0x01E85 },
+	{ 0x00077 , 0x0030A , 0x01E98 },
+	{ 0x00077 , 0x00323 , 0x01E89 },
+	{ 0x00078 , 0x00307 , 0x01E8B },
+	{ 0x00078 , 0x00308 , 0x01E8D },
+	{ 0x00079 , 0x00300 , 0x01EF3 },
+	{ 0x00079 , 0x00301 , 0x000FD },
+	{ 0x00079 , 0x00302 , 0x00177 },
+	{ 0x00079 , 0x00303 , 0x01EF9 },
+	{ 0x00079 , 0x00304 , 0x00233 },
+	{ 0x00079 , 0x00307 , 0x01E8F },
+	{ 0x00079 , 0x00308 , 0x000FF },
+	{ 0x00079 , 0x00309 , 0x01EF7 },
+	{ 0x00079 , 0x0030A , 0x01E99 },
+	{ 0x00079 , 0x00323 , 0x01EF5 },
+	{ 0x0007A , 0x00301 , 0x0017A },
+	{ 0x0007A , 0x00302 , 0x01E91 },
+	{ 0x0007A , 0x00307 , 0x0017C },
+	{ 0x0007A , 0x0030C , 0x0017E },
+	{ 0x0007A , 0x00323 , 0x01E93 },
+	{ 0x0007A , 0x00331 , 0x01E95 },
+	{ 0x000A8 , 0x00300 , 0x01FED },
+	{ 0x000A8 , 0x00301 , 0x00385 },
+	{ 0x000A8 , 0x00342 , 0x01FC1 },
+	{ 0x000C2 , 0x00300 , 0x01EA6 },
+	{ 0x000C2 , 0x00301 , 0x01EA4 },
+	{ 0x000C2 , 0x00303 , 0x01EAA },
+	{ 0x000C2 , 0x00309 , 0x01EA8 },
+	{ 0x000C4 , 0x00304 , 0x001DE },
+	{ 0x000C5 , 0x00301 , 0x001FA },
+	{ 0x000C6 , 0x00301 , 0x001FC },
+	{ 0x000C6 , 0x00304 , 0x001E2 },
+	{ 0x000C7 , 0x00301 , 0x01E08 },
+	{ 0x000CA , 0x00300 , 0x01EC0 },
+	{ 0x000CA , 0x00301 , 0x01EBE },
+	{ 0x000CA , 0x00303 , 0x01EC4 },
+	{ 0x000CA , 0x00309 , 0x01EC2 },
+	{ 0x000CF , 0x00301 , 0x01E2E },
+	{ 0x000D4 , 0x00300 , 0x01ED2 },
+	{ 0x000D4 , 0x00301 , 0x01ED0 },
+	{ 0x000D4 , 0x00303 , 0x01ED6 },
+	{ 0x000D4 , 0x00309 , 0x01ED4 },
+	{ 0x000D5 , 0x00301 , 0x01E4C },
+	{ 0x000D5 , 0x00304 , 0x0022C },
+	{ 0x000D5 , 0x00308 , 0x01E4E },
+	{ 0x000D6 , 0x00304 , 0x0022A },
+	{ 0x000D8 , 0x00301 , 0x001FE },
+	{ 0x000DC , 0x00300 , 0x001DB },
+	{ 0x000DC , 0x00301 , 0x001D7 },
+	{ 0x000DC , 0x00304 , 0x001D5 },
+	{ 0x000DC , 0x0030C , 0x001D9 },
+	{ 0x000E2 , 0x00300 , 0x01EA7 },
+	{ 0x000E2 , 0x00301 , 0x01EA5 },
+	{ 0x000E2 , 0x00303 , 0x01EAB },
+	{ 0x000E2 , 0x00309 , 0x01EA9 },
+	{ 0x000E4 , 0x00304 , 0x001DF },
+	{ 0x000E5 , 0x00301 , 0x001FB },
+	{ 0x000E6 , 0x00301 , 0x001FD },
+	{ 0x000E6 , 0x00304 , 0x001E3 },
+	{ 0x000E7 , 0x00301 , 0x01E09 },
+	{ 0x000EA , 0x00300 , 0x01EC1 },
+	{ 0x000EA , 0x00301 , 0x01EBF },
+	{ 0x000EA , 0x00303 , 0x01EC5 },
+	{ 0x000EA , 0x00309 , 0x01EC3 },
+	{ 0x000EF , 0x00301 , 0x01E2F },
+	{ 0x000F4 , 0x00300 , 0x01ED3 },
+	{ 0x000F4 , 0x00301 , 0x01ED1 },
+	{ 0x000F4 , 0x00303 , 0x01ED7 },
+	{ 0x000F4 , 0x00309 , 0x01ED5 },
+	{ 0x000F5 , 0x00301 , 0x01E4D },
+	{ 0x000F5 , 0x00304 , 0x0022D },
+	{ 0x000F5 , 0x00308 , 0x01E4F },
+	{ 0x000F6 , 0x00304 , 0x0022B },
+	{ 0x000F8 , 0x00301 , 0x001FF },
+	{ 0x000FC , 0x00300 , 0x001DC },
+	{ 0x000FC , 0x00301 , 0x001D8 },
+	{ 0x000FC , 0x00304 , 0x001D6 },
+	{ 0x000FC , 0x0030C , 0x001DA },
+	{ 0x00102 , 0x00300 , 0x01EB0 },
+	{ 0x00102 , 0x00301 , 0x01EAE },
+	{ 0x00102 , 0x00303 , 0x01EB4 },
+	{ 0x00102 , 0x00309 , 0x01EB2 },
+	{ 0x00103 , 0x00300 , 0x01EB1 },
+	{ 0x00103 , 0x00301 , 0x01EAF },
+	{ 0x00103 , 0x00303 , 0x01EB5 },
+	{ 0x00103 , 0x00309 , 0x01EB3 },
+	{ 0x00112 , 0x00300 , 0x01E14 },
+	{ 0x00112 , 0x00301 , 0x01E16 },
+	{ 0x00113 , 0x00300 , 0x01E15 },
+	{ 0x00113 , 0x00301 , 0x01E17 },
+	{ 0x0014C , 0x00300 , 0x01E50 },
+	{ 0x0014C , 0x00301 , 0x01E52 },
+	{ 0x0014D , 0x00300 , 0x01E51 },
+	{ 0x0014D , 0x00301 , 0x01E53 },
+	{ 0x0015A , 0x00307 , 0x01E64 },
+	{ 0x0015B , 0x00307 , 0x01E65 },
+	{ 0x00160 , 0x00307 , 0x01E66 },
+	{ 0x00161 , 0x00307 , 0x01E67 },
+	{ 0x00168 , 0x00301 , 0x01E78 },
+	{ 0x00169 , 0x00301 , 0x01E79 },
+	{ 0x0016A , 0x00308 , 0x01E7A },
+	{ 0x0016B , 0x00308 , 0x01E7B },
+	{ 0x0017F , 0x00307 , 0x01E9B },
+	{ 0x001A0 , 0x00300 , 0x01EDC },
+	{ 0x001A0 , 0x00301 , 0x01EDA },
+	{ 0x001A0 , 0x00303 , 0x01EE0 },
+	{ 0x001A0 , 0x00309 , 0x01EDE },
+	{ 0x001A0 , 0x00323 , 0x01EE2 },
+	{ 0x001A1 , 0x00300 , 0x01EDD },
+	{ 0x001A1 , 0x00301 , 0x01EDB },
+	{ 0x001A1 , 0x00303 , 0x01EE1 },
+	{ 0x001A1 , 0x00309 , 0x01EDF },
+	{ 0x001A1 , 0x00323 , 0x01EE3 },
+	{ 0x001AF , 0x00300 , 0x01EEA },
+	{ 0x001AF , 0x00301 , 0x01EE8 },
+	{ 0x001AF , 0x00303 , 0x01EEE },
+	{ 0x001AF , 0x00309 , 0x01EEC },
+	{ 0x001AF , 0x00323 , 0x01EF0 },
+	{ 0x001B0 , 0x00300 , 0x01EEB },
+	{ 0x001B0 , 0x00301 , 0x01EE9 },
+	{ 0x001B0 , 0x00303 , 0x01EEF },
+	{ 0x001B0 , 0x00309 , 0x01EED },
+	{ 0x001B0 , 0x00323 , 0x01EF1 },
+	{ 0x001B7 , 0x0030C , 0x001EE },
+	{ 0x001EA , 0x00304 , 0x001EC },
+	{ 0x001EB , 0x00304 , 0x001ED },
+	{ 0x00226 , 0x00304 , 0x001E0 },
+	{ 0x00227 , 0x00304 , 0x001E1 },
+	{ 0x00228 , 0x00306 , 0x01E1C },
+	{ 0x00229 , 0x00306 , 0x01E1D },
+	{ 0x0022E , 0x00304 , 0x00230 },
+	{ 0x0022F , 0x00304 , 0x00231 },
+	{ 0x00292 , 0x0030C , 0x001EF },
+	{ 0x00391 , 0x00300 , 0x01FBA },
+	{ 0x00391 , 0x00301 , 0x00386 },
+	{ 0x00391 , 0x00304 , 0x01FB9 },
+	{ 0x00391 , 0x00306 , 0x01FB8 },
+	{ 0x00391 , 0x00313 , 0x01F08 },
+	{ 0x00391 , 0x00314 , 0x01F09 },
+	{ 0x00391 , 0x00345 , 0x01FBC },
+	{ 0x00395 , 0x00300 , 0x01FC8 },
+	{ 0x00395 , 0x00301 , 0x00388 },
+	{ 0x00395 , 0x00313 , 0x01F18 },
+	{ 0x00395 , 0x00314 , 0x01F19 },
+	{ 0x00397 , 0x00300 , 0x01FCA },
+	{ 0x00397 , 0x00301 , 0x00389 },
+	{ 0x00397 , 0x00313 , 0x01F28 },
+	{ 0x00397 , 0x00314 , 0x01F29 },
+	{ 0x00397 , 0x00345 , 0x01FCC },
+	{ 0x00399 , 0x00300 , 0x01FDA },
+	{ 0x00399 , 0x00301 , 0x0038A },
+	{ 0x00399 , 0x00304 , 0x01FD9 },
+	{ 0x00399 , 0x00306 , 0x01FD8 },
+	{ 0x00399 , 0x00308 , 0x003AA },
+	{ 0x00399 , 0x00313 , 0x01F38 },
+	{ 0x00399 , 0x00314 , 0x01F39 },
+	{ 0x0039F , 0x00300 , 0x01FF8 },
+	{ 0x0039F , 0x00301 , 0x0038C },
+	{ 0x0039F , 0x00313 , 0x01F48 },
+	{ 0x0039F , 0x00314 , 0x01F49 },
+	{ 0x003A1 , 0x00314 , 0x01FEC },
+	{ 0x003A5 , 0x00300 , 0x01FEA },
+	{ 0x003A5 , 0x00301 , 0x0038E },
+	{ 0x003A5 , 0x00304 , 0x01FE9 },
+	{ 0x003A5 , 0x00306 , 0x01FE8 },
+	{ 0x003A5 , 0x00308 , 0x003AB },
+	{ 0x003A5 , 0x00314 , 0x01F59 },
+	{ 0x003A9 , 0x00300 , 0x01FFA },
+	{ 0x003A9 , 0x00301 , 0x0038F },
+	{ 0x003A9 , 0x00313 , 0x01F68 },
+	{ 0x003A9 , 0x00314 , 0x01F69 },
+	{ 0x003A9 , 0x00345 , 0x01FFC },
+	{ 0x003AC , 0x00345 , 0x01FB4 },
+	{ 0x003AE , 0x00345 , 0x01FC4 },
+	{ 0x003B1 , 0x00300 , 0x01F70 },
+	{ 0x003B1 , 0x00301 , 0x003AC },
+	{ 0x003B1 , 0x00304 , 0x01FB1 },
+	{ 0x003B1 , 0x00306 , 0x01FB0 },
+	{ 0x003B1 , 0x00313 , 0x01F00 },
+	{ 0x003B1 , 0x00314 , 0x01F01 },
+	{ 0x003B1 , 0x00342 , 0x01FB6 },
+	{ 0x003B1 , 0x00345 , 0x01FB3 },
+	{ 0x003B5 , 0x00300 , 0x01F72 },
+	{ 0x003B5 , 0x00301 , 0x003AD },
+	{ 0x003B5 , 0x00313 , 0x01F10 },
+	{ 0x003B5 , 0x00314 , 0x01F11 },
+	{ 0x003B7 , 0x00300 , 0x01F74 },
+	{ 0x003B7 , 0x00301 , 0x003AE },
+	{ 0x003B7 , 0x00313 , 0x01F20 },
+	{ 0x003B7 , 0x00314 , 0x01F21 },
+	{ 0x003B7 , 0x00342 , 0x01FC6 },
+	{ 0x003B7 , 0x00345 , 0x01FC3 },
+	{ 0x003B9 , 0x00300 , 0x01F76 },
+	{ 0x003B9 , 0x00301 , 0x003AF },
+	{ 0x003B9 , 0x00304 , 0x01FD1 },
+	{ 0x003B9 , 0x00306 , 0x01FD0 },
+	{ 0x003B9 , 0x00308 , 0x003CA },
+	{ 0x003B9 , 0x00313 , 0x01F30 },
+	{ 0x003B9 , 0x00314 , 0x01F31 },
+	{ 0x003B9 , 0x00342 , 0x01FD6 },
+	{ 0x003BF , 0x00300 , 0x01F78 },
+	{ 0x003BF , 0x00301 , 0x003CC },
+	{ 0x003BF , 0x00313 , 0x01F40 },
+	{ 0x003BF , 0x00314 , 0x01F41 },
+	{ 0x003C1 , 0x00313 , 0x01FE4 },
+	{ 0x003C1 , 0x00314 , 0x01FE5 },
+	{ 0x003C5 , 0x00300 , 0x01F7A },
+	{ 0x003C5 , 0x00301 , 0x003CD },
+	{ 0x003C5 , 0x00304 , 0x01FE1 },
+	{ 0x003C5 , 0x00306 , 0x01FE0 },
+	{ 0x003C5 , 0x00308 , 0x003CB },
+	{ 0x003C5 , 0x00313 , 0x01F50 },
+	{ 0x003C5 , 0x00314 , 0x01F51 },
+	{ 0x003C5 , 0x00342 , 0x01FE6 },
+	{ 0x003C9 , 0x00300 , 0x01F7C },
+	{ 0x003C9 , 0x00301 , 0x003CE },
+	{ 0x003C9 , 0x00313 , 0x01F60 },
+	{ 0x003C9 , 0x00314 , 0x01F61 },
+	{ 0x003C9 , 0x00342 , 0x01FF6 },
+	{ 0x003C9 , 0x00345 , 0x01FF3 },
+	{ 0x003CA , 0x00300 , 0x01FD2 },
+	{ 0x003CA , 0x00301 , 0x00390 },
+	{ 0x003CA , 0x00342 , 0x01FD7 },
+	{ 0x003CB , 0x00300 , 0x01FE2 },
+	{ 0x003CB , 0x00301 , 0x003B0 },
+	{ 0x003CB , 0x00342 , 0x01FE7 },
+	{ 0x003CE , 0x00345 , 0x01FF4 },
+	{ 0x003D2 , 0x00301 , 0x003D3 },
+	{ 0x003D2 , 0x00308 , 0x003D4 },
+	{ 0x00406 , 0x00308 , 0x00407 },
+	{ 0x00410 , 0x00306 , 0x004D0 },
+	{ 0x00410 , 0x00308 , 0x004D2 },
+	{ 0x00413 , 0x00301 , 0x00403 },
+	{ 0x00415 , 0x00300 , 0x00400 },
+	{ 0x00415 , 0x00306 , 0x004D6 },
+	{ 0x00415 , 0x00308 , 0x00401 },
+	{ 0x00416 , 0x00306 , 0x004C1 },
+	{ 0x00416 , 0x00308 , 0x004DC },
+	{ 0x00417 , 0x00308 , 0x004DE },
+	{ 0x00418 , 0x00300 , 0x0040D },
+	{ 0x00418 , 0x00304 , 0x004E2 },
+	{ 0x00418 , 0x00306 , 0x00419 },
+	{ 0x00418 , 0x00308 , 0x004E4 },
+	{ 0x0041A , 0x00301 , 0x0040C },
+	{ 0x0041E , 0x00308 , 0x004E6 },
+	{ 0x00423 , 0x00304 , 0x004EE },
+	{ 0x00423 , 0x00306 , 0x0040E },
+	{ 0x00423 , 0x00308 , 0x004F0 },
+	{ 0x00423 , 0x0030B , 0x004F2 },
+	{ 0x00427 , 0x00308 , 0x004F4 },
+	{ 0x0042B , 0x00308 , 0x004F8 },
+	{ 0x0042D , 0x00308 , 0x004EC },
+	{ 0x00430 , 0x00306 , 0x004D1 },
+	{ 0x00430 , 0x00308 , 0x004D3 },
+	{ 0x00433 , 0x00301 , 0x00453 },
+	{ 0x00435 , 0x00300 , 0x00450 },
+	{ 0x00435 , 0x00306 , 0x004D7 },
+	{ 0x00435 , 0x00308 , 0x00451 },
+	{ 0x00436 , 0x00306 , 0x004C2 },
+	{ 0x00436 , 0x00308 , 0x004DD },
+	{ 0x00437 , 0x00308 , 0x004DF },
+	{ 0x00438 , 0x00300 , 0x0045D },
+	{ 0x00438 , 0x00304 , 0x004E3 },
+	{ 0x00438 , 0x00306 , 0x00439 },
+	{ 0x00438 , 0x00308 , 0x004E5 },
+	{ 0x0043A , 0x00301 , 0x0045C },
+	{ 0x0043E , 0x00308 , 0x004E7 },
+	{ 0x00443 , 0x00304 , 0x004EF },
+	{ 0x00443 , 0x00306 , 0x0045E },
+	{ 0x00443 , 0x00308 , 0x004F1 },
+	{ 0x00443 , 0x0030B , 0x004F3 },
+	{ 0x00447 , 0x00308 , 0x004F5 },
+	{ 0x0044B , 0x00308 , 0x004F9 },
+	{ 0x0044D , 0x00308 , 0x004ED },
+	{ 0x00456 , 0x00308 , 0x00457 },
+	{ 0x00474 , 0x0030F , 0x00476 },
+	{ 0x00475 , 0x0030F , 0x00477 },
+	{ 0x004D8 , 0x00308 , 0x004DA },
+	{ 0x004D9 , 0x00308 , 0x004DB },
+	{ 0x004E8 , 0x00308 , 0x004EA },
+	{ 0x004E9 , 0x00308 , 0x004EB },
+	{ 0x00627 , 0x00653 , 0x00622 },
+	{ 0x00627 , 0x00654 , 0x00623 },
+	{ 0x00627 , 0x00655 , 0x00625 },
+	{ 0x00648 , 0x00654 , 0x00624 },
+	{ 0x0064A , 0x00654 , 0x00626 },
+	{ 0x006C1 , 0x00654 , 0x006C2 },
+	{ 0x006D2 , 0x00654 , 0x006D3 },
+	{ 0x006D5 , 0x00654 , 0x006C0 },
+	{ 0x00928 , 0x0093C , 0x00929 },
+	{ 0x00930 , 0x0093C , 0x00931 },
+	{ 0x00933 , 0x0093C , 0x00934 },
+	{ 0x009C7 , 0x009BE , 0x009CB },
+	{ 0x009C7 , 0x009D7 , 0x009CC },
+	{ 0x00B47 , 0x00B3E , 0x00B4B },
+	{ 0x00B47 , 0x00B56 , 0x00B48 },
+	{ 0x00B47 , 0x00B57 , 0x00B4C },
+	{ 0x00B92 , 0x00BD7 , 0x00B94 },
+	{ 0x00BC6 , 0x00BBE , 0x00BCA },
+	{ 0x00BC6 , 0x00BD7 , 0x00BCC },
+	{ 0x00BC7 , 0x00BBE , 0x00BCB },
+	{ 0x00C46 , 0x00C56 , 0x00C48 },
+	{ 0x00CBF , 0x00CD5 , 0x00CC0 },
+	{ 0x00CC6 , 0x00CC2 , 0x00CCA },
+	{ 0x00CC6 , 0x00CD5 , 0x00CC7 },
+	{ 0x00CC6 , 0x00CD6 , 0x00CC8 },
+	{ 0x00CCA , 0x00CD5 , 0x00CCB },
+	{ 0x00D46 , 0x00D3E , 0x00D4A },
+	{ 0x00D46 , 0x00D57 , 0x00D4C },
+	{ 0x00D47 , 0x00D3E , 0x00D4B },
+	{ 0x00DD9 , 0x00DCA , 0x00DDA },
+	{ 0x00DD9 , 0x00DCF , 0x00DDC },
+	{ 0x00DD9 , 0x00DDF , 0x00DDE },
+	{ 0x00DDC , 0x00DCA , 0x00DDD },
+	{ 0x01025 , 0x0102E , 0x01026 },
+	{ 0x01B05 , 0x01B35 , 0x01B06 },
+	{ 0x01B07 , 0x01B35 , 0x01B08 },
+	{ 0x01B09 , 0x01B35 , 0x01B0A },
+	{ 0x01B0B , 0x01B35 , 0x01B0C },
+	{ 0x01B0D , 0x01B35 , 0x01B0E },
+	{ 0x01B11 , 0x01B35 , 0x01B12 },
+	{ 0x01B3A , 0x01B35 , 0x01B3B },
+	{ 0x01B3C , 0x01B35 , 0x01B3D },
+	{ 0x01B3E , 0x01B35 , 0x01B40 },
+	{ 0x01B3F , 0x01B35 , 0x01B41 },
+	{ 0x01B42 , 0x01B35 , 0x01B43 },
+	{ 0x01E36 , 0x00304 , 0x01E38 },
+	{ 0x01E37 , 0x00304 , 0x01E39 },
+	{ 0x01E5A , 0x00304 , 0x01E5C },
+	{ 0x01E5B , 0x00304 , 0x01E5D },
+	{ 0x01E62 , 0x00307 , 0x01E68 },
+	{ 0x01E63 , 0x00307 , 0x01E69 },
+	{ 0x01EA0 , 0x00302 , 0x01EAC },
+	{ 0x01EA0 , 0x00306 , 0x01EB6 },
+	{ 0x01EA1 , 0x00302 , 0x01EAD },
+	{ 0x01EA1 , 0x00306 , 0x01EB7 },
+	{ 0x01EB8 , 0x00302 , 0x01EC6 },
+	{ 0x01EB9 , 0x00302 , 0x01EC7 },
+	{ 0x01ECC , 0x00302 , 0x01ED8 },
+	{ 0x01ECD , 0x00302 , 0x01ED9 },
+	{ 0x01F00 , 0x00300 , 0x01F02 },
+	{ 0x01F00 , 0x00301 , 0x01F04 },
+	{ 0x01F00 , 0x00342 , 0x01F06 },
+	{ 0x01F00 , 0x00345 , 0x01F80 },
+	{ 0x01F01 , 0x00300 , 0x01F03 },
+	{ 0x01F01 , 0x00301 , 0x01F05 },
+	{ 0x01F01 , 0x00342 , 0x01F07 },
+	{ 0x01F01 , 0x00345 , 0x01F81 },
+	{ 0x01F02 , 0x00345 , 0x01F82 },
+	{ 0x01F03 , 0x00345 , 0x01F83 },
+	{ 0x01F04 , 0x00345 , 0x01F84 },
+	{ 0x01F05 , 0x00345 , 0x01F85 },
+	{ 0x01F06 , 0x00345 , 0x01F86 },
+	{ 0x01F07 , 0x00345 , 0x01F87 },
+	{ 0x01F08 , 0x00300 , 0x01F0A },
+	{ 0x01F08 , 0x00301 , 0x01F0C },
+	{ 0x01F08 , 0x00342 , 0x01F0E },
+	{ 0x01F08 , 0x00345 , 0x01F88 },
+	{ 0x01F09 , 0x00300 , 0x01F0B },
+	{ 0x01F09 , 0x00301 , 0x01F0D },
+	{ 0x01F09 , 0x00342 , 0x01F0F },
+	{ 0x01F09 , 0x00345 , 0x01F89 },
+	{ 0x01F0A , 0x00345 , 0x01F8A },
+	{ 0x01F0B , 0x00345 , 0x01F8B },
+	{ 0x01F0C , 0x00345 , 0x01F8C },
+	{ 0x01F0D , 0x00345 , 0x01F8D },
+	{ 0x01F0E , 0x00345 , 0x01F8E },
+	{ 0x01F0F , 0x00345 , 0x01F8F },
+	{ 0x01F10 , 0x00300 , 0x01F12 },
+	{ 0x01F10 , 0x00301 , 0x01F14 },
+	{ 0x01F11 , 0x00300 , 0x01F13 },
+	{ 0x01F11 , 0x00301 , 0x01F15 },
+	{ 0x01F18 , 0x00300 , 0x01F1A },
+	{ 0x01F18 , 0x00301 , 0x01F1C },
+	{ 0x01F19 , 0x00300 , 0x01F1B },
+	{ 0x01F19 , 0x00301 , 0x01F1D },
+	{ 0x01F20 , 0x00300 , 0x01F22 },
+	{ 0x01F20 , 0x00301 , 0x01F24 },
+	{ 0x01F20 , 0x00342 , 0x01F26 },
+	{ 0x01F20 , 0x00345 , 0x01F90 },
+	{ 0x01F21 , 0x00300 , 0x01F23 },
+	{ 0x01F21 , 0x00301 , 0x01F25 },
+	{ 0x01F21 , 0x00342 , 0x01F27 },
+	{ 0x01F21 , 0x00345 , 0x01F91 },
+	{ 0x01F22 , 0x00345 , 0x01F92 },
+	{ 0x01F23 , 0x00345 , 0x01F93 },
+	{ 0x01F24 , 0x00345 , 0x01F94 },
+	{ 0x01F25 , 0x00345 , 0x01F95 },
+	{ 0x01F26 , 0x00345 , 0x01F96 },
+	{ 0x01F27 , 0x00345 , 0x01F97 },
+	{ 0x01F28 , 0x00300 , 0x01F2A },
+	{ 0x01F28 , 0x00301 , 0x01F2C },
+	{ 0x01F28 , 0x00342 , 0x01F2E },
+	{ 0x01F28 , 0x00345 , 0x01F98 },
+	{ 0x01F29 , 0x00300 , 0x01F2B },
+	{ 0x01F29 , 0x00301 , 0x01F2D },
+	{ 0x01F29 , 0x00342 , 0x01F2F },
+	{ 0x01F29 , 0x00345 , 0x01F99 },
+	{ 0x01F2A , 0x00345 , 0x01F9A },
+	{ 0x01F2B , 0x00345 , 0x01F9B },
+	{ 0x01F2C , 0x00345 , 0x01F9C },
+	{ 0x01F2D , 0x00345 , 0x01F9D },
+	{ 0x01F2E , 0x00345 , 0x01F9E },
+	{ 0x01F2F , 0x00345 , 0x01F9F },
+	{ 0x01F30 , 0x00300 , 0x01F32 },
+	{ 0x01F30 , 0x00301 , 0x01F34 },
+	{ 0x01F30 , 0x00342 , 0x01F36 },
+	{ 0x01F31 , 0x00300 , 0x01F33 },
+	{ 0x01F31 , 0x00301 , 0x01F35 },
+	{ 0x01F31 , 0x00342 , 0x01F37 },
+	{ 0x01F38 , 0x00300 , 0x01F3A },
+	{ 0x01F38 , 0x00301 , 0x01F3C },
+	{ 0x01F38 , 0x00342 , 0x01F3E },
+	{ 0x01F39 , 0x00300 , 0x01F3B },
+	{ 0x01F39 , 0x00301 , 0x01F3D },
+	{ 0x01F39 , 0x00342 , 0x01F3F },
+	{ 0x01F40 , 0x00300 , 0x01F42 },
+	{ 0x01F40 , 0x00301 , 0x01F44 },
+	{ 0x01F41 , 0x00300 , 0x01F43 },
+	{ 0x01F41 , 0x00301 , 0x01F45 },
+	{ 0x01F48 , 0x00300 , 0x01F4A },
+	{ 0x01F48 , 0x00301 , 0x01F4C },
+	{ 0x01F49 , 0x00300 , 0x01F4B },
+	{ 0x01F49 , 0x00301 , 0x01F4D },
+	{ 0x01F50 , 0x00300 , 0x01F52 },
+	{ 0x01F50 , 0x00301 , 0x01F54 },
+	{ 0x01F50 , 0x00342 , 0x01F56 },
+	{ 0x01F51 , 0x00300 , 0x01F53 },
+	{ 0x01F51 , 0x00301 , 0x01F55 },
+	{ 0x01F51 , 0x00342 , 0x01F57 },
+	{ 0x01F59 , 0x00300 , 0x01F5B },
+	{ 0x01F59 , 0x00301 , 0x01F5D },
+	{ 0x01F59 , 0x00342 , 0x01F5F },
+	{ 0x01F60 , 0x00300 , 0x01F62 },
+	{ 0x01F60 , 0x00301 , 0x01F64 },
+	{ 0x01F60 , 0x00342 , 0x01F66 },
+	{ 0x01F60 , 0x00345 , 0x01FA0 },
+	{ 0x01F61 , 0x00300 , 0x01F63 },
+	{ 0x01F61 , 0x00301 , 0x01F65 },
+	{ 0x01F61 , 0x00342 , 0x01F67 },
+	{ 0x01F61 , 0x00345 , 0x01FA1 },
+	{ 0x01F62 , 0x00345 , 0x01FA2 },
+	{ 0x01F63 , 0x00345 , 0x01FA3 },
+	{ 0x01F64 , 0x00345 , 0x01FA4 },
+	{ 0x01F65 , 0x00345 , 0x01FA5 },
+	{ 0x01F66 , 0x00345 , 0x01FA6 },
+	{ 0x01F67 , 0x00345 , 0x01FA7 },
+	{ 0x01F68 , 0x00300 , 0x01F6A },
+	{ 0x01F68 , 0x00301 , 0x01F6C },
+	{ 0x01F68 , 0x00342 , 0x01F6E },
+	{ 0x01F68 , 0x00345 , 0x01FA8 },
+	{ 0x01F69 , 0x00300 , 0x01F6B },
+	{ 0x01F69 , 0x00301 , 0x01F6D },
+	{ 0x01F69 , 0x00342 , 0x01F6F },
+	{ 0x01F69 , 0x00345 , 0x01FA9 },
+	{ 0x01F6A , 0x00345 , 0x01FAA },
+	{ 0x01F6B , 0x00345 , 0x01FAB },
+	{ 0x01F6C , 0x00345 , 0x01FAC },
+	{ 0x01F6D , 0x00345 , 0x01FAD },
+	{ 0x01F6E , 0x00345 , 0x01FAE },
+	{ 0x01F6F , 0x00345 , 0x01FAF },
+	{ 0x01F70 , 0x00345 , 0x01FB2 },
+	{ 0x01F74 , 0x00345 , 0x01FC2 },
+	{ 0x01F7C , 0x00345 , 0x01FF2 },
+	{ 0x01FB6 , 0x00345 , 0x01FB7 },
+	{ 0x01FBF , 0x00300 , 0x01FCD },
+	{ 0x01FBF , 0x00301 , 0x01FCE },
+	{ 0x01FBF , 0x00342 , 0x01FCF },
+	{ 0x01FC6 , 0x00345 , 0x01FC7 },
+	{ 0x01FF6 , 0x00345 , 0x01FF7 },
+	{ 0x01FFE , 0x00300 , 0x01FDD },
+	{ 0x01FFE , 0x00301 , 0x01FDE },
+	{ 0x01FFE , 0x00342 , 0x01FDF },
+	{ 0x02190 , 0x00338 , 0x0219A },
+	{ 0x02192 , 0x00338 , 0x0219B },
+	{ 0x02194 , 0x00338 , 0x021AE },
+	{ 0x021D0 , 0x00338 , 0x021CD },
+	{ 0x021D2 , 0x00338 , 0x021CF },
+	{ 0x021D4 , 0x00338 , 0x021CE },
+	{ 0x02203 , 0x00338 , 0x02204 },
+	{ 0x02208 , 0x00338 , 0x02209 },
+	{ 0x0220B , 0x00338 , 0x0220C },
+	{ 0x02223 , 0x00338 , 0x02224 },
+	{ 0x02225 , 0x00338 , 0x02226 },
+	{ 0x0223C , 0x00338 , 0x02241 },
+	{ 0x02243 , 0x00338 , 0x02244 },
+	{ 0x02245 , 0x00338 , 0x02247 },
+	{ 0x02248 , 0x00338 , 0x02249 },
+	{ 0x0224D , 0x00338 , 0x0226D },
+	{ 0x02261 , 0x00338 , 0x02262 },
+	{ 0x02264 , 0x00338 , 0x02270 },
+	{ 0x02265 , 0x00338 , 0x02271 },
+	{ 0x02272 , 0x00338 , 0x02274 },
+	{ 0x02273 , 0x00338 , 0x02275 },
+	{ 0x02276 , 0x00338 , 0x02278 },
+	{ 0x02277 , 0x00338 , 0x02279 },
+	{ 0x0227A , 0x00338 , 0x02280 },
+	{ 0x0227B , 0x00338 , 0x02281 },
+	{ 0x0227C , 0x00338 , 0x022E0 },
+	{ 0x0227D , 0x00338 , 0x022E1 },
+	{ 0x02282 , 0x00338 , 0x02284 },
+	{ 0x02283 , 0x00338 , 0x02285 },
+	{ 0x02286 , 0x00338 , 0x02288 },
+	{ 0x02287 , 0x00338 , 0x02289 },
+	{ 0x02291 , 0x00338 , 0x022E2 },
+	{ 0x02292 , 0x00338 , 0x022E3 },
+	{ 0x022A2 , 0x00338 , 0x022AC },
+	{ 0x022A8 , 0x00338 , 0x022AD },
+	{ 0x022A9 , 0x00338 , 0x022AE },
+	{ 0x022AB , 0x00338 , 0x022AF },
+	{ 0x022B2 , 0x00338 , 0x022EA },
+	{ 0x022B3 , 0x00338 , 0x022EB },
+	{ 0x022B4 , 0x00338 , 0x022EC },
+	{ 0x022B5 , 0x00338 , 0x022ED },
+	{ 0x03046 , 0x03099 , 0x03094 },
+	{ 0x0304B , 0x03099 , 0x0304C },
+	{ 0x0304D , 0x03099 , 0x0304E },
+	{ 0x0304F , 0x03099 , 0x03050 },
+	{ 0x03051 , 0x03099 , 0x03052 },
+	{ 0x03053 , 0x03099 , 0x03054 },
+	{ 0x03055 , 0x03099 , 0x03056 },
+	{ 0x03057 , 0x03099 , 0x03058 },
+	{ 0x03059 , 0x03099 , 0x0305A },
+	{ 0x0305B , 0x03099 , 0x0305C },
+	{ 0x0305D , 0x03099 , 0x0305E },
+	{ 0x0305F , 0x03099 , 0x03060 },
+	{ 0x03061 , 0x03099 , 0x03062 },
+	{ 0x03064 , 0x03099 , 0x03065 },
+	{ 0x03066 , 0x03099 , 0x03067 },
+	{ 0x03068 , 0x03099 , 0x03069 },
+	{ 0x0306F , 0x03099 , 0x03070 },
+	{ 0x0306F , 0x0309A , 0x03071 },
+	{ 0x03072 , 0x03099 , 0x03073 },
+	{ 0x03072 , 0x0309A , 0x03074 },
+	{ 0x03075 , 0x03099 , 0x03076 },
+	{ 0x03075 , 0x0309A , 0x03077 },
+	{ 0x03078 , 0x03099 , 0x03079 },
+	{ 0x03078 , 0x0309A , 0x0307A },
+	{ 0x0307B , 0x03099 , 0x0307C },
+	{ 0x0307B , 0x0309A , 0x0307D },
+	{ 0x0309D , 0x03099 , 0x0309E },
+	{ 0x030A6 , 0x03099 , 0x030F4 },
+	{ 0x030AB , 0x03099 , 0x030AC },
+	{ 0x030AD , 0x03099 , 0x030AE },
+	{ 0x030AF , 0x03099 , 0x030B0 },
+	{ 0x030B1 , 0x03099 , 0x030B2 },
+	{ 0x030B3 , 0x03099 , 0x030B4 },
+	{ 0x030B5 , 0x03099 , 0x030B6 },
+	{ 0x030B7 , 0x03099 , 0x030B8 },
+	{ 0x030B9 , 0x03099 , 0x030BA },
+	{ 0x030BB , 0x03099 , 0x030BC },
+	{ 0x030BD , 0x03099 , 0x030BE },
+	{ 0x030BF , 0x03099 , 0x030C0 },
+	{ 0x030C1 , 0x03099 , 0x030C2 },
+	{ 0x030C4 , 0x03099 , 0x030C5 },
+	{ 0x030C6 , 0x03099 , 0x030C7 },
+	{ 0x030C8 , 0x03099 , 0x030C9 },
+	{ 0x030CF , 0x03099 , 0x030D0 },
+	{ 0x030CF , 0x0309A , 0x030D1 },
+	{ 0x030D2 , 0x03099 , 0x030D3 },
+	{ 0x030D2 , 0x0309A , 0x030D4 },
+	{ 0x030D5 , 0x03099 , 0x030D6 },
+	{ 0x030D5 , 0x0309A , 0x030D7 },
+	{ 0x030D8 , 0x03099 , 0x030D9 },
+	{ 0x030D8 , 0x0309A , 0x030DA },
+	{ 0x030DB , 0x03099 , 0x030DC },
+	{ 0x030DB , 0x0309A , 0x030DD },
+	{ 0x030EF , 0x03099 , 0x030F7 },
+	{ 0x030F0 , 0x03099 , 0x030F8 },
+	{ 0x030F1 , 0x03099 , 0x030F9 },
+	{ 0x030F2 , 0x03099 , 0x030FA },
+	{ 0x030FD , 0x03099 , 0x030FE },
+	{ 0x11099 , 0x110BA , 0x1109A },
+	{ 0x1109B , 0x110BA , 0x1109C },
+	{ 0x110A5 , 0x110BA , 0x110AB },
+};
+
+#define CANONICAL_CLASS_MIN	0x0300
+#define CANONICAL_CLASS_MAX	0x1D244
+
+#define IS_DECOMPOSABLE_BLOCK(uc)	\
+	(((uc)>>8) <= 0x1D2 && u_decomposable_blocks[(uc)>>8])
+static const char u_decomposable_blocks[0x1D2+1] = {
+	0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,0,0,1,1,1,1,1,1,1,0,0,
+	1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,
+	0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
+};
+
+/* Get Canonical Combining Class(CCC). */
+#define CCC(uc)	\
+	(((uc) > 0x1D244)?0:\
+	ccc_val[ccc_val_index[ccc_index[(uc)>>8]][((uc)>>4)&0x0F]][(uc)&0x0F])
+
+/* The table of the value of Canonical Combining Class */
+static const unsigned char ccc_val[][16] = {
+ /* idx=0: XXXX0 - XXXXF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=1: 00300 - 0030F */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=2: 00310 - 0031F */
+ {230, 230, 230, 230, 230, 232, 220, 220, 220, 220, 232, 216, 220, 220, 220, 220 },
+ /* idx=3: 00320 - 0032F */
+ {220, 202, 202, 220, 220, 220, 220, 202, 202, 220, 220, 220, 220, 220, 220, 220 },
+ /* idx=4: 00330 - 0033F */
+ {220, 220, 220, 220, 1, 1, 1, 1, 1, 220, 220, 220, 220, 230, 230, 230 },
+ /* idx=5: 00340 - 0034F */
+ {230, 230, 230, 230, 230, 240, 230, 220, 220, 220, 230, 230, 230, 220, 220, 0 },
+ /* idx=6: 00350 - 0035F */
+ {230, 230, 230, 220, 220, 220, 220, 230, 232, 220, 220, 230, 233, 234, 234, 233 },
+ /* idx=7: 00360 - 0036F */
+ {234, 234, 233, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=8: 00480 - 0048F */
+ {0, 0, 0, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=9: 00590 - 0059F */
+ {0, 220, 230, 230, 230, 230, 220, 230, 230, 230, 222, 220, 230, 230, 230, 230 },
+ /* idx=10: 005A0 - 005AF */
+ {230, 230, 220, 220, 220, 220, 220, 220, 230, 230, 220, 230, 230, 222, 228, 230 },
+ /* idx=11: 005B0 - 005BF */
+ {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23 },
+ /* idx=12: 005C0 - 005CF */
+ {0, 24, 25, 0, 230, 220, 0, 18, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=13: 00610 - 0061F */
+ {230, 230, 230, 230, 230, 230, 230, 230, 30, 31, 32, 0, 0, 0, 0, 0 },
+ /* idx=14: 00640 - 0064F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31 },
+ /* idx=15: 00650 - 0065F */
+ {32, 33, 34, 230, 230, 220, 220, 230, 230, 230, 230, 230, 220, 230, 230, 220 },
+ /* idx=16: 00670 - 0067F */
+ {35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=17: 006D0 - 006DF */
+ {0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 230, 230, 230, 0, 0, 230 },
+ /* idx=18: 006E0 - 006EF */
+ {230, 230, 230, 220, 230, 0, 0, 230, 230, 0, 220, 230, 230, 220, 0, 0 },
+ /* idx=19: 00710 - 0071F */
+ {0, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=20: 00730 - 0073F */
+ {230, 220, 230, 230, 220, 230, 230, 220, 220, 220, 230, 220, 220, 230, 220, 230 },
+ /* idx=21: 00740 - 0074F */
+ {230, 230, 220, 230, 220, 230, 220, 230, 220, 230, 230, 0, 0, 0, 0, 0 },
+ /* idx=22: 007E0 - 007EF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 230 },
+ /* idx=23: 007F0 - 007FF */
+ {230, 230, 220, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=24: 00810 - 0081F */
+ {0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 230, 230, 230, 230, 230 },
+ /* idx=25: 00820 - 0082F */
+ {230, 230, 230, 230, 0, 230, 230, 230, 0, 230, 230, 230, 230, 230, 0, 0 },
+ /* idx=26: 00850 - 0085F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 0, 0, 0, 0 },
+ /* idx=27: 00930 - 0093F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=28: 00940 - 0094F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=29: 00950 - 0095F */
+ {0, 230, 220, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=30: 009B0 - 009BF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=31: 009C0 - 009CF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=32: 00A30 - 00A3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=33: 00A40 - 00A4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=34: 00AB0 - 00ABF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=35: 00AC0 - 00ACF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=36: 00B30 - 00B3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=37: 00B40 - 00B4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=38: 00BC0 - 00BCF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=39: 00C40 - 00C4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=40: 00C50 - 00C5F */
+ {0, 0, 0, 0, 0, 84, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=41: 00CB0 - 00CBF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0 },
+ /* idx=42: 00CC0 - 00CCF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=43: 00D40 - 00D4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=44: 00DC0 - 00DCF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0 },
+ /* idx=45: 00E30 - 00E3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 103, 103, 9, 0, 0, 0, 0, 0 },
+ /* idx=46: 00E40 - 00E4F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 107, 107, 107, 107, 0, 0, 0, 0 },
+ /* idx=47: 00EB0 - 00EBF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 118, 118, 0, 0, 0, 0, 0, 0 },
+ /* idx=48: 00EC0 - 00ECF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 122, 122, 122, 122, 0, 0, 0, 0 },
+ /* idx=49: 00F10 - 00F1F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 0, 0, 0, 0, 0, 0 },
+ /* idx=50: 00F30 - 00F3F */
+ {0, 0, 0, 0, 0, 220, 0, 220, 0, 216, 0, 0, 0, 0, 0, 0 },
+ /* idx=51: 00F70 - 00F7F */
+ {0, 129, 130, 0, 132, 0, 0, 0, 0, 0, 130, 130, 130, 130, 0, 0 },
+ /* idx=52: 00F80 - 00F8F */
+ {130, 0, 230, 230, 9, 0, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=53: 00FC0 - 00FCF */
+ {0, 0, 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=54: 01030 - 0103F */
+ {0, 0, 0, 0, 0, 0, 0, 7, 0, 9, 9, 0, 0, 0, 0, 0 },
+ /* idx=55: 01080 - 0108F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0 },
+ /* idx=56: 01350 - 0135F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230 },
+ /* idx=57: 01710 - 0171F */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=58: 01730 - 0173F */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=59: 017D0 - 017DF */
+ {0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, 0 },
+ /* idx=60: 018A0 - 018AF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 0, 0, 0 },
+ /* idx=61: 01930 - 0193F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 222, 230, 220, 0, 0, 0, 0 },
+ /* idx=62: 01A10 - 01A1F */
+ {0, 0, 0, 0, 0, 0, 0, 230, 220, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=63: 01A60 - 01A6F */
+ {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=64: 01A70 - 01A7F */
+ {0, 0, 0, 0, 0, 230, 230, 230, 230, 230, 230, 230, 230, 0, 0, 220 },
+ /* idx=65: 01B30 - 01B3F */
+ {0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=66: 01B40 - 01B4F */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=67: 01B60 - 01B6F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 220, 230, 230, 230 },
+ /* idx=68: 01B70 - 01B7F */
+ {230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=69: 01BA0 - 01BAF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0 },
+ /* idx=70: 01BE0 - 01BEF */
+ {0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=71: 01BF0 - 01BFF */
+ {0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=72: 01C30 - 01C3F */
+ {0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=73: 01CD0 - 01CDF */
+ {230, 230, 230, 0, 1, 220, 220, 220, 220, 220, 230, 230, 220, 220, 220, 220 },
+ /* idx=74: 01CE0 - 01CEF */
+ {230, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 220, 0, 0 },
+ /* idx=75: 01DC0 - 01DCF */
+ {230, 230, 220, 230, 230, 230, 230, 230, 230, 230, 220, 230, 230, 234, 214, 220 },
+ /* idx=76: 01DD0 - 01DDF */
+ {202, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=77: 01DE0 - 01DEF */
+ {230, 230, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=78: 01DF0 - 01DFF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 233, 220, 230, 220 },
+ /* idx=79: 020D0 - 020DF */
+ {230, 230, 1, 1, 230, 230, 230, 230, 1, 1, 1, 230, 230, 0, 0, 0 },
+ /* idx=80: 020E0 - 020EF */
+ {0, 230, 0, 0, 0, 1, 1, 230, 220, 230, 1, 1, 220, 220, 220, 220 },
+ /* idx=81: 020F0 - 020FF */
+ {230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=82: 02CE0 - 02CEF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230 },
+ /* idx=83: 02CF0 - 02CFF */
+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=84: 02D70 - 02D7F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 },
+ /* idx=85: 02DE0 - 02DEF */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=86: 02DF0 - 02DFF */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=87: 03020 - 0302F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 218, 228, 232, 222, 224, 224 },
+ /* idx=88: 03090 - 0309F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0 },
+ /* idx=89: 0A660 - 0A66F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230 },
+ /* idx=90: 0A670 - 0A67F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 0, 0 },
+ /* idx=91: 0A6F0 - 0A6FF */
+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=92: 0A800 - 0A80F */
+ {0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=93: 0A8C0 - 0A8CF */
+ {0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=94: 0A8E0 - 0A8EF */
+ {230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230 },
+ /* idx=95: 0A8F0 - 0A8FF */
+ {230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=96: 0A920 - 0A92F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 0, 0 },
+ /* idx=97: 0A950 - 0A95F */
+ {0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=98: 0A9B0 - 0A9BF */
+ {0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=99: 0A9C0 - 0A9CF */
+ {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=100: 0AAB0 - 0AABF */
+ {230, 0, 230, 230, 220, 0, 0, 230, 230, 0, 0, 0, 0, 0, 230, 230 },
+ /* idx=101: 0AAC0 - 0AACF */
+ {0, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=102: 0ABE0 - 0ABEF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0 },
+ /* idx=103: 0FB10 - 0FB1F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0 },
+ /* idx=104: 0FE20 - 0FE2F */
+ {230, 230, 230, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=105: 101F0 - 101FF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0 },
+ /* idx=106: 10A00 - 10A0F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, 230 },
+ /* idx=107: 10A30 - 10A3F */
+ {0, 0, 0, 0, 0, 0, 0, 0, 230, 1, 220, 0, 0, 0, 0, 9 },
+ /* idx=108: 11040 - 1104F */
+ {0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=109: 110B0 - 110BF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 7, 0, 0, 0, 0, 0 },
+ /* idx=110: 1D160 - 1D16F */
+ {0, 0, 0, 0, 0, 216, 216, 1, 1, 1, 0, 0, 0, 226, 216, 216 },
+ /* idx=111: 1D170 - 1D17F */
+ {216, 216, 216, 0, 0, 0, 0, 0, 0, 0, 0, 220, 220, 220, 220, 220 },
+ /* idx=112: 1D180 - 1D18F */
+ {220, 220, 220, 0, 0, 230, 230, 230, 230, 230, 220, 220, 0, 0, 0, 0 },
+ /* idx=113: 1D1A0 - 1D1AF */
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 230, 230, 230, 0, 0 },
+ /* idx=114: 1D240 - 1D24F */
+ {0, 0, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* The index table to ccc_val[*][16] */
+static const unsigned char ccc_val_index[][16] = {
+ /* idx=0: XXX00 - XXXFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=1: 00300 - 003FF */
+ { 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=2: 00400 - 004FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=3: 00500 - 005FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,10,11,12, 0, 0, 0 },
+ /* idx=4: 00600 - 006FF */
+ { 0,13, 0, 0,14,15, 0,16, 0, 0, 0, 0, 0,17,18, 0 },
+ /* idx=5: 00700 - 007FF */
+ { 0,19, 0,20,21, 0, 0, 0, 0, 0, 0, 0, 0, 0,22,23 },
+ /* idx=6: 00800 - 008FF */
+ { 0,24,25, 0, 0,26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=7: 00900 - 009FF */
+ { 0, 0, 0,27,28,29, 0, 0, 0, 0, 0,30,31, 0, 0, 0 },
+ /* idx=8: 00A00 - 00AFF */
+ { 0, 0, 0,32,33, 0, 0, 0, 0, 0, 0,34,35, 0, 0, 0 },
+ /* idx=9: 00B00 - 00BFF */
+ { 0, 0, 0,36,37, 0, 0, 0, 0, 0, 0, 0,38, 0, 0, 0 },
+ /* idx=10: 00C00 - 00CFF */
+ { 0, 0, 0, 0,39,40, 0, 0, 0, 0, 0,41,42, 0, 0, 0 },
+ /* idx=11: 00D00 - 00DFF */
+ { 0, 0, 0, 0,43, 0, 0, 0, 0, 0, 0, 0,44, 0, 0, 0 },
+ /* idx=12: 00E00 - 00EFF */
+ { 0, 0, 0,45,46, 0, 0, 0, 0, 0, 0,47,48, 0, 0, 0 },
+ /* idx=13: 00F00 - 00FFF */
+ { 0,49, 0,50, 0, 0, 0,51,52, 0, 0, 0,53, 0, 0, 0 },
+ /* idx=14: 01000 - 010FF */
+ { 0, 0, 0,54, 0, 0, 0, 0,55, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=15: 01300 - 013FF */
+ { 0, 0, 0, 0, 0,56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=16: 01700 - 017FF */
+ { 0,57, 0,58, 0, 0, 0, 0, 0, 0, 0, 0, 0,59, 0, 0 },
+ /* idx=17: 01800 - 018FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,60, 0, 0, 0, 0, 0 },
+ /* idx=18: 01900 - 019FF */
+ { 0, 0, 0,61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=19: 01A00 - 01AFF */
+ { 0,62, 0, 0, 0, 0,63,64, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=20: 01B00 - 01BFF */
+ { 0, 0, 0,65,66, 0,67,68, 0, 0,69, 0, 0, 0,70,71 },
+ /* idx=21: 01C00 - 01CFF */
+ { 0, 0, 0,72, 0, 0, 0, 0, 0, 0, 0, 0, 0,73,74, 0 },
+ /* idx=22: 01D00 - 01DFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,75,76,77,78 },
+ /* idx=23: 02000 - 020FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,79,80,81 },
+ /* idx=24: 02C00 - 02CFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,82,83 },
+ /* idx=25: 02D00 - 02DFF */
+ { 0, 0, 0, 0, 0, 0, 0,84, 0, 0, 0, 0, 0, 0,85,86 },
+ /* idx=26: 03000 - 030FF */
+ { 0, 0,87, 0, 0, 0, 0, 0, 0,88, 0, 0, 0, 0, 0, 0 },
+ /* idx=27: 0A600 - 0A6FF */
+ { 0, 0, 0, 0, 0, 0,89,90, 0, 0, 0, 0, 0, 0, 0,91 },
+ /* idx=28: 0A800 - 0A8FF */
+ {92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,93, 0,94,95 },
+ /* idx=29: 0A900 - 0A9FF */
+ { 0, 0,96, 0, 0,97, 0, 0, 0, 0, 0,98,99, 0, 0, 0 },
+ /* idx=30: 0AA00 - 0AAFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,100,101, 0, 0, 0 },
+ /* idx=31: 0AB00 - 0ABFF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,102, 0 },
+ /* idx=32: 0FB00 - 0FBFF */
+ { 0,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=33: 0FE00 - 0FEFF */
+ { 0, 0,104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=34: 10100 - 101FF */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,105 },
+ /* idx=35: 10A00 - 10AFF */
+ {106, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ /* idx=36: 11000 - 110FF */
+ { 0, 0, 0, 0,108, 0, 0, 0, 0, 0, 0,109, 0, 0, 0, 0 },
+ /* idx=37: 1D100 - 1D1FF */
+ { 0, 0, 0, 0, 0, 0,110,111,112, 0,113, 0, 0, 0, 0, 0 },
+ /* idx=38: 1D200 - 1D2FF */
+ { 0, 0, 0, 0,114, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+};
+
+/* The index table to ccc_val_index[*][16] */
+static const unsigned char ccc_index[] = {
+  0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 0, 0,15, 0, 0, 0,16,
+ 17,18,19,20,21,22, 0, 0,23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,24,25, 0, 0,
+ 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,27, 0,
+ 28,29,30,31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,32, 0, 0,33, 0, 0,34, 0, 0, 0, 0, 0, 0,
+  0, 0,35, 0, 0, 0, 0, 0,36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  0, 0, 0, 0, 0, 0, 0, 0, 0,37,38,};
+
+struct unicode_decomposition_table {
+	uint32_t nfc;
+	uint32_t cp1;
+	uint32_t cp2;
+};
+
+static const struct unicode_decomposition_table u_decomposition_table[] = {
+	{ 0x000C0 , 0x00041 , 0x00300 },
+	{ 0x000C1 , 0x00041 , 0x00301 },
+	{ 0x000C2 , 0x00041 , 0x00302 },
+	{ 0x000C3 , 0x00041 , 0x00303 },
+	{ 0x000C4 , 0x00041 , 0x00308 },
+	{ 0x000C5 , 0x00041 , 0x0030A },
+	{ 0x000C7 , 0x00043 , 0x00327 },
+	{ 0x000C8 , 0x00045 , 0x00300 },
+	{ 0x000C9 , 0x00045 , 0x00301 },
+	{ 0x000CA , 0x00045 , 0x00302 },
+	{ 0x000CB , 0x00045 , 0x00308 },
+	{ 0x000CC , 0x00049 , 0x00300 },
+	{ 0x000CD , 0x00049 , 0x00301 },
+	{ 0x000CE , 0x00049 , 0x00302 },
+	{ 0x000CF , 0x00049 , 0x00308 },
+	{ 0x000D1 , 0x0004E , 0x00303 },
+	{ 0x000D2 , 0x0004F , 0x00300 },
+	{ 0x000D3 , 0x0004F , 0x00301 },
+	{ 0x000D4 , 0x0004F , 0x00302 },
+	{ 0x000D5 , 0x0004F , 0x00303 },
+	{ 0x000D6 , 0x0004F , 0x00308 },
+	{ 0x000D9 , 0x00055 , 0x00300 },
+	{ 0x000DA , 0x00055 , 0x00301 },
+	{ 0x000DB , 0x00055 , 0x00302 },
+	{ 0x000DC , 0x00055 , 0x00308 },
+	{ 0x000DD , 0x00059 , 0x00301 },
+	{ 0x000E0 , 0x00061 , 0x00300 },
+	{ 0x000E1 , 0x00061 , 0x00301 },
+	{ 0x000E2 , 0x00061 , 0x00302 },
+	{ 0x000E3 , 0x00061 , 0x00303 },
+	{ 0x000E4 , 0x00061 , 0x00308 },
+	{ 0x000E5 , 0x00061 , 0x0030A },
+	{ 0x000E7 , 0x00063 , 0x00327 },
+	{ 0x000E8 , 0x00065 , 0x00300 },
+	{ 0x000E9 , 0x00065 , 0x00301 },
+	{ 0x000EA , 0x00065 , 0x00302 },
+	{ 0x000EB , 0x00065 , 0x00308 },
+	{ 0x000EC , 0x00069 , 0x00300 },
+	{ 0x000ED , 0x00069 , 0x00301 },
+	{ 0x000EE , 0x00069 , 0x00302 },
+	{ 0x000EF , 0x00069 , 0x00308 },
+	{ 0x000F1 , 0x0006E , 0x00303 },
+	{ 0x000F2 , 0x0006F , 0x00300 },
+	{ 0x000F3 , 0x0006F , 0x00301 },
+	{ 0x000F4 , 0x0006F , 0x00302 },
+	{ 0x000F5 , 0x0006F , 0x00303 },
+	{ 0x000F6 , 0x0006F , 0x00308 },
+	{ 0x000F9 , 0x00075 , 0x00300 },
+	{ 0x000FA , 0x00075 , 0x00301 },
+	{ 0x000FB , 0x00075 , 0x00302 },
+	{ 0x000FC , 0x00075 , 0x00308 },
+	{ 0x000FD , 0x00079 , 0x00301 },
+	{ 0x000FF , 0x00079 , 0x00308 },
+	{ 0x00100 , 0x00041 , 0x00304 },
+	{ 0x00101 , 0x00061 , 0x00304 },
+	{ 0x00102 , 0x00041 , 0x00306 },
+	{ 0x00103 , 0x00061 , 0x00306 },
+	{ 0x00104 , 0x00041 , 0x00328 },
+	{ 0x00105 , 0x00061 , 0x00328 },
+	{ 0x00106 , 0x00043 , 0x00301 },
+	{ 0x00107 , 0x00063 , 0x00301 },
+	{ 0x00108 , 0x00043 , 0x00302 },
+	{ 0x00109 , 0x00063 , 0x00302 },
+	{ 0x0010A , 0x00043 , 0x00307 },
+	{ 0x0010B , 0x00063 , 0x00307 },
+	{ 0x0010C , 0x00043 , 0x0030C },
+	{ 0x0010D , 0x00063 , 0x0030C },
+	{ 0x0010E , 0x00044 , 0x0030C },
+	{ 0x0010F , 0x00064 , 0x0030C },
+	{ 0x00112 , 0x00045 , 0x00304 },
+	{ 0x00113 , 0x00065 , 0x00304 },
+	{ 0x00114 , 0x00045 , 0x00306 },
+	{ 0x00115 , 0x00065 , 0x00306 },
+	{ 0x00116 , 0x00045 , 0x00307 },
+	{ 0x00117 , 0x00065 , 0x00307 },
+	{ 0x00118 , 0x00045 , 0x00328 },
+	{ 0x00119 , 0x00065 , 0x00328 },
+	{ 0x0011A , 0x00045 , 0x0030C },
+	{ 0x0011B , 0x00065 , 0x0030C },
+	{ 0x0011C , 0x00047 , 0x00302 },
+	{ 0x0011D , 0x00067 , 0x00302 },
+	{ 0x0011E , 0x00047 , 0x00306 },
+	{ 0x0011F , 0x00067 , 0x00306 },
+	{ 0x00120 , 0x00047 , 0x00307 },
+	{ 0x00121 , 0x00067 , 0x00307 },
+	{ 0x00122 , 0x00047 , 0x00327 },
+	{ 0x00123 , 0x00067 , 0x00327 },
+	{ 0x00124 , 0x00048 , 0x00302 },
+	{ 0x00125 , 0x00068 , 0x00302 },
+	{ 0x00128 , 0x00049 , 0x00303 },
+	{ 0x00129 , 0x00069 , 0x00303 },
+	{ 0x0012A , 0x00049 , 0x00304 },
+	{ 0x0012B , 0x00069 , 0x00304 },
+	{ 0x0012C , 0x00049 , 0x00306 },
+	{ 0x0012D , 0x00069 , 0x00306 },
+	{ 0x0012E , 0x00049 , 0x00328 },
+	{ 0x0012F , 0x00069 , 0x00328 },
+	{ 0x00130 , 0x00049 , 0x00307 },
+	{ 0x00134 , 0x0004A , 0x00302 },
+	{ 0x00135 , 0x0006A , 0x00302 },
+	{ 0x00136 , 0x0004B , 0x00327 },
+	{ 0x00137 , 0x0006B , 0x00327 },
+	{ 0x00139 , 0x0004C , 0x00301 },
+	{ 0x0013A , 0x0006C , 0x00301 },
+	{ 0x0013B , 0x0004C , 0x00327 },
+	{ 0x0013C , 0x0006C , 0x00327 },
+	{ 0x0013D , 0x0004C , 0x0030C },
+	{ 0x0013E , 0x0006C , 0x0030C },
+	{ 0x00143 , 0x0004E , 0x00301 },
+	{ 0x00144 , 0x0006E , 0x00301 },
+	{ 0x00145 , 0x0004E , 0x00327 },
+	{ 0x00146 , 0x0006E , 0x00327 },
+	{ 0x00147 , 0x0004E , 0x0030C },
+	{ 0x00148 , 0x0006E , 0x0030C },
+	{ 0x0014C , 0x0004F , 0x00304 },
+	{ 0x0014D , 0x0006F , 0x00304 },
+	{ 0x0014E , 0x0004F , 0x00306 },
+	{ 0x0014F , 0x0006F , 0x00306 },
+	{ 0x00150 , 0x0004F , 0x0030B },
+	{ 0x00151 , 0x0006F , 0x0030B },
+	{ 0x00154 , 0x00052 , 0x00301 },
+	{ 0x00155 , 0x00072 , 0x00301 },
+	{ 0x00156 , 0x00052 , 0x00327 },
+	{ 0x00157 , 0x00072 , 0x00327 },
+	{ 0x00158 , 0x00052 , 0x0030C },
+	{ 0x00159 , 0x00072 , 0x0030C },
+	{ 0x0015A , 0x00053 , 0x00301 },
+	{ 0x0015B , 0x00073 , 0x00301 },
+	{ 0x0015C , 0x00053 , 0x00302 },
+	{ 0x0015D , 0x00073 , 0x00302 },
+	{ 0x0015E , 0x00053 , 0x00327 },
+	{ 0x0015F , 0x00073 , 0x00327 },
+	{ 0x00160 , 0x00053 , 0x0030C },
+	{ 0x00161 , 0x00073 , 0x0030C },
+	{ 0x00162 , 0x00054 , 0x00327 },
+	{ 0x00163 , 0x00074 , 0x00327 },
+	{ 0x00164 , 0x00054 , 0x0030C },
+	{ 0x00165 , 0x00074 , 0x0030C },
+	{ 0x00168 , 0x00055 , 0x00303 },
+	{ 0x00169 , 0x00075 , 0x00303 },
+	{ 0x0016A , 0x00055 , 0x00304 },
+	{ 0x0016B , 0x00075 , 0x00304 },
+	{ 0x0016C , 0x00055 , 0x00306 },
+	{ 0x0016D , 0x00075 , 0x00306 },
+	{ 0x0016E , 0x00055 , 0x0030A },
+	{ 0x0016F , 0x00075 , 0x0030A },
+	{ 0x00170 , 0x00055 , 0x0030B },
+	{ 0x00171 , 0x00075 , 0x0030B },
+	{ 0x00172 , 0x00055 , 0x00328 },
+	{ 0x00173 , 0x00075 , 0x00328 },
+	{ 0x00174 , 0x00057 , 0x00302 },
+	{ 0x00175 , 0x00077 , 0x00302 },
+	{ 0x00176 , 0x00059 , 0x00302 },
+	{ 0x00177 , 0x00079 , 0x00302 },
+	{ 0x00178 , 0x00059 , 0x00308 },
+	{ 0x00179 , 0x0005A , 0x00301 },
+	{ 0x0017A , 0x0007A , 0x00301 },
+	{ 0x0017B , 0x0005A , 0x00307 },
+	{ 0x0017C , 0x0007A , 0x00307 },
+	{ 0x0017D , 0x0005A , 0x0030C },
+	{ 0x0017E , 0x0007A , 0x0030C },
+	{ 0x001A0 , 0x0004F , 0x0031B },
+	{ 0x001A1 , 0x0006F , 0x0031B },
+	{ 0x001AF , 0x00055 , 0x0031B },
+	{ 0x001B0 , 0x00075 , 0x0031B },
+	{ 0x001CD , 0x00041 , 0x0030C },
+	{ 0x001CE , 0x00061 , 0x0030C },
+	{ 0x001CF , 0x00049 , 0x0030C },
+	{ 0x001D0 , 0x00069 , 0x0030C },
+	{ 0x001D1 , 0x0004F , 0x0030C },
+	{ 0x001D2 , 0x0006F , 0x0030C },
+	{ 0x001D3 , 0x00055 , 0x0030C },
+	{ 0x001D4 , 0x00075 , 0x0030C },
+	{ 0x001D5 , 0x000DC , 0x00304 },
+	{ 0x001D6 , 0x000FC , 0x00304 },
+	{ 0x001D7 , 0x000DC , 0x00301 },
+	{ 0x001D8 , 0x000FC , 0x00301 },
+	{ 0x001D9 , 0x000DC , 0x0030C },
+	{ 0x001DA , 0x000FC , 0x0030C },
+	{ 0x001DB , 0x000DC , 0x00300 },
+	{ 0x001DC , 0x000FC , 0x00300 },
+	{ 0x001DE , 0x000C4 , 0x00304 },
+	{ 0x001DF , 0x000E4 , 0x00304 },
+	{ 0x001E0 , 0x00226 , 0x00304 },
+	{ 0x001E1 , 0x00227 , 0x00304 },
+	{ 0x001E2 , 0x000C6 , 0x00304 },
+	{ 0x001E3 , 0x000E6 , 0x00304 },
+	{ 0x001E6 , 0x00047 , 0x0030C },
+	{ 0x001E7 , 0x00067 , 0x0030C },
+	{ 0x001E8 , 0x0004B , 0x0030C },
+	{ 0x001E9 , 0x0006B , 0x0030C },
+	{ 0x001EA , 0x0004F , 0x00328 },
+	{ 0x001EB , 0x0006F , 0x00328 },
+	{ 0x001EC , 0x001EA , 0x00304 },
+	{ 0x001ED , 0x001EB , 0x00304 },
+	{ 0x001EE , 0x001B7 , 0x0030C },
+	{ 0x001EF , 0x00292 , 0x0030C },
+	{ 0x001F0 , 0x0006A , 0x0030C },
+	{ 0x001F4 , 0x00047 , 0x00301 },
+	{ 0x001F5 , 0x00067 , 0x00301 },
+	{ 0x001F8 , 0x0004E , 0x00300 },
+	{ 0x001F9 , 0x0006E , 0x00300 },
+	{ 0x001FA , 0x000C5 , 0x00301 },
+	{ 0x001FB , 0x000E5 , 0x00301 },
+	{ 0x001FC , 0x000C6 , 0x00301 },
+	{ 0x001FD , 0x000E6 , 0x00301 },
+	{ 0x001FE , 0x000D8 , 0x00301 },
+	{ 0x001FF , 0x000F8 , 0x00301 },
+	{ 0x00200 , 0x00041 , 0x0030F },
+	{ 0x00201 , 0x00061 , 0x0030F },
+	{ 0x00202 , 0x00041 , 0x00311 },
+	{ 0x00203 , 0x00061 , 0x00311 },
+	{ 0x00204 , 0x00045 , 0x0030F },
+	{ 0x00205 , 0x00065 , 0x0030F },
+	{ 0x00206 , 0x00045 , 0x00311 },
+	{ 0x00207 , 0x00065 , 0x00311 },
+	{ 0x00208 , 0x00049 , 0x0030F },
+	{ 0x00209 , 0x00069 , 0x0030F },
+	{ 0x0020A , 0x00049 , 0x00311 },
+	{ 0x0020B , 0x00069 , 0x00311 },
+	{ 0x0020C , 0x0004F , 0x0030F },
+	{ 0x0020D , 0x0006F , 0x0030F },
+	{ 0x0020E , 0x0004F , 0x00311 },
+	{ 0x0020F , 0x0006F , 0x00311 },
+	{ 0x00210 , 0x00052 , 0x0030F },
+	{ 0x00211 , 0x00072 , 0x0030F },
+	{ 0x00212 , 0x00052 , 0x00311 },
+	{ 0x00213 , 0x00072 , 0x00311 },
+	{ 0x00214 , 0x00055 , 0x0030F },
+	{ 0x00215 , 0x00075 , 0x0030F },
+	{ 0x00216 , 0x00055 , 0x00311 },
+	{ 0x00217 , 0x00075 , 0x00311 },
+	{ 0x00218 , 0x00053 , 0x00326 },
+	{ 0x00219 , 0x00073 , 0x00326 },
+	{ 0x0021A , 0x00054 , 0x00326 },
+	{ 0x0021B , 0x00074 , 0x00326 },
+	{ 0x0021E , 0x00048 , 0x0030C },
+	{ 0x0021F , 0x00068 , 0x0030C },
+	{ 0x00226 , 0x00041 , 0x00307 },
+	{ 0x00227 , 0x00061 , 0x00307 },
+	{ 0x00228 , 0x00045 , 0x00327 },
+	{ 0x00229 , 0x00065 , 0x00327 },
+	{ 0x0022A , 0x000D6 , 0x00304 },
+	{ 0x0022B , 0x000F6 , 0x00304 },
+	{ 0x0022C , 0x000D5 , 0x00304 },
+	{ 0x0022D , 0x000F5 , 0x00304 },
+	{ 0x0022E , 0x0004F , 0x00307 },
+	{ 0x0022F , 0x0006F , 0x00307 },
+	{ 0x00230 , 0x0022E , 0x00304 },
+	{ 0x00231 , 0x0022F , 0x00304 },
+	{ 0x00232 , 0x00059 , 0x00304 },
+	{ 0x00233 , 0x00079 , 0x00304 },
+	{ 0x00385 , 0x000A8 , 0x00301 },
+	{ 0x00386 , 0x00391 , 0x00301 },
+	{ 0x00388 , 0x00395 , 0x00301 },
+	{ 0x00389 , 0x00397 , 0x00301 },
+	{ 0x0038A , 0x00399 , 0x00301 },
+	{ 0x0038C , 0x0039F , 0x00301 },
+	{ 0x0038E , 0x003A5 , 0x00301 },
+	{ 0x0038F , 0x003A9 , 0x00301 },
+	{ 0x00390 , 0x003CA , 0x00301 },
+	{ 0x003AA , 0x00399 , 0x00308 },
+	{ 0x003AB , 0x003A5 , 0x00308 },
+	{ 0x003AC , 0x003B1 , 0x00301 },
+	{ 0x003AD , 0x003B5 , 0x00301 },
+	{ 0x003AE , 0x003B7 , 0x00301 },
+	{ 0x003AF , 0x003B9 , 0x00301 },
+	{ 0x003B0 , 0x003CB , 0x00301 },
+	{ 0x003CA , 0x003B9 , 0x00308 },
+	{ 0x003CB , 0x003C5 , 0x00308 },
+	{ 0x003CC , 0x003BF , 0x00301 },
+	{ 0x003CD , 0x003C5 , 0x00301 },
+	{ 0x003CE , 0x003C9 , 0x00301 },
+	{ 0x003D3 , 0x003D2 , 0x00301 },
+	{ 0x003D4 , 0x003D2 , 0x00308 },
+	{ 0x00400 , 0x00415 , 0x00300 },
+	{ 0x00401 , 0x00415 , 0x00308 },
+	{ 0x00403 , 0x00413 , 0x00301 },
+	{ 0x00407 , 0x00406 , 0x00308 },
+	{ 0x0040C , 0x0041A , 0x00301 },
+	{ 0x0040D , 0x00418 , 0x00300 },
+	{ 0x0040E , 0x00423 , 0x00306 },
+	{ 0x00419 , 0x00418 , 0x00306 },
+	{ 0x00439 , 0x00438 , 0x00306 },
+	{ 0x00450 , 0x00435 , 0x00300 },
+	{ 0x00451 , 0x00435 , 0x00308 },
+	{ 0x00453 , 0x00433 , 0x00301 },
+	{ 0x00457 , 0x00456 , 0x00308 },
+	{ 0x0045C , 0x0043A , 0x00301 },
+	{ 0x0045D , 0x00438 , 0x00300 },
+	{ 0x0045E , 0x00443 , 0x00306 },
+	{ 0x00476 , 0x00474 , 0x0030F },
+	{ 0x00477 , 0x00475 , 0x0030F },
+	{ 0x004C1 , 0x00416 , 0x00306 },
+	{ 0x004C2 , 0x00436 , 0x00306 },
+	{ 0x004D0 , 0x00410 , 0x00306 },
+	{ 0x004D1 , 0x00430 , 0x00306 },
+	{ 0x004D2 , 0x00410 , 0x00308 },
+	{ 0x004D3 , 0x00430 , 0x00308 },
+	{ 0x004D6 , 0x00415 , 0x00306 },
+	{ 0x004D7 , 0x00435 , 0x00306 },
+	{ 0x004DA , 0x004D8 , 0x00308 },
+	{ 0x004DB , 0x004D9 , 0x00308 },
+	{ 0x004DC , 0x00416 , 0x00308 },
+	{ 0x004DD , 0x00436 , 0x00308 },
+	{ 0x004DE , 0x00417 , 0x00308 },
+	{ 0x004DF , 0x00437 , 0x00308 },
+	{ 0x004E2 , 0x00418 , 0x00304 },
+	{ 0x004E3 , 0x00438 , 0x00304 },
+	{ 0x004E4 , 0x00418 , 0x00308 },
+	{ 0x004E5 , 0x00438 , 0x00308 },
+	{ 0x004E6 , 0x0041E , 0x00308 },
+	{ 0x004E7 , 0x0043E , 0x00308 },
+	{ 0x004EA , 0x004E8 , 0x00308 },
+	{ 0x004EB , 0x004E9 , 0x00308 },
+	{ 0x004EC , 0x0042D , 0x00308 },
+	{ 0x004ED , 0x0044D , 0x00308 },
+	{ 0x004EE , 0x00423 , 0x00304 },
+	{ 0x004EF , 0x00443 , 0x00304 },
+	{ 0x004F0 , 0x00423 , 0x00308 },
+	{ 0x004F1 , 0x00443 , 0x00308 },
+	{ 0x004F2 , 0x00423 , 0x0030B },
+	{ 0x004F3 , 0x00443 , 0x0030B },
+	{ 0x004F4 , 0x00427 , 0x00308 },
+	{ 0x004F5 , 0x00447 , 0x00308 },
+	{ 0x004F8 , 0x0042B , 0x00308 },
+	{ 0x004F9 , 0x0044B , 0x00308 },
+	{ 0x00622 , 0x00627 , 0x00653 },
+	{ 0x00623 , 0x00627 , 0x00654 },
+	{ 0x00624 , 0x00648 , 0x00654 },
+	{ 0x00625 , 0x00627 , 0x00655 },
+	{ 0x00626 , 0x0064A , 0x00654 },
+	{ 0x006C0 , 0x006D5 , 0x00654 },
+	{ 0x006C2 , 0x006C1 , 0x00654 },
+	{ 0x006D3 , 0x006D2 , 0x00654 },
+	{ 0x00929 , 0x00928 , 0x0093C },
+	{ 0x00931 , 0x00930 , 0x0093C },
+	{ 0x00934 , 0x00933 , 0x0093C },
+	{ 0x009CB , 0x009C7 , 0x009BE },
+	{ 0x009CC , 0x009C7 , 0x009D7 },
+	{ 0x00B48 , 0x00B47 , 0x00B56 },
+	{ 0x00B4B , 0x00B47 , 0x00B3E },
+	{ 0x00B4C , 0x00B47 , 0x00B57 },
+	{ 0x00B94 , 0x00B92 , 0x00BD7 },
+	{ 0x00BCA , 0x00BC6 , 0x00BBE },
+	{ 0x00BCB , 0x00BC7 , 0x00BBE },
+	{ 0x00BCC , 0x00BC6 , 0x00BD7 },
+	{ 0x00C48 , 0x00C46 , 0x00C56 },
+	{ 0x00CC0 , 0x00CBF , 0x00CD5 },
+	{ 0x00CC7 , 0x00CC6 , 0x00CD5 },
+	{ 0x00CC8 , 0x00CC6 , 0x00CD6 },
+	{ 0x00CCA , 0x00CC6 , 0x00CC2 },
+	{ 0x00CCB , 0x00CCA , 0x00CD5 },
+	{ 0x00D4A , 0x00D46 , 0x00D3E },
+	{ 0x00D4B , 0x00D47 , 0x00D3E },
+	{ 0x00D4C , 0x00D46 , 0x00D57 },
+	{ 0x00DDA , 0x00DD9 , 0x00DCA },
+	{ 0x00DDC , 0x00DD9 , 0x00DCF },
+	{ 0x00DDD , 0x00DDC , 0x00DCA },
+	{ 0x00DDE , 0x00DD9 , 0x00DDF },
+	{ 0x01026 , 0x01025 , 0x0102E },
+	{ 0x01B06 , 0x01B05 , 0x01B35 },
+	{ 0x01B08 , 0x01B07 , 0x01B35 },
+	{ 0x01B0A , 0x01B09 , 0x01B35 },
+	{ 0x01B0C , 0x01B0B , 0x01B35 },
+	{ 0x01B0E , 0x01B0D , 0x01B35 },
+	{ 0x01B12 , 0x01B11 , 0x01B35 },
+	{ 0x01B3B , 0x01B3A , 0x01B35 },
+	{ 0x01B3D , 0x01B3C , 0x01B35 },
+	{ 0x01B40 , 0x01B3E , 0x01B35 },
+	{ 0x01B41 , 0x01B3F , 0x01B35 },
+	{ 0x01B43 , 0x01B42 , 0x01B35 },
+	{ 0x01E00 , 0x00041 , 0x00325 },
+	{ 0x01E01 , 0x00061 , 0x00325 },
+	{ 0x01E02 , 0x00042 , 0x00307 },
+	{ 0x01E03 , 0x00062 , 0x00307 },
+	{ 0x01E04 , 0x00042 , 0x00323 },
+	{ 0x01E05 , 0x00062 , 0x00323 },
+	{ 0x01E06 , 0x00042 , 0x00331 },
+	{ 0x01E07 , 0x00062 , 0x00331 },
+	{ 0x01E08 , 0x000C7 , 0x00301 },
+	{ 0x01E09 , 0x000E7 , 0x00301 },
+	{ 0x01E0A , 0x00044 , 0x00307 },
+	{ 0x01E0B , 0x00064 , 0x00307 },
+	{ 0x01E0C , 0x00044 , 0x00323 },
+	{ 0x01E0D , 0x00064 , 0x00323 },
+	{ 0x01E0E , 0x00044 , 0x00331 },
+	{ 0x01E0F , 0x00064 , 0x00331 },
+	{ 0x01E10 , 0x00044 , 0x00327 },
+	{ 0x01E11 , 0x00064 , 0x00327 },
+	{ 0x01E12 , 0x00044 , 0x0032D },
+	{ 0x01E13 , 0x00064 , 0x0032D },
+	{ 0x01E14 , 0x00112 , 0x00300 },
+	{ 0x01E15 , 0x00113 , 0x00300 },
+	{ 0x01E16 , 0x00112 , 0x00301 },
+	{ 0x01E17 , 0x00113 , 0x00301 },
+	{ 0x01E18 , 0x00045 , 0x0032D },
+	{ 0x01E19 , 0x00065 , 0x0032D },
+	{ 0x01E1A , 0x00045 , 0x00330 },
+	{ 0x01E1B , 0x00065 , 0x00330 },
+	{ 0x01E1C , 0x00228 , 0x00306 },
+	{ 0x01E1D , 0x00229 , 0x00306 },
+	{ 0x01E1E , 0x00046 , 0x00307 },
+	{ 0x01E1F , 0x00066 , 0x00307 },
+	{ 0x01E20 , 0x00047 , 0x00304 },
+	{ 0x01E21 , 0x00067 , 0x00304 },
+	{ 0x01E22 , 0x00048 , 0x00307 },
+	{ 0x01E23 , 0x00068 , 0x00307 },
+	{ 0x01E24 , 0x00048 , 0x00323 },
+	{ 0x01E25 , 0x00068 , 0x00323 },
+	{ 0x01E26 , 0x00048 , 0x00308 },
+	{ 0x01E27 , 0x00068 , 0x00308 },
+	{ 0x01E28 , 0x00048 , 0x00327 },
+	{ 0x01E29 , 0x00068 , 0x00327 },
+	{ 0x01E2A , 0x00048 , 0x0032E },
+	{ 0x01E2B , 0x00068 , 0x0032E },
+	{ 0x01E2C , 0x00049 , 0x00330 },
+	{ 0x01E2D , 0x00069 , 0x00330 },
+	{ 0x01E2E , 0x000CF , 0x00301 },
+	{ 0x01E2F , 0x000EF , 0x00301 },
+	{ 0x01E30 , 0x0004B , 0x00301 },
+	{ 0x01E31 , 0x0006B , 0x00301 },
+	{ 0x01E32 , 0x0004B , 0x00323 },
+	{ 0x01E33 , 0x0006B , 0x00323 },
+	{ 0x01E34 , 0x0004B , 0x00331 },
+	{ 0x01E35 , 0x0006B , 0x00331 },
+	{ 0x01E36 , 0x0004C , 0x00323 },
+	{ 0x01E37 , 0x0006C , 0x00323 },
+	{ 0x01E38 , 0x01E36 , 0x00304 },
+	{ 0x01E39 , 0x01E37 , 0x00304 },
+	{ 0x01E3A , 0x0004C , 0x00331 },
+	{ 0x01E3B , 0x0006C , 0x00331 },
+	{ 0x01E3C , 0x0004C , 0x0032D },
+	{ 0x01E3D , 0x0006C , 0x0032D },
+	{ 0x01E3E , 0x0004D , 0x00301 },
+	{ 0x01E3F , 0x0006D , 0x00301 },
+	{ 0x01E40 , 0x0004D , 0x00307 },
+	{ 0x01E41 , 0x0006D , 0x00307 },
+	{ 0x01E42 , 0x0004D , 0x00323 },
+	{ 0x01E43 , 0x0006D , 0x00323 },
+	{ 0x01E44 , 0x0004E , 0x00307 },
+	{ 0x01E45 , 0x0006E , 0x00307 },
+	{ 0x01E46 , 0x0004E , 0x00323 },
+	{ 0x01E47 , 0x0006E , 0x00323 },
+	{ 0x01E48 , 0x0004E , 0x00331 },
+	{ 0x01E49 , 0x0006E , 0x00331 },
+	{ 0x01E4A , 0x0004E , 0x0032D },
+	{ 0x01E4B , 0x0006E , 0x0032D },
+	{ 0x01E4C , 0x000D5 , 0x00301 },
+	{ 0x01E4D , 0x000F5 , 0x00301 },
+	{ 0x01E4E , 0x000D5 , 0x00308 },
+	{ 0x01E4F , 0x000F5 , 0x00308 },
+	{ 0x01E50 , 0x0014C , 0x00300 },
+	{ 0x01E51 , 0x0014D , 0x00300 },
+	{ 0x01E52 , 0x0014C , 0x00301 },
+	{ 0x01E53 , 0x0014D , 0x00301 },
+	{ 0x01E54 , 0x00050 , 0x00301 },
+	{ 0x01E55 , 0x00070 , 0x00301 },
+	{ 0x01E56 , 0x00050 , 0x00307 },
+	{ 0x01E57 , 0x00070 , 0x00307 },
+	{ 0x01E58 , 0x00052 , 0x00307 },
+	{ 0x01E59 , 0x00072 , 0x00307 },
+	{ 0x01E5A , 0x00052 , 0x00323 },
+	{ 0x01E5B , 0x00072 , 0x00323 },
+	{ 0x01E5C , 0x01E5A , 0x00304 },
+	{ 0x01E5D , 0x01E5B , 0x00304 },
+	{ 0x01E5E , 0x00052 , 0x00331 },
+	{ 0x01E5F , 0x00072 , 0x00331 },
+	{ 0x01E60 , 0x00053 , 0x00307 },
+	{ 0x01E61 , 0x00073 , 0x00307 },
+	{ 0x01E62 , 0x00053 , 0x00323 },
+	{ 0x01E63 , 0x00073 , 0x00323 },
+	{ 0x01E64 , 0x0015A , 0x00307 },
+	{ 0x01E65 , 0x0015B , 0x00307 },
+	{ 0x01E66 , 0x00160 , 0x00307 },
+	{ 0x01E67 , 0x00161 , 0x00307 },
+	{ 0x01E68 , 0x01E62 , 0x00307 },
+	{ 0x01E69 , 0x01E63 , 0x00307 },
+	{ 0x01E6A , 0x00054 , 0x00307 },
+	{ 0x01E6B , 0x00074 , 0x00307 },
+	{ 0x01E6C , 0x00054 , 0x00323 },
+	{ 0x01E6D , 0x00074 , 0x00323 },
+	{ 0x01E6E , 0x00054 , 0x00331 },
+	{ 0x01E6F , 0x00074 , 0x00331 },
+	{ 0x01E70 , 0x00054 , 0x0032D },
+	{ 0x01E71 , 0x00074 , 0x0032D },
+	{ 0x01E72 , 0x00055 , 0x00324 },
+	{ 0x01E73 , 0x00075 , 0x00324 },
+	{ 0x01E74 , 0x00055 , 0x00330 },
+	{ 0x01E75 , 0x00075 , 0x00330 },
+	{ 0x01E76 , 0x00055 , 0x0032D },
+	{ 0x01E77 , 0x00075 , 0x0032D },
+	{ 0x01E78 , 0x00168 , 0x00301 },
+	{ 0x01E79 , 0x00169 , 0x00301 },
+	{ 0x01E7A , 0x0016A , 0x00308 },
+	{ 0x01E7B , 0x0016B , 0x00308 },
+	{ 0x01E7C , 0x00056 , 0x00303 },
+	{ 0x01E7D , 0x00076 , 0x00303 },
+	{ 0x01E7E , 0x00056 , 0x00323 },
+	{ 0x01E7F , 0x00076 , 0x00323 },
+	{ 0x01E80 , 0x00057 , 0x00300 },
+	{ 0x01E81 , 0x00077 , 0x00300 },
+	{ 0x01E82 , 0x00057 , 0x00301 },
+	{ 0x01E83 , 0x00077 , 0x00301 },
+	{ 0x01E84 , 0x00057 , 0x00308 },
+	{ 0x01E85 , 0x00077 , 0x00308 },
+	{ 0x01E86 , 0x00057 , 0x00307 },
+	{ 0x01E87 , 0x00077 , 0x00307 },
+	{ 0x01E88 , 0x00057 , 0x00323 },
+	{ 0x01E89 , 0x00077 , 0x00323 },
+	{ 0x01E8A , 0x00058 , 0x00307 },
+	{ 0x01E8B , 0x00078 , 0x00307 },
+	{ 0x01E8C , 0x00058 , 0x00308 },
+	{ 0x01E8D , 0x00078 , 0x00308 },
+	{ 0x01E8E , 0x00059 , 0x00307 },
+	{ 0x01E8F , 0x00079 , 0x00307 },
+	{ 0x01E90 , 0x0005A , 0x00302 },
+	{ 0x01E91 , 0x0007A , 0x00302 },
+	{ 0x01E92 , 0x0005A , 0x00323 },
+	{ 0x01E93 , 0x0007A , 0x00323 },
+	{ 0x01E94 , 0x0005A , 0x00331 },
+	{ 0x01E95 , 0x0007A , 0x00331 },
+	{ 0x01E96 , 0x00068 , 0x00331 },
+	{ 0x01E97 , 0x00074 , 0x00308 },
+	{ 0x01E98 , 0x00077 , 0x0030A },
+	{ 0x01E99 , 0x00079 , 0x0030A },
+	{ 0x01E9B , 0x0017F , 0x00307 },
+	{ 0x01EA0 , 0x00041 , 0x00323 },
+	{ 0x01EA1 , 0x00061 , 0x00323 },
+	{ 0x01EA2 , 0x00041 , 0x00309 },
+	{ 0x01EA3 , 0x00061 , 0x00309 },
+	{ 0x01EA4 , 0x000C2 , 0x00301 },
+	{ 0x01EA5 , 0x000E2 , 0x00301 },
+	{ 0x01EA6 , 0x000C2 , 0x00300 },
+	{ 0x01EA7 , 0x000E2 , 0x00300 },
+	{ 0x01EA8 , 0x000C2 , 0x00309 },
+	{ 0x01EA9 , 0x000E2 , 0x00309 },
+	{ 0x01EAA , 0x000C2 , 0x00303 },
+	{ 0x01EAB , 0x000E2 , 0x00303 },
+	{ 0x01EAC , 0x01EA0 , 0x00302 },
+	{ 0x01EAD , 0x01EA1 , 0x00302 },
+	{ 0x01EAE , 0x00102 , 0x00301 },
+	{ 0x01EAF , 0x00103 , 0x00301 },
+	{ 0x01EB0 , 0x00102 , 0x00300 },
+	{ 0x01EB1 , 0x00103 , 0x00300 },
+	{ 0x01EB2 , 0x00102 , 0x00309 },
+	{ 0x01EB3 , 0x00103 , 0x00309 },
+	{ 0x01EB4 , 0x00102 , 0x00303 },
+	{ 0x01EB5 , 0x00103 , 0x00303 },
+	{ 0x01EB6 , 0x01EA0 , 0x00306 },
+	{ 0x01EB7 , 0x01EA1 , 0x00306 },
+	{ 0x01EB8 , 0x00045 , 0x00323 },
+	{ 0x01EB9 , 0x00065 , 0x00323 },
+	{ 0x01EBA , 0x00045 , 0x00309 },
+	{ 0x01EBB , 0x00065 , 0x00309 },
+	{ 0x01EBC , 0x00045 , 0x00303 },
+	{ 0x01EBD , 0x00065 , 0x00303 },
+	{ 0x01EBE , 0x000CA , 0x00301 },
+	{ 0x01EBF , 0x000EA , 0x00301 },
+	{ 0x01EC0 , 0x000CA , 0x00300 },
+	{ 0x01EC1 , 0x000EA , 0x00300 },
+	{ 0x01EC2 , 0x000CA , 0x00309 },
+	{ 0x01EC3 , 0x000EA , 0x00309 },
+	{ 0x01EC4 , 0x000CA , 0x00303 },
+	{ 0x01EC5 , 0x000EA , 0x00303 },
+	{ 0x01EC6 , 0x01EB8 , 0x00302 },
+	{ 0x01EC7 , 0x01EB9 , 0x00302 },
+	{ 0x01EC8 , 0x00049 , 0x00309 },
+	{ 0x01EC9 , 0x00069 , 0x00309 },
+	{ 0x01ECA , 0x00049 , 0x00323 },
+	{ 0x01ECB , 0x00069 , 0x00323 },
+	{ 0x01ECC , 0x0004F , 0x00323 },
+	{ 0x01ECD , 0x0006F , 0x00323 },
+	{ 0x01ECE , 0x0004F , 0x00309 },
+	{ 0x01ECF , 0x0006F , 0x00309 },
+	{ 0x01ED0 , 0x000D4 , 0x00301 },
+	{ 0x01ED1 , 0x000F4 , 0x00301 },
+	{ 0x01ED2 , 0x000D4 , 0x00300 },
+	{ 0x01ED3 , 0x000F4 , 0x00300 },
+	{ 0x01ED4 , 0x000D4 , 0x00309 },
+	{ 0x01ED5 , 0x000F4 , 0x00309 },
+	{ 0x01ED6 , 0x000D4 , 0x00303 },
+	{ 0x01ED7 , 0x000F4 , 0x00303 },
+	{ 0x01ED8 , 0x01ECC , 0x00302 },
+	{ 0x01ED9 , 0x01ECD , 0x00302 },
+	{ 0x01EDA , 0x001A0 , 0x00301 },
+	{ 0x01EDB , 0x001A1 , 0x00301 },
+	{ 0x01EDC , 0x001A0 , 0x00300 },
+	{ 0x01EDD , 0x001A1 , 0x00300 },
+	{ 0x01EDE , 0x001A0 , 0x00309 },
+	{ 0x01EDF , 0x001A1 , 0x00309 },
+	{ 0x01EE0 , 0x001A0 , 0x00303 },
+	{ 0x01EE1 , 0x001A1 , 0x00303 },
+	{ 0x01EE2 , 0x001A0 , 0x00323 },
+	{ 0x01EE3 , 0x001A1 , 0x00323 },
+	{ 0x01EE4 , 0x00055 , 0x00323 },
+	{ 0x01EE5 , 0x00075 , 0x00323 },
+	{ 0x01EE6 , 0x00055 , 0x00309 },
+	{ 0x01EE7 , 0x00075 , 0x00309 },
+	{ 0x01EE8 , 0x001AF , 0x00301 },
+	{ 0x01EE9 , 0x001B0 , 0x00301 },
+	{ 0x01EEA , 0x001AF , 0x00300 },
+	{ 0x01EEB , 0x001B0 , 0x00300 },
+	{ 0x01EEC , 0x001AF , 0x00309 },
+	{ 0x01EED , 0x001B0 , 0x00309 },
+	{ 0x01EEE , 0x001AF , 0x00303 },
+	{ 0x01EEF , 0x001B0 , 0x00303 },
+	{ 0x01EF0 , 0x001AF , 0x00323 },
+	{ 0x01EF1 , 0x001B0 , 0x00323 },
+	{ 0x01EF2 , 0x00059 , 0x00300 },
+	{ 0x01EF3 , 0x00079 , 0x00300 },
+	{ 0x01EF4 , 0x00059 , 0x00323 },
+	{ 0x01EF5 , 0x00079 , 0x00323 },
+	{ 0x01EF6 , 0x00059 , 0x00309 },
+	{ 0x01EF7 , 0x00079 , 0x00309 },
+	{ 0x01EF8 , 0x00059 , 0x00303 },
+	{ 0x01EF9 , 0x00079 , 0x00303 },
+	{ 0x01F00 , 0x003B1 , 0x00313 },
+	{ 0x01F01 , 0x003B1 , 0x00314 },
+	{ 0x01F02 , 0x01F00 , 0x00300 },
+	{ 0x01F03 , 0x01F01 , 0x00300 },
+	{ 0x01F04 , 0x01F00 , 0x00301 },
+	{ 0x01F05 , 0x01F01 , 0x00301 },
+	{ 0x01F06 , 0x01F00 , 0x00342 },
+	{ 0x01F07 , 0x01F01 , 0x00342 },
+	{ 0x01F08 , 0x00391 , 0x00313 },
+	{ 0x01F09 , 0x00391 , 0x00314 },
+	{ 0x01F0A , 0x01F08 , 0x00300 },
+	{ 0x01F0B , 0x01F09 , 0x00300 },
+	{ 0x01F0C , 0x01F08 , 0x00301 },
+	{ 0x01F0D , 0x01F09 , 0x00301 },
+	{ 0x01F0E , 0x01F08 , 0x00342 },
+	{ 0x01F0F , 0x01F09 , 0x00342 },
+	{ 0x01F10 , 0x003B5 , 0x00313 },
+	{ 0x01F11 , 0x003B5 , 0x00314 },
+	{ 0x01F12 , 0x01F10 , 0x00300 },
+	{ 0x01F13 , 0x01F11 , 0x00300 },
+	{ 0x01F14 , 0x01F10 , 0x00301 },
+	{ 0x01F15 , 0x01F11 , 0x00301 },
+	{ 0x01F18 , 0x00395 , 0x00313 },
+	{ 0x01F19 , 0x00395 , 0x00314 },
+	{ 0x01F1A , 0x01F18 , 0x00300 },
+	{ 0x01F1B , 0x01F19 , 0x00300 },
+	{ 0x01F1C , 0x01F18 , 0x00301 },
+	{ 0x01F1D , 0x01F19 , 0x00301 },
+	{ 0x01F20 , 0x003B7 , 0x00313 },
+	{ 0x01F21 , 0x003B7 , 0x00314 },
+	{ 0x01F22 , 0x01F20 , 0x00300 },
+	{ 0x01F23 , 0x01F21 , 0x00300 },
+	{ 0x01F24 , 0x01F20 , 0x00301 },
+	{ 0x01F25 , 0x01F21 , 0x00301 },
+	{ 0x01F26 , 0x01F20 , 0x00342 },
+	{ 0x01F27 , 0x01F21 , 0x00342 },
+	{ 0x01F28 , 0x00397 , 0x00313 },
+	{ 0x01F29 , 0x00397 , 0x00314 },
+	{ 0x01F2A , 0x01F28 , 0x00300 },
+	{ 0x01F2B , 0x01F29 , 0x00300 },
+	{ 0x01F2C , 0x01F28 , 0x00301 },
+	{ 0x01F2D , 0x01F29 , 0x00301 },
+	{ 0x01F2E , 0x01F28 , 0x00342 },
+	{ 0x01F2F , 0x01F29 , 0x00342 },
+	{ 0x01F30 , 0x003B9 , 0x00313 },
+	{ 0x01F31 , 0x003B9 , 0x00314 },
+	{ 0x01F32 , 0x01F30 , 0x00300 },
+	{ 0x01F33 , 0x01F31 , 0x00300 },
+	{ 0x01F34 , 0x01F30 , 0x00301 },
+	{ 0x01F35 , 0x01F31 , 0x00301 },
+	{ 0x01F36 , 0x01F30 , 0x00342 },
+	{ 0x01F37 , 0x01F31 , 0x00342 },
+	{ 0x01F38 , 0x00399 , 0x00313 },
+	{ 0x01F39 , 0x00399 , 0x00314 },
+	{ 0x01F3A , 0x01F38 , 0x00300 },
+	{ 0x01F3B , 0x01F39 , 0x00300 },
+	{ 0x01F3C , 0x01F38 , 0x00301 },
+	{ 0x01F3D , 0x01F39 , 0x00301 },
+	{ 0x01F3E , 0x01F38 , 0x00342 },
+	{ 0x01F3F , 0x01F39 , 0x00342 },
+	{ 0x01F40 , 0x003BF , 0x00313 },
+	{ 0x01F41 , 0x003BF , 0x00314 },
+	{ 0x01F42 , 0x01F40 , 0x00300 },
+	{ 0x01F43 , 0x01F41 , 0x00300 },
+	{ 0x01F44 , 0x01F40 , 0x00301 },
+	{ 0x01F45 , 0x01F41 , 0x00301 },
+	{ 0x01F48 , 0x0039F , 0x00313 },
+	{ 0x01F49 , 0x0039F , 0x00314 },
+	{ 0x01F4A , 0x01F48 , 0x00300 },
+	{ 0x01F4B , 0x01F49 , 0x00300 },
+	{ 0x01F4C , 0x01F48 , 0x00301 },
+	{ 0x01F4D , 0x01F49 , 0x00301 },
+	{ 0x01F50 , 0x003C5 , 0x00313 },
+	{ 0x01F51 , 0x003C5 , 0x00314 },
+	{ 0x01F52 , 0x01F50 , 0x00300 },
+	{ 0x01F53 , 0x01F51 , 0x00300 },
+	{ 0x01F54 , 0x01F50 , 0x00301 },
+	{ 0x01F55 , 0x01F51 , 0x00301 },
+	{ 0x01F56 , 0x01F50 , 0x00342 },
+	{ 0x01F57 , 0x01F51 , 0x00342 },
+	{ 0x01F59 , 0x003A5 , 0x00314 },
+	{ 0x01F5B , 0x01F59 , 0x00300 },
+	{ 0x01F5D , 0x01F59 , 0x00301 },
+	{ 0x01F5F , 0x01F59 , 0x00342 },
+	{ 0x01F60 , 0x003C9 , 0x00313 },
+	{ 0x01F61 , 0x003C9 , 0x00314 },
+	{ 0x01F62 , 0x01F60 , 0x00300 },
+	{ 0x01F63 , 0x01F61 , 0x00300 },
+	{ 0x01F64 , 0x01F60 , 0x00301 },
+	{ 0x01F65 , 0x01F61 , 0x00301 },
+	{ 0x01F66 , 0x01F60 , 0x00342 },
+	{ 0x01F67 , 0x01F61 , 0x00342 },
+	{ 0x01F68 , 0x003A9 , 0x00313 },
+	{ 0x01F69 , 0x003A9 , 0x00314 },
+	{ 0x01F6A , 0x01F68 , 0x00300 },
+	{ 0x01F6B , 0x01F69 , 0x00300 },
+	{ 0x01F6C , 0x01F68 , 0x00301 },
+	{ 0x01F6D , 0x01F69 , 0x00301 },
+	{ 0x01F6E , 0x01F68 , 0x00342 },
+	{ 0x01F6F , 0x01F69 , 0x00342 },
+	{ 0x01F70 , 0x003B1 , 0x00300 },
+	{ 0x01F72 , 0x003B5 , 0x00300 },
+	{ 0x01F74 , 0x003B7 , 0x00300 },
+	{ 0x01F76 , 0x003B9 , 0x00300 },
+	{ 0x01F78 , 0x003BF , 0x00300 },
+	{ 0x01F7A , 0x003C5 , 0x00300 },
+	{ 0x01F7C , 0x003C9 , 0x00300 },
+	{ 0x01F80 , 0x01F00 , 0x00345 },
+	{ 0x01F81 , 0x01F01 , 0x00345 },
+	{ 0x01F82 , 0x01F02 , 0x00345 },
+	{ 0x01F83 , 0x01F03 , 0x00345 },
+	{ 0x01F84 , 0x01F04 , 0x00345 },
+	{ 0x01F85 , 0x01F05 , 0x00345 },
+	{ 0x01F86 , 0x01F06 , 0x00345 },
+	{ 0x01F87 , 0x01F07 , 0x00345 },
+	{ 0x01F88 , 0x01F08 , 0x00345 },
+	{ 0x01F89 , 0x01F09 , 0x00345 },
+	{ 0x01F8A , 0x01F0A , 0x00345 },
+	{ 0x01F8B , 0x01F0B , 0x00345 },
+	{ 0x01F8C , 0x01F0C , 0x00345 },
+	{ 0x01F8D , 0x01F0D , 0x00345 },
+	{ 0x01F8E , 0x01F0E , 0x00345 },
+	{ 0x01F8F , 0x01F0F , 0x00345 },
+	{ 0x01F90 , 0x01F20 , 0x00345 },
+	{ 0x01F91 , 0x01F21 , 0x00345 },
+	{ 0x01F92 , 0x01F22 , 0x00345 },
+	{ 0x01F93 , 0x01F23 , 0x00345 },
+	{ 0x01F94 , 0x01F24 , 0x00345 },
+	{ 0x01F95 , 0x01F25 , 0x00345 },
+	{ 0x01F96 , 0x01F26 , 0x00345 },
+	{ 0x01F97 , 0x01F27 , 0x00345 },
+	{ 0x01F98 , 0x01F28 , 0x00345 },
+	{ 0x01F99 , 0x01F29 , 0x00345 },
+	{ 0x01F9A , 0x01F2A , 0x00345 },
+	{ 0x01F9B , 0x01F2B , 0x00345 },
+	{ 0x01F9C , 0x01F2C , 0x00345 },
+	{ 0x01F9D , 0x01F2D , 0x00345 },
+	{ 0x01F9E , 0x01F2E , 0x00345 },
+	{ 0x01F9F , 0x01F2F , 0x00345 },
+	{ 0x01FA0 , 0x01F60 , 0x00345 },
+	{ 0x01FA1 , 0x01F61 , 0x00345 },
+	{ 0x01FA2 , 0x01F62 , 0x00345 },
+	{ 0x01FA3 , 0x01F63 , 0x00345 },
+	{ 0x01FA4 , 0x01F64 , 0x00345 },
+	{ 0x01FA5 , 0x01F65 , 0x00345 },
+	{ 0x01FA6 , 0x01F66 , 0x00345 },
+	{ 0x01FA7 , 0x01F67 , 0x00345 },
+	{ 0x01FA8 , 0x01F68 , 0x00345 },
+	{ 0x01FA9 , 0x01F69 , 0x00345 },
+	{ 0x01FAA , 0x01F6A , 0x00345 },
+	{ 0x01FAB , 0x01F6B , 0x00345 },
+	{ 0x01FAC , 0x01F6C , 0x00345 },
+	{ 0x01FAD , 0x01F6D , 0x00345 },
+	{ 0x01FAE , 0x01F6E , 0x00345 },
+	{ 0x01FAF , 0x01F6F , 0x00345 },
+	{ 0x01FB0 , 0x003B1 , 0x00306 },
+	{ 0x01FB1 , 0x003B1 , 0x00304 },
+	{ 0x01FB2 , 0x01F70 , 0x00345 },
+	{ 0x01FB3 , 0x003B1 , 0x00345 },
+	{ 0x01FB4 , 0x003AC , 0x00345 },
+	{ 0x01FB6 , 0x003B1 , 0x00342 },
+	{ 0x01FB7 , 0x01FB6 , 0x00345 },
+	{ 0x01FB8 , 0x00391 , 0x00306 },
+	{ 0x01FB9 , 0x00391 , 0x00304 },
+	{ 0x01FBA , 0x00391 , 0x00300 },
+	{ 0x01FBC , 0x00391 , 0x00345 },
+	{ 0x01FC1 , 0x000A8 , 0x00342 },
+	{ 0x01FC2 , 0x01F74 , 0x00345 },
+	{ 0x01FC3 , 0x003B7 , 0x00345 },
+	{ 0x01FC4 , 0x003AE , 0x00345 },
+	{ 0x01FC6 , 0x003B7 , 0x00342 },
+	{ 0x01FC7 , 0x01FC6 , 0x00345 },
+	{ 0x01FC8 , 0x00395 , 0x00300 },
+	{ 0x01FCA , 0x00397 , 0x00300 },
+	{ 0x01FCC , 0x00397 , 0x00345 },
+	{ 0x01FCD , 0x01FBF , 0x00300 },
+	{ 0x01FCE , 0x01FBF , 0x00301 },
+	{ 0x01FCF , 0x01FBF , 0x00342 },
+	{ 0x01FD0 , 0x003B9 , 0x00306 },
+	{ 0x01FD1 , 0x003B9 , 0x00304 },
+	{ 0x01FD2 , 0x003CA , 0x00300 },
+	{ 0x01FD6 , 0x003B9 , 0x00342 },
+	{ 0x01FD7 , 0x003CA , 0x00342 },
+	{ 0x01FD8 , 0x00399 , 0x00306 },
+	{ 0x01FD9 , 0x00399 , 0x00304 },
+	{ 0x01FDA , 0x00399 , 0x00300 },
+	{ 0x01FDD , 0x01FFE , 0x00300 },
+	{ 0x01FDE , 0x01FFE , 0x00301 },
+	{ 0x01FDF , 0x01FFE , 0x00342 },
+	{ 0x01FE0 , 0x003C5 , 0x00306 },
+	{ 0x01FE1 , 0x003C5 , 0x00304 },
+	{ 0x01FE2 , 0x003CB , 0x00300 },
+	{ 0x01FE4 , 0x003C1 , 0x00313 },
+	{ 0x01FE5 , 0x003C1 , 0x00314 },
+	{ 0x01FE6 , 0x003C5 , 0x00342 },
+	{ 0x01FE7 , 0x003CB , 0x00342 },
+	{ 0x01FE8 , 0x003A5 , 0x00306 },
+	{ 0x01FE9 , 0x003A5 , 0x00304 },
+	{ 0x01FEA , 0x003A5 , 0x00300 },
+	{ 0x01FEC , 0x003A1 , 0x00314 },
+	{ 0x01FED , 0x000A8 , 0x00300 },
+	{ 0x01FF2 , 0x01F7C , 0x00345 },
+	{ 0x01FF3 , 0x003C9 , 0x00345 },
+	{ 0x01FF4 , 0x003CE , 0x00345 },
+	{ 0x01FF6 , 0x003C9 , 0x00342 },
+	{ 0x01FF7 , 0x01FF6 , 0x00345 },
+	{ 0x01FF8 , 0x0039F , 0x00300 },
+	{ 0x01FFA , 0x003A9 , 0x00300 },
+	{ 0x01FFC , 0x003A9 , 0x00345 },
+	{ 0x0219A , 0x02190 , 0x00338 },
+	{ 0x0219B , 0x02192 , 0x00338 },
+	{ 0x021AE , 0x02194 , 0x00338 },
+	{ 0x021CD , 0x021D0 , 0x00338 },
+	{ 0x021CE , 0x021D4 , 0x00338 },
+	{ 0x021CF , 0x021D2 , 0x00338 },
+	{ 0x02204 , 0x02203 , 0x00338 },
+	{ 0x02209 , 0x02208 , 0x00338 },
+	{ 0x0220C , 0x0220B , 0x00338 },
+	{ 0x02224 , 0x02223 , 0x00338 },
+	{ 0x02226 , 0x02225 , 0x00338 },
+	{ 0x02241 , 0x0223C , 0x00338 },
+	{ 0x02244 , 0x02243 , 0x00338 },
+	{ 0x02247 , 0x02245 , 0x00338 },
+	{ 0x02249 , 0x02248 , 0x00338 },
+	{ 0x02260 , 0x0003D , 0x00338 },
+	{ 0x02262 , 0x02261 , 0x00338 },
+	{ 0x0226D , 0x0224D , 0x00338 },
+	{ 0x0226E , 0x0003C , 0x00338 },
+	{ 0x0226F , 0x0003E , 0x00338 },
+	{ 0x02270 , 0x02264 , 0x00338 },
+	{ 0x02271 , 0x02265 , 0x00338 },
+	{ 0x02274 , 0x02272 , 0x00338 },
+	{ 0x02275 , 0x02273 , 0x00338 },
+	{ 0x02278 , 0x02276 , 0x00338 },
+	{ 0x02279 , 0x02277 , 0x00338 },
+	{ 0x02280 , 0x0227A , 0x00338 },
+	{ 0x02281 , 0x0227B , 0x00338 },
+	{ 0x02284 , 0x02282 , 0x00338 },
+	{ 0x02285 , 0x02283 , 0x00338 },
+	{ 0x02288 , 0x02286 , 0x00338 },
+	{ 0x02289 , 0x02287 , 0x00338 },
+	{ 0x022AC , 0x022A2 , 0x00338 },
+	{ 0x022AD , 0x022A8 , 0x00338 },
+	{ 0x022AE , 0x022A9 , 0x00338 },
+	{ 0x022AF , 0x022AB , 0x00338 },
+	{ 0x022E0 , 0x0227C , 0x00338 },
+	{ 0x022E1 , 0x0227D , 0x00338 },
+	{ 0x022E2 , 0x02291 , 0x00338 },
+	{ 0x022E3 , 0x02292 , 0x00338 },
+	{ 0x022EA , 0x022B2 , 0x00338 },
+	{ 0x022EB , 0x022B3 , 0x00338 },
+	{ 0x022EC , 0x022B4 , 0x00338 },
+	{ 0x022ED , 0x022B5 , 0x00338 },
+	{ 0x0304C , 0x0304B , 0x03099 },
+	{ 0x0304E , 0x0304D , 0x03099 },
+	{ 0x03050 , 0x0304F , 0x03099 },
+	{ 0x03052 , 0x03051 , 0x03099 },
+	{ 0x03054 , 0x03053 , 0x03099 },
+	{ 0x03056 , 0x03055 , 0x03099 },
+	{ 0x03058 , 0x03057 , 0x03099 },
+	{ 0x0305A , 0x03059 , 0x03099 },
+	{ 0x0305C , 0x0305B , 0x03099 },
+	{ 0x0305E , 0x0305D , 0x03099 },
+	{ 0x03060 , 0x0305F , 0x03099 },
+	{ 0x03062 , 0x03061 , 0x03099 },
+	{ 0x03065 , 0x03064 , 0x03099 },
+	{ 0x03067 , 0x03066 , 0x03099 },
+	{ 0x03069 , 0x03068 , 0x03099 },
+	{ 0x03070 , 0x0306F , 0x03099 },
+	{ 0x03071 , 0x0306F , 0x0309A },
+	{ 0x03073 , 0x03072 , 0x03099 },
+	{ 0x03074 , 0x03072 , 0x0309A },
+	{ 0x03076 , 0x03075 , 0x03099 },
+	{ 0x03077 , 0x03075 , 0x0309A },
+	{ 0x03079 , 0x03078 , 0x03099 },
+	{ 0x0307A , 0x03078 , 0x0309A },
+	{ 0x0307C , 0x0307B , 0x03099 },
+	{ 0x0307D , 0x0307B , 0x0309A },
+	{ 0x03094 , 0x03046 , 0x03099 },
+	{ 0x0309E , 0x0309D , 0x03099 },
+	{ 0x030AC , 0x030AB , 0x03099 },
+	{ 0x030AE , 0x030AD , 0x03099 },
+	{ 0x030B0 , 0x030AF , 0x03099 },
+	{ 0x030B2 , 0x030B1 , 0x03099 },
+	{ 0x030B4 , 0x030B3 , 0x03099 },
+	{ 0x030B6 , 0x030B5 , 0x03099 },
+	{ 0x030B8 , 0x030B7 , 0x03099 },
+	{ 0x030BA , 0x030B9 , 0x03099 },
+	{ 0x030BC , 0x030BB , 0x03099 },
+	{ 0x030BE , 0x030BD , 0x03099 },
+	{ 0x030C0 , 0x030BF , 0x03099 },
+	{ 0x030C2 , 0x030C1 , 0x03099 },
+	{ 0x030C5 , 0x030C4 , 0x03099 },
+	{ 0x030C7 , 0x030C6 , 0x03099 },
+	{ 0x030C9 , 0x030C8 , 0x03099 },
+	{ 0x030D0 , 0x030CF , 0x03099 },
+	{ 0x030D1 , 0x030CF , 0x0309A },
+	{ 0x030D3 , 0x030D2 , 0x03099 },
+	{ 0x030D4 , 0x030D2 , 0x0309A },
+	{ 0x030D6 , 0x030D5 , 0x03099 },
+	{ 0x030D7 , 0x030D5 , 0x0309A },
+	{ 0x030D9 , 0x030D8 , 0x03099 },
+	{ 0x030DA , 0x030D8 , 0x0309A },
+	{ 0x030DC , 0x030DB , 0x03099 },
+	{ 0x030DD , 0x030DB , 0x0309A },
+	{ 0x030F4 , 0x030A6 , 0x03099 },
+	{ 0x030F7 , 0x030EF , 0x03099 },
+	{ 0x030F8 , 0x030F0 , 0x03099 },
+	{ 0x030F9 , 0x030F1 , 0x03099 },
+	{ 0x030FA , 0x030F2 , 0x03099 },
+	{ 0x030FE , 0x030FD , 0x03099 },
+	{ 0x1109A , 0x11099 , 0x110BA },
+	{ 0x1109C , 0x1109B , 0x110BA },
+	{ 0x110AB , 0x110A5 , 0x110BA },
+};
+
+#endif /* ARCHIVE_STRING_COMPOSITION_H_INCLUDED */
+
diff --git a/c/archive_string_sprintf.c b/c/archive_string_sprintf.c
new file mode 100644
--- /dev/null
+++ b/c/archive_string_sprintf.c
@@ -0,0 +1,192 @@
+/*-
+ * 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: head/lib/libarchive/archive_string_sprintf.c 189435 2009-03-06 05:14:55Z kientzle $");
+
+/*
+ * The use of printf()-family functions can be troublesome
+ * for space-constrained applications.  In addition, correctly
+ * implementing this function in terms of vsnprintf() requires
+ * two calls (one to determine the size, another to format the
+ * result), which in turn requires duplicating the argument list
+ * using va_copy, which isn't yet universally available. <sigh>
+ *
+ * So, I've implemented a bare minimum of printf()-like capability
+ * here.  This is only used to format error messages, so doesn't
+ * require any floating-point support or field-width handling.
+ */
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdio.h>
+
+#include "archive_string.h"
+#include "archive_private.h"
+
+/*
+ * Utility functions to format signed/unsigned integers and append
+ * them to an archive_string.
+ */
+static void
+append_uint(struct archive_string *as, uintmax_t d, unsigned base)
+{
+	static const char digits[] = "0123456789abcdef";
+	if (d >= base)
+		append_uint(as, d/base, base);
+	archive_strappend_char(as, digits[d % base]);
+}
+
+static void
+append_int(struct archive_string *as, intmax_t d, unsigned base)
+{
+	uintmax_t ud;
+
+	if (d < 0) {
+		archive_strappend_char(as, '-');
+		ud = (d == INTMAX_MIN) ? (uintmax_t)(INTMAX_MAX) + 1 : (uintmax_t)(-d);
+	} else
+		ud = d;
+	append_uint(as, ud, base);
+}
+
+
+void
+archive_string_sprintf(struct archive_string *as, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	archive_string_vsprintf(as, fmt, ap);
+	va_end(ap);
+}
+
+/*
+ * Like 'vsprintf', but ensures the target is big enough, resizing if
+ * necessary.
+ */
+void
+archive_string_vsprintf(struct archive_string *as, const char *fmt,
+    va_list ap)
+{
+	char long_flag;
+	intmax_t s; /* Signed integer temp. */
+	uintmax_t u; /* Unsigned integer temp. */
+	const char *p, *p2;
+	const wchar_t *pw;
+
+	if (archive_string_ensure(as, 64) == NULL)
+		__archive_errx(1, "Out of memory");
+
+	if (fmt == NULL) {
+		as->s[0] = 0;
+		return;
+	}
+
+	for (p = fmt; *p != '\0'; p++) {
+		const char *saved_p = p;
+
+		if (*p != '%') {
+			archive_strappend_char(as, *p);
+			continue;
+		}
+
+		p++;
+
+		long_flag = '\0';
+		switch(*p) {
+		case 'j':
+		case 'l':
+		case 'z':
+			long_flag = *p;
+			p++;
+			break;
+		}
+
+		switch (*p) {
+		case '%':
+			archive_strappend_char(as, '%');
+			break;
+		case 'c':
+			s = va_arg(ap, int);
+			archive_strappend_char(as, (char)s);
+			break;
+		case 'd':
+			switch(long_flag) {
+			case 'j': s = va_arg(ap, intmax_t); break;
+			case 'l': s = va_arg(ap, long); break;
+			case 'z': s = va_arg(ap, ssize_t); break;
+			default:  s = va_arg(ap, int); break;
+			}
+		        append_int(as, s, 10);
+			break;
+		case 's':
+			switch(long_flag) {
+			case 'l':
+				pw = va_arg(ap, wchar_t *);
+				if (pw == NULL)
+					pw = L"(null)";
+				if (archive_string_append_from_wcs(as, pw,
+				    wcslen(pw)) != 0 && errno == ENOMEM)
+					__archive_errx(1, "Out of memory");
+				break;
+			default:
+				p2 = va_arg(ap, char *);
+				if (p2 == NULL)
+					p2 = "(null)";
+				archive_strcat(as, p2);
+				break;
+			}
+			break;
+		case 'S':
+			pw = va_arg(ap, wchar_t *);
+			if (pw == NULL)
+				pw = L"(null)";
+			if (archive_string_append_from_wcs(as, pw,
+			    wcslen(pw)) != 0 && errno == ENOMEM)
+				__archive_errx(1, "Out of memory");
+			break;
+		case 'o': case 'u': case 'x': case 'X':
+			/* Common handling for unsigned integer formats. */
+			switch(long_flag) {
+			case 'j': u = va_arg(ap, uintmax_t); break;
+			case 'l': u = va_arg(ap, unsigned long); break;
+			case 'z': u = va_arg(ap, size_t); break;
+			default:  u = va_arg(ap, unsigned int); break;
+			}
+			/* Format it in the correct base. */
+			switch (*p) {
+			case 'o': append_uint(as, u, 8); break;
+			case 'u': append_uint(as, u, 10); break;
+			default: append_uint(as, u, 16); break;
+			}
+			break;
+		default:
+			/* Rewind and print the initial '%' literally. */
+			p = saved_p;
+			archive_strappend_char(as, *p);
+		}
+	}
+}
diff --git a/c/archive_util.c b/c/archive_util.c
new file mode 100644
--- /dev/null
+++ b/c/archive_util.c
@@ -0,0 +1,650 @@
+/*-
+ * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.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
+#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
+#include <wincrypt.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#include <lzma.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_random_private.h"
+#include "archive_string.h"
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC	0
+#endif
+
+static int archive_utility_string_sort_helper(char **, unsigned int);
+
+/* Generic initialization of 'struct archive' objects. */
+int
+__archive_clean(struct archive *a)
+{
+	archive_string_conversion_free(a);
+	return (ARCHIVE_OK);
+}
+
+int
+archive_version_number(void)
+{
+	return (ARCHIVE_VERSION_NUMBER);
+}
+
+const char *
+archive_version_string(void)
+{
+	return (ARCHIVE_VERSION_STRING);
+}
+
+int
+archive_errno(struct archive *a)
+{
+	return (a->archive_error_number);
+}
+
+const char *
+archive_error_string(struct archive *a)
+{
+
+	if (a->error != NULL  &&  *a->error != '\0')
+		return (a->error);
+	else
+		return (NULL);
+}
+
+int
+archive_file_count(struct archive *a)
+{
+	return (a->file_count);
+}
+
+int
+archive_format(struct archive *a)
+{
+	return (a->archive_format);
+}
+
+const char *
+archive_format_name(struct archive *a)
+{
+	return (a->archive_format_name);
+}
+
+
+int
+archive_compression(struct archive *a)
+{
+	return archive_filter_code(a, 0);
+}
+
+const char *
+archive_compression_name(struct archive *a)
+{
+	return archive_filter_name(a, 0);
+}
+
+
+/*
+ * Return a count of the number of compressed bytes processed.
+ */
+la_int64_t
+archive_position_compressed(struct archive *a)
+{
+	return archive_filter_bytes(a, -1);
+}
+
+/*
+ * Return a count of the number of uncompressed bytes processed.
+ */
+la_int64_t
+archive_position_uncompressed(struct archive *a)
+{
+	return archive_filter_bytes(a, 0);
+}
+
+void
+archive_clear_error(struct archive *a)
+{
+	archive_string_empty(&a->error_string);
+	a->error = NULL;
+	a->archive_error_number = 0;
+}
+
+void
+archive_set_error(struct archive *a, int error_number, const char *fmt, ...)
+{
+	va_list ap;
+
+	a->archive_error_number = error_number;
+	if (fmt == NULL) {
+		a->error = NULL;
+		return;
+	}
+
+	archive_string_empty(&(a->error_string));
+	va_start(ap, fmt);
+	archive_string_vsprintf(&(a->error_string), fmt, ap);
+	va_end(ap);
+	a->error = a->error_string.s;
+}
+
+void
+archive_copy_error(struct archive *dest, struct archive *src)
+{
+	dest->archive_error_number = src->archive_error_number;
+
+	archive_string_copy(&dest->error_string, &src->error_string);
+	dest->error = dest->error_string.s;
+}
+
+void
+__archive_errx(int retvalue, const char *msg)
+{
+	static const char msg1[] = "Fatal Internal Error in libarchive: ";
+	size_t s;
+
+	s = write(2, msg1, strlen(msg1));
+	(void)s; /* UNUSED */
+	s = write(2, msg, strlen(msg));
+	(void)s; /* UNUSED */
+	s = write(2, "\n", 1);
+	(void)s; /* UNUSED */
+	exit(retvalue);
+}
+
+/*
+ * Create a temporary file
+ */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+/*
+ * Do not use Windows tmpfile() function.
+ * It will make a temporary file under the root directory
+ * and it'll cause permission error if a user who is
+ * non-Administrator creates temporary files.
+ * Also Windows version of mktemp family including _mktemp_s
+ * are not secure.
+ */
+static int
+__archive_mktempx(const char *tmpdir, wchar_t *template)
+{
+	static const wchar_t prefix[] = L"libarchive_";
+	static const wchar_t suffix[] = L"XXXXXXXXXX";
+	static const wchar_t num[] = {
+		L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7',
+		L'8', L'9', L'A', L'B', L'C', L'D', L'E', L'F',
+		L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N',
+		L'O', L'P', L'Q', L'R', L'S', L'T', L'U', L'V',
+		L'W', L'X', L'Y', L'Z', L'a', L'b', L'c', L'd',
+		L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l',
+		L'm', L'n', L'o', L'p', L'q', L'r', L's', L't',
+		L'u', L'v', L'w', L'x', L'y', L'z'
+	};
+	HCRYPTPROV hProv;
+	struct archive_wstring temp_name;
+	wchar_t *ws;
+	DWORD attr;
+	wchar_t *xp, *ep;
+	int fd;
+
+	hProv = (HCRYPTPROV)NULL;
+	fd = -1;
+	ws = NULL;
+
+	if (template == NULL) {
+		archive_string_init(&temp_name);
+
+		/* Get a temporary directory. */
+		if (tmpdir == NULL) {
+			size_t l;
+			wchar_t *tmp;
+
+			l = GetTempPathW(0, NULL);
+			if (l == 0) {
+				la_dosmaperr(GetLastError());
+				goto exit_tmpfile;
+			}
+			tmp = malloc(l*sizeof(wchar_t));
+			if (tmp == NULL) {
+				errno = ENOMEM;
+				goto exit_tmpfile;
+			}
+			GetTempPathW((DWORD)l, tmp);
+			archive_wstrcpy(&temp_name, tmp);
+			free(tmp);
+		} else {
+			if (archive_wstring_append_from_mbs(&temp_name, tmpdir,
+			    strlen(tmpdir)) < 0)
+				goto exit_tmpfile;
+			if (temp_name.s[temp_name.length-1] != L'/')
+				archive_wstrappend_wchar(&temp_name, L'/');
+		}
+
+		/* Check if temp_name is a directory. */
+		attr = GetFileAttributesW(temp_name.s);
+		if (attr == (DWORD)-1) {
+			if (GetLastError() != ERROR_FILE_NOT_FOUND) {
+				la_dosmaperr(GetLastError());
+				goto exit_tmpfile;
+			}
+			ws = __la_win_permissive_name_w(temp_name.s);
+			if (ws == NULL) {
+				errno = EINVAL;
+				goto exit_tmpfile;
+			}
+			attr = GetFileAttributesW(ws);
+			if (attr == (DWORD)-1) {
+				la_dosmaperr(GetLastError());
+				goto exit_tmpfile;
+			}
+		}
+		if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
+			errno = ENOTDIR;
+			goto exit_tmpfile;
+		}
+
+		/*
+		 * Create a temporary file.
+		 */
+		archive_wstrcat(&temp_name, prefix);
+		archive_wstrcat(&temp_name, suffix);
+		ep = temp_name.s + archive_strlen(&temp_name);
+		xp = ep - wcslen(suffix);
+		template = temp_name.s;
+	} else {
+		xp = wcschr(template, L'X');
+		if (xp == NULL)	/* No X, programming error */
+			abort();
+		for (ep = xp; *ep == L'X'; ep++)
+			continue;
+		if (*ep)	/* X followed by non X, programming error */
+			abort();
+	}
+
+	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
+		CRYPT_VERIFYCONTEXT)) {
+		la_dosmaperr(GetLastError());
+		goto exit_tmpfile;
+	}
+
+	for (;;) {
+		wchar_t *p;
+		HANDLE h;
+
+		/* Generate a random file name through CryptGenRandom(). */
+		p = xp;
+		if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
+		    (BYTE*)p)) {
+			la_dosmaperr(GetLastError());
+			goto exit_tmpfile;
+		}
+		for (; p < ep; p++)
+			*p = num[((DWORD)*p) % (sizeof(num)/sizeof(num[0]))];
+
+		free(ws);
+		ws = __la_win_permissive_name_w(template);
+		if (ws == NULL) {
+			errno = EINVAL;
+			goto exit_tmpfile;
+		}
+		if (template == temp_name.s) {
+			attr = FILE_ATTRIBUTE_TEMPORARY |
+			       FILE_FLAG_DELETE_ON_CLOSE;
+		} else {
+			/* mkstemp */
+			attr = FILE_ATTRIBUTE_NORMAL;
+		}
+		h = CreateFileW(ws,
+		    GENERIC_READ | GENERIC_WRITE | DELETE,
+		    0,/* Not share */
+		    NULL,
+		    CREATE_NEW,/* Create a new file only */
+		    attr,
+		    NULL);
+		if (h == INVALID_HANDLE_VALUE) {
+			/* The same file already exists. retry with
+			 * a new filename. */
+			if (GetLastError() == ERROR_FILE_EXISTS)
+				continue;
+			/* Otherwise, fail creation temporary file. */
+			la_dosmaperr(GetLastError());
+			goto exit_tmpfile;
+		}
+		fd = _open_osfhandle((intptr_t)h, _O_BINARY | _O_RDWR);
+		if (fd == -1) {
+			la_dosmaperr(GetLastError());
+			CloseHandle(h);
+			goto exit_tmpfile;
+		} else
+			break;/* success! */
+	}
+exit_tmpfile:
+	if (hProv != (HCRYPTPROV)NULL)
+		CryptReleaseContext(hProv, 0);
+	free(ws);
+	if (template == temp_name.s)
+		archive_wstring_free(&temp_name);
+	return (fd);
+}
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+	return __archive_mktempx(tmpdir, NULL);
+}
+
+int
+__archive_mkstemp(wchar_t *template)
+{
+	return __archive_mktempx(NULL, template);
+}
+
+#else
+
+static int
+get_tempdir(struct archive_string *temppath)
+{
+	const char *tmp;
+
+	tmp = getenv("TMPDIR");
+	if (tmp == NULL)
+#ifdef _PATH_TMP
+		tmp = _PATH_TMP;
+#else
+                tmp = "/tmp";
+#endif
+	archive_strcpy(temppath, tmp);
+	if (temppath->s[temppath->length-1] != '/')
+		archive_strappend_char(temppath, '/');
+	return (ARCHIVE_OK);
+}
+
+#if defined(HAVE_MKSTEMP)
+
+/*
+ * We can use mkstemp().
+ */
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+	struct archive_string temp_name;
+	int fd = -1;
+
+	archive_string_init(&temp_name);
+	if (tmpdir == NULL) {
+		if (get_tempdir(&temp_name) != ARCHIVE_OK)
+			goto exit_tmpfile;
+	} else {
+		archive_strcpy(&temp_name, tmpdir);
+		if (temp_name.s[temp_name.length-1] != '/')
+			archive_strappend_char(&temp_name, '/');
+	}
+	archive_strcat(&temp_name, "libarchive_XXXXXX");
+	fd = mkstemp(temp_name.s);
+	if (fd < 0)
+		goto exit_tmpfile;
+	__archive_ensure_cloexec_flag(fd);
+	unlink(temp_name.s);
+exit_tmpfile:
+	archive_string_free(&temp_name);
+	return (fd);
+}
+
+int
+__archive_mkstemp(char *template)
+{
+	int fd = -1;
+	fd = mkstemp(template);
+	if (fd >= 0)
+		__archive_ensure_cloexec_flag(fd);
+	return (fd);
+}
+
+#else /* !HAVE_MKSTEMP */
+
+/*
+ * We use a private routine.
+ */
+
+static int
+__archive_mktempx(const char *tmpdir, char *template)
+{
+        static const char num[] = {
+		'0', '1', '2', '3', '4', '5', '6', '7',
+		'8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+		'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+		'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+		'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+		'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+		'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+		'u', 'v', 'w', 'x', 'y', 'z'
+        };
+	struct archive_string temp_name;
+	struct stat st;
+	int fd;
+	char *tp, *ep;
+
+	fd = -1;
+	if (template == NULL) {
+		archive_string_init(&temp_name);
+		if (tmpdir == NULL) {
+			if (get_tempdir(&temp_name) != ARCHIVE_OK)
+				goto exit_tmpfile;
+		} else
+			archive_strcpy(&temp_name, tmpdir);
+		if (temp_name.s[temp_name.length-1] == '/') {
+			temp_name.s[temp_name.length-1] = '\0';
+			temp_name.length --;
+		}
+		if (la_stat(temp_name.s, &st) < 0)
+			goto exit_tmpfile;
+		if (!S_ISDIR(st.st_mode)) {
+			errno = ENOTDIR;
+			goto exit_tmpfile;
+		}
+		archive_strcat(&temp_name, "/libarchive_");
+		tp = temp_name.s + archive_strlen(&temp_name);
+		archive_strcat(&temp_name, "XXXXXXXXXX");
+		ep = temp_name.s + archive_strlen(&temp_name);
+		template = temp_name.s;
+	} else {
+		tp = strchr(template, 'X');
+		if (tp == NULL)	/* No X, programming error */
+			abort();
+		for (ep = tp; *ep == 'X'; ep++)
+			continue;
+		if (*ep)	/* X followed by non X, programming error */
+			abort();
+	}
+
+	do {
+		char *p;
+
+		p = tp;
+		archive_random(p, ep - p);
+		while (p < ep) {
+			int d = *((unsigned char *)p) % sizeof(num);
+			*p++ = num[d];
+		}
+		fd = open(template, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC,
+			  0600);
+	} while (fd < 0 && errno == EEXIST);
+	if (fd < 0)
+		goto exit_tmpfile;
+	__archive_ensure_cloexec_flag(fd);
+	if (template == temp_name.s)
+		unlink(temp_name.s);
+exit_tmpfile:
+	if (template == temp_name.s)
+		archive_string_free(&temp_name);
+	return (fd);
+}
+
+int
+__archive_mktemp(const char *tmpdir)
+{
+	return __archive_mktempx(tmpdir, NULL);
+}
+
+int
+__archive_mkstemp(char *template)
+{
+	return __archive_mktempx(NULL, template);
+}
+
+#endif /* !HAVE_MKSTEMP */
+#endif /* !_WIN32 || __CYGWIN__ */
+
+/*
+ * Set FD_CLOEXEC flag to a file descriptor if it is not set.
+ * We have to set the flag if the platform does not provide O_CLOEXEC
+ * or F_DUPFD_CLOEXEC flags.
+ *
+ * Note: This function is absolutely called after creating a new file
+ * descriptor even if the platform seemingly provides O_CLOEXEC or
+ * F_DUPFD_CLOEXEC macros because it is possible that the platform
+ * merely declares those macros, especially Linux 2.6.18 - 2.6.24 do it.
+ */
+void
+__archive_ensure_cloexec_flag(int fd)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	(void)fd; /* UNUSED */
+#else
+	int flags;
+
+	if (fd >= 0) {
+		flags = fcntl(fd, F_GETFD);
+		if (flags != -1 && (flags & FD_CLOEXEC) == 0)
+			fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
+	}
+#endif
+}
+
+/*
+ * Utility function to sort a group of strings using quicksort.
+ */
+static int
+archive_utility_string_sort_helper(char **strings, unsigned int n)
+{
+	unsigned int i, lesser_count, greater_count;
+	char **lesser, **greater, **tmp, *pivot;
+	int retval1, retval2;
+
+	/* A list of 0 or 1 elements is already sorted */
+	if (n <= 1)
+		return (ARCHIVE_OK);
+
+	lesser_count = greater_count = 0;
+	lesser = greater = NULL;
+	pivot = strings[0];
+	for (i = 1; i < n; i++)
+	{
+		if (strcmp(strings[i], pivot) < 0)
+		{
+			lesser_count++;
+			tmp = (char **)realloc(lesser,
+				lesser_count * sizeof(char *));
+			if (!tmp) {
+				free(greater);
+				free(lesser);
+				return (ARCHIVE_FATAL);
+			}
+			lesser = tmp;
+			lesser[lesser_count - 1] = strings[i];
+		}
+		else
+		{
+			greater_count++;
+			tmp = (char **)realloc(greater,
+				greater_count * sizeof(char *));
+			if (!tmp) {
+				free(greater);
+				free(lesser);
+				return (ARCHIVE_FATAL);
+			}
+			greater = tmp;
+			greater[greater_count - 1] = strings[i];
+		}
+	}
+
+	/* quicksort(lesser) */
+	retval1 = archive_utility_string_sort_helper(lesser, lesser_count);
+	for (i = 0; i < lesser_count; i++)
+		strings[i] = lesser[i];
+	free(lesser);
+
+	/* pivot */
+	strings[lesser_count] = pivot;
+
+	/* quicksort(greater) */
+	retval2 = archive_utility_string_sort_helper(greater, greater_count);
+	for (i = 0; i < greater_count; i++)
+		strings[lesser_count + 1 + i] = greater[i];
+	free(greater);
+
+	return (retval1 < retval2) ? retval1 : retval2;
+}
+
+int
+archive_utility_string_sort(char **strings)
+{
+	  unsigned int size = 0;
+	  while (strings[size] != NULL)
+		size++;
+	  return archive_utility_string_sort_helper(strings, size);
+}
diff --git a/c/archive_version_details.c b/c/archive_version_details.c
new file mode 100644
--- /dev/null
+++ b/c/archive_version_details.c
@@ -0,0 +1,151 @@
+/*-
+ * Copyright (c) 2009-2012,2014 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_util.c 201098 2009-12-28 02:58:14Z kientzle $");
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+#ifdef HAVE_LZMA_H
+#include <lzma.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#ifdef HAVE_LZ4_H
+#include <lz4.h>
+#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+
+const char *
+archive_version_details(void)
+{
+	static struct archive_string str;
+	static int init = 0;
+	const char *zlib = archive_zlib_version();
+	const char *liblzma = archive_liblzma_version();
+	const char *bzlib = archive_bzlib_version();
+	const char *liblz4 = archive_liblz4_version();
+	const char *libzstd = archive_libzstd_version();
+
+	if (!init) {
+		archive_string_init(&str);
+
+		archive_strcat(&str, ARCHIVE_VERSION_STRING);
+		if (zlib != NULL) {
+			archive_strcat(&str, " zlib/");
+			archive_strcat(&str, zlib);
+		}
+		if (liblzma) {
+			archive_strcat(&str, " liblzma/");
+			archive_strcat(&str, liblzma);
+		}
+		if (bzlib) {
+			const char *p = bzlib;
+			const char *sep = strchr(p, ',');
+			if (sep == NULL)
+				sep = p + strlen(p);
+			archive_strcat(&str, " bz2lib/");
+			archive_strncat(&str, p, sep - p);
+		}
+		if (liblz4) {
+			archive_strcat(&str, " liblz4/");
+			archive_strcat(&str, liblz4);
+		}
+		if (libzstd) {
+			archive_strcat(&str, " libzstd/");
+			archive_strcat(&str, libzstd);
+		}
+	}
+	return str.s;
+}
+
+const char *
+archive_zlib_version(void)
+{
+#ifdef HAVE_ZLIB_H
+	return ZLIB_VERSION;
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_liblzma_version(void)
+{
+#ifdef HAVE_LZMA_H
+	return LZMA_VERSION_STRING;
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_bzlib_version(void)
+{
+#ifdef HAVE_BZLIB_H
+	return BZ2_bzlibVersion();
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_liblz4_version(void)
+{
+#if defined(HAVE_LZ4_H) && defined(HAVE_LIBLZ4)
+#define str(s) #s
+#define NUMBER(x) str(x)
+	return NUMBER(LZ4_VERSION_MAJOR) "." NUMBER(LZ4_VERSION_MINOR) "." NUMBER(LZ4_VERSION_RELEASE);
+#undef NUMBER
+#undef str
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libzstd_version(void)
+{
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+	return ZSTD_VERSION_STRING;
+#else
+	return NULL;
+#endif
+}
diff --git a/c/archive_virtual.c b/c/archive_virtual.c
new file mode 100644
--- /dev/null
+++ b/c/archive_virtual.c
@@ -0,0 +1,163 @@
+/*-
+ * 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: head/lib/libarchive/archive_virtual.c 201098 2009-12-28 02:58:14Z kientzle $");
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+int
+archive_filter_code(struct archive *a, int n)
+{
+	return ((a->vtable->archive_filter_code)(a, n));
+}
+
+int
+archive_filter_count(struct archive *a)
+{
+	return ((a->vtable->archive_filter_count)(a));
+}
+
+const char *
+archive_filter_name(struct archive *a, int n)
+{
+	return ((a->vtable->archive_filter_name)(a, n));
+}
+
+la_int64_t
+archive_filter_bytes(struct archive *a, int n)
+{
+	return ((a->vtable->archive_filter_bytes)(a, n));
+}
+
+int
+archive_free(struct archive *a)
+{
+	if (a == NULL)
+		return (ARCHIVE_OK);
+	return ((a->vtable->archive_free)(a));
+}
+
+int
+archive_write_close(struct archive *a)
+{
+	return ((a->vtable->archive_close)(a));
+}
+
+int
+archive_read_close(struct archive *a)
+{
+	return ((a->vtable->archive_close)(a));
+}
+
+int
+archive_write_fail(struct archive *a)
+{
+	a->state = ARCHIVE_STATE_FATAL;
+	return a->state;
+}
+
+int
+archive_write_free(struct archive *a)
+{
+	return archive_free(a);
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* For backwards compatibility; will be removed with libarchive 4.0. */
+int
+archive_write_finish(struct archive *a)
+{
+	return archive_write_free(a);
+}
+#endif
+
+int
+archive_read_free(struct archive *a)
+{
+	return archive_free(a);
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* For backwards compatibility; will be removed with libarchive 4.0. */
+int
+archive_read_finish(struct archive *a)
+{
+	return archive_read_free(a);
+}
+#endif
+
+int
+archive_write_header(struct archive *a, struct archive_entry *entry)
+{
+	++a->file_count;
+	return ((a->vtable->archive_write_header)(a, entry));
+}
+
+int
+archive_write_finish_entry(struct archive *a)
+{
+	return ((a->vtable->archive_write_finish_entry)(a));
+}
+
+la_ssize_t
+archive_write_data(struct archive *a, const void *buff, size_t s)
+{
+	return ((a->vtable->archive_write_data)(a, buff, s));
+}
+
+la_ssize_t
+archive_write_data_block(struct archive *a, const void *buff, size_t s,
+    la_int64_t o)
+{
+	if (a->vtable->archive_write_data_block == NULL) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "archive_write_data_block not supported");
+		a->state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+	return ((a->vtable->archive_write_data_block)(a, buff, s, o));
+}
+
+int
+archive_read_next_header(struct archive *a, struct archive_entry **entry)
+{
+	return ((a->vtable->archive_read_next_header)(a, entry));
+}
+
+int
+archive_read_next_header2(struct archive *a, struct archive_entry *entry)
+{
+	return ((a->vtable->archive_read_next_header2)(a, entry));
+}
+
+int
+archive_read_data_block(struct archive *a,
+    const void **buff, size_t *s, la_int64_t *o)
+{
+	return ((a->vtable->archive_read_data_block)(a, buff, s, o));
+}
diff --git a/c/archive_windows.c b/c/archive_windows.c
new file mode 100644
--- /dev/null
+++ b/c/archive_windows.c
@@ -0,0 +1,909 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * Copyright (c) 2003-2007 Kees Zeelenberg
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * A set of compatibility glue for building libarchive on Windows platforms.
+ *
+ * Originally created as "libarchive-nonposix.c" by Kees Zeelenberg
+ * for the GnuWin32 project, trimmed significantly by Tim Kientzle.
+ *
+ * Much of the original file was unnecessary for libarchive, because
+ * many of the features it emulated were not strictly necessary for
+ * libarchive.  I hope for this to shrink further as libarchive
+ * internals are gradually reworked to sit more naturally on both
+ * POSIX and Windows.  Any ideas for this are greatly appreciated.
+ *
+ * The biggest remaining issue is the dev/ino emulation; libarchive
+ * has a couple of public APIs that rely on dev/ino uniquely
+ * identifying a file.  This doesn't match well with Windows.  I'm
+ * considering alternative APIs.
+ */
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#include "archive_platform.h"
+#include "archive_private.h"
+#include "archive_entry.h"
+#include <ctype.h>
+#include <errno.h>
+#include <stddef.h>
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#include <sys/stat.h>
+#include <locale.h>
+#include <process.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <windows.h>
+#include <share.h>
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+
+#if defined(__LA_LSEEK_NEEDED)
+static BOOL SetFilePointerEx_perso(HANDLE hFile,
+				   LARGE_INTEGER liDistanceToMove,
+				   PLARGE_INTEGER lpNewFilePointer,
+				   DWORD dwMoveMethod)
+{
+	LARGE_INTEGER li;
+	li.QuadPart = liDistanceToMove.QuadPart;
+	li.LowPart = SetFilePointer(
+		hFile, li.LowPart, &li.HighPart, dwMoveMethod);
+	if(lpNewFilePointer) {
+		lpNewFilePointer->QuadPart = li.QuadPart;
+	}
+	return li.LowPart != -1 || GetLastError() == NO_ERROR;
+}
+#endif
+
+struct ustat {
+	int64_t		st_atime;
+	uint32_t	st_atime_nsec;
+	int64_t		st_ctime;
+	uint32_t	st_ctime_nsec;
+	int64_t		st_mtime;
+	uint32_t	st_mtime_nsec;
+	gid_t		st_gid;
+	/* 64bits ino */
+	int64_t		st_ino;
+	mode_t		st_mode;
+	uint32_t	st_nlink;
+	uint64_t	st_size;
+	uid_t		st_uid;
+	dev_t		st_dev;
+	dev_t		st_rdev;
+};
+
+/* Transform 64-bits ino into 32-bits by hashing.
+ * You do not forget that really unique number size is 64-bits.
+ */
+#define INOSIZE (8*sizeof(ino_t)) /* 32 */
+static __inline ino_t
+getino(struct ustat *ub)
+{
+	ULARGE_INTEGER ino64;
+	ino64.QuadPart = ub->st_ino;
+	/* I don't know this hashing is correct way */
+	return ((ino_t)(ino64.LowPart ^ (ino64.LowPart >> INOSIZE)));
+}
+
+/*
+ * Prepend "\\?\" to the path name and convert it to unicode to permit
+ * an extended-length path for a maximum total path length of 32767
+ * characters.
+ * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx
+ */
+wchar_t *
+__la_win_permissive_name(const char *name)
+{
+	wchar_t *wn;
+	wchar_t *ws;
+	size_t ll;
+
+	ll = strlen(name);
+	wn = malloc((ll + 1) * sizeof(wchar_t));
+	if (wn == NULL)
+		return (NULL);
+	ll = mbstowcs(wn, name, ll);
+	if (ll == (size_t)-1) {
+		free(wn);
+		return (NULL);
+	}
+	wn[ll] = L'\0';
+	ws = __la_win_permissive_name_w(wn);
+	free(wn);
+	return (ws);
+}
+
+wchar_t *
+__la_win_permissive_name_w(const wchar_t *wname)
+{
+	wchar_t *wn, *wnp;
+	wchar_t *ws, *wsp;
+	DWORD l, len, slen;
+	int unc;
+
+	/* Get a full-pathname. */
+	l = GetFullPathNameW(wname, 0, NULL, NULL);
+	if (l == 0)
+		return (NULL);
+	/* NOTE: GetFullPathNameW has a bug that if the length of the file
+	 * name is just 1 then it returns incomplete buffer size. Thus, we
+	 * have to add three to the size to allocate a sufficient buffer
+	 * size for the full-pathname of the file name. */
+	l += 3;
+	wnp = malloc(l * sizeof(wchar_t));
+	if (wnp == NULL)
+		return (NULL);
+	len = GetFullPathNameW(wname, l, wnp, NULL);
+	wn = wnp;
+
+	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+	    wnp[2] == L'?' && wnp[3] == L'\\')
+		/* We have already a permissive name. */
+		return (wn);
+
+	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+		wnp[2] == L'.' && wnp[3] == L'\\') {
+		/* This is a device name */
+		if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
+		     (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
+		    wnp[5] == L':' && wnp[6] == L'\\')
+			wnp[2] = L'?';/* Not device name. */
+		return (wn);
+	}
+
+	unc = 0;
+	if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
+		wchar_t *p = &wnp[2];
+
+		/* Skip server-name letters. */
+		while (*p != L'\\' && *p != L'\0')
+			++p;
+		if (*p == L'\\') {
+			wchar_t *rp = ++p;
+			/* Skip share-name letters. */
+			while (*p != L'\\' && *p != L'\0')
+				++p;
+			if (*p == L'\\' && p != rp) {
+				/* Now, match patterns such as
+				 * "\\server-name\share-name\" */
+				wnp += 2;
+				len -= 2;
+				unc = 1;
+			}
+		}
+	}
+
+	slen = 4 + (unc * 4) + len + 1;
+	ws = wsp = malloc(slen * sizeof(wchar_t));
+	if (ws == NULL) {
+		free(wn);
+		return (NULL);
+	}
+	/* prepend "\\?\" */
+	wcsncpy(wsp, L"\\\\?\\", 4);
+	wsp += 4;
+	slen -= 4;
+	if (unc) {
+		/* append "UNC\" ---> "\\?\UNC\" */
+		wcsncpy(wsp, L"UNC\\", 4);
+		wsp += 4;
+		slen -= 4;
+	}
+	wcsncpy(wsp, wnp, slen);
+	wsp[slen - 1] = L'\0'; /* Ensure null termination. */
+	free(wn);
+	return (ws);
+}
+
+/*
+ * Create a file handle.
+ * This can exceed MAX_PATH limitation.
+ */
+static HANDLE
+la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,
+    LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
+    DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
+{
+	wchar_t *wpath;
+	HANDLE handle;
+
+	handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
+	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+	    hTemplateFile);
+	if (handle != INVALID_HANDLE_VALUE)
+		return (handle);
+	if (GetLastError() != ERROR_PATH_NOT_FOUND)
+		return (handle);
+	wpath = __la_win_permissive_name(path);
+	if (wpath == NULL)
+		return (handle);
+	handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
+	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+	    hTemplateFile);
+	free(wpath);
+	return (handle);
+}
+
+#if defined(__LA_LSEEK_NEEDED)
+__int64
+__la_lseek(int fd, __int64 offset, int whence)
+{
+	LARGE_INTEGER distance;
+	LARGE_INTEGER newpointer;
+	HANDLE handle;
+
+	if (fd < 0) {
+		errno = EBADF;
+		return (-1);
+	}
+	handle = (HANDLE)_get_osfhandle(fd);
+	if (GetFileType(handle) != FILE_TYPE_DISK) {
+		errno = EBADF;
+		return (-1);
+	}
+	distance.QuadPart = offset;
+	if (!SetFilePointerEx_perso(handle, distance, &newpointer, whence)) {
+		DWORD lasterr;
+
+		lasterr = GetLastError();
+		if (lasterr == ERROR_BROKEN_PIPE)
+			return (0);
+		if (lasterr == ERROR_ACCESS_DENIED)
+			errno = EBADF;
+		else
+			la_dosmaperr(lasterr);
+		return (-1);
+	}
+	return (newpointer.QuadPart);
+}
+#endif
+
+/* This can exceed MAX_PATH limitation. */
+int
+__la_open(const char *path, int flags, ...)
+{
+	va_list ap;
+	wchar_t *ws;
+	int r, pmode;
+	DWORD attr;
+
+	va_start(ap, flags);
+	pmode = va_arg(ap, int);
+	va_end(ap);
+	ws = NULL;
+	if ((flags & ~O_BINARY) == O_RDONLY) {
+		/*
+		 * When we open a directory, _open function returns
+		 * "Permission denied" error.
+		 */
+		attr = GetFileAttributesA(path);
+		if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND) {
+			ws = __la_win_permissive_name(path);
+			if (ws == NULL) {
+				errno = EINVAL;
+				return (-1);
+			}
+			attr = GetFileAttributesW(ws);
+		}
+		if (attr == (DWORD)-1) {
+			la_dosmaperr(GetLastError());
+			free(ws);
+			return (-1);
+		}
+		if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+			HANDLE handle;
+
+			if (ws != NULL)
+				handle = CreateFileW(ws, 0, 0, NULL,
+				    OPEN_EXISTING,
+				    FILE_FLAG_BACKUP_SEMANTICS |
+				    FILE_ATTRIBUTE_READONLY,
+					NULL);
+			else
+				handle = CreateFileA(path, 0, 0, NULL,
+				    OPEN_EXISTING,
+				    FILE_FLAG_BACKUP_SEMANTICS |
+				    FILE_ATTRIBUTE_READONLY,
+					NULL);
+			free(ws);
+			if (handle == INVALID_HANDLE_VALUE) {
+				la_dosmaperr(GetLastError());
+				return (-1);
+			}
+			r = _open_osfhandle((intptr_t)handle, _O_RDONLY);
+			return (r);
+		}
+	}
+	if (ws == NULL) {
+#if defined(__BORLANDC__)
+		/* Borland has no mode argument.
+		   TODO: Fix mode of new file.  */
+		r = _open(path, flags);
+#else
+		r = _open(path, flags, pmode);
+#endif
+		if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
+			/* Simulate other POSIX system action to pass our test suite. */
+			attr = GetFileAttributesA(path);
+			if (attr == (DWORD)-1)
+				la_dosmaperr(GetLastError());
+			else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+				errno = EISDIR;
+			else
+				errno = EACCES;
+			return (-1);
+		}
+		if (r >= 0 || errno != ENOENT)
+			return (r);
+		ws = __la_win_permissive_name(path);
+		if (ws == NULL) {
+			errno = EINVAL;
+			return (-1);
+		}
+	}
+	r = _wopen(ws, flags, pmode);
+	if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
+		/* Simulate other POSIX system action to pass our test suite. */
+		attr = GetFileAttributesW(ws);
+		if (attr == (DWORD)-1)
+			la_dosmaperr(GetLastError());
+		else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+			errno = EISDIR;
+		else
+			errno = EACCES;
+	}
+	free(ws);
+	return (r);
+}
+
+ssize_t
+__la_read(int fd, void *buf, size_t nbytes)
+{
+	HANDLE handle;
+	DWORD bytes_read, lasterr;
+	int r;
+
+#ifdef _WIN64
+	if (nbytes > UINT32_MAX)
+		nbytes = UINT32_MAX;
+#endif
+	if (fd < 0) {
+		errno = EBADF;
+		return (-1);
+	}
+	/* Do not pass 0 to third parameter of ReadFile(), read bytes.
+	 * This will not return to application side. */
+	if (nbytes == 0)
+		return (0);
+	handle = (HANDLE)_get_osfhandle(fd);
+	r = ReadFile(handle, buf, (uint32_t)nbytes,
+	    &bytes_read, NULL);
+	if (r == 0) {
+		lasterr = GetLastError();
+		if (lasterr == ERROR_NO_DATA) {
+			errno = EAGAIN;
+			return (-1);
+		}
+		if (lasterr == ERROR_BROKEN_PIPE)
+			return (0);
+		if (lasterr == ERROR_ACCESS_DENIED)
+			errno = EBADF;
+		else
+			la_dosmaperr(lasterr);
+		return (-1);
+	}
+	return ((ssize_t)bytes_read);
+}
+
+/* Convert Windows FILETIME to UTC */
+__inline static void
+fileTimeToUTC(const FILETIME *filetime, time_t *t, long *ns)
+{
+	ULARGE_INTEGER utc;
+
+	utc.HighPart = filetime->dwHighDateTime;
+	utc.LowPart  = filetime->dwLowDateTime;
+	if (utc.QuadPart >= EPOC_TIME) {
+		utc.QuadPart -= EPOC_TIME;
+		*t = (time_t)(utc.QuadPart / 10000000);	/* milli seconds base */
+		*ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
+	} else {
+		*t = 0;
+		*ns = 0;
+	}
+}
+
+/* Stat by handle
+ * Windows' stat() does not accept the path added "\\?\" especially "?"
+ * character.
+ * It means we cannot access the long name path longer than MAX_PATH.
+ * So I've implemented a function similar to Windows' stat() to access the
+ * long name path.
+ * And I've added some feature.
+ * 1. set st_ino by nFileIndexHigh and nFileIndexLow of
+ *    BY_HANDLE_FILE_INFORMATION.
+ * 2. set st_nlink by nNumberOfLinks of BY_HANDLE_FILE_INFORMATION.
+ * 3. set st_dev by dwVolumeSerialNumber by BY_HANDLE_FILE_INFORMATION.
+ */
+static int
+__hstat(HANDLE handle, struct ustat *st)
+{
+	BY_HANDLE_FILE_INFORMATION info;
+	ULARGE_INTEGER ino64;
+	DWORD ftype;
+	mode_t mode;
+	time_t t;
+	long ns;
+
+	switch (ftype = GetFileType(handle)) {
+	case FILE_TYPE_UNKNOWN:
+		errno = EBADF;
+		return (-1);
+	case FILE_TYPE_CHAR:
+	case FILE_TYPE_PIPE:
+		if (ftype == FILE_TYPE_CHAR) {
+			st->st_mode = S_IFCHR;
+			st->st_size = 0;
+		} else {
+			DWORD avail;
+
+			st->st_mode = S_IFIFO;
+			if (PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL))
+				st->st_size = avail;
+			else
+				st->st_size = 0;
+		}
+		st->st_atime = 0;
+		st->st_atime_nsec = 0;
+		st->st_mtime = 0;
+		st->st_mtime_nsec = 0;
+		st->st_ctime = 0;
+		st->st_ctime_nsec = 0;
+		st->st_ino = 0;
+		st->st_nlink = 1;
+		st->st_uid = 0;
+		st->st_gid = 0;
+		st->st_rdev = 0;
+		st->st_dev = 0;
+		return (0);
+	case FILE_TYPE_DISK:
+		break;
+	default:
+		/* This ftype is undocumented type. */
+		la_dosmaperr(GetLastError());
+		return (-1);
+	}
+
+	ZeroMemory(&info, sizeof(info));
+	if (!GetFileInformationByHandle (handle, &info)) {
+		la_dosmaperr(GetLastError());
+		return (-1);
+	}
+
+	mode = S_IRUSR | S_IRGRP | S_IROTH;
+	if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+		mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+		mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+	else
+		mode |= S_IFREG;
+	st->st_mode = mode;
+
+	fileTimeToUTC(&info.ftLastAccessTime, &t, &ns);
+	st->st_atime = t;
+	st->st_atime_nsec = ns;
+	fileTimeToUTC(&info.ftLastWriteTime, &t, &ns);
+	st->st_mtime = t;
+	st->st_mtime_nsec = ns;
+	fileTimeToUTC(&info.ftCreationTime, &t, &ns);
+	st->st_ctime = t;
+	st->st_ctime_nsec = ns;
+	st->st_size =
+	    ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1))
+		+ (int64_t)(info.nFileSizeLow);
+#ifdef SIMULATE_WIN_STAT
+	st->st_ino = 0;
+	st->st_nlink = 1;
+	st->st_dev = 0;
+#else
+	/* Getting FileIndex as i-node. We should remove a sequence which
+	 * is high-16-bits of nFileIndexHigh. */
+	ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL;
+	ino64.LowPart  = info.nFileIndexLow;
+	st->st_ino = ino64.QuadPart;
+	st->st_nlink = info.nNumberOfLinks;
+	if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+		++st->st_nlink;/* Add parent directory. */
+	st->st_dev = info.dwVolumeSerialNumber;
+#endif
+	st->st_uid = 0;
+	st->st_gid = 0;
+	st->st_rdev = 0;
+	return (0);
+}
+
+static void
+copy_stat(struct stat *st, struct ustat *us)
+{
+	st->st_atime = us->st_atime;
+	st->st_ctime = us->st_ctime;
+	st->st_mtime = us->st_mtime;
+	st->st_gid = us->st_gid;
+	st->st_ino = getino(us);
+	st->st_mode = us->st_mode;
+	st->st_nlink = us->st_nlink;
+	st->st_size = (off_t)us->st_size;
+	st->st_uid = us->st_uid;
+	st->st_dev = us->st_dev;
+	st->st_rdev = us->st_rdev;
+}
+
+/*
+ * TODO: Remove a use of __la_fstat and __la_stat.
+ * We should use GetFileInformationByHandle in place
+ * where We still use the *stat functions.
+ */
+int
+__la_fstat(int fd, struct stat *st)
+{
+	struct ustat u;
+	int ret;
+
+	if (fd < 0) {
+		errno = EBADF;
+		return (-1);
+	}
+	ret = __hstat((HANDLE)_get_osfhandle(fd), &u);
+	if (ret >= 0) {
+		copy_stat(st, &u);
+		if (u.st_mode & (S_IFCHR | S_IFIFO)) {
+			st->st_dev = fd;
+			st->st_rdev = fd;
+		}
+	}
+	return (ret);
+}
+
+/* This can exceed MAX_PATH limitation. */
+int
+__la_stat(const char *path, struct stat *st)
+{
+	HANDLE handle;
+	struct ustat u;
+	int ret;
+
+	handle = la_CreateFile(path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+		FILE_FLAG_BACKUP_SEMANTICS,
+		NULL);
+	if (handle == INVALID_HANDLE_VALUE) {
+		la_dosmaperr(GetLastError());
+		return (-1);
+	}
+	ret = __hstat(handle, &u);
+	CloseHandle(handle);
+	if (ret >= 0) {
+		char *p;
+
+		copy_stat(st, &u);
+		p = strrchr(path, '.');
+		if (p != NULL && strlen(p) == 4) {
+			char exttype[4];
+
+			++ p;
+			exttype[0] = toupper(*p++);
+			exttype[1] = toupper(*p++);
+			exttype[2] = toupper(*p++);
+			exttype[3] = '\0';
+			if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") ||
+				!strcmp(exttype, "BAT") || !strcmp(exttype, "COM"))
+				st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+		}
+	}
+	return (ret);
+}
+
+/*
+ * This waitpid is limited implementation.
+ */
+pid_t
+__la_waitpid(HANDLE child, int *status, int option)
+{
+	DWORD cs;
+
+	(void)option;/* UNUSED */
+	do {
+		if (GetExitCodeProcess(child, &cs) == 0) {
+			CloseHandle(child);
+			la_dosmaperr(GetLastError());
+			*status = 0;
+			return (-1);
+		}
+	} while (cs == STILL_ACTIVE);
+
+	*status = (int)(cs & 0xff);
+	return (0);
+}
+
+ssize_t
+__la_write(int fd, const void *buf, size_t nbytes)
+{
+	DWORD bytes_written;
+
+#ifdef _WIN64
+	if (nbytes > UINT32_MAX)
+		nbytes = UINT32_MAX;
+#endif
+	if (fd < 0) {
+		errno = EBADF;
+		return (-1);
+	}
+	if (!WriteFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes,
+	    &bytes_written, NULL)) {
+		DWORD lasterr;
+
+		lasterr = GetLastError();
+		if (lasterr == ERROR_ACCESS_DENIED)
+			errno = EBADF;
+		else
+			la_dosmaperr(lasterr);
+		return (-1);
+	}
+	return (bytes_written);
+}
+
+/*
+ * Replace the Windows path separator '\' with '/'.
+ */
+static int
+replace_pathseparator(struct archive_wstring *ws, const wchar_t *wp)
+{
+	wchar_t *w;
+	size_t path_length;
+
+	if (wp == NULL)
+		return(0);
+	if (wcschr(wp, L'\\') == NULL)
+		return(0);
+	path_length = wcslen(wp);
+	if (archive_wstring_ensure(ws, path_length) == NULL)
+		return(-1);
+	archive_wstrncpy(ws, wp, path_length);
+	for (w = ws->s; *w; w++) {
+		if (*w == L'\\')
+			*w = L'/';
+	}
+	return(1);
+}
+
+static int
+fix_pathseparator(struct archive_entry *entry)
+{
+	struct archive_wstring ws;
+	const wchar_t *wp;
+	int ret = ARCHIVE_OK;
+
+	archive_string_init(&ws);
+	wp = archive_entry_pathname_w(entry);
+	switch (replace_pathseparator(&ws, wp)) {
+	case 0: /* Not replaced. */
+		break;
+	case 1: /* Replaced. */
+		archive_entry_copy_pathname_w(entry, ws.s);
+		break;
+	default:
+		ret = ARCHIVE_FAILED;
+	}
+	wp = archive_entry_hardlink_w(entry);
+	switch (replace_pathseparator(&ws, wp)) {
+	case 0: /* Not replaced. */
+		break;
+	case 1: /* Replaced. */
+		archive_entry_copy_hardlink_w(entry, ws.s);
+		break;
+	default:
+		ret = ARCHIVE_FAILED;
+	}
+	wp = archive_entry_symlink_w(entry);
+	switch (replace_pathseparator(&ws, wp)) {
+	case 0: /* Not replaced. */
+		break;
+	case 1: /* Replaced. */
+		archive_entry_copy_symlink_w(entry, ws.s);
+		break;
+	default:
+		ret = ARCHIVE_FAILED;
+	}
+	archive_wstring_free(&ws);
+	return(ret);
+}
+
+struct archive_entry *
+__la_win_entry_in_posix_pathseparator(struct archive_entry *entry)
+{
+	struct archive_entry *entry_main;
+	const wchar_t *wp;
+	int has_backslash = 0;
+	int ret;
+
+	wp = archive_entry_pathname_w(entry);
+	if (wp != NULL && wcschr(wp, L'\\') != NULL)
+		has_backslash = 1;
+	if (!has_backslash) {
+		wp = archive_entry_hardlink_w(entry);
+		if (wp != NULL && wcschr(wp, L'\\') != NULL)
+			has_backslash = 1;
+	}
+	if (!has_backslash) {
+		wp = archive_entry_symlink_w(entry);
+		if (wp != NULL && wcschr(wp, L'\\') != NULL)
+			has_backslash = 1;
+	}
+	/*
+	 * If there is no backslash chars, return the original.
+	 */
+	if (!has_backslash)
+		return (entry);
+
+	/* Copy entry so we can modify it as needed. */
+	entry_main = archive_entry_clone(entry);
+	if (entry_main == NULL)
+		return (NULL);
+	/* Replace the Windows path-separator '\' with '/'. */
+	ret = fix_pathseparator(entry_main);
+	if (ret < ARCHIVE_WARN) {
+		archive_entry_free(entry_main);
+		return (NULL);
+	}
+	return (entry_main);
+}
+
+
+/*
+ * The following function was modified from PostgreSQL sources and is
+ * subject to the copyright below.
+ */
+/*-------------------------------------------------------------------------
+ *
+ * win32error.c
+ *	  Map win32 error codes to errno values
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ *	  $PostgreSQL: pgsql/src/port/win32error.c,v 1.4 2008/01/01 19:46:00 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+PostgreSQL Database Management System
+(formerly known as Postgres, then as Postgres95)
+
+Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+
+Portions Copyright (c) 1994, The Regents of the University of California
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose, without fee, and without a written agreement
+is hereby granted, provided that the above copyright notice and this
+paragraph and the following two paragraphs appear in all copies.
+
+IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
+LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
+DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
+PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+*/
+
+static const struct {
+	DWORD		winerr;
+	int		doserr;
+} doserrors[] =
+{
+	{	ERROR_INVALID_FUNCTION, EINVAL	},
+	{	ERROR_FILE_NOT_FOUND, ENOENT	},
+	{	ERROR_PATH_NOT_FOUND, ENOENT	},
+	{	ERROR_TOO_MANY_OPEN_FILES, EMFILE	},
+	{	ERROR_ACCESS_DENIED, EACCES	},
+	{	ERROR_INVALID_HANDLE, EBADF	},
+	{	ERROR_ARENA_TRASHED, ENOMEM	},
+	{	ERROR_NOT_ENOUGH_MEMORY, ENOMEM	},
+	{	ERROR_INVALID_BLOCK, ENOMEM	},
+	{	ERROR_BAD_ENVIRONMENT, E2BIG	},
+	{	ERROR_BAD_FORMAT, ENOEXEC	},
+	{	ERROR_INVALID_ACCESS, EINVAL	},
+	{	ERROR_INVALID_DATA, EINVAL	},
+	{	ERROR_INVALID_DRIVE, ENOENT	},
+	{	ERROR_CURRENT_DIRECTORY, EACCES	},
+	{	ERROR_NOT_SAME_DEVICE, EXDEV	},
+	{	ERROR_NO_MORE_FILES, ENOENT	},
+	{	ERROR_LOCK_VIOLATION, EACCES	},
+	{	ERROR_SHARING_VIOLATION, EACCES	},
+	{	ERROR_BAD_NETPATH, ENOENT	},
+	{	ERROR_NETWORK_ACCESS_DENIED, EACCES	},
+	{	ERROR_BAD_NET_NAME, ENOENT	},
+	{	ERROR_FILE_EXISTS, EEXIST	},
+	{	ERROR_CANNOT_MAKE, EACCES	},
+	{	ERROR_FAIL_I24, EACCES	},
+	{	ERROR_INVALID_PARAMETER, EINVAL	},
+	{	ERROR_NO_PROC_SLOTS, EAGAIN	},
+	{	ERROR_DRIVE_LOCKED, EACCES	},
+	{	ERROR_BROKEN_PIPE, EPIPE	},
+	{	ERROR_DISK_FULL, ENOSPC	},
+	{	ERROR_INVALID_TARGET_HANDLE, EBADF	},
+	{	ERROR_INVALID_HANDLE, EINVAL	},
+	{	ERROR_WAIT_NO_CHILDREN, ECHILD	},
+	{	ERROR_CHILD_NOT_COMPLETE, ECHILD	},
+	{	ERROR_DIRECT_ACCESS_HANDLE, EBADF	},
+	{	ERROR_NEGATIVE_SEEK, EINVAL	},
+	{	ERROR_SEEK_ON_DEVICE, EACCES	},
+	{	ERROR_DIR_NOT_EMPTY, ENOTEMPTY	},
+	{	ERROR_NOT_LOCKED, EACCES	},
+	{	ERROR_BAD_PATHNAME, ENOENT	},
+	{	ERROR_MAX_THRDS_REACHED, EAGAIN	},
+	{	ERROR_LOCK_FAILED, EACCES	},
+	{	ERROR_ALREADY_EXISTS, EEXIST	},
+	{	ERROR_FILENAME_EXCED_RANGE, ENOENT	},
+	{	ERROR_NESTING_NOT_ALLOWED, EAGAIN	},
+	{	ERROR_NOT_ENOUGH_QUOTA, ENOMEM	}
+};
+
+void
+__la_dosmaperr(unsigned long e)
+{
+	int			i;
+
+	if (e == 0)
+	{
+		errno = 0;
+		return;
+	}
+
+	for (i = 0; i < (int)(sizeof(doserrors)/sizeof(doserrors[0])); i++)
+	{
+		if (doserrors[i].winerr == e)
+		{
+			errno = doserrors[i].doserr;
+			return;
+		}
+	}
+
+	/* fprintf(stderr, "unrecognized win32 error code: %lu", e); */
+	errno = EINVAL;
+	return;
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
diff --git a/c/archive_windows.h b/c/archive_windows.h
new file mode 100644
--- /dev/null
+++ b/c/archive_windows.h
@@ -0,0 +1,315 @@
+/*-
+ * Copyright (c) 2009-2011 Michihiro NAKAJIMA
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * TODO: A lot of stuff in here isn't actually used by libarchive and
+ * can be trimmed out.  Note that this file is used by libarchive and
+ * libarchive_test but nowhere else.  (But note that it gets compiled
+ * with many different Windows environments, including MinGW, Visual
+ * Studio, and Cygwin.  Significant changes should be tested in all three.)
+ */
+
+/*
+ * TODO: Don't use off_t in here.  Use __int64 instead.  Note that
+ * Visual Studio and the Windows SDK define off_t as 32 bits; Win32's
+ * more modern file handling APIs all use __int64 instead of off_t.
+ */
+
+#ifndef LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED
+#define	LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+/* Start of configuration for native Win32  */
+#ifndef MINGW_HAS_SECURE_API
+#define MINGW_HAS_SECURE_API 1
+#endif
+
+#include <errno.h>
+#define	set_errno(val)	((errno)=val)
+#include <io.h>
+#include <stdlib.h>   //brings in NULL
+#if defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <process.h>
+#include <direct.h>
+#if defined(__MINGW32__) && defined(HAVE_UNISTD_H)
+/* Prevent build error from a type mismatch of ftruncate().
+ * This unistd.h defines it as ftruncate(int, off_t). */
+#include <unistd.h>
+#endif
+#define NOCRYPT
+#include <windows.h>
+//#define	EFTYPE 7
+
+#if defined(__BORLANDC__)
+#pragma warn -8068	/* Constant out of range in comparison. */
+#pragma warn -8072	/* Suspicious pointer arithmetic. */
+#endif
+
+#ifndef NULL
+#ifdef  __cplusplus
+#define	NULL    0
+#else
+#define	NULL    ((void *)0)
+#endif
+#endif
+
+/* Alias the Windows _function to the POSIX equivalent. */
+#define	close		_close
+#define	fcntl(fd, cmd, flg)	/* No operation. */
+#ifndef fileno
+#define	fileno		_fileno
+#endif
+#ifdef fstat
+#undef fstat
+#endif
+#define	fstat		__la_fstat
+#if !defined(__BORLANDC__)
+#ifdef lseek
+#undef lseek
+#endif
+#define	lseek		_lseeki64
+#else
+#define	lseek		__la_lseek
+#define __LA_LSEEK_NEEDED
+#endif
+#define	lstat		__la_stat
+#define	open		__la_open
+#define	read		__la_read
+#if !defined(__BORLANDC__) && !defined(__WATCOMC__)
+#define setmode		_setmode
+#endif
+#define	la_stat(path,stref)		__la_stat(path,stref)
+#if !defined(__WATCOMC__)
+#if !defined(__BORLANDC__)
+#define	strdup		_strdup
+#endif
+#define	tzset		_tzset
+#if !defined(__BORLANDC__)
+#define	umask		_umask
+#endif
+#endif
+#define	waitpid		__la_waitpid
+#define	write		__la_write
+
+#if !defined(__WATCOMC__)
+
+#ifndef O_RDONLY
+#define	O_RDONLY	_O_RDONLY
+#define	O_WRONLY	_O_WRONLY
+#define	O_TRUNC		_O_TRUNC
+#define	O_CREAT		_O_CREAT
+#define	O_EXCL		_O_EXCL
+#define	O_BINARY	_O_BINARY
+#endif
+
+#ifndef _S_IFIFO
+  #define	_S_IFIFO        0010000   /* pipe */
+#endif
+#ifndef _S_IFCHR
+  #define	_S_IFCHR        0020000   /* character special */
+#endif
+#ifndef _S_IFDIR
+  #define	_S_IFDIR        0040000   /* directory */
+#endif
+#ifndef _S_IFBLK
+  #define	_S_IFBLK        0060000   /* block special */
+#endif
+#ifndef _S_IFLNK
+  #define	_S_IFLNK        0120000   /* symbolic link */
+#endif
+#ifndef _S_IFSOCK
+  #define	_S_IFSOCK       0140000   /* socket */
+#endif
+#ifndef	_S_IFREG
+  #define	_S_IFREG        0100000   /* regular */
+#endif
+#ifndef	_S_IFMT
+  #define	_S_IFMT         0170000   /* file type mask */
+#endif
+
+#ifndef S_IFIFO
+#define	S_IFIFO     _S_IFIFO
+#endif
+//#define	S_IFCHR  _S_IFCHR
+//#define	S_IFDIR  _S_IFDIR
+#ifndef S_IFBLK
+#define	S_IFBLK     _S_IFBLK
+#endif
+#ifndef S_IFLNK
+#define	S_IFLNK     _S_IFLNK
+#endif
+#ifndef S_IFSOCK
+#define	S_IFSOCK    _S_IFSOCK
+#endif
+//#define	S_IFREG  _S_IFREG
+//#define	S_IFMT   _S_IFMT
+
+#ifndef S_ISBLK
+#define	S_ISBLK(m)	(((m) & S_IFMT) == S_IFBLK)	/* block special */
+#define	S_ISFIFO(m)	(((m) & S_IFMT) == S_IFIFO)	/* fifo or socket */
+#define	S_ISCHR(m)	(((m) & S_IFMT) == S_IFCHR)	/* char special */
+#define	S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)	/* directory */
+#define	S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)	/* regular file */
+#endif
+#define	S_ISLNK(m)  (((m) & S_IFMT) == S_IFLNK) /* Symbolic link */
+#define	S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) /* Socket */
+
+#define	_S_ISUID        0004000   /* set user id on execution */
+#define	_S_ISGID        0002000   /* set group id on execution */
+#define	_S_ISVTX        0001000   /* save swapped text even after use */
+
+#define	S_ISUID        _S_ISUID
+#define	S_ISGID        _S_ISGID
+#define	S_ISVTX        _S_ISVTX
+
+#define	_S_IRWXU	     (_S_IREAD | _S_IWRITE | _S_IEXEC)
+#define	_S_IXUSR	     _S_IEXEC  /* read permission, user */
+#define	_S_IWUSR	     _S_IWRITE /* write permission, user */
+#define	_S_IRUSR	     _S_IREAD  /* execute/search permission, user */
+#define	_S_IRWXG        (_S_IRWXU >> 3)
+#define	_S_IXGRP        (_S_IXUSR >> 3) /* read permission, group */
+#define	_S_IWGRP        (_S_IWUSR >> 3) /* write permission, group */
+#define	_S_IRGRP        (_S_IRUSR >> 3) /* execute/search permission, group */
+#define	_S_IRWXO        (_S_IRWXG >> 3)
+#define	_S_IXOTH        (_S_IXGRP >> 3) /* read permission, other */
+#define	_S_IWOTH        (_S_IWGRP >> 3) /* write permission, other */
+#define	_S_IROTH        (_S_IRGRP  >> 3) /* execute/search permission, other */
+
+#ifndef S_IRWXU
+#define	S_IRWXU	     _S_IRWXU
+#define	S_IXUSR	     _S_IXUSR
+#define	S_IWUSR	     _S_IWUSR
+#define	S_IRUSR	     _S_IRUSR
+#endif
+#ifndef S_IRWXG
+#define	S_IRWXG        _S_IRWXG
+#define	S_IXGRP        _S_IXGRP
+#define	S_IWGRP        _S_IWGRP
+#endif
+#ifndef S_IRGRP
+#define	S_IRGRP        _S_IRGRP
+#endif
+#ifndef S_IRWXO
+#define	S_IRWXO        _S_IRWXO
+#define	S_IXOTH        _S_IXOTH
+#define	S_IWOTH        _S_IWOTH
+#define	S_IROTH        _S_IROTH
+#endif
+
+#endif
+
+#define	F_DUPFD	  	0	/* Duplicate file descriptor.  */
+#define	F_GETFD		1	/* Get file descriptor flags.  */
+#define	F_SETFD		2	/* Set file descriptor flags.  */
+#define	F_GETFL		3	/* Get file status flags.  */
+#define	F_SETFL		4	/* Set file status flags.  */
+#define	F_GETOWN		5	/* Get owner (receiver of SIGIO).  */
+#define	F_SETOWN		6	/* Set owner (receiver of SIGIO).  */
+#define	F_GETLK		7	/* Get record locking info.  */
+#define	F_SETLK		8	/* Set record locking info (non-blocking).  */
+#define	F_SETLKW		9	/* Set record locking info (blocking).  */
+
+/* XXX missing */
+#define	F_GETLK64	7	/* Get record locking info.  */
+#define	F_SETLK64	8	/* Set record locking info (non-blocking).  */
+#define	F_SETLKW64	9	/* Set record locking info (blocking).  */
+
+/* File descriptor flags used with F_GETFD and F_SETFD.  */
+#define	FD_CLOEXEC	1	/* Close on exec.  */
+
+//NOT SURE IF O_NONBLOCK is OK here but at least the 0x0004 flag is not used by anything else...
+#define	O_NONBLOCK 0x0004 /* Non-blocking I/O.  */
+//#define	O_NDELAY   O_NONBLOCK
+
+/* Symbolic constants for the access() function */
+#if !defined(F_OK)
+    #define	R_OK    4       /*  Test for read permission    */
+    #define	W_OK    2       /*  Test for write permission   */
+    #define	X_OK    1       /*  Test for execute permission */
+    #define	F_OK    0       /*  Test for existence of file  */
+#endif
+
+
+/* Replacement POSIX function */
+extern int	 __la_fstat(int fd, struct stat *st);
+extern int	 __la_lstat(const char *path, struct stat *st);
+#if defined(__LA_LSEEK_NEEDED)
+extern __int64	 __la_lseek(int fd, __int64 offset, int whence);
+#endif
+extern int	 __la_open(const char *path, int flags, ...);
+extern ssize_t	 __la_read(int fd, void *buf, size_t nbytes);
+extern int	 __la_stat(const char *path, struct stat *st);
+extern pid_t	 __la_waitpid(HANDLE child, int *status, int option);
+extern ssize_t	 __la_write(int fd, const void *buf, size_t nbytes);
+
+#define _stat64i32(path, st)	__la_stat(path, st)
+#define _stat64(path, st)	__la_stat(path, st)
+/* for status returned by la_waitpid */
+#define WIFEXITED(sts)		((sts & 0x100) == 0)
+#define WEXITSTATUS(sts)	(sts & 0x0FF)
+
+extern wchar_t *__la_win_permissive_name(const char *name);
+extern wchar_t *__la_win_permissive_name_w(const wchar_t *wname);
+extern void __la_dosmaperr(unsigned long e);
+#define la_dosmaperr(e) __la_dosmaperr(e)
+extern struct archive_entry *__la_win_entry_in_posix_pathseparator(
+    struct archive_entry *);
+
+#if defined(HAVE_WCRTOMB) && defined(__BORLANDC__)
+typedef int mbstate_t;
+size_t wcrtomb(char *, wchar_t, mbstate_t *);
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+WINBASEAPI BOOL WINAPI GetVolumePathNameW(
+       LPCWSTR lpszFileName,
+       LPWSTR lpszVolumePathName,
+       DWORD cchBufferLength
+       );
+# if _WIN32_WINNT < 0x0500 /* windows.h not providing 0x500 API */
+typedef struct _FILE_ALLOCATED_RANGE_BUFFER {
+       LARGE_INTEGER FileOffset;
+       LARGE_INTEGER Length;
+} FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER;
+#  define FSCTL_SET_SPARSE \
+     CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 49, METHOD_BUFFERED, FILE_WRITE_DATA)
+#  define FSCTL_QUERY_ALLOCATED_RANGES \
+     CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51,  METHOD_NEITHER, FILE_READ_DATA)
+# endif
+#endif
+
+#endif /* LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED */
diff --git a/c/archive_write.c b/c/archive_write.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write.c
@@ -0,0 +1,789 @@
+/*-
+ * Copyright (c) 2003-2010 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: head/lib/libarchive/archive_write.c 201099 2009-12-28 03:03:00Z kientzle $");
+
+/*
+ * This file contains the "essential" portions of the write API, that
+ * is, stuff that will essentially always be used by any client that
+ * actually needs to write an archive.  Optional pieces have been, as
+ * far as possible, separated out into separate files to reduce
+ * needlessly bloating statically-linked clients.
+ */
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+static struct archive_vtable *archive_write_vtable(void);
+
+static int	_archive_filter_code(struct archive *, int);
+static const char *_archive_filter_name(struct archive *, int);
+static int64_t	_archive_filter_bytes(struct archive *, int);
+static int  _archive_write_filter_count(struct archive *);
+static int	_archive_write_close(struct archive *);
+static int	_archive_write_free(struct archive *);
+static int	_archive_write_header(struct archive *, struct archive_entry *);
+static int	_archive_write_finish_entry(struct archive *);
+static ssize_t	_archive_write_data(struct archive *, const void *, size_t);
+
+struct archive_none {
+	size_t buffer_size;
+	size_t avail;
+	char *buffer;
+	char *next;
+};
+
+static struct archive_vtable *
+archive_write_vtable(void)
+{
+	static struct archive_vtable av;
+	static int inited = 0;
+
+	if (!inited) {
+		av.archive_close = _archive_write_close;
+		av.archive_filter_bytes = _archive_filter_bytes;
+		av.archive_filter_code = _archive_filter_code;
+		av.archive_filter_name = _archive_filter_name;
+		av.archive_filter_count = _archive_write_filter_count;
+		av.archive_free = _archive_write_free;
+		av.archive_write_header = _archive_write_header;
+		av.archive_write_finish_entry = _archive_write_finish_entry;
+		av.archive_write_data = _archive_write_data;
+		inited = 1;
+	}
+	return (&av);
+}
+
+/*
+ * Allocate, initialize and return an archive object.
+ */
+struct archive *
+archive_write_new(void)
+{
+	struct archive_write *a;
+	unsigned char *nulls;
+
+	a = (struct archive_write *)calloc(1, sizeof(*a));
+	if (a == NULL)
+		return (NULL);
+	a->archive.magic = ARCHIVE_WRITE_MAGIC;
+	a->archive.state = ARCHIVE_STATE_NEW;
+	a->archive.vtable = archive_write_vtable();
+	/*
+	 * The value 10240 here matches the traditional tar default,
+	 * but is otherwise arbitrary.
+	 * TODO: Set the default block size from the format selected.
+	 */
+	a->bytes_per_block = 10240;
+	a->bytes_in_last_block = -1;	/* Default */
+
+	/* Initialize a block of nulls for padding purposes. */
+	a->null_length = 1024;
+	nulls = (unsigned char *)calloc(1, a->null_length);
+	if (nulls == NULL) {
+		free(a);
+		return (NULL);
+	}
+	a->nulls = nulls;
+	return (&a->archive);
+}
+
+/*
+ * Set the block size.  Returns 0 if successful.
+ */
+int
+archive_write_set_bytes_per_block(struct archive *_a, int bytes_per_block)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_bytes_per_block");
+	a->bytes_per_block = bytes_per_block;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Get the current block size.  -1 if it has never been set.
+ */
+int
+archive_write_get_bytes_per_block(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_get_bytes_per_block");
+	return (a->bytes_per_block);
+}
+
+/*
+ * Set the size for the last block.
+ * Returns 0 if successful.
+ */
+int
+archive_write_set_bytes_in_last_block(struct archive *_a, int bytes)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_set_bytes_in_last_block");
+	a->bytes_in_last_block = bytes;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Return the value set above.  -1 indicates it has not been set.
+ */
+int
+archive_write_get_bytes_in_last_block(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_get_bytes_in_last_block");
+	return (a->bytes_in_last_block);
+}
+
+/*
+ * dev/ino of a file to be rejected.  Used to prevent adding
+ * an archive to itself recursively.
+ */
+int
+archive_write_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_set_skip_file");
+	a->skip_file_set = 1;
+	a->skip_file_dev = d;
+	a->skip_file_ino = i;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Allocate and return the next filter structure.
+ */
+struct archive_write_filter *
+__archive_write_allocate_filter(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f;
+
+	f = calloc(1, sizeof(*f));
+	f->archive = _a;
+	f->state = ARCHIVE_WRITE_FILTER_STATE_NEW;
+	if (a->filter_first == NULL)
+		a->filter_first = f;
+	else
+		a->filter_last->next_filter = f;
+	a->filter_last = f;
+	return f;
+}
+
+/*
+ * Write data to a particular filter.
+ */
+int
+__archive_write_filter(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	int r;
+	/* Never write to non-open filters */
+	if (f->state != ARCHIVE_WRITE_FILTER_STATE_OPEN)
+		return(ARCHIVE_FATAL);
+	if (length == 0)
+		return(ARCHIVE_OK);
+	if (f->write == NULL)
+		/* If unset, a fatal error has already occurred, so this filter
+		 * didn't open. We cannot write anything. */
+		return(ARCHIVE_FATAL);
+	r = (f->write)(f, buff, length);
+	f->bytes_written += length;
+	return (r);
+}
+
+/*
+ * Recursive function for opening the filter chain
+ * Last filter is opened first
+ */
+static int
+__archive_write_open_filter(struct archive_write_filter *f)
+{
+	int ret;
+
+	ret = ARCHIVE_OK;
+	if (f->next_filter != NULL)
+		ret = __archive_write_open_filter(f->next_filter);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+	if (f->state != ARCHIVE_WRITE_FILTER_STATE_NEW)
+		return (ARCHIVE_FATAL);
+	if (f->open == NULL) {
+		f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN;
+		return (ARCHIVE_OK);
+	}
+	ret = (f->open)(f);
+	if (ret == ARCHIVE_OK)
+		f->state = ARCHIVE_WRITE_FILTER_STATE_OPEN;
+	else
+		f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL;
+	return (ret);
+}
+
+/*
+ * Open all filters
+ */
+static int
+__archive_write_filters_open(struct archive_write *a)
+{
+	return (__archive_write_open_filter(a->filter_first));
+}
+
+/*
+ * Close all filtes
+ */
+static int
+__archive_write_filters_close(struct archive_write *a)
+{
+	struct archive_write_filter *f;
+	int ret, ret1;
+	ret = ARCHIVE_OK;
+	for (f = a->filter_first; f != NULL; f = f->next_filter) {
+		/* Do not close filters that are not open */
+		if (f->state == ARCHIVE_WRITE_FILTER_STATE_OPEN) {
+			if (f->close != NULL) {
+				ret1 = (f->close)(f);
+				if (ret1 < ret)
+					ret = ret1;
+				if (ret1 == ARCHIVE_OK) {
+					f->state =
+					    ARCHIVE_WRITE_FILTER_STATE_CLOSED;
+				} else {
+					f->state =
+					    ARCHIVE_WRITE_FILTER_STATE_FATAL;
+				}
+			} else
+				f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED;
+		}
+	}
+	return (ret);
+}
+
+int
+__archive_write_output(struct archive_write *a, const void *buff, size_t length)
+{
+	return (__archive_write_filter(a->filter_first, buff, length));
+}
+
+int
+__archive_write_nulls(struct archive_write *a, size_t length)
+{
+	if (length == 0)
+		return (ARCHIVE_OK);
+
+	while (length > 0) {
+		size_t to_write = length < a->null_length ? length : a->null_length;
+		int r = __archive_write_output(a, a->nulls, to_write);
+		if (r < ARCHIVE_OK)
+			return (r);
+		length -= to_write;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_client_open(struct archive_write_filter *f)
+{
+	struct archive_write *a = (struct archive_write *)f->archive;
+	struct archive_none *state;
+	void *buffer;
+	size_t buffer_size;
+	int ret;
+
+	f->bytes_per_block = archive_write_get_bytes_per_block(f->archive);
+	f->bytes_in_last_block =
+	    archive_write_get_bytes_in_last_block(f->archive);
+	buffer_size = f->bytes_per_block;
+
+	state = (struct archive_none *)calloc(1, sizeof(*state));
+	buffer = (char *)malloc(buffer_size);
+	if (state == NULL || buffer == NULL) {
+		free(state);
+		free(buffer);
+		archive_set_error(f->archive, ENOMEM,
+		    "Can't allocate data for output buffering");
+		return (ARCHIVE_FATAL);
+	}
+
+	state->buffer_size = buffer_size;
+	state->buffer = buffer;
+	state->next = state->buffer;
+	state->avail = state->buffer_size;
+	f->data = state;
+
+	if (a->client_opener == NULL)
+		return (ARCHIVE_OK);
+	ret = a->client_opener(f->archive, a->client_data);
+	if (ret != ARCHIVE_OK) {
+		free(state->buffer);
+		free(state);
+		f->data = NULL;
+	}
+	return (ret);
+}
+
+static int
+archive_write_client_write(struct archive_write_filter *f,
+    const void *_buff, size_t length)
+{
+	struct archive_write *a = (struct archive_write *)f->archive;
+        struct archive_none *state = (struct archive_none *)f->data;
+	const char *buff = (const char *)_buff;
+	ssize_t remaining, to_copy;
+	ssize_t bytes_written;
+
+	remaining = length;
+
+	/*
+	 * If there is no buffer for blocking, just pass the data
+	 * straight through to the client write callback.  In
+	 * particular, this supports "no write delay" operation for
+	 * special applications.  Just set the block size to zero.
+	 */
+	if (state->buffer_size == 0) {
+		while (remaining > 0) {
+			bytes_written = (a->client_writer)(&a->archive,
+			    a->client_data, buff, remaining);
+			if (bytes_written <= 0)
+				return (ARCHIVE_FATAL);
+			remaining -= bytes_written;
+			buff += bytes_written;
+		}
+		return (ARCHIVE_OK);
+	}
+
+	/* If the copy buffer isn't empty, try to fill it. */
+	if (state->avail < state->buffer_size) {
+		/* If buffer is not empty... */
+		/* ... copy data into buffer ... */
+		to_copy = ((size_t)remaining > state->avail) ?
+			state->avail : (size_t)remaining;
+		memcpy(state->next, buff, to_copy);
+		state->next += to_copy;
+		state->avail -= to_copy;
+		buff += to_copy;
+		remaining -= to_copy;
+		/* ... if it's full, write it out. */
+		if (state->avail == 0) {
+			char *p = state->buffer;
+			size_t to_write = state->buffer_size;
+			while (to_write > 0) {
+				bytes_written = (a->client_writer)(&a->archive,
+				    a->client_data, p, to_write);
+				if (bytes_written <= 0)
+					return (ARCHIVE_FATAL);
+				if ((size_t)bytes_written > to_write) {
+					archive_set_error(&(a->archive),
+					    -1, "write overrun");
+					return (ARCHIVE_FATAL);
+				}
+				p += bytes_written;
+				to_write -= bytes_written;
+			}
+			state->next = state->buffer;
+			state->avail = state->buffer_size;
+		}
+	}
+
+	while ((size_t)remaining >= state->buffer_size) {
+		/* Write out full blocks directly to client. */
+		bytes_written = (a->client_writer)(&a->archive,
+		    a->client_data, buff, state->buffer_size);
+		if (bytes_written <= 0)
+			return (ARCHIVE_FATAL);
+		buff += bytes_written;
+		remaining -= bytes_written;
+	}
+
+	if (remaining > 0) {
+		/* Copy last bit into copy buffer. */
+		memcpy(state->next, buff, remaining);
+		state->next += remaining;
+		state->avail -= remaining;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_client_close(struct archive_write_filter *f)
+{
+	struct archive_write *a = (struct archive_write *)f->archive;
+	struct archive_none *state = (struct archive_none *)f->data;
+	ssize_t block_length;
+	ssize_t target_block_length;
+	ssize_t bytes_written;
+	int ret = ARCHIVE_OK;
+
+	/* If there's pending data, pad and write the last block */
+	if (state->next != state->buffer) {
+		block_length = state->buffer_size - state->avail;
+
+		/* Tricky calculation to determine size of last block */
+		if (a->bytes_in_last_block <= 0)
+			/* Default or Zero: pad to full block */
+			target_block_length = a->bytes_per_block;
+		else
+			/* Round to next multiple of bytes_in_last_block. */
+			target_block_length = a->bytes_in_last_block *
+			    ( (block_length + a->bytes_in_last_block - 1) /
+			        a->bytes_in_last_block);
+		if (target_block_length > a->bytes_per_block)
+			target_block_length = a->bytes_per_block;
+		if (block_length < target_block_length) {
+			memset(state->next, 0,
+			    target_block_length - block_length);
+			block_length = target_block_length;
+		}
+		bytes_written = (a->client_writer)(&a->archive,
+		    a->client_data, state->buffer, block_length);
+		ret = bytes_written <= 0 ? ARCHIVE_FATAL : ARCHIVE_OK;
+	}
+	if (a->client_closer)
+		(*a->client_closer)(&a->archive, a->client_data);
+	free(state->buffer);
+	free(state);
+	a->client_data = NULL;
+	/* Clear passphrase. */
+	if (a->passphrase != NULL) {
+		memset(a->passphrase, 0, strlen(a->passphrase));
+		free(a->passphrase);
+		a->passphrase = NULL;
+	}
+	/* Clear the close handler myself not to be called again. */
+	f->state = ARCHIVE_WRITE_FILTER_STATE_CLOSED;
+	return (ret);
+}
+
+/*
+ * Open the archive using the current settings.
+ */
+int
+archive_write_open(struct archive *_a, void *client_data,
+    archive_open_callback *opener, archive_write_callback *writer,
+    archive_close_callback *closer)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *client_filter;
+	int ret, r1;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_open");
+	archive_clear_error(&a->archive);
+
+	a->client_writer = writer;
+	a->client_opener = opener;
+	a->client_closer = closer;
+	a->client_data = client_data;
+
+	client_filter = __archive_write_allocate_filter(_a);
+	client_filter->open = archive_write_client_open;
+	client_filter->write = archive_write_client_write;
+	client_filter->close = archive_write_client_close;
+
+	ret = __archive_write_filters_open(a);
+	if (ret < ARCHIVE_WARN) {
+		r1 = __archive_write_filters_close(a);
+		__archive_write_filters_free(_a);
+		return (r1 < ret ? r1 : ret);
+	}
+
+	a->archive.state = ARCHIVE_STATE_HEADER;
+	if (a->format_init)
+		ret = (a->format_init)(a);
+	return (ret);
+}
+
+/*
+ * Close out the archive.
+ */
+static int
+_archive_write_close(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int r = ARCHIVE_OK, r1 = ARCHIVE_OK;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL,
+	    "archive_write_close");
+	if (a->archive.state == ARCHIVE_STATE_NEW
+	    || a->archive.state == ARCHIVE_STATE_CLOSED)
+		return (ARCHIVE_OK); /* Okay to close() when not open. */
+
+	archive_clear_error(&a->archive);
+
+	/* Finish the last entry if a finish callback is specified */
+	if (a->archive.state == ARCHIVE_STATE_DATA
+	    && a->format_finish_entry != NULL)
+		r = ((a->format_finish_entry)(a));
+
+	/* Finish off the archive. */
+	/* TODO: have format closers invoke compression close. */
+	if (a->format_close != NULL) {
+		r1 = (a->format_close)(a);
+		if (r1 < r)
+			r = r1;
+	}
+
+	/* Finish the compression and close the stream. */
+	r1 = __archive_write_filters_close(a);
+	if (r1 < r)
+		r = r1;
+
+	if (a->archive.state != ARCHIVE_STATE_FATAL)
+		a->archive.state = ARCHIVE_STATE_CLOSED;
+	return (r);
+}
+
+static int
+_archive_write_filter_count(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *p = a->filter_first;
+	int count = 0;
+	while(p) {
+		count++;
+		p = p->next_filter;
+	}
+	return count;
+}
+
+void
+__archive_write_filters_free(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int r = ARCHIVE_OK, r1;
+
+	while (a->filter_first != NULL) {
+		struct archive_write_filter *next
+		    = a->filter_first->next_filter;
+		if (a->filter_first->free != NULL) {
+			r1 = (*a->filter_first->free)(a->filter_first);
+			if (r > r1)
+				r = r1;
+		}
+		free(a->filter_first);
+		a->filter_first = next;
+	}
+	a->filter_last = NULL;
+}
+
+/*
+ * Destroy the archive structure.
+ *
+ * Be careful: user might just call write_new and then write_free.
+ * Don't assume we actually wrote anything or performed any non-trivial
+ * initialization.
+ */
+static int
+_archive_write_free(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int r = ARCHIVE_OK, r1;
+
+	if (_a == NULL)
+		return (ARCHIVE_OK);
+	/* It is okay to call free() in state FATAL. */
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_free");
+	if (a->archive.state != ARCHIVE_STATE_FATAL)
+		r = archive_write_close(&a->archive);
+
+	/* Release format resources. */
+	if (a->format_free != NULL) {
+		r1 = (a->format_free)(a);
+		if (r1 < r)
+			r = r1;
+	}
+
+	__archive_write_filters_free(_a);
+
+	/* Release various dynamic buffers. */
+	free((void *)(uintptr_t)(const void *)a->nulls);
+	archive_string_free(&a->archive.error_string);
+	if (a->passphrase != NULL) {
+		/* A passphrase should be cleaned. */
+		memset(a->passphrase, 0, strlen(a->passphrase));
+		free(a->passphrase);
+	}
+	a->archive.magic = 0;
+	__archive_clean(&a->archive);
+	free(a);
+	return (r);
+}
+
+/*
+ * Write the appropriate header.
+ */
+static int
+_archive_write_header(struct archive *_a, struct archive_entry *entry)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int ret, r2;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_DATA | ARCHIVE_STATE_HEADER, "archive_write_header");
+	archive_clear_error(&a->archive);
+
+	if (a->format_write_header == NULL) {
+		archive_set_error(&(a->archive), -1,
+		    "Format must be set before you can write to an archive.");
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+
+	/* In particular, "retry" and "fatal" get returned immediately. */
+	ret = archive_write_finish_entry(&a->archive);
+	if (ret == ARCHIVE_FATAL) {
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+	if (ret < ARCHIVE_OK && ret != ARCHIVE_WARN)
+		return (ret);
+
+	if (a->skip_file_set &&
+	    archive_entry_dev_is_set(entry) &&
+	    archive_entry_ino_is_set(entry) &&
+	    archive_entry_dev(entry) == (dev_t)a->skip_file_dev &&
+	    archive_entry_ino64(entry) == a->skip_file_ino) {
+		archive_set_error(&a->archive, 0,
+		    "Can't add archive to itself");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Format and write header. */
+	r2 = ((a->format_write_header)(a, entry));
+	if (r2 == ARCHIVE_FAILED) {
+		return (ARCHIVE_FAILED);
+	}
+	if (r2 == ARCHIVE_FATAL) {
+		a->archive.state = ARCHIVE_STATE_FATAL;
+		return (ARCHIVE_FATAL);
+	}
+	if (r2 < ret)
+		ret = r2;
+
+	a->archive.state = ARCHIVE_STATE_DATA;
+	return (ret);
+}
+
+static int
+_archive_write_finish_entry(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int ret = ARCHIVE_OK;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_write_finish_entry");
+	if (a->archive.state & ARCHIVE_STATE_DATA
+	    && a->format_finish_entry != NULL)
+		ret = (a->format_finish_entry)(a);
+	a->archive.state = ARCHIVE_STATE_HEADER;
+	return (ret);
+}
+
+/*
+ * Note that the compressor is responsible for blocking.
+ */
+static ssize_t
+_archive_write_data(struct archive *_a, const void *buff, size_t s)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	const size_t max_write = INT_MAX;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_DATA, "archive_write_data");
+	/* In particular, this catches attempts to pass negative values. */
+	if (s > max_write)
+		s = max_write;
+	archive_clear_error(&a->archive);
+	return ((a->format_write_data)(a, buff, s));
+}
+
+static struct archive_write_filter *
+filter_lookup(struct archive *_a, int n)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f = a->filter_first;
+	if (n == -1)
+		return a->filter_last;
+	if (n < 0)
+		return NULL;
+	while (n > 0 && f != NULL) {
+		f = f->next_filter;
+		--n;
+	}
+	return f;
+}
+
+static int
+_archive_filter_code(struct archive *_a, int n)
+{
+	struct archive_write_filter *f = filter_lookup(_a, n);
+	return f == NULL ? -1 : f->code;
+}
+
+static const char *
+_archive_filter_name(struct archive *_a, int n)
+{
+	struct archive_write_filter *f = filter_lookup(_a, n);
+	return f != NULL ? f->name : NULL;
+}
+
+static int64_t
+_archive_filter_bytes(struct archive *_a, int n)
+{
+	struct archive_write_filter *f = filter_lookup(_a, n);
+	return f == NULL ? -1 : f->bytes_written;
+}
diff --git a/c/archive_write_add_filter.c b/c/archive_write_add_filter.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2012 Ondrej Holy
+ * 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$");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps filter codes to functions. */
+static const
+struct { int code; int (*setter)(struct archive *); } codes[] =
+{
+	{ ARCHIVE_FILTER_NONE,		archive_write_add_filter_none },
+	{ ARCHIVE_FILTER_GZIP,		archive_write_add_filter_gzip },
+	{ ARCHIVE_FILTER_BZIP2,		archive_write_add_filter_bzip2 },
+	{ ARCHIVE_FILTER_COMPRESS,	archive_write_add_filter_compress },
+	{ ARCHIVE_FILTER_GRZIP,		archive_write_add_filter_grzip },
+	{ ARCHIVE_FILTER_LRZIP,		archive_write_add_filter_lrzip },
+	{ ARCHIVE_FILTER_LZ4,		archive_write_add_filter_lz4 },
+	{ ARCHIVE_FILTER_LZIP,		archive_write_add_filter_lzip },
+	{ ARCHIVE_FILTER_LZMA,		archive_write_add_filter_lzma },
+	{ ARCHIVE_FILTER_LZOP,		archive_write_add_filter_lzip },
+	{ ARCHIVE_FILTER_UU,		archive_write_add_filter_uuencode },
+	{ ARCHIVE_FILTER_XZ,		archive_write_add_filter_xz },
+	{ ARCHIVE_FILTER_ZSTD,		archive_write_add_filter_zstd },
+	{ -1,			NULL }
+};
+
+int
+archive_write_add_filter(struct archive *a, int code)
+{
+	int i;
+
+	for (i = 0; codes[i].code != -1; i++) {
+		if (code == codes[i].code)
+			return ((codes[i].setter)(a));
+	}
+
+	archive_set_error(a, EINVAL, "No such filter");
+	return (ARCHIVE_FATAL);
+}
diff --git a/c/archive_write_add_filter_b64encode.c b/c/archive_write_add_filter_b64encode.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_b64encode.c
@@ -0,0 +1,304 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * 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$");
+
+#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_string.h"
+#include "archive_write_private.h"
+
+#define LBYTES	57
+
+struct private_b64encode {
+	int			mode;
+	struct archive_string	name;
+	struct archive_string	encoded_buff;
+	size_t			bs;
+	size_t			hold_len;
+	unsigned char		hold[LBYTES];
+};
+
+static int archive_filter_b64encode_options(struct archive_write_filter *,
+    const char *, const char *);
+static int archive_filter_b64encode_open(struct archive_write_filter *);
+static int archive_filter_b64encode_write(struct archive_write_filter *,
+    const void *, size_t);
+static int archive_filter_b64encode_close(struct archive_write_filter *);
+static int archive_filter_b64encode_free(struct archive_write_filter *);
+static void la_b64_encode(struct archive_string *, const unsigned char *, size_t);
+static int64_t atol8(const char *, size_t);
+
+static const char base64[] = {
+	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+	'w', 'x', 'y', 'z', '0', '1', '2', '3',
+	'4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+/*
+ * Add a compress filter to this write handle.
+ */
+int
+archive_write_add_filter_b64encode(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct private_b64encode *state;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
+
+	state = (struct private_b64encode *)calloc(1, sizeof(*state));
+	if (state == NULL) {
+		archive_set_error(f->archive, ENOMEM,
+		    "Can't allocate data for b64encode filter");
+		return (ARCHIVE_FATAL);
+	}
+	archive_strcpy(&state->name, "-");
+	state->mode = 0644;
+
+	f->data = state;
+	f->name = "b64encode";
+	f->code = ARCHIVE_FILTER_UU;
+	f->open = archive_filter_b64encode_open;
+	f->options = archive_filter_b64encode_options;
+	f->write = archive_filter_b64encode_write;
+	f->close = archive_filter_b64encode_close;
+	f->free = archive_filter_b64encode_free;
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_filter_b64encode_options(struct archive_write_filter *f, const char *key,
+    const char *value)
+{
+	struct private_b64encode *state = (struct private_b64encode *)f->data;
+
+	if (strcmp(key, "mode") == 0) {
+		if (value == NULL) {
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+			    "mode option requires octal digits");
+			return (ARCHIVE_FAILED);
+		}
+		state->mode = (int)atol8(value, strlen(value)) & 0777;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "name") == 0) {
+		if (value == NULL) {
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+			    "name option requires a string");
+			return (ARCHIVE_FAILED);
+		}
+		archive_strcpy(&state->name, value);
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_filter_b64encode_open(struct archive_write_filter *f)
+{
+	struct private_b64encode *state = (struct private_b64encode *)f->data;
+	size_t bs = 65536, bpb;
+
+	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+		/* Buffer size should be a multiple number of the of bytes
+		 * per block for performance. */
+		bpb = archive_write_get_bytes_per_block(f->archive);
+		if (bpb > bs)
+			bs = bpb;
+		else if (bpb != 0)
+			bs -= bs % bpb;
+	}
+
+	state->bs = bs;
+	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
+		archive_set_error(f->archive, ENOMEM,
+		    "Can't allocate data for b64encode buffer");
+		return (ARCHIVE_FATAL);
+	}
+
+	archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",
+	    state->mode, state->name.s);
+
+	f->data = state;
+	return (0);
+}
+
+static void
+la_b64_encode(struct archive_string *as, const unsigned char *p, size_t len)
+{
+	int c;
+
+	for (; len >= 3; p += 3, len -= 3) {
+		c = p[0] >> 2;
+		archive_strappend_char(as, base64[c]);
+		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
+		archive_strappend_char(as, base64[c]);
+		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
+		archive_strappend_char(as, base64[c]);
+		c = p[2] & 0x3f;
+		archive_strappend_char(as, base64[c]);
+	}
+	if (len > 0) {
+		c = p[0] >> 2;
+		archive_strappend_char(as, base64[c]);
+		c = (p[0] & 0x03) << 4;
+		if (len == 1) {
+			archive_strappend_char(as, base64[c]);
+			archive_strappend_char(as, '=');
+			archive_strappend_char(as, '=');
+		} else {
+			c |= (p[1] & 0xf0) >> 4;
+			archive_strappend_char(as, base64[c]);
+			c = (p[1] & 0x0f) << 2;
+			archive_strappend_char(as, base64[c]);
+			archive_strappend_char(as, '=');
+		}
+	}
+	archive_strappend_char(as, '\n');
+}
+
+/*
+ * Write data to the encoded stream.
+ */
+static int
+archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
+    size_t length)
+{
+	struct private_b64encode *state = (struct private_b64encode *)f->data;
+	const unsigned char *p = buff;
+	int ret = ARCHIVE_OK;
+
+	if (length == 0)
+		return (ret);
+
+	if (state->hold_len) {
+		while (state->hold_len < LBYTES && length > 0) {
+			state->hold[state->hold_len++] = *p++;
+			length--;
+		}
+		if (state->hold_len < LBYTES)
+			return (ret);
+		la_b64_encode(&state->encoded_buff, state->hold, LBYTES);
+		state->hold_len = 0;
+	}
+
+	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
+		la_b64_encode(&state->encoded_buff, p, LBYTES);
+
+	/* Save remaining bytes. */
+	if (length > 0) {
+		memcpy(state->hold, p, length);
+		state->hold_len = length;
+	}
+	while (archive_strlen(&state->encoded_buff) >= state->bs) {
+		ret = __archive_write_filter(f->next_filter,
+		    state->encoded_buff.s, state->bs);
+		memmove(state->encoded_buff.s,
+		    state->encoded_buff.s + state->bs,
+		    state->encoded_buff.length - state->bs);
+		state->encoded_buff.length -= state->bs;
+	}
+
+	return (ret);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_filter_b64encode_close(struct archive_write_filter *f)
+{
+	struct private_b64encode *state = (struct private_b64encode *)f->data;
+
+	/* Flush remaining bytes. */
+	if (state->hold_len != 0)
+		la_b64_encode(&state->encoded_buff, state->hold, state->hold_len);
+	archive_string_sprintf(&state->encoded_buff, "====\n");
+	/* Write the last block */
+	archive_write_set_bytes_in_last_block(f->archive, 1);
+	return __archive_write_filter(f->next_filter,
+	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
+}
+
+static int
+archive_filter_b64encode_free(struct archive_write_filter *f)
+{
+	struct private_b64encode *state = (struct private_b64encode *)f->data;
+
+	archive_string_free(&state->name);
+	archive_string_free(&state->encoded_buff);
+	free(state);
+	return (ARCHIVE_OK);
+}
+
+static int64_t
+atol8(const char *p, size_t char_cnt)
+{
+	int64_t l;
+	int digit;
+        
+	l = 0;
+	while (char_cnt-- > 0) {
+		if (*p >= '0' && *p <= '7')
+			digit = *p - '0';
+		else
+			break;
+		p++;
+		l <<= 3;
+		l |= digit;
+	}
+	return (l);
+}
+
diff --git a/c/archive_write_add_filter_by_name.c b/c/archive_write_add_filter_by_name.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_by_name.c
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * 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$");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps names to functions. */
+static const
+struct { const char *name; int (*setter)(struct archive *); } names[] =
+{
+	{ "b64encode",		archive_write_add_filter_b64encode },
+	{ "bzip2",		archive_write_add_filter_bzip2 },
+	{ "compress",		archive_write_add_filter_compress },
+	{ "grzip",		archive_write_add_filter_grzip },
+	{ "gzip",		archive_write_add_filter_gzip },
+	{ "lrzip",		archive_write_add_filter_lrzip },
+	{ "lz4",		archive_write_add_filter_lz4 },
+	{ "lzip",		archive_write_add_filter_lzip },
+	{ "lzma",		archive_write_add_filter_lzma },
+	{ "lzop",		archive_write_add_filter_lzop },
+	{ "uuencode",		archive_write_add_filter_uuencode },
+	{ "xz",			archive_write_add_filter_xz },
+	{ "zstd",		archive_write_add_filter_zstd },
+	{ NULL,			NULL }
+};
+
+int
+archive_write_add_filter_by_name(struct archive *a, const char *name)
+{
+	int i;
+
+	for (i = 0; names[i].name != NULL; i++) {
+		if (strcmp(name, names[i].name) == 0)
+			return ((names[i].setter)(a));
+	}
+
+	archive_set_error(a, EINVAL, "No such filter '%s'", name);
+	a->state = ARCHIVE_STATE_FATAL;
+	return (ARCHIVE_FATAL);
+}
diff --git a/c/archive_write_add_filter_bzip2.c b/c/archive_write_add_filter_bzip2.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_bzip2.c
@@ -0,0 +1,401 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_write_set_compression_bzip2.c 201091 2009-12-28 02:22:41Z kientzle $");
+
+#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_BZLIB_H
+#include <bzlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_bzip2(struct archive *a)
+{
+	__archive_write_filters_free(a);
+	return (archive_write_add_filter_bzip2(a));
+}
+#endif
+
+struct private_data {
+	int		 compression_level;
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	bz_stream	 stream;
+	int64_t		 total_in;
+	char		*compressed;
+	size_t		 compressed_buffer_size;
+#else
+	struct archive_write_program_data *pdata;
+#endif
+};
+
+static int archive_compressor_bzip2_close(struct archive_write_filter *);
+static int archive_compressor_bzip2_free(struct archive_write_filter *);
+static int archive_compressor_bzip2_open(struct archive_write_filter *);
+static int archive_compressor_bzip2_options(struct archive_write_filter *,
+		    const char *, const char *);
+static int archive_compressor_bzip2_write(struct archive_write_filter *,
+		    const void *, size_t);
+
+/*
+ * Add a bzip2 compression filter to this write handle.
+ */
+int
+archive_write_add_filter_bzip2(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct private_data *data;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_bzip2");
+
+	data = calloc(1, sizeof(*data));
+	if (data == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	data->compression_level = 9; /* default */
+
+	f->data = data;
+	f->options = &archive_compressor_bzip2_options;
+	f->close = &archive_compressor_bzip2_close;
+	f->free = &archive_compressor_bzip2_free;
+	f->open = &archive_compressor_bzip2_open;
+	f->code = ARCHIVE_FILTER_BZIP2;
+	f->name = "bzip2";
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	return (ARCHIVE_OK);
+#else
+	data->pdata = __archive_write_program_allocate("bzip2");
+	if (data->pdata == NULL) {
+		free(data);
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	data->compression_level = 0;
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Using external bzip2 program");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_bzip2_options(struct archive_write_filter *f,
+    const char *key, const char *value)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (strcmp(key, "compression-level") == 0) {
+		if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
+		    value[1] != '\0')
+			return (ARCHIVE_WARN);
+		data->compression_level = value[0] - '0';
+		/* Make '0' be a synonym for '1'. */
+		/* This way, bzip2 compressor supports the same 0..9
+		 * range of levels as gzip. */
+		if (data->compression_level < 1)
+			data->compression_level = 1;
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+/* Don't compile this if we don't have bzlib. */
+
+/*
+ * Yuck.  bzlib.h is not const-correct, so I need this one bit
+ * of ugly hackery to convert a const * pointer to a non-const pointer.
+ */
+#define	SET_NEXT_IN(st,src)					\
+	(st)->stream.next_in = (char *)(uintptr_t)(const void *)(src)
+static int drive_compressor(struct archive_write_filter *,
+		    struct private_data *, int finishing);
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_bzip2_open(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	int ret;
+
+	if (data->compressed == NULL) {
+		size_t bs = 65536, bpb;
+		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+			/* Buffer size should be a multiple number of the of bytes
+			 * per block for performance. */
+			bpb = archive_write_get_bytes_per_block(f->archive);
+			if (bpb > bs)
+				bs = bpb;
+			else if (bpb != 0)
+				bs -= bs % bpb;
+		}
+		data->compressed_buffer_size = bs;
+		data->compressed
+		    = (char *)malloc(data->compressed_buffer_size);
+		if (data->compressed == NULL) {
+			archive_set_error(f->archive, ENOMEM,
+			    "Can't allocate data for compression buffer");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	memset(&data->stream, 0, sizeof(data->stream));
+	data->stream.next_out = data->compressed;
+	data->stream.avail_out = data->compressed_buffer_size;
+	f->write = archive_compressor_bzip2_write;
+
+	/* Initialize compression library */
+	ret = BZ2_bzCompressInit(&(data->stream),
+	    data->compression_level, 0, 30);
+	if (ret == BZ_OK) {
+		f->data = data;
+		return (ARCHIVE_OK);
+	}
+
+	/* Library setup failed: clean up. */
+	archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+	    "Internal error initializing compression library");
+
+	/* Override the error message if we know what really went wrong. */
+	switch (ret) {
+	case BZ_PARAM_ERROR:
+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library: "
+		    "invalid setup parameter");
+		break;
+	case BZ_MEM_ERROR:
+		archive_set_error(f->archive, ENOMEM,
+		    "Internal error initializing compression library: "
+		    "out of memory");
+		break;
+	case BZ_CONFIG_ERROR:
+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library: "
+		    "mis-compiled library");
+		break;
+	}
+
+	return (ARCHIVE_FATAL);
+
+}
+
+/*
+ * Write data to the compressed stream.
+ *
+ * Returns ARCHIVE_OK if all data written, error otherwise.
+ */
+static int
+archive_compressor_bzip2_write(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	/* Update statistics */
+	data->total_in += length;
+
+	/* Compress input data to output buffer */
+	SET_NEXT_IN(data, buff);
+	data->stream.avail_in = length;
+	if (drive_compressor(f, data, 0))
+		return (ARCHIVE_FATAL);
+	return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression.
+ */
+static int
+archive_compressor_bzip2_close(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	int ret;
+
+	/* Finish compression cycle. */
+	ret = drive_compressor(f, data, 1);
+	if (ret == ARCHIVE_OK) {
+		/* Write the last block */
+		ret = __archive_write_filter(f->next_filter,
+		    data->compressed,
+		    data->compressed_buffer_size - data->stream.avail_out);
+	}
+
+	switch (BZ2_bzCompressEnd(&(data->stream))) {
+	case BZ_OK:
+		break;
+	default:
+		archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
+		    "Failed to clean up compressor");
+		ret = ARCHIVE_FATAL;
+	}
+	return ret;
+}
+
+static int
+archive_compressor_bzip2_free(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	free(data->compressed);
+	free(data);
+	f->data = NULL;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Utility function to push input data through compressor, writing
+ * full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+    struct private_data *data, int finishing)
+{
+	int ret;
+
+	for (;;) {
+		if (data->stream.avail_out == 0) {
+			ret = __archive_write_filter(f->next_filter,
+			    data->compressed,
+			    data->compressed_buffer_size);
+			if (ret != ARCHIVE_OK) {
+				/* TODO: Handle this write failure */
+				return (ARCHIVE_FATAL);
+			}
+			data->stream.next_out = data->compressed;
+			data->stream.avail_out = data->compressed_buffer_size;
+		}
+
+		/* If there's nothing to do, we're done. */
+		if (!finishing && data->stream.avail_in == 0)
+			return (ARCHIVE_OK);
+
+		ret = BZ2_bzCompress(&(data->stream),
+		    finishing ? BZ_FINISH : BZ_RUN);
+
+		switch (ret) {
+		case BZ_RUN_OK:
+			/* In non-finishing case, did compressor
+			 * consume everything? */
+			if (!finishing && data->stream.avail_in == 0)
+				return (ARCHIVE_OK);
+			break;
+		case BZ_FINISH_OK:  /* Finishing: There's more work to do */
+			break;
+		case BZ_STREAM_END: /* Finishing: all done */
+			/* Only occurs in finishing case */
+			return (ARCHIVE_OK);
+		default:
+			/* Any other return value indicates an error */
+			archive_set_error(f->archive,
+			    ARCHIVE_ERRNO_PROGRAMMER,
+			    "Bzip2 compression failed;"
+			    " BZ2_bzCompress() returned %d",
+			    ret);
+			return (ARCHIVE_FATAL);
+		}
+	}
+}
+
+#else /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
+
+static int
+archive_compressor_bzip2_open(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	struct archive_string as;
+	int r;
+
+	archive_string_init(&as);
+	archive_strcpy(&as, "bzip2");
+
+	/* Specify compression level. */
+	if (data->compression_level > 0) {
+		archive_strcat(&as, " -");
+		archive_strappend_char(&as, '0' + data->compression_level);
+	}
+	f->write = archive_compressor_bzip2_write;
+
+	r = __archive_write_program_open(f, data->pdata, as.s);
+	archive_string_free(&as);
+	return (r);
+}
+
+static int
+archive_compressor_bzip2_write(struct archive_write_filter *f, const void *buff,
+    size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_bzip2_close(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_compressor_bzip2_free(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	__archive_write_program_free(data->pdata);
+	free(data);
+	return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_BZLIB_H && BZ_CONFIG_ERROR */
diff --git a/c/archive_write_add_filter_compress.c b/c/archive_write_add_filter_compress.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_compress.c
@@ -0,0 +1,447 @@
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * 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.
+ */
+
+/*-
+ * 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.
+ * 3. 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: head/lib/libarchive/archive_write_set_compression_compress.c 201111 2009-12-28 03:33:05Z kientzle $");
+
+#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_write_private.h"
+
+#define	HSIZE		69001	/* 95% occupancy */
+#define	HSHIFT		8	/* 8 - trunc(log2(HSIZE / 65536)) */
+#define	CHECK_GAP 10000		/* Ratio check interval. */
+
+#define	MAXCODE(bits)	((1 << (bits)) - 1)
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define	FIRST	257		/* First free entry. */
+#define	CLEAR	256		/* Table clear output code. */
+
+struct private_data {
+	int64_t in_count, out_count, checkpoint;
+
+	int code_len;			/* Number of bits/code. */
+	int cur_maxcode;		/* Maximum code, given n_bits. */
+	int max_maxcode;		/* Should NEVER generate this code. */
+	int hashtab [HSIZE];
+	unsigned short codetab [HSIZE];
+	int first_free;		/* First unused entry. */
+	int compress_ratio;
+
+	int cur_code, cur_fcode;
+
+	int bit_offset;
+	unsigned char bit_buf;
+
+	unsigned char	*compressed;
+	size_t		 compressed_buffer_size;
+	size_t		 compressed_offset;
+};
+
+static int archive_compressor_compress_open(struct archive_write_filter *);
+static int archive_compressor_compress_write(struct archive_write_filter *,
+		    const void *, size_t);
+static int archive_compressor_compress_close(struct archive_write_filter *);
+static int archive_compressor_compress_free(struct archive_write_filter *);
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_compress(struct archive *a)
+{
+	__archive_write_filters_free(a);
+	return (archive_write_add_filter_compress(a));
+}
+#endif
+
+/*
+ * Add a compress filter to this write handle.
+ */
+int
+archive_write_add_filter_compress(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_compress");
+	f->open = &archive_compressor_compress_open;
+	f->code = ARCHIVE_FILTER_COMPRESS;
+	f->name = "compress";
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_compress_open(struct archive_write_filter *f)
+{
+	struct private_data *state;
+	size_t bs = 65536, bpb;
+
+	f->code = ARCHIVE_FILTER_COMPRESS;
+	f->name = "compress";
+
+	state = (struct private_data *)calloc(1, sizeof(*state));
+	if (state == NULL) {
+		archive_set_error(f->archive, ENOMEM,
+		    "Can't allocate data for compression");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+		/* Buffer size should be a multiple number of the of bytes
+		 * per block for performance. */
+		bpb = archive_write_get_bytes_per_block(f->archive);
+		if (bpb > bs)
+			bs = bpb;
+		else if (bpb != 0)
+			bs -= bs % bpb;
+	}
+	state->compressed_buffer_size = bs;
+	state->compressed = malloc(state->compressed_buffer_size);
+
+	if (state->compressed == NULL) {
+		archive_set_error(f->archive, ENOMEM,
+		    "Can't allocate data for compression buffer");
+		free(state);
+		return (ARCHIVE_FATAL);
+	}
+
+	f->write = archive_compressor_compress_write;
+	f->close = archive_compressor_compress_close;
+	f->free = archive_compressor_compress_free;
+
+	state->max_maxcode = 0x10000;	/* Should NEVER generate this code. */
+	state->in_count = 0;		/* Length of input. */
+	state->bit_buf = 0;
+	state->bit_offset = 0;
+	state->out_count = 3;		/* Includes 3-byte header mojo. */
+	state->compress_ratio = 0;
+	state->checkpoint = CHECK_GAP;
+	state->code_len = 9;
+	state->cur_maxcode = MAXCODE(state->code_len);
+	state->first_free = FIRST;
+
+	memset(state->hashtab, 0xff, sizeof(state->hashtab));
+
+	/* Prime output buffer with a gzip header. */
+	state->compressed[0] = 0x1f; /* Compress */
+	state->compressed[1] = 0x9d;
+	state->compressed[2] = 0x90; /* Block mode, 16bit max */
+	state->compressed_offset = 3;
+
+	f->data = state;
+	return (0);
+}
+
+/*-
+ * Output the given code.
+ * Inputs:
+ * 	code:	A n_bits-bit integer.  If == -1, then EOF.  This assumes
+ *		that n_bits <= (long)wordsize - 1.
+ * Outputs:
+ * 	Outputs code to the file.
+ * Assumptions:
+ *	Chars are 8 bits long.
+ * Algorithm:
+ * 	Maintain a BITS character long buffer (so that 8 codes will
+ * fit in it exactly).  Use the VAX insv instruction to insert each
+ * code in turn.  When the buffer fills up empty it and start over.
+ */
+
+static const unsigned char rmask[9] =
+	{0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
+
+static int
+output_byte(struct archive_write_filter *f, unsigned char c)
+{
+	struct private_data *state = f->data;
+
+	state->compressed[state->compressed_offset++] = c;
+	++state->out_count;
+
+	if (state->compressed_buffer_size == state->compressed_offset) {
+		int ret = __archive_write_filter(f->next_filter,
+		    state->compressed, state->compressed_buffer_size);
+		if (ret != ARCHIVE_OK)
+			return ARCHIVE_FATAL;
+		state->compressed_offset = 0;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int
+output_code(struct archive_write_filter *f, int ocode)
+{
+	struct private_data *state = f->data;
+	int bits, ret, clear_flg, bit_offset;
+
+	clear_flg = ocode == CLEAR;
+
+	/*
+	 * Since ocode is always >= 8 bits, only need to mask the first
+	 * hunk on the left.
+	 */
+	bit_offset = state->bit_offset % 8;
+	state->bit_buf |= (ocode << bit_offset) & 0xff;
+	output_byte(f, state->bit_buf);
+
+	bits = state->code_len - (8 - bit_offset);
+	ocode >>= 8 - bit_offset;
+	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
+	if (bits >= 8) {
+		output_byte(f, ocode & 0xff);
+		ocode >>= 8;
+		bits -= 8;
+	}
+	/* Last bits. */
+	state->bit_offset += state->code_len;
+	state->bit_buf = ocode & rmask[bits];
+	if (state->bit_offset == state->code_len * 8)
+		state->bit_offset = 0;
+
+	/*
+	 * If the next entry is going to be too big for the ocode size,
+	 * then increase it, if possible.
+	 */
+	if (clear_flg || state->first_free > state->cur_maxcode) {
+	       /*
+		* Write the whole buffer, because the input side won't
+		* discover the size increase until after it has read it.
+		*/
+		if (state->bit_offset > 0) {
+			while (state->bit_offset < state->code_len * 8) {
+				ret = output_byte(f, state->bit_buf);
+				if (ret != ARCHIVE_OK)
+					return ret;
+				state->bit_offset += 8;
+				state->bit_buf = 0;
+			}
+		}
+		state->bit_buf = 0;
+		state->bit_offset = 0;
+
+		if (clear_flg) {
+			state->code_len = 9;
+			state->cur_maxcode = MAXCODE(state->code_len);
+		} else {
+			state->code_len++;
+			if (state->code_len == 16)
+				state->cur_maxcode = state->max_maxcode;
+			else
+				state->cur_maxcode = MAXCODE(state->code_len);
+		}
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
+output_flush(struct archive_write_filter *f)
+{
+	struct private_data *state = f->data;
+	int ret;
+
+	/* At EOF, write the rest of the buffer. */
+	if (state->bit_offset % 8) {
+		state->code_len = (state->bit_offset % 8 + 7) / 8;
+		ret = output_byte(f, state->bit_buf);
+		if (ret != ARCHIVE_OK)
+			return ret;
+	}
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_compress_write(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	struct private_data *state = (struct private_data *)f->data;
+	int i;
+	int ratio;
+	int c, disp, ret;
+	const unsigned char *bp;
+
+	if (length == 0)
+		return ARCHIVE_OK;
+
+	bp = buff;
+
+	if (state->in_count == 0) {
+		state->cur_code = *bp++;
+		++state->in_count;
+		--length;
+	}
+
+	while (length--) {
+		c = *bp++;
+		state->in_count++;
+		state->cur_fcode = (c << 16) + state->cur_code;
+		i = ((c << HSHIFT) ^ state->cur_code);	/* Xor hashing. */
+
+		if (state->hashtab[i] == state->cur_fcode) {
+			state->cur_code = state->codetab[i];
+			continue;
+		}
+		if (state->hashtab[i] < 0)	/* Empty slot. */
+			goto nomatch;
+		/* Secondary hash (after G. Knott). */
+		if (i == 0)
+			disp = 1;
+		else
+			disp = HSIZE - i;
+ probe:
+		if ((i -= disp) < 0)
+			i += HSIZE;
+
+		if (state->hashtab[i] == state->cur_fcode) {
+			state->cur_code = state->codetab[i];
+			continue;
+		}
+		if (state->hashtab[i] >= 0)
+			goto probe;
+ nomatch:
+		ret = output_code(f, state->cur_code);
+		if (ret != ARCHIVE_OK)
+			return ret;
+		state->cur_code = c;
+		if (state->first_free < state->max_maxcode) {
+			state->codetab[i] = state->first_free++;	/* code -> hashtable */
+			state->hashtab[i] = state->cur_fcode;
+			continue;
+		}
+		if (state->in_count < state->checkpoint)
+			continue;
+
+		state->checkpoint = state->in_count + CHECK_GAP;
+
+		if (state->in_count <= 0x007fffff && state->out_count != 0)
+			ratio = (int)(state->in_count * 256 / state->out_count);
+		else if ((ratio = (int)(state->out_count / 256)) == 0)
+			ratio = 0x7fffffff;
+		else
+			ratio = (int)(state->in_count / ratio);
+
+		if (ratio > state->compress_ratio)
+			state->compress_ratio = ratio;
+		else {
+			state->compress_ratio = 0;
+			memset(state->hashtab, 0xff, sizeof(state->hashtab));
+			state->first_free = FIRST;
+			ret = output_code(f, CLEAR);
+			if (ret != ARCHIVE_OK)
+				return ret;
+		}
+	}
+
+	return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_compress_close(struct archive_write_filter *f)
+{
+	struct private_data *state = (struct private_data *)f->data;
+	int ret;
+
+	ret = output_code(f, state->cur_code);
+	if (ret != ARCHIVE_OK)
+		return ret;
+	ret = output_flush(f);
+	if (ret != ARCHIVE_OK)
+		return ret;
+
+	/* Write the last block */
+	ret = __archive_write_filter(f->next_filter,
+	    state->compressed, state->compressed_offset);
+	return (ret);
+}
+
+static int
+archive_compressor_compress_free(struct archive_write_filter *f)
+{
+	struct private_data *state = (struct private_data *)f->data;
+
+	free(state->compressed);
+	free(state);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_write_add_filter_grzip.c b/c/archive_write_add_filter_grzip.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_grzip.c
@@ -0,0 +1,135 @@
+/*-
+ * 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_write_private.h"
+
+struct write_grzip {
+	struct archive_write_program_data *pdata;
+};
+
+static int archive_write_grzip_open(struct archive_write_filter *);
+static int archive_write_grzip_options(struct archive_write_filter *,
+		    const char *, const char *);
+static int archive_write_grzip_write(struct archive_write_filter *,
+		    const void *, size_t);
+static int archive_write_grzip_close(struct archive_write_filter *);
+static int archive_write_grzip_free(struct archive_write_filter *);
+
+int
+archive_write_add_filter_grzip(struct archive *_a)
+{
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct write_grzip *data;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_grzip");
+
+	data = calloc(1, sizeof(*data));
+	if (data == NULL) {
+		archive_set_error(_a, ENOMEM, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	data->pdata = __archive_write_program_allocate("grzip");
+	if (data->pdata == NULL) {
+		free(data);
+		archive_set_error(_a, ENOMEM, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	f->name = "grzip";
+	f->code = ARCHIVE_FILTER_GRZIP;
+	f->data = data;
+	f->open = archive_write_grzip_open;
+	f->options = archive_write_grzip_options;
+	f->write = archive_write_grzip_write;
+	f->close = archive_write_grzip_close;
+	f->free = archive_write_grzip_free;
+
+	/* Note: This filter always uses an external program, so we
+	 * return "warn" to inform of the fact. */
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external grzip program for grzip compression");
+	return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_grzip_options(struct archive_write_filter *f, const char *key,
+    const char *value)
+{
+	(void)f; /* UNUSED */
+	(void)key; /* UNUSED */
+	(void)value; /* UNUSED */
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_grzip_open(struct archive_write_filter *f)
+{
+	struct write_grzip *data = (struct write_grzip *)f->data;
+
+	return __archive_write_program_open(f, data->pdata, "grzip");
+}
+
+static int
+archive_write_grzip_write(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	struct write_grzip *data = (struct write_grzip *)f->data;
+
+	return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_write_grzip_close(struct archive_write_filter *f)
+{
+	struct write_grzip *data = (struct write_grzip *)f->data;
+
+	return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_write_grzip_free(struct archive_write_filter *f)
+{
+	struct write_grzip *data = (struct write_grzip *)f->data;
+
+	__archive_write_program_free(data->pdata);
+	free(data);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_write_add_filter_gzip.c b/c/archive_write_add_filter_gzip.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_gzip.c
@@ -0,0 +1,442 @@
+/*-
+ * 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: head/lib/libarchive/archive_write_set_compression_gzip.c 201081 2009-12-28 02:04:42Z kientzle $");
+
+#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 <time.h>
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_gzip(struct archive *a)
+{
+	__archive_write_filters_free(a);
+	return (archive_write_add_filter_gzip(a));
+}
+#endif
+
+/* Don't compile this if we don't have zlib. */
+
+struct private_data {
+	int		 compression_level;
+	int		 timestamp;
+#ifdef HAVE_ZLIB_H
+	z_stream	 stream;
+	int64_t		 total_in;
+	unsigned char	*compressed;
+	size_t		 compressed_buffer_size;
+	unsigned long	 crc;
+#else
+	struct archive_write_program_data *pdata;
+#endif
+};
+
+/*
+ * Yuck.  zlib.h is not const-correct, so I need this one bit
+ * of ugly hackery to convert a const * pointer to a non-const pointer.
+ */
+#define	SET_NEXT_IN(st,src)					\
+	(st)->stream.next_in = (Bytef *)(uintptr_t)(const void *)(src)
+
+static int archive_compressor_gzip_options(struct archive_write_filter *,
+		    const char *, const char *);
+static int archive_compressor_gzip_open(struct archive_write_filter *);
+static int archive_compressor_gzip_write(struct archive_write_filter *,
+		    const void *, size_t);
+static int archive_compressor_gzip_close(struct archive_write_filter *);
+static int archive_compressor_gzip_free(struct archive_write_filter *);
+#ifdef HAVE_ZLIB_H
+static int drive_compressor(struct archive_write_filter *,
+		    struct private_data *, int finishing);
+#endif
+
+
+/*
+ * Add a gzip compression filter to this write handle.
+ */
+int
+archive_write_add_filter_gzip(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct private_data *data;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_gzip");
+
+	data = calloc(1, sizeof(*data));
+	if (data == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	f->data = data;
+	f->open = &archive_compressor_gzip_open;
+	f->options = &archive_compressor_gzip_options;
+	f->close = &archive_compressor_gzip_close;
+	f->free = &archive_compressor_gzip_free;
+	f->code = ARCHIVE_FILTER_GZIP;
+	f->name = "gzip";
+#ifdef HAVE_ZLIB_H
+	data->compression_level = Z_DEFAULT_COMPRESSION;
+	return (ARCHIVE_OK);
+#else
+	data->pdata = __archive_write_program_allocate("gzip");
+	if (data->pdata == NULL) {
+		free(data);
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	data->compression_level = 0;
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Using external gzip program");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+archive_compressor_gzip_free(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+#ifdef HAVE_ZLIB_H
+	free(data->compressed);
+#else
+	__archive_write_program_free(data->pdata);
+#endif
+	free(data);
+	f->data = NULL;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_gzip_options(struct archive_write_filter *f, const char *key,
+    const char *value)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (strcmp(key, "compression-level") == 0) {
+		if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
+		    value[1] != '\0')
+			return (ARCHIVE_WARN);
+		data->compression_level = value[0] - '0';
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "timestamp") == 0) {
+		data->timestamp = (value == NULL)?-1:1;
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+#ifdef HAVE_ZLIB_H
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_gzip_open(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	int ret;
+
+	if (data->compressed == NULL) {
+		size_t bs = 65536, bpb;
+		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+			/* Buffer size should be a multiple number of
+			 * the of bytes per block for performance. */
+			bpb = archive_write_get_bytes_per_block(f->archive);
+			if (bpb > bs)
+				bs = bpb;
+			else if (bpb != 0)
+				bs -= bs % bpb;
+		}
+		data->compressed_buffer_size = bs;
+		data->compressed
+		    = (unsigned char *)malloc(data->compressed_buffer_size);
+		if (data->compressed == NULL) {
+			archive_set_error(f->archive, ENOMEM,
+			    "Can't allocate data for compression buffer");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	data->crc = crc32(0L, NULL, 0);
+	data->stream.next_out = data->compressed;
+	data->stream.avail_out = (uInt)data->compressed_buffer_size;
+
+	/* Prime output buffer with a gzip header. */
+	data->compressed[0] = 0x1f; /* GZip signature bytes */
+	data->compressed[1] = 0x8b;
+	data->compressed[2] = 0x08; /* "Deflate" compression */
+	data->compressed[3] = 0; /* No options */
+	if (data->timestamp >= 0) {
+		time_t t = time(NULL);
+		data->compressed[4] = (uint8_t)(t)&0xff;  /* Timestamp */
+		data->compressed[5] = (uint8_t)(t>>8)&0xff;
+		data->compressed[6] = (uint8_t)(t>>16)&0xff;
+		data->compressed[7] = (uint8_t)(t>>24)&0xff;
+	} else
+		memset(&data->compressed[4], 0, 4);
+    if (data->compression_level == 9)
+	    data->compressed[8] = 2;
+    else if(data->compression_level == 1)
+	    data->compressed[8] = 4;
+    else
+	    data->compressed[8] = 0;
+	data->compressed[9] = 3; /* OS=Unix */
+	data->stream.next_out += 10;
+	data->stream.avail_out -= 10;
+
+	f->write = archive_compressor_gzip_write;
+
+	/* Initialize compression library. */
+	ret = deflateInit2(&(data->stream),
+	    data->compression_level,
+	    Z_DEFLATED,
+	    -15 /* < 0 to suppress zlib header */,
+	    8,
+	    Z_DEFAULT_STRATEGY);
+
+	if (ret == Z_OK) {
+		f->data = data;
+		return (ARCHIVE_OK);
+	}
+
+	/* Library setup failed: clean up. */
+	archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, "Internal error "
+	    "initializing compression library");
+
+	/* Override the error message if we know what really went wrong. */
+	switch (ret) {
+	case Z_STREAM_ERROR:
+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing "
+		    "compression library: invalid setup parameter");
+		break;
+	case Z_MEM_ERROR:
+		archive_set_error(f->archive, ENOMEM,
+		    "Internal error initializing compression library");
+		break;
+	case Z_VERSION_ERROR:
+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing "
+		    "compression library: invalid library version");
+		break;
+	}
+
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
+    size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	int ret;
+
+	/* Update statistics */
+	data->crc = crc32(data->crc, (const Bytef *)buff, (uInt)length);
+	data->total_in += length;
+
+	/* Compress input data to output buffer */
+	SET_NEXT_IN(data, buff);
+	data->stream.avail_in = (uInt)length;
+	if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)
+		return (ret);
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_gzip_close(struct archive_write_filter *f)
+{
+	unsigned char trailer[8];
+	struct private_data *data = (struct private_data *)f->data;
+	int ret;
+
+	/* Finish compression cycle */
+	ret = drive_compressor(f, data, 1);
+	if (ret == ARCHIVE_OK) {
+		/* Write the last compressed data. */
+		ret = __archive_write_filter(f->next_filter,
+		    data->compressed,
+		    data->compressed_buffer_size - data->stream.avail_out);
+	}
+	if (ret == ARCHIVE_OK) {
+		/* Build and write out 8-byte trailer. */
+		trailer[0] = (uint8_t)(data->crc)&0xff;
+		trailer[1] = (uint8_t)(data->crc >> 8)&0xff;
+		trailer[2] = (uint8_t)(data->crc >> 16)&0xff;
+		trailer[3] = (uint8_t)(data->crc >> 24)&0xff;
+		trailer[4] = (uint8_t)(data->total_in)&0xff;
+		trailer[5] = (uint8_t)(data->total_in >> 8)&0xff;
+		trailer[6] = (uint8_t)(data->total_in >> 16)&0xff;
+		trailer[7] = (uint8_t)(data->total_in >> 24)&0xff;
+		ret = __archive_write_filter(f->next_filter, trailer, 8);
+	}
+
+	switch (deflateEnd(&(data->stream))) {
+	case Z_OK:
+		break;
+	default:
+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to clean up compressor");
+		ret = ARCHIVE_FATAL;
+	}
+	return ret;
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+    struct private_data *data, int finishing)
+{
+	int ret;
+
+	for (;;) {
+		if (data->stream.avail_out == 0) {
+			ret = __archive_write_filter(f->next_filter,
+			    data->compressed,
+			    data->compressed_buffer_size);
+			if (ret != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			data->stream.next_out = data->compressed;
+			data->stream.avail_out =
+			    (uInt)data->compressed_buffer_size;
+		}
+
+		/* If there's nothing to do, we're done. */
+		if (!finishing && data->stream.avail_in == 0)
+			return (ARCHIVE_OK);
+
+		ret = deflate(&(data->stream),
+		    finishing ? Z_FINISH : Z_NO_FLUSH );
+
+		switch (ret) {
+		case Z_OK:
+			/* In non-finishing case, check if compressor
+			 * consumed everything */
+			if (!finishing && data->stream.avail_in == 0)
+				return (ARCHIVE_OK);
+			/* In finishing case, this return always means
+			 * there's more work */
+			break;
+		case Z_STREAM_END:
+			/* This return can only occur in finishing case. */
+			return (ARCHIVE_OK);
+		default:
+			/* Any other return value indicates an error. */
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+			    "GZip compression failed:"
+			    " deflate() call returned status %d",
+			    ret);
+			return (ARCHIVE_FATAL);
+		}
+	}
+}
+
+#else /* HAVE_ZLIB_H */
+
+static int
+archive_compressor_gzip_open(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	struct archive_string as;
+	int r;
+
+	archive_string_init(&as);
+	archive_strcpy(&as, "gzip");
+
+	/* Specify compression level. */
+	if (data->compression_level > 0) {
+		archive_strcat(&as, " -");
+		archive_strappend_char(&as, '0' + data->compression_level);
+	}
+	if (data->timestamp < 0)
+		/* Do not save timestamp. */
+		archive_strcat(&as, " -n");
+	else if (data->timestamp > 0)
+		/* Save timestamp. */
+		archive_strcat(&as, " -N");
+
+	f->write = archive_compressor_gzip_write;
+	r = __archive_write_program_open(f, data->pdata, as.s);
+	archive_string_free(&as);
+	return (r);
+}
+
+static int
+archive_compressor_gzip_write(struct archive_write_filter *f, const void *buff,
+    size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_gzip_close(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_close(f, data->pdata);
+}
+
+#endif /* HAVE_ZLIB_H */
diff --git a/c/archive_write_add_filter_lrzip.c b/c/archive_write_add_filter_lrzip.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_lrzip.c
@@ -0,0 +1,197 @@
+/*-
+ * 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$");
+
+#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_string.h"
+#include "archive_write_private.h"
+
+struct write_lrzip {
+	struct archive_write_program_data *pdata;
+	int	compression_level;
+	enum { lzma = 0, bzip2, gzip, lzo, none, zpaq } compression;
+};
+
+static int archive_write_lrzip_open(struct archive_write_filter *);
+static int archive_write_lrzip_options(struct archive_write_filter *,
+		    const char *, const char *);
+static int archive_write_lrzip_write(struct archive_write_filter *,
+		    const void *, size_t);
+static int archive_write_lrzip_close(struct archive_write_filter *);
+static int archive_write_lrzip_free(struct archive_write_filter *);
+
+int
+archive_write_add_filter_lrzip(struct archive *_a)
+{
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct write_lrzip *data;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lrzip");
+
+	data = calloc(1, sizeof(*data));
+	if (data == NULL) {
+		archive_set_error(_a, ENOMEM, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	data->pdata = __archive_write_program_allocate("lrzip");
+	if (data->pdata == NULL) {
+		free(data);
+		archive_set_error(_a, ENOMEM, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	f->name = "lrzip";
+	f->code = ARCHIVE_FILTER_LRZIP;
+	f->data = data;
+	f->open = archive_write_lrzip_open;
+	f->options = archive_write_lrzip_options;
+	f->write = archive_write_lrzip_write;
+	f->close = archive_write_lrzip_close;
+	f->free = archive_write_lrzip_free;
+
+	/* Note: This filter always uses an external program, so we
+	 * return "warn" to inform of the fact. */
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external lrzip program for lrzip compression");
+	return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_lrzip_options(struct archive_write_filter *f, const char *key,
+    const char *value)
+{
+	struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+	if (strcmp(key, "compression") == 0) {
+		if (value == NULL)
+			return (ARCHIVE_WARN);
+		else if (strcmp(value, "bzip2") == 0)
+			data->compression = bzip2;
+		else if (strcmp(value, "gzip") == 0)
+			data->compression = gzip;
+		else if (strcmp(value, "lzo") == 0)
+			data->compression = lzo;
+		else if (strcmp(value, "none") == 0)
+			data->compression = none;
+		else if (strcmp(value, "zpaq") == 0)
+			data->compression = zpaq;
+		else
+			return (ARCHIVE_WARN);
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "compression-level") == 0) {
+		if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
+		    value[1] != '\0')
+			return (ARCHIVE_WARN);
+		data->compression_level = value[0] - '0';
+		return (ARCHIVE_OK);
+	}
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_lrzip_open(struct archive_write_filter *f)
+{
+	struct write_lrzip *data = (struct write_lrzip *)f->data;
+	struct archive_string as;
+	int r;
+
+	archive_string_init(&as);
+	archive_strcpy(&as, "lrzip -q");
+
+	/* Specify compression type. */
+	switch (data->compression) {
+	case lzma:/* default compression */
+		break;
+	case bzip2:
+		archive_strcat(&as, " -b");
+		break;
+	case gzip:
+		archive_strcat(&as, " -g");
+		break;
+	case lzo:
+		archive_strcat(&as, " -l");
+		break;
+	case none:
+		archive_strcat(&as, " -n");
+		break;
+	case zpaq:
+		archive_strcat(&as, " -z");
+		break;
+	}
+
+	/* Specify compression level. */
+	if (data->compression_level > 0) {
+		archive_strcat(&as, " -L ");
+		archive_strappend_char(&as, '0' + data->compression_level);
+	}
+
+	r = __archive_write_program_open(f, data->pdata, as.s);
+	archive_string_free(&as);
+	return (r);
+}
+
+static int
+archive_write_lrzip_write(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+	return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_write_lrzip_close(struct archive_write_filter *f)
+{
+	struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+	return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_write_lrzip_free(struct archive_write_filter *f)
+{
+	struct write_lrzip *data = (struct write_lrzip *)f->data;
+
+	__archive_write_program_free(data->pdata);
+	free(data);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_write_add_filter_lz4.c b/c/archive_write_add_filter_lz4.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_lz4.c
@@ -0,0 +1,700 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * 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$");
+
+#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_LZ4_H
+#include <lz4.h>
+#endif
+#ifdef HAVE_LZ4HC_H
+#include <lz4hc.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_xxhash.h"
+
+#define LZ4_MAGICNUMBER	0x184d2204
+
+struct private_data {
+	int		 compression_level;
+	unsigned	 header_written:1;
+	unsigned	 version_number:1;
+	unsigned	 block_independence:1;
+	unsigned	 block_checksum:1;
+	unsigned	 stream_size:1;
+	unsigned	 stream_checksum:1;
+	unsigned	 preset_dictionary:1;
+	unsigned	 block_maximum_size:3;
+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
+	int64_t		 total_in;
+	char		*out;
+	char		*out_buffer;
+	size_t		 out_buffer_size;
+	size_t		 out_block_size;
+	char		*in;
+	char		*in_buffer_allocated;
+	char		*in_buffer;
+	size_t		 in_buffer_size;
+	size_t		 block_size;
+
+	void		*xxh32_state;
+	void		*lz4_stream;
+#else
+	struct archive_write_program_data *pdata;
+#endif
+};
+
+static int archive_filter_lz4_close(struct archive_write_filter *);
+static int archive_filter_lz4_free(struct archive_write_filter *);
+static int archive_filter_lz4_open(struct archive_write_filter *);
+static int archive_filter_lz4_options(struct archive_write_filter *,
+		    const char *, const char *);
+static int archive_filter_lz4_write(struct archive_write_filter *,
+		    const void *, size_t);
+
+/*
+ * Add a lz4 compression filter to this write handle.
+ */
+int
+archive_write_add_filter_lz4(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct private_data *data;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lz4");
+
+	data = calloc(1, sizeof(*data));
+	if (data == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Setup default settings.
+	 */
+	data->compression_level = 1;
+	data->version_number = 0x01;
+	data->block_independence = 1;
+	data->block_checksum = 0;
+	data->stream_size = 0;
+	data->stream_checksum = 1;
+	data->preset_dictionary = 0;
+	data->block_maximum_size = 7;
+
+	/*
+	 * Setup a filter setting.
+	 */
+	f->data = data;
+	f->options = &archive_filter_lz4_options;
+	f->close = &archive_filter_lz4_close;
+	f->free = &archive_filter_lz4_free;
+	f->open = &archive_filter_lz4_open;
+	f->code = ARCHIVE_FILTER_LZ4;
+	f->name = "lz4";
+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
+	return (ARCHIVE_OK);
+#else
+	/*
+	 * We don't have lz4 library, and execute external lz4 program
+	 * instead.
+	 */
+	data->pdata = __archive_write_program_allocate("lz4");
+	if (data->pdata == NULL) {
+		free(data);
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	data->compression_level = 0;
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Using external lz4 program");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_filter_lz4_options(struct archive_write_filter *f,
+    const char *key, const char *value)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (strcmp(key, "compression-level") == 0) {
+		int val;
+		if (value == NULL || !((val = value[0] - '0') >= 1 && val <= 9) ||
+		    value[1] != '\0')
+			return (ARCHIVE_WARN);
+
+#ifndef HAVE_LZ4HC_H
+		if(val >= 3)
+		{
+			archive_set_error(f->archive, ARCHIVE_ERRNO_PROGRAMMER,
+				"High compression not included in this build");
+			return (ARCHIVE_FATAL);
+		}
+#endif
+		data->compression_level = val;
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "stream-checksum") == 0) {
+		data->stream_checksum = value != NULL;
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "block-checksum") == 0) {
+		data->block_checksum = value != NULL;
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "block-size") == 0) {
+		if (value == NULL || !(value[0] >= '4' && value[0] <= '7') ||
+		    value[1] != '\0')
+			return (ARCHIVE_WARN);
+		data->block_maximum_size = value[0] - '0';
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "block-dependence") == 0) {
+		data->block_independence = value == NULL;
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+#if defined(HAVE_LIBLZ4) && LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 2
+/* Don't compile this if we don't have liblz4. */
+
+static int drive_compressor(struct archive_write_filter *, const char *,
+    size_t);
+static int drive_compressor_independence(struct archive_write_filter *,
+    const char *, size_t);
+static int drive_compressor_dependence(struct archive_write_filter *,
+    const char *, size_t);
+static int lz4_write_stream_descriptor(struct archive_write_filter *);
+static ssize_t lz4_write_one_block(struct archive_write_filter *, const char *,
+    size_t);
+
+
+/*
+ * Setup callback.
+ */
+static int
+archive_filter_lz4_open(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	size_t required_size;
+	static size_t const bkmap[] = { 64 * 1024, 256 * 1024, 1 * 1024 * 1024,
+			   4 * 1024 * 1024 };
+	size_t pre_block_size;
+
+	if (data->block_maximum_size < 4)
+		data->block_size = bkmap[0];
+	else
+		data->block_size = bkmap[data->block_maximum_size - 4];
+
+	required_size = 4 + 15 + 4 + data->block_size + 4 + 4;
+	if (data->out_buffer_size < required_size) {
+		size_t bs = required_size, bpb;
+		free(data->out_buffer);
+		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+			/* Buffer size should be a multiple number of
+			 * the of bytes per block for performance. */
+			bpb = archive_write_get_bytes_per_block(f->archive);
+			if (bpb > bs)
+				bs = bpb;
+			else if (bpb != 0) {
+				bs += bpb;
+				bs -= bs % bpb;
+			}
+		}
+		data->out_block_size = bs;
+		bs += required_size;
+		data->out_buffer = malloc(bs);
+		data->out = data->out_buffer;
+		data->out_buffer_size = bs;
+	}
+
+	pre_block_size = (data->block_independence)? 0: 64 * 1024;
+	if (data->in_buffer_size < data->block_size + pre_block_size) {
+		free(data->in_buffer_allocated);
+		data->in_buffer_size = data->block_size;
+		data->in_buffer_allocated =
+		    malloc(data->in_buffer_size + pre_block_size);
+		data->in_buffer = data->in_buffer_allocated + pre_block_size;
+		if (!data->block_independence && data->compression_level >= 3)
+		    data->in_buffer = data->in_buffer_allocated;
+		data->in = data->in_buffer;
+		data->in_buffer_size = data->block_size;
+	}
+
+	if (data->out_buffer == NULL || data->in_buffer_allocated == NULL) {
+		archive_set_error(f->archive, ENOMEM,
+		    "Can't allocate data for compression buffer");
+		return (ARCHIVE_FATAL);
+	}
+
+	f->write = archive_filter_lz4_write;
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the out stream.
+ *
+ * Returns ARCHIVE_OK if all data written, error otherwise.
+ */
+static int
+archive_filter_lz4_write(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	int ret = ARCHIVE_OK;
+	const char *p;
+	size_t remaining;
+	ssize_t size;
+
+	/* If we haven't written a stream descriptor, we have to do it first. */
+	if (!data->header_written) {
+		ret = lz4_write_stream_descriptor(f);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+		data->header_written = 1;
+	}
+
+	/* Update statistics */
+	data->total_in += length;
+
+	p = (const char *)buff;
+	remaining = length;
+	while (remaining) {
+		size_t l;
+		/* Compress input data to output buffer */
+		size = lz4_write_one_block(f, p, remaining);
+		if (size < ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		l = data->out - data->out_buffer;
+		if (l >= data->out_block_size) {
+			ret = __archive_write_filter(f->next_filter,
+			    data->out_buffer, data->out_block_size);
+			l -= data->out_block_size;
+			memcpy(data->out_buffer,
+			    data->out_buffer + data->out_block_size, l);
+			data->out = data->out_buffer + l;
+			if (ret < ARCHIVE_WARN)
+				break;
+		}
+		p += size;
+		remaining -= size;
+	}
+
+	return (ret);
+}
+
+/*
+ * Finish the compression.
+ */
+static int
+archive_filter_lz4_close(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	int ret;
+
+	/* Finish compression cycle. */
+	ret = (int)lz4_write_one_block(f, NULL, 0);
+	if (ret >= 0) {
+		/*
+		 * Write the last block and the end of the stream data.
+		 */
+
+		/* Write End Of Stream. */
+		memset(data->out, 0, 4); data->out += 4;
+		/* Write Stream checksum if needed. */
+		if (data->stream_checksum) {
+			unsigned int checksum;
+			checksum = __archive_xxhash.XXH32_digest(
+					data->xxh32_state);
+			data->xxh32_state = NULL;
+			archive_le32enc(data->out, checksum);
+			data->out += 4;
+		}
+		ret = __archive_write_filter(f->next_filter,
+			    data->out_buffer, data->out - data->out_buffer);
+	}
+	return ret;
+}
+
+static int
+archive_filter_lz4_free(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (data->lz4_stream != NULL) {
+#ifdef HAVE_LZ4HC_H
+		if (data->compression_level >= 3)
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+			LZ4_freeStreamHC(data->lz4_stream);
+#else
+			LZ4_freeHC(data->lz4_stream);
+#endif
+		else
+#endif
+#if LZ4_VERSION_MINOR >= 3
+			LZ4_freeStream(data->lz4_stream);
+#else
+			LZ4_free(data->lz4_stream);
+#endif
+	}
+	free(data->out_buffer);
+	free(data->in_buffer_allocated);
+	free(data->xxh32_state);
+	free(data);
+	f->data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+lz4_write_stream_descriptor(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	uint8_t *sd;
+
+	sd = (uint8_t *)data->out;
+	/* Write Magic Number. */
+	archive_le32enc(&sd[0], LZ4_MAGICNUMBER);
+	/* FLG */
+	sd[4] = (data->version_number << 6)
+	      | (data->block_independence << 5)
+	      | (data->block_checksum << 4)
+	      | (data->stream_size << 3)
+	      | (data->stream_checksum << 2)
+	      | (data->preset_dictionary << 0);
+	/* BD */
+	sd[5] = (data->block_maximum_size << 4);
+	sd[6] = (__archive_xxhash.XXH32(&sd[4], 2, 0) >> 8) & 0xff;
+	data->out += 7;
+	if (data->stream_checksum)
+		data->xxh32_state = __archive_xxhash.XXH32_init(0);
+	else
+		data->xxh32_state = NULL;
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+lz4_write_one_block(struct archive_write_filter *f, const char *p,
+    size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	ssize_t r;
+
+	if (p == NULL) {
+		/* Compress remaining uncompressed data. */
+		if (data->in_buffer == data->in)
+			return 0;
+		else {
+			size_t l = data->in - data->in_buffer;
+			r = drive_compressor(f, data->in_buffer, l);
+			if (r == ARCHIVE_OK)
+				r = (ssize_t)l;
+		}
+	} else if ((data->block_independence || data->compression_level < 3) &&
+	    data->in_buffer == data->in && length >= data->block_size) {
+		r = drive_compressor(f, p, data->block_size);
+		if (r == ARCHIVE_OK)
+			r = (ssize_t)data->block_size;
+	} else {
+		size_t remaining_size = data->in_buffer_size -
+			(data->in - data->in_buffer);
+		size_t l = (remaining_size > length)? length: remaining_size;
+		memcpy(data->in, p, l);
+		data->in += l;
+		if (l == remaining_size) {
+			r = drive_compressor(f, data->in_buffer,
+			    data->block_size);
+			if (r == ARCHIVE_OK)
+				r = (ssize_t)l;
+			data->in = data->in_buffer;
+		} else
+			r = (ssize_t)l;
+	}
+
+	return (r);
+}
+
+
+/*
+ * Utility function to push input data through compressor, writing
+ * full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f, const char *p, size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (data->stream_checksum)
+		__archive_xxhash.XXH32_update(data->xxh32_state,
+			p, (int)length);
+	if (data->block_independence)
+		return drive_compressor_independence(f, p, length);
+	else
+		return drive_compressor_dependence(f, p, length);
+}
+
+static int
+drive_compressor_independence(struct archive_write_filter *f, const char *p,
+    size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	unsigned int outsize;
+
+#ifdef HAVE_LZ4HC_H
+	if (data->compression_level >= 3)
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+		outsize = LZ4_compress_HC(p, data->out + 4,
+		     (int)length, (int)data->block_size,
+		    data->compression_level);
+#else
+		outsize = LZ4_compressHC2_limitedOutput(p, data->out + 4,
+		    (int)length, (int)data->block_size,
+		    data->compression_level);
+#endif
+	else
+#endif
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+		outsize = LZ4_compress_default(p, data->out + 4,
+		    (int)length, (int)data->block_size);
+#else
+		outsize = LZ4_compress_limitedOutput(p, data->out + 4,
+		    (int)length, (int)data->block_size);
+#endif
+
+	if (outsize) {
+		/* The buffer is compressed. */
+		archive_le32enc(data->out, outsize);
+		data->out += 4;
+	} else {
+		/* The buffer is not compressed. The compressed size was
+		 * bigger than its uncompressed size. */
+		archive_le32enc(data->out, length | 0x80000000);
+		data->out += 4;
+		memcpy(data->out, p, length);
+		outsize = length;
+	}
+	data->out += outsize;
+	if (data->block_checksum) {
+		unsigned int checksum =
+		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
+		archive_le32enc(data->out, checksum);
+		data->out += 4;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+drive_compressor_dependence(struct archive_write_filter *f, const char *p,
+    size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	int outsize;
+
+#define DICT_SIZE	(64 * 1024)
+#ifdef HAVE_LZ4HC_H
+	if (data->compression_level >= 3) {
+		if (data->lz4_stream == NULL) {
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+			data->lz4_stream = LZ4_createStreamHC();
+			LZ4_resetStreamHC(data->lz4_stream, data->compression_level);
+#else
+			data->lz4_stream =
+			    LZ4_createHC(data->in_buffer_allocated);
+#endif
+			if (data->lz4_stream == NULL) {
+				archive_set_error(f->archive, ENOMEM,
+				    "Can't allocate data for compression"
+				    " buffer");
+				return (ARCHIVE_FATAL);
+			}
+		}
+		else
+			LZ4_loadDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+		outsize = LZ4_compress_HC_continue(
+		    data->lz4_stream, p, data->out + 4, (int)length,
+		    (int)data->block_size);
+#else
+		outsize = LZ4_compressHC2_limitedOutput_continue(
+		    data->lz4_stream, p, data->out + 4, (int)length,
+		    (int)data->block_size, data->compression_level);
+#endif
+	} else
+#endif
+	{
+		if (data->lz4_stream == NULL) {
+			data->lz4_stream = LZ4_createStream();
+			if (data->lz4_stream == NULL) {
+				archive_set_error(f->archive, ENOMEM,
+				    "Can't allocate data for compression"
+				    " buffer");
+				return (ARCHIVE_FATAL);
+			}
+		}
+		else
+			LZ4_loadDict(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+		outsize = LZ4_compress_fast_continue(
+		    data->lz4_stream, p, data->out + 4, (int)length,
+		    (int)data->block_size, 1);
+#else
+		outsize = LZ4_compress_limitedOutput_continue(
+		    data->lz4_stream, p, data->out + 4, (int)length,
+		    (int)data->block_size);
+#endif
+	}
+
+	if (outsize) {
+		/* The buffer is compressed. */
+		archive_le32enc(data->out, outsize);
+		data->out += 4;
+	} else {
+		/* The buffer is not compressed. The compressed size was
+		 * bigger than its uncompressed size. */
+		archive_le32enc(data->out, length | 0x80000000);
+		data->out += 4;
+		memcpy(data->out, p, length);
+		outsize = length;
+	}
+	data->out += outsize;
+	if (data->block_checksum) {
+		unsigned int checksum =
+		    __archive_xxhash.XXH32(data->out - outsize, outsize, 0);
+		archive_le32enc(data->out, checksum);
+		data->out += 4;
+	}
+
+	if (length == data->block_size) {
+#ifdef HAVE_LZ4HC_H
+		if (data->compression_level >= 3) {
+#if LZ4_VERSION_MAJOR >= 1 && LZ4_VERSION_MINOR >= 7
+			LZ4_saveDictHC(data->lz4_stream, data->in_buffer_allocated, DICT_SIZE);
+#else
+			LZ4_slideInputBufferHC(data->lz4_stream);
+#endif
+			data->in_buffer = data->in_buffer_allocated + DICT_SIZE;
+		}
+		else
+#endif
+			LZ4_saveDict(data->lz4_stream,
+			    data->in_buffer_allocated, DICT_SIZE);
+#undef DICT_SIZE
+	}
+	return (ARCHIVE_OK);
+}
+
+#else /* HAVE_LIBLZ4 */
+
+static int
+archive_filter_lz4_open(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	struct archive_string as;
+	int r;
+
+	archive_string_init(&as);
+	archive_strcpy(&as, "lz4 -z -q -q");
+
+	/* Specify a compression level. */
+	if (data->compression_level > 0) {
+		archive_strcat(&as, " -");
+		archive_strappend_char(&as, '0' + data->compression_level);
+	}
+	/* Specify a block size. */
+	archive_strcat(&as, " -B");
+	archive_strappend_char(&as, '0' + data->block_maximum_size);
+
+	if (data->block_checksum)
+		archive_strcat(&as, " -BX");
+	if (data->stream_checksum == 0)
+		archive_strcat(&as, " --no-frame-crc");
+	if (data->block_independence == 0)
+		archive_strcat(&as, " -BD");
+
+	f->write = archive_filter_lz4_write;
+
+	r = __archive_write_program_open(f, data->pdata, as.s);
+	archive_string_free(&as);
+	return (r);
+}
+
+static int
+archive_filter_lz4_write(struct archive_write_filter *f, const void *buff,
+    size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_filter_lz4_close(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_filter_lz4_free(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	__archive_write_program_free(data->pdata);
+	free(data);
+	return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_LIBLZ4 */
diff --git a/c/archive_write_add_filter_lzop.c b/c/archive_write_add_filter_lzop.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_lzop.c
@@ -0,0 +1,478 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * 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$");
+//#undef HAVE_LZO_LZOCONF_H
+//#undef HAVE_LZO_LZO1X_H
+
+#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 <time.h>
+#ifdef HAVE_LZO_LZOCONF_H
+#include <lzo/lzoconf.h>
+#endif
+#ifdef HAVE_LZO_LZO1X_H
+#include <lzo/lzo1x.h>
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_endian.h"
+#include "archive_write_private.h"
+
+enum lzo_method {
+	METHOD_LZO1X_1 = 1,
+	METHOD_LZO1X_1_15 = 2,
+	METHOD_LZO1X_999 = 3
+};
+struct write_lzop {
+	int compression_level;
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+	unsigned char	*uncompressed;
+	size_t		 uncompressed_buffer_size;
+	size_t		 uncompressed_avail_bytes;
+	unsigned char	*compressed;
+	size_t		 compressed_buffer_size;
+	enum lzo_method	 method;
+	unsigned char	 level;
+	lzo_voidp	 work_buffer;
+	lzo_uint32	 work_buffer_size;
+	char		 header_written;
+#else
+	struct archive_write_program_data *pdata;
+#endif
+};
+
+static int archive_write_lzop_open(struct archive_write_filter *);
+static int archive_write_lzop_options(struct archive_write_filter *,
+		    const char *, const char *);
+static int archive_write_lzop_write(struct archive_write_filter *,
+		    const void *, size_t);
+static int archive_write_lzop_close(struct archive_write_filter *);
+static int archive_write_lzop_free(struct archive_write_filter *);
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+/* Maximum block size. */
+#define BLOCK_SIZE			(256 * 1024)
+/* Block information is composed of uncompressed size(4 bytes),
+ * compressed size(4 bytes) and the checksum of uncompressed data(4 bytes)
+ * in this lzop writer. */
+#define BLOCK_INfO_SIZE			12
+
+#define HEADER_VERSION			9
+#define HEADER_LIBVERSION		11
+#define HEADER_METHOD			15
+#define HEADER_LEVEL			16
+#define HEADER_MTIME_LOW		25
+#define HEADER_MTIME_HIGH		29
+#define HEADER_H_CHECKSUM		34
+
+/*
+ * Header template.
+ */
+static const unsigned char header[] = {
+	/* LZOP Magic code 9 bytes */
+	0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a,
+	/* LZOP utility version(fake data) 2 bytes */
+	0x10, 0x30,
+	/* LZO library version 2 bytes */
+	0x09, 0x40,
+	/* Minimum required LZO library version 2 bytes */
+	0x09, 0x40,
+	/* Method */
+	1,
+	/* Level */
+	5,
+	/* Flags 4 bytes
+	 *  -OS Unix
+	 *  -Stdout
+	 *  -Stdin
+	 *  -Adler32 used for uncompressed data 4 bytes */
+	0x03, 0x00, 0x00, 0x0d,
+	/* Mode (AE_IFREG | 0644) 4 bytes */
+	0x00, 0x00, 0x81, 0xa4,
+	/* Mtime low 4 bytes */
+	0x00, 0x00, 0x00, 0x00,
+	/* Mtime high 4 bytes */
+	0x00, 0x00, 0x00, 0x00,
+	/* Filename length */
+	0x00,
+	/* Header checksum 4 bytes */
+	0x00, 0x00, 0x00, 0x00,
+};
+#endif
+
+int
+archive_write_add_filter_lzop(struct archive *_a)
+{
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct write_lzop *data;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lzop");
+
+	data = calloc(1, sizeof(*data));
+	if (data == NULL) {
+		archive_set_error(_a, ENOMEM, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	f->name = "lzop";
+	f->code = ARCHIVE_FILTER_LZOP;
+	f->data = data;
+	f->open = archive_write_lzop_open;
+	f->options = archive_write_lzop_options;
+	f->write = archive_write_lzop_write;
+	f->close = archive_write_lzop_close;
+	f->free = archive_write_lzop_free;
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+	if (lzo_init() != LZO_E_OK) {
+		free(data);
+		archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+		    "lzo_init(type check) failed");
+		return (ARCHIVE_FATAL);
+	}
+	if (lzo_version() < 0x940) {
+		free(data);
+		archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+		    "liblzo library is too old(%s < 0.940)",
+		    lzo_version_string());
+		return (ARCHIVE_FATAL);
+	}
+	data->compression_level = 5;
+	return (ARCHIVE_OK);
+#else
+	data->pdata = __archive_write_program_allocate("lzop");
+	if (data->pdata == NULL) {
+		free(data);
+		archive_set_error(_a, ENOMEM, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	data->compression_level = 0;
+	/* Note: We return "warn" to inform of using an external lzop
+	 * program. */
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external lzop program for lzop compression");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+archive_write_lzop_free(struct archive_write_filter *f)
+{
+	struct write_lzop *data = (struct write_lzop *)f->data;
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+	free(data->uncompressed);
+	free(data->compressed);
+	free(data->work_buffer);
+#else
+	__archive_write_program_free(data->pdata);
+#endif
+	free(data);
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_lzop_options(struct archive_write_filter *f, const char *key,
+    const char *value)
+{
+	struct write_lzop *data = (struct write_lzop *)f->data;
+
+	if (strcmp(key, "compression-level") == 0) {
+		if (value == NULL || !(value[0] >= '1' && value[0] <= '9') ||
+		    value[1] != '\0')
+			return (ARCHIVE_WARN);
+		data->compression_level = value[0] - '0';
+		return (ARCHIVE_OK);
+	}
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+#if defined(HAVE_LZO_LZOCONF_H) && defined(HAVE_LZO_LZO1X_H)
+static int
+archive_write_lzop_open(struct archive_write_filter *f)
+{
+	struct write_lzop *data = (struct write_lzop *)f->data;
+
+	switch (data->compression_level) {
+	case 1:
+		data->method = METHOD_LZO1X_1_15; data->level = 1; break;
+	default:
+	case 2: case 3: case 4: case 5: case 6:
+		data->method = METHOD_LZO1X_1; data->level = 5; break;
+	case 7:
+		data->method = METHOD_LZO1X_999; data->level = 7; break;
+	case 8:
+		data->method = METHOD_LZO1X_999; data->level = 8; break;
+	case 9:
+		data->method = METHOD_LZO1X_999; data->level = 9; break;
+	}
+	switch (data->method) {
+	case METHOD_LZO1X_1:
+		data->work_buffer_size = LZO1X_1_MEM_COMPRESS; break;
+	case METHOD_LZO1X_1_15:
+		data->work_buffer_size = LZO1X_1_15_MEM_COMPRESS; break;
+	case METHOD_LZO1X_999:
+		data->work_buffer_size = LZO1X_999_MEM_COMPRESS; break;
+	}
+	if (data->work_buffer == NULL) {
+		data->work_buffer = (lzo_voidp)malloc(data->work_buffer_size);
+		if (data->work_buffer == NULL) {
+			archive_set_error(f->archive, ENOMEM,
+			    "Can't allocate data for compression buffer");
+			return (ARCHIVE_FATAL);
+		}
+	}
+	if (data->compressed == NULL) {
+		data->compressed_buffer_size = sizeof(header) +
+		    BLOCK_SIZE + (BLOCK_SIZE >> 4) + 64 + 3;
+		data->compressed = (unsigned char *)
+		    malloc(data->compressed_buffer_size);
+		if (data->compressed == NULL) {
+			archive_set_error(f->archive, ENOMEM,
+			    "Can't allocate data for compression buffer");
+			return (ARCHIVE_FATAL);
+		}
+	}
+	if (data->uncompressed == NULL) {
+		data->uncompressed_buffer_size = BLOCK_SIZE;
+		data->uncompressed = (unsigned char *)
+		    malloc(data->uncompressed_buffer_size);
+		if (data->uncompressed == NULL) {
+			archive_set_error(f->archive, ENOMEM,
+			    "Can't allocate data for compression buffer");
+			return (ARCHIVE_FATAL);
+		}
+		data->uncompressed_avail_bytes = BLOCK_SIZE;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+make_header(struct archive_write_filter *f)
+{
+	struct write_lzop *data = (struct write_lzop *)f->data;
+	int64_t t;
+	uint32_t checksum;
+
+	memcpy(data->compressed, header, sizeof(header));
+	/* Overwrite library version. */
+	data->compressed[HEADER_LIBVERSION] = (unsigned char )
+	    (lzo_version() >> 8) & 0xff;
+	data->compressed[HEADER_LIBVERSION + 1] = (unsigned char )
+	    lzo_version() & 0xff;
+	/* Overwrite method and level. */
+	data->compressed[HEADER_METHOD] = (unsigned char)data->method;
+	data->compressed[HEADER_LEVEL] = data->level;
+	/* Overwrite mtime with current time. */
+	t = (int64_t)time(NULL);
+	archive_be32enc(&data->compressed[HEADER_MTIME_LOW],
+	    (uint32_t)(t & 0xffffffff));
+	archive_be32enc(&data->compressed[HEADER_MTIME_HIGH],
+	    (uint32_t)((t >> 32) & 0xffffffff));
+	/* Overwrite header checksum with calculated value. */
+	checksum = lzo_adler32(1, data->compressed + HEADER_VERSION,
+			(lzo_uint)(HEADER_H_CHECKSUM - HEADER_VERSION));
+	archive_be32enc(&data->compressed[HEADER_H_CHECKSUM], checksum);
+	return (sizeof(header));
+}
+
+static int
+drive_compressor(struct archive_write_filter *f)
+{
+	struct write_lzop *data = (struct write_lzop *)f->data;
+	unsigned char *p;
+	const int block_info_bytes = 12;
+	int header_bytes, r;
+	lzo_uint usize, csize;
+	uint32_t checksum;
+
+	if (!data->header_written) {
+		header_bytes = make_header(f);
+		data->header_written = 1;
+	} else
+		header_bytes = 0;
+	p = data->compressed;
+
+	usize = (lzo_uint)
+	    (data->uncompressed_buffer_size - data->uncompressed_avail_bytes);
+	csize = 0;
+	switch (data->method) {
+	default:
+	case METHOD_LZO1X_1:
+		r = lzo1x_1_compress(data->uncompressed, usize,
+			p + header_bytes + block_info_bytes, &csize,
+			data->work_buffer);
+		break;
+	case METHOD_LZO1X_1_15:
+		r = lzo1x_1_15_compress(data->uncompressed, usize,
+			p + header_bytes + block_info_bytes, &csize,
+			data->work_buffer);
+		break;
+	case METHOD_LZO1X_999:
+		r = lzo1x_999_compress_level(data->uncompressed, usize,
+			p + header_bytes + block_info_bytes, &csize,
+			data->work_buffer, NULL, 0, 0, data->level);
+		break;
+	}
+	if (r != LZO_E_OK) {
+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+		    "Lzop compression failed: returned status %d", r);
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Store uncompressed size. */
+	archive_be32enc(p + header_bytes, (uint32_t)usize);
+	/* Store the checksum of the uncompressed data. */
+	checksum = lzo_adler32(1, data->uncompressed, usize);
+	archive_be32enc(p + header_bytes + 8, checksum);
+
+	if (csize < usize) {
+		/* Store compressed size. */
+		archive_be32enc(p + header_bytes + 4, (uint32_t)csize);
+		r = __archive_write_filter(f->next_filter, data->compressed,
+			header_bytes + block_info_bytes + csize);
+	} else {
+		/*
+		 * This case, we output uncompressed data instead.
+		 */
+		/* Store uncompressed size as compressed size. */
+		archive_be32enc(p + header_bytes + 4, (uint32_t)usize);
+		r = __archive_write_filter(f->next_filter, data->compressed,
+			header_bytes + block_info_bytes);
+		if (r != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		r = __archive_write_filter(f->next_filter, data->uncompressed,
+			usize);
+	}
+
+	if (r != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_lzop_write(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	struct write_lzop *data = (struct write_lzop *)f->data;
+	const char *p = buff;
+	int r;
+
+	do {
+		if (data->uncompressed_avail_bytes > length) {
+			memcpy(data->uncompressed
+				+ data->uncompressed_buffer_size
+				- data->uncompressed_avail_bytes,
+			    p, length);
+			data->uncompressed_avail_bytes -= length;
+			return (ARCHIVE_OK);
+		}
+
+		memcpy(data->uncompressed + data->uncompressed_buffer_size
+			- data->uncompressed_avail_bytes,
+		    p, data->uncompressed_avail_bytes);
+		length -= data->uncompressed_avail_bytes;
+		p += data->uncompressed_avail_bytes;
+		data->uncompressed_avail_bytes = 0;
+
+		r = drive_compressor(f);
+		if (r != ARCHIVE_OK) return (r);
+		data->uncompressed_avail_bytes = BLOCK_SIZE;
+	} while (length);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_lzop_close(struct archive_write_filter *f)
+{
+	struct write_lzop *data = (struct write_lzop *)f->data;
+	const uint32_t endmark = 0;
+	int r;
+
+	if (data->uncompressed_avail_bytes < BLOCK_SIZE) {
+		/* Compress and output remaining data. */
+		r = drive_compressor(f);
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+	/* Write a zero uncompressed size as the end mark of the series of
+	 * compressed block. */
+	return __archive_write_filter(f->next_filter, &endmark, sizeof(endmark));
+}
+
+#else
+static int
+archive_write_lzop_open(struct archive_write_filter *f)
+{
+	struct write_lzop *data = (struct write_lzop *)f->data;
+	struct archive_string as;
+	int r;
+
+	archive_string_init(&as);
+	archive_strcpy(&as, "lzop");
+	/* Specify compression level. */
+	if (data->compression_level > 0) {
+		archive_strappend_char(&as, ' ');
+		archive_strappend_char(&as, '-');
+		archive_strappend_char(&as, '0' + data->compression_level);
+	}
+
+	r = __archive_write_program_open(f, data->pdata, as.s);
+	archive_string_free(&as);
+	return (r);
+}
+
+static int
+archive_write_lzop_write(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	struct write_lzop *data = (struct write_lzop *)f->data;
+
+	return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_write_lzop_close(struct archive_write_filter *f)
+{
+	struct write_lzop *data = (struct write_lzop *)f->data;
+
+	return __archive_write_program_close(f, data->pdata);
+}
+#endif
diff --git a/c/archive_write_add_filter_none.c b/c/archive_write_add_filter_none.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_none.c
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2003-2010 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: head/lib/libarchive/archive_write_set_compression_none.c 201080 2009-12-28 02:03:54Z kientzle $");
+
+#include "archive.h"
+
+int
+archive_write_set_compression_none(struct archive *a)
+{
+	(void)a; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_add_filter_none(struct archive *a)
+{
+	(void)a; /* UNUSED */
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_write_add_filter_program.c b/c/archive_write_add_filter_program.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_program.c
@@ -0,0 +1,391 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_write_set_compression_program.c 201104 2009-12-28 03:14:30Z kientzle $");
+
+#ifdef HAVE_SYS_WAIT_H
+#  include <sys/wait.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
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+#include "filter_fork.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_program(struct archive *a, const char *cmd)
+{
+	__archive_write_filters_free(a);
+	return (archive_write_add_filter_program(a, cmd));
+}
+#endif
+
+struct archive_write_program_data {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	HANDLE		 child;
+#else
+	pid_t		 child;
+#endif
+	int		 child_stdin, child_stdout;
+
+	char		*child_buf;
+	size_t		 child_buf_len, child_buf_avail;
+	char		*program_name;
+};
+
+struct private_data {
+	struct archive_write_program_data *pdata;
+	struct archive_string description;
+	char		*cmd;
+};
+
+static int archive_compressor_program_open(struct archive_write_filter *);
+static int archive_compressor_program_write(struct archive_write_filter *,
+		    const void *, size_t);
+static int archive_compressor_program_close(struct archive_write_filter *);
+static int archive_compressor_program_free(struct archive_write_filter *);
+
+/*
+ * Add a filter to this write handle that passes all data through an
+ * external program.
+ */
+int
+archive_write_add_filter_program(struct archive *_a, const char *cmd)
+{
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct private_data *data;
+	static const char prefix[] = "Program: ";
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_program");
+
+	f->data = calloc(1, sizeof(*data));
+	if (f->data == NULL)
+		goto memerr;
+	data = (struct private_data *)f->data;
+
+	data->cmd = strdup(cmd);
+	if (data->cmd == NULL)
+		goto memerr;
+
+	data->pdata = __archive_write_program_allocate(cmd);
+	if (data->pdata == NULL)
+		goto memerr;
+
+	/* Make up a description string. */
+	if (archive_string_ensure(&data->description,
+	    strlen(prefix) + strlen(cmd) + 1) == NULL)
+		goto memerr;
+	archive_strcpy(&data->description, prefix);
+	archive_strcat(&data->description, cmd);
+
+	f->name = data->description.s;
+	f->code = ARCHIVE_FILTER_PROGRAM;
+	f->open = archive_compressor_program_open;
+	f->write = archive_compressor_program_write;
+	f->close = archive_compressor_program_close;
+	f->free = archive_compressor_program_free;
+	return (ARCHIVE_OK);
+memerr:
+	archive_compressor_program_free(f);
+	archive_set_error(_a, ENOMEM,
+	    "Can't allocate memory for filter program");
+	return (ARCHIVE_FATAL);
+}
+
+static int
+archive_compressor_program_open(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_open(f, data->pdata, data->cmd);
+}
+
+static int
+archive_compressor_program_write(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_program_close(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_close(f, data->pdata);
+}
+
+static int
+archive_compressor_program_free(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (data) {
+		free(data->cmd);
+		archive_string_free(&data->description);
+		__archive_write_program_free(data->pdata);
+		free(data);
+		f->data = NULL;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Allocate resources for executing an external program.
+ */
+struct archive_write_program_data *
+__archive_write_program_allocate(const char *program)
+{
+	struct archive_write_program_data *data;
+
+	data = calloc(1, sizeof(struct archive_write_program_data));
+	if (data == NULL)
+		return (data);
+	data->child_stdin = -1;
+	data->child_stdout = -1;
+	data->program_name = strdup(program);
+	return (data);
+}
+
+/*
+ * Release the resources.
+ */
+int
+__archive_write_program_free(struct archive_write_program_data *data)
+{
+
+	if (data) {
+		free(data->program_name);
+		free(data->child_buf);
+		free(data);
+	}
+	return (ARCHIVE_OK);
+}
+
+int
+__archive_write_program_open(struct archive_write_filter *f,
+    struct archive_write_program_data *data, const char *cmd)
+{
+	int ret;
+
+	if (data->child_buf == NULL) {
+		data->child_buf_len = 65536;
+		data->child_buf_avail = 0;
+		data->child_buf = malloc(data->child_buf_len);
+
+		if (data->child_buf == NULL) {
+			archive_set_error(f->archive, ENOMEM,
+			    "Can't allocate compression buffer");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	ret = __archive_create_child(cmd, &data->child_stdin,
+		    &data->child_stdout, &data->child);
+	if (ret != ARCHIVE_OK) {
+		archive_set_error(f->archive, EINVAL,
+		    "Can't launch external program: %s", cmd);
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+child_write(struct archive_write_filter *f,
+    struct archive_write_program_data *data, const char *buf, size_t buf_len)
+{
+	ssize_t ret;
+
+	if (data->child_stdin == -1)
+		return (-1);
+
+	if (buf_len == 0)
+		return (-1);
+
+	for (;;) {
+		do {
+			ret = write(data->child_stdin, buf, buf_len);
+		} while (ret == -1 && errno == EINTR);
+
+		if (ret > 0)
+			return (ret);
+		if (ret == 0) {
+			close(data->child_stdin);
+			data->child_stdin = -1;
+			fcntl(data->child_stdout, F_SETFL, 0);
+			return (0);
+		}
+		if (ret == -1 && errno != EAGAIN)
+			return (-1);
+
+		if (data->child_stdout == -1) {
+			fcntl(data->child_stdin, F_SETFL, 0);
+			__archive_check_child(data->child_stdin,
+				data->child_stdout);
+			continue;
+		}
+
+		do {
+			ret = read(data->child_stdout,
+			    data->child_buf + data->child_buf_avail,
+			    data->child_buf_len - data->child_buf_avail);
+		} while (ret == -1 && errno == EINTR);
+
+		if (ret == 0 || (ret == -1 && errno == EPIPE)) {
+			close(data->child_stdout);
+			data->child_stdout = -1;
+			fcntl(data->child_stdin, F_SETFL, 0);
+			continue;
+		}
+		if (ret == -1 && errno == EAGAIN) {
+			__archive_check_child(data->child_stdin,
+				data->child_stdout);
+			continue;
+		}
+		if (ret == -1)
+			return (-1);
+
+		data->child_buf_avail += ret;
+
+		ret = __archive_write_filter(f->next_filter,
+		    data->child_buf, data->child_buf_avail);
+		if (ret != ARCHIVE_OK)
+			return (-1);
+		data->child_buf_avail = 0;
+	}
+}
+
+/*
+ * Write data to the filter stream.
+ */
+int
+__archive_write_program_write(struct archive_write_filter *f,
+    struct archive_write_program_data *data, const void *buff, size_t length)
+{
+	ssize_t ret;
+	const char *buf;
+
+	if (data->child == 0)
+		return (ARCHIVE_OK);
+
+	buf = buff;
+	while (length > 0) {
+		ret = child_write(f, data, buf, length);
+		if (ret == -1 || ret == 0) {
+			archive_set_error(f->archive, EIO,
+			    "Can't write to program: %s", data->program_name);
+			return (ARCHIVE_FATAL);
+		}
+		length -= ret;
+		buf += ret;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Finish the filtering...
+ */
+int
+__archive_write_program_close(struct archive_write_filter *f,
+    struct archive_write_program_data *data)
+{
+	int ret, status;
+	ssize_t bytes_read;
+
+	if (data->child == 0)
+		return ARCHIVE_OK;
+
+	ret = 0;
+	close(data->child_stdin);
+	data->child_stdin = -1;
+	fcntl(data->child_stdout, F_SETFL, 0);
+
+	for (;;) {
+		do {
+			bytes_read = read(data->child_stdout,
+			    data->child_buf + data->child_buf_avail,
+			    data->child_buf_len - data->child_buf_avail);
+		} while (bytes_read == -1 && errno == EINTR);
+
+		if (bytes_read == 0 || (bytes_read == -1 && errno == EPIPE))
+			break;
+
+		if (bytes_read == -1) {
+			archive_set_error(f->archive, errno,
+			    "Error reading from program: %s", data->program_name);
+			ret = ARCHIVE_FATAL;
+			goto cleanup;
+		}
+		data->child_buf_avail += bytes_read;
+
+		ret = __archive_write_filter(f->next_filter,
+		    data->child_buf, data->child_buf_avail);
+		if (ret != ARCHIVE_OK) {
+			ret = ARCHIVE_FATAL;
+			goto cleanup;
+		}
+		data->child_buf_avail = 0;
+	}
+
+cleanup:
+	/* Shut down the child. */
+	if (data->child_stdin != -1)
+		close(data->child_stdin);
+	if (data->child_stdout != -1)
+		close(data->child_stdout);
+	while (waitpid(data->child, &status, 0) == -1 && errno == EINTR)
+		continue;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	CloseHandle(data->child);
+#endif
+	data->child = 0;
+
+	if (status != 0) {
+		archive_set_error(f->archive, EIO,
+		    "Error closing program: %s", data->program_name);
+		ret = ARCHIVE_FATAL;
+	}
+	return ret;
+}
+
diff --git a/c/archive_write_add_filter_uuencode.c b/c/archive_write_add_filter_uuencode.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_uuencode.c
@@ -0,0 +1,295 @@
+/*-
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * 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$");
+
+#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_string.h"
+#include "archive_write_private.h"
+
+#define LBYTES 45
+
+struct private_uuencode {
+	int			mode;
+	struct archive_string	name;
+	struct archive_string	encoded_buff;
+	size_t			bs;
+	size_t			hold_len;
+	unsigned char		hold[LBYTES];
+};
+
+static int archive_filter_uuencode_options(struct archive_write_filter *,
+    const char *, const char *);
+static int archive_filter_uuencode_open(struct archive_write_filter *);
+static int archive_filter_uuencode_write(struct archive_write_filter *,
+    const void *, size_t);
+static int archive_filter_uuencode_close(struct archive_write_filter *);
+static int archive_filter_uuencode_free(struct archive_write_filter *);
+static void uu_encode(struct archive_string *, const unsigned char *, size_t);
+static int64_t atol8(const char *, size_t);
+
+/*
+ * Add a compress filter to this write handle.
+ */
+int
+archive_write_add_filter_uuencode(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct private_uuencode *state;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
+
+	state = (struct private_uuencode *)calloc(1, sizeof(*state));
+	if (state == NULL) {
+		archive_set_error(f->archive, ENOMEM,
+		    "Can't allocate data for uuencode filter");
+		return (ARCHIVE_FATAL);
+	}
+	archive_strcpy(&state->name, "-");
+	state->mode = 0644;
+
+	f->data = state;
+	f->name = "uuencode";
+	f->code = ARCHIVE_FILTER_UU;
+	f->open = archive_filter_uuencode_open;
+	f->options = archive_filter_uuencode_options;
+	f->write = archive_filter_uuencode_write;
+	f->close = archive_filter_uuencode_close;
+	f->free = archive_filter_uuencode_free;
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
+    const char *value)
+{
+	struct private_uuencode *state = (struct private_uuencode *)f->data;
+
+	if (strcmp(key, "mode") == 0) {
+		if (value == NULL) {
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+			    "mode option requires octal digits");
+			return (ARCHIVE_FAILED);
+		}
+		state->mode = (int)atol8(value, strlen(value)) & 0777;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "name") == 0) {
+		if (value == NULL) {
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+			    "name option requires a string");
+			return (ARCHIVE_FAILED);
+		}
+		archive_strcpy(&state->name, value);
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_filter_uuencode_open(struct archive_write_filter *f)
+{
+	struct private_uuencode *state = (struct private_uuencode *)f->data;
+	size_t bs = 65536, bpb;
+
+	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+		/* Buffer size should be a multiple number of the of bytes
+		 * per block for performance. */
+		bpb = archive_write_get_bytes_per_block(f->archive);
+		if (bpb > bs)
+			bs = bpb;
+		else if (bpb != 0)
+			bs -= bs % bpb;
+	}
+
+	state->bs = bs;
+	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
+		archive_set_error(f->archive, ENOMEM,
+		    "Can't allocate data for uuencode buffer");
+		return (ARCHIVE_FATAL);
+	}
+
+	archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
+	    state->mode, state->name.s);
+
+	f->data = state;
+	return (0);
+}
+
+static void
+uu_encode(struct archive_string *as, const unsigned char *p, size_t len)
+{
+	int c;
+
+	c = (int)len;
+	archive_strappend_char(as, c?c + 0x20:'`');
+	for (; len >= 3; p += 3, len -= 3) {
+		c = p[0] >> 2;
+		archive_strappend_char(as, c?c + 0x20:'`');
+		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
+		archive_strappend_char(as, c?c + 0x20:'`');
+		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
+		archive_strappend_char(as, c?c + 0x20:'`');
+		c = p[2] & 0x3f;
+		archive_strappend_char(as, c?c + 0x20:'`');
+	}
+	if (len > 0) {
+		c = p[0] >> 2;
+		archive_strappend_char(as, c?c + 0x20:'`');
+		c = (p[0] & 0x03) << 4;
+		if (len == 1) {
+			archive_strappend_char(as, c?c + 0x20:'`');
+			archive_strappend_char(as, '`');
+			archive_strappend_char(as, '`');
+		} else {
+			c |= (p[1] & 0xf0) >> 4;
+			archive_strappend_char(as, c?c + 0x20:'`');
+			c = (p[1] & 0x0f) << 2;
+			archive_strappend_char(as, c?c + 0x20:'`');
+			archive_strappend_char(as, '`');
+		}
+	}
+	archive_strappend_char(as, '\n');
+}
+
+/*
+ * Write data to the encoded stream.
+ */
+static int
+archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
+    size_t length)
+{
+	struct private_uuencode *state = (struct private_uuencode *)f->data;
+	const unsigned char *p = buff;
+	int ret = ARCHIVE_OK;
+
+	if (length == 0)
+		return (ret);
+
+	if (state->hold_len) {
+		while (state->hold_len < LBYTES && length > 0) {
+			state->hold[state->hold_len++] = *p++;
+			length--;
+		}
+		if (state->hold_len < LBYTES)
+			return (ret);
+		uu_encode(&state->encoded_buff, state->hold, LBYTES);
+		state->hold_len = 0;
+	}
+
+	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
+		uu_encode(&state->encoded_buff, p, LBYTES);
+
+	/* Save remaining bytes. */
+	if (length > 0) {
+		memcpy(state->hold, p, length);
+		state->hold_len = length;
+	}
+	while (archive_strlen(&state->encoded_buff) >= state->bs) {
+		ret = __archive_write_filter(f->next_filter,
+		    state->encoded_buff.s, state->bs);
+		memmove(state->encoded_buff.s,
+		    state->encoded_buff.s + state->bs,
+		    state->encoded_buff.length - state->bs);
+		state->encoded_buff.length -= state->bs;
+	}
+
+	return (ret);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_filter_uuencode_close(struct archive_write_filter *f)
+{
+	struct private_uuencode *state = (struct private_uuencode *)f->data;
+
+	/* Flush remaining bytes. */
+	if (state->hold_len != 0)
+		uu_encode(&state->encoded_buff, state->hold, state->hold_len);
+	archive_string_sprintf(&state->encoded_buff, "`\nend\n");
+	/* Write the last block */
+	archive_write_set_bytes_in_last_block(f->archive, 1);
+	return __archive_write_filter(f->next_filter,
+	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
+}
+
+static int
+archive_filter_uuencode_free(struct archive_write_filter *f)
+{
+	struct private_uuencode *state = (struct private_uuencode *)f->data;
+
+	archive_string_free(&state->name);
+	archive_string_free(&state->encoded_buff);
+	free(state);
+	return (ARCHIVE_OK);
+}
+
+static int64_t
+atol8(const char *p, size_t char_cnt)
+{
+	int64_t l;
+	int digit;
+        
+	l = 0;
+	while (char_cnt-- > 0) {
+		if (*p >= '0' && *p <= '7')
+			digit = *p - '0';
+		else
+			break;
+		p++;
+		l <<= 3;
+		l |= digit;
+	}
+	return (l);
+}
+
diff --git a/c/archive_write_add_filter_xz.c b/c/archive_write_add_filter_xz.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_xz.c
@@ -0,0 +1,545 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_write_set_compression_xz.c 201108 2009-12-28 03:28:21Z kientzle $");
+
+#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 <time.h>
+#ifdef HAVE_LZMA_H
+#include <lzma.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+int
+archive_write_set_compression_lzip(struct archive *a)
+{
+	__archive_write_filters_free(a);
+	return (archive_write_add_filter_lzip(a));
+}
+
+int
+archive_write_set_compression_lzma(struct archive *a)
+{
+	__archive_write_filters_free(a);
+	return (archive_write_add_filter_lzma(a));
+}
+
+int
+archive_write_set_compression_xz(struct archive *a)
+{
+	__archive_write_filters_free(a);
+	return (archive_write_add_filter_xz(a));
+}
+
+#endif
+
+#ifndef HAVE_LZMA_H
+int
+archive_write_add_filter_xz(struct archive *a)
+{
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
+	    "xz compression not supported on this platform");
+	return (ARCHIVE_FATAL);
+}
+
+int
+archive_write_add_filter_lzma(struct archive *a)
+{
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
+	    "lzma compression not supported on this platform");
+	return (ARCHIVE_FATAL);
+}
+
+int
+archive_write_add_filter_lzip(struct archive *a)
+{
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
+	    "lzma compression not supported on this platform");
+	return (ARCHIVE_FATAL);
+}
+#else
+/* Don't compile this if we don't have liblzma. */
+
+struct private_data {
+	int		 compression_level;
+	uint32_t	 threads;
+	lzma_stream	 stream;
+	lzma_filter	 lzmafilters[2];
+	lzma_options_lzma lzma_opt;
+	int64_t		 total_in;
+	unsigned char	*compressed;
+	size_t		 compressed_buffer_size;
+	int64_t		 total_out;
+	/* the CRC32 value of uncompressed data for lzip */
+	uint32_t	 crc32;
+};
+
+static int	archive_compressor_xz_options(struct archive_write_filter *,
+		    const char *, const char *);
+static int	archive_compressor_xz_open(struct archive_write_filter *);
+static int	archive_compressor_xz_write(struct archive_write_filter *,
+		    const void *, size_t);
+static int	archive_compressor_xz_close(struct archive_write_filter *);
+static int	archive_compressor_xz_free(struct archive_write_filter *);
+static int	drive_compressor(struct archive_write_filter *,
+		    struct private_data *, int finishing);
+
+struct option_value {
+	uint32_t dict_size;
+	uint32_t nice_len;
+	lzma_match_finder mf;
+};
+static const struct option_value option_values[] = {
+	{ 1 << 16, 32, LZMA_MF_HC3},
+	{ 1 << 20, 32, LZMA_MF_HC3},
+	{ 3 << 19, 32, LZMA_MF_HC4},
+	{ 1 << 21, 32, LZMA_MF_BT4},
+	{ 3 << 20, 32, LZMA_MF_BT4},
+	{ 1 << 22, 32, LZMA_MF_BT4},
+	{ 1 << 23, 64, LZMA_MF_BT4},
+	{ 1 << 24, 64, LZMA_MF_BT4},
+	{ 3 << 23, 64, LZMA_MF_BT4},
+	{ 1 << 25, 64, LZMA_MF_BT4}
+};
+
+static int
+common_setup(struct archive_write_filter *f)
+{
+	struct private_data *data;
+	struct archive_write *a = (struct archive_write *)f->archive;
+	data = calloc(1, sizeof(*data));
+	if (data == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	f->data = data;
+	data->compression_level = LZMA_PRESET_DEFAULT;
+	data->threads = 1;
+	f->open = &archive_compressor_xz_open;
+	f->close = archive_compressor_xz_close;
+	f->free = archive_compressor_xz_free;
+	f->options = &archive_compressor_xz_options;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Add an xz compression filter to this write handle.
+ */
+int
+archive_write_add_filter_xz(struct archive *_a)
+{
+	struct archive_write_filter *f;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_xz");
+	f = __archive_write_allocate_filter(_a);
+	r = common_setup(f);
+	if (r == ARCHIVE_OK) {
+		f->code = ARCHIVE_FILTER_XZ;
+		f->name = "xz";
+	}
+	return (r);
+}
+
+/* LZMA is handled identically, we just need a different compression
+ * code set.  (The liblzma setup looks at the code to determine
+ * the one place that XZ and LZMA require different handling.) */
+int
+archive_write_add_filter_lzma(struct archive *_a)
+{
+	struct archive_write_filter *f;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lzma");
+	f = __archive_write_allocate_filter(_a);
+	r = common_setup(f);
+	if (r == ARCHIVE_OK) {
+		f->code = ARCHIVE_FILTER_LZMA;
+		f->name = "lzma";
+	}
+	return (r);
+}
+
+int
+archive_write_add_filter_lzip(struct archive *_a)
+{
+	struct archive_write_filter *f;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_lzip");
+	f = __archive_write_allocate_filter(_a);
+	r = common_setup(f);
+	if (r == ARCHIVE_OK) {
+		f->code = ARCHIVE_FILTER_LZIP;
+		f->name = "lzip";
+	}
+	return (r);
+}
+
+static int
+archive_compressor_xz_init_stream(struct archive_write_filter *f,
+    struct private_data *data)
+{
+	static const lzma_stream lzma_stream_init_data = LZMA_STREAM_INIT;
+	int ret;
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+	lzma_mt mt_options;
+#endif
+
+	data->stream = lzma_stream_init_data;
+	data->stream.next_out = data->compressed;
+	data->stream.avail_out = data->compressed_buffer_size;
+	if (f->code == ARCHIVE_FILTER_XZ) {
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+		if (data->threads != 1) {
+			memset(&mt_options, 0, sizeof(mt_options));
+			mt_options.threads = data->threads;
+			mt_options.timeout = 300;
+			mt_options.filters = data->lzmafilters;
+			mt_options.check = LZMA_CHECK_CRC64;
+			ret = lzma_stream_encoder_mt(&(data->stream),
+			    &mt_options);
+		} else
+#endif
+			ret = lzma_stream_encoder(&(data->stream),
+			    data->lzmafilters, LZMA_CHECK_CRC64);
+	} else if (f->code == ARCHIVE_FILTER_LZMA) {
+		ret = lzma_alone_encoder(&(data->stream), &data->lzma_opt);
+	} else {	/* ARCHIVE_FILTER_LZIP */
+		int dict_size = data->lzma_opt.dict_size;
+		int ds, log2dic, wedges;
+
+		/* Calculate a coded dictionary size */
+		if (dict_size < (1 << 12) || dict_size > (1 << 27)) {
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+			    "Unacceptable dictionary size for lzip: %d",
+			    dict_size);
+			return (ARCHIVE_FATAL);
+		}
+		for (log2dic = 27; log2dic >= 12; log2dic--) {
+			if (dict_size & (1 << log2dic))
+				break;
+		}
+		if (dict_size > (1 << log2dic)) {
+			log2dic++;
+			wedges =
+			    ((1 << log2dic) - dict_size) / (1 << (log2dic - 4));
+		} else
+			wedges = 0;
+		ds = ((wedges << 5) & 0xe0) | (log2dic & 0x1f);
+
+		data->crc32 = 0;
+		/* Make a header */
+		data->compressed[0] = 0x4C;
+		data->compressed[1] = 0x5A;
+		data->compressed[2] = 0x49;
+		data->compressed[3] = 0x50;
+		data->compressed[4] = 1;/* Version */
+		data->compressed[5] = (unsigned char)ds;
+		data->stream.next_out += 6;
+		data->stream.avail_out -= 6;
+
+		ret = lzma_raw_encoder(&(data->stream), data->lzmafilters);
+	}
+	if (ret == LZMA_OK)
+		return (ARCHIVE_OK);
+
+	switch (ret) {
+	case LZMA_MEM_ERROR:
+		archive_set_error(f->archive, ENOMEM,
+		    "Internal error initializing compression library: "
+		    "Cannot allocate memory");
+		break;
+	default:
+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library: "
+		    "It's a bug in liblzma");
+		break;
+	}
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_xz_open(struct archive_write_filter *f)
+{
+	struct private_data *data = f->data;
+	int ret;
+
+	if (data->compressed == NULL) {
+		size_t bs = 65536, bpb;
+		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+			/* Buffer size should be a multiple number of the of bytes
+			 * per block for performance. */
+			bpb = archive_write_get_bytes_per_block(f->archive);
+			if (bpb > bs)
+				bs = bpb;
+			else if (bpb != 0)
+				bs -= bs % bpb;
+		}
+		data->compressed_buffer_size = bs;
+		data->compressed
+		    = (unsigned char *)malloc(data->compressed_buffer_size);
+		if (data->compressed == NULL) {
+			archive_set_error(f->archive, ENOMEM,
+			    "Can't allocate data for compression buffer");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	f->write = archive_compressor_xz_write;
+
+	/* Initialize compression library. */
+	if (f->code == ARCHIVE_FILTER_LZIP) {
+		const struct option_value *val =
+		    &option_values[data->compression_level];
+
+		data->lzma_opt.dict_size = val->dict_size;
+		data->lzma_opt.preset_dict = NULL;
+		data->lzma_opt.preset_dict_size = 0;
+		data->lzma_opt.lc = LZMA_LC_DEFAULT;
+		data->lzma_opt.lp = LZMA_LP_DEFAULT;
+		data->lzma_opt.pb = LZMA_PB_DEFAULT;
+		data->lzma_opt.mode =
+		    data->compression_level<= 2? LZMA_MODE_FAST:LZMA_MODE_NORMAL;
+		data->lzma_opt.nice_len = val->nice_len;
+		data->lzma_opt.mf = val->mf;
+		data->lzma_opt.depth = 0;
+		data->lzmafilters[0].id = LZMA_FILTER_LZMA1;
+		data->lzmafilters[0].options = &data->lzma_opt;
+		data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+	} else {
+		if (lzma_lzma_preset(&data->lzma_opt, data->compression_level)) {
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+			    "Internal error initializing compression library");
+		}
+		data->lzmafilters[0].id = LZMA_FILTER_LZMA2;
+		data->lzmafilters[0].options = &data->lzma_opt;
+		data->lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+	}
+	ret = archive_compressor_xz_init_stream(f, data);
+	if (ret == LZMA_OK) {
+		f->data = data;
+		return (0);
+	}
+	return (ARCHIVE_FATAL);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_xz_options(struct archive_write_filter *f,
+    const char *key, const char *value)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (strcmp(key, "compression-level") == 0) {
+		if (value == NULL || !(value[0] >= '0' && value[0] <= '9') ||
+		    value[1] != '\0')
+			return (ARCHIVE_WARN);
+		data->compression_level = value[0] - '0';
+		if (data->compression_level > 6)
+			data->compression_level = 6;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "threads") == 0) {
+		char *endptr;
+
+		if (value == NULL)
+			return (ARCHIVE_WARN);
+		errno = 0;
+		data->threads = (int)strtoul(value, &endptr, 10);
+		if (errno != 0 || *endptr != '\0') {
+			data->threads = 1;
+			return (ARCHIVE_WARN);
+		}
+		if (data->threads == 0) {
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+			data->threads = lzma_cputhreads();
+#else
+			data->threads = 1;
+#endif
+		}
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_xz_write(struct archive_write_filter *f,
+    const void *buff, size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	int ret;
+
+	/* Update statistics */
+	data->total_in += length;
+	if (f->code == ARCHIVE_FILTER_LZIP)
+		data->crc32 = lzma_crc32(buff, length, data->crc32);
+
+	/* Compress input data to output buffer */
+	data->stream.next_in = buff;
+	data->stream.avail_in = length;
+	if ((ret = drive_compressor(f, data, 0)) != ARCHIVE_OK)
+		return (ret);
+
+	return (ARCHIVE_OK);
+}
+
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_xz_close(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	int ret;
+
+	ret = drive_compressor(f, data, 1);
+	if (ret == ARCHIVE_OK) {
+		data->total_out +=
+		    data->compressed_buffer_size - data->stream.avail_out;
+		ret = __archive_write_filter(f->next_filter,
+		    data->compressed,
+		    data->compressed_buffer_size - data->stream.avail_out);
+		if (f->code == ARCHIVE_FILTER_LZIP && ret == ARCHIVE_OK) {
+			archive_le32enc(data->compressed, data->crc32);
+			archive_le64enc(data->compressed+4, data->total_in);
+			archive_le64enc(data->compressed+12, data->total_out + 20);
+			ret = __archive_write_filter(f->next_filter,
+			    data->compressed, 20);
+		}
+	}
+	lzma_end(&(data->stream));
+	return ret;
+}
+
+static int
+archive_compressor_xz_free(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	free(data->compressed);
+	free(data);
+	f->data = NULL;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+    struct private_data *data, int finishing)
+{
+	int ret;
+
+	for (;;) {
+		if (data->stream.avail_out == 0) {
+			data->total_out += data->compressed_buffer_size;
+			ret = __archive_write_filter(f->next_filter,
+			    data->compressed,
+			    data->compressed_buffer_size);
+			if (ret != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			data->stream.next_out = data->compressed;
+			data->stream.avail_out = data->compressed_buffer_size;
+		}
+
+		/* If there's nothing to do, we're done. */
+		if (!finishing && data->stream.avail_in == 0)
+			return (ARCHIVE_OK);
+
+		ret = lzma_code(&(data->stream),
+		    finishing ? LZMA_FINISH : LZMA_RUN );
+
+		switch (ret) {
+		case LZMA_OK:
+			/* In non-finishing case, check if compressor
+			 * consumed everything */
+			if (!finishing && data->stream.avail_in == 0)
+				return (ARCHIVE_OK);
+			/* In finishing case, this return always means
+			 * there's more work */
+			break;
+		case LZMA_STREAM_END:
+			/* This return can only occur in finishing case. */
+			if (finishing)
+				return (ARCHIVE_OK);
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+			    "lzma compression data error");
+			return (ARCHIVE_FATAL);
+		case LZMA_MEMLIMIT_ERROR:
+			archive_set_error(f->archive, ENOMEM,
+			    "lzma compression error: "
+			    "%ju MiB would have been needed",
+			    (uintmax_t)((lzma_memusage(&(data->stream))
+				    + 1024 * 1024 -1)
+				/ (1024 * 1024)));
+			return (ARCHIVE_FATAL);
+		default:
+			/* Any other return value indicates an error. */
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+			    "lzma compression failed:"
+			    " lzma_code() call returned status %d",
+			    ret);
+			return (ARCHIVE_FATAL);
+		}
+	}
+}
+
+#endif /* HAVE_LZMA_H */
diff --git a/c/archive_write_add_filter_zstd.c b/c/archive_write_add_filter_zstd.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_add_filter_zstd.c
@@ -0,0 +1,392 @@
+/*-
+ * Copyright (c) 2017 Sean Purcell
+ * 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$");
+
+
+#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_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+/* Don't compile this if we don't have zstd.h */
+
+struct private_data {
+	int		 compression_level;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+	ZSTD_CStream	*cstream;
+	int64_t		 total_in;
+	ZSTD_outBuffer	 out;
+#else
+	struct archive_write_program_data *pdata;
+#endif
+};
+
+/* If we don't have the library use default range values (zstdcli.c v1.4.0) */
+#define CLEVEL_MIN -99
+#define CLEVEL_STD_MIN 0 /* prior to 1.3.4 and more recent without using --fast */
+#define CLEVEL_DEFAULT 3
+#define CLEVEL_STD_MAX 19 /* without using --ultra */
+#define CLEVEL_MAX 22
+
+#define MINVER_NEGCLEVEL 10304
+#define MINVER_MINCLEVEL 10306
+
+static int archive_compressor_zstd_options(struct archive_write_filter *,
+		    const char *, const char *);
+static int archive_compressor_zstd_open(struct archive_write_filter *);
+static int archive_compressor_zstd_write(struct archive_write_filter *,
+		    const void *, size_t);
+static int archive_compressor_zstd_close(struct archive_write_filter *);
+static int archive_compressor_zstd_free(struct archive_write_filter *);
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+static int drive_compressor(struct archive_write_filter *,
+		    struct private_data *, int, const void *, size_t);
+#endif
+
+
+/*
+ * Add a zstd compression filter to this write handle.
+ */
+int
+archive_write_add_filter_zstd(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+	struct private_data *data;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd");
+
+	data = calloc(1, sizeof(*data));
+	if (data == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	f->data = data;
+	f->open = &archive_compressor_zstd_open;
+	f->options = &archive_compressor_zstd_options;
+	f->close = &archive_compressor_zstd_close;
+	f->free = &archive_compressor_zstd_free;
+	f->code = ARCHIVE_FILTER_ZSTD;
+	f->name = "zstd";
+	data->compression_level = CLEVEL_DEFAULT;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+	data->cstream = ZSTD_createCStream();
+	if (data->cstream == NULL) {
+		free(data);
+		archive_set_error(&a->archive, ENOMEM,
+		    "Failed to allocate zstd compressor object");
+		return (ARCHIVE_FATAL);
+	}
+
+	return (ARCHIVE_OK);
+#else
+	data->pdata = __archive_write_program_allocate("zstd");
+	if (data->pdata == NULL) {
+		free(data);
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Using external zstd program");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+archive_compressor_zstd_free(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+	ZSTD_freeCStream(data->cstream);
+	free(data->out.dst);
+#else
+	__archive_write_program_free(data->pdata);
+#endif
+	free(data);
+	f->data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int string_is_numeric (const char* value)
+{
+       size_t len = strlen(value);
+       size_t i;
+
+       if (len == 0) {
+               return (ARCHIVE_WARN);
+       }
+       else if (len == 1 && !(value[0] >= '0' && value[0] <= '9')) {
+               return (ARCHIVE_WARN);
+       }
+       else if (!(value[0] >= '0' && value[0] <= '9') &&
+                value[0] != '-' && value[0] != '+') {
+               return (ARCHIVE_WARN);
+       }
+
+       for (i = 1; i < len; i++) {
+               if (!(value[i] >= '0' && value[i] <= '9')) {
+                       return (ARCHIVE_WARN);
+               }
+       }
+
+       return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
+    const char *value)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (strcmp(key, "compression-level") == 0) {
+		int level = atoi(value);
+		/* If we don't have the library, hard-code the max level */
+		int minimum = CLEVEL_MIN;
+		int maximum = CLEVEL_MAX;
+		if (string_is_numeric(value) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+		maximum = ZSTD_maxCLevel();
+#if ZSTD_VERSION_NUMBER >= MINVER_MINCLEVEL
+		if (ZSTD_versionNumber() >= MINVER_MINCLEVEL) {
+			minimum = ZSTD_minCLevel();
+		}
+		else
+#endif
+		if (ZSTD_versionNumber() < MINVER_NEGCLEVEL) {
+			minimum = CLEVEL_STD_MIN;
+		}
+#endif
+		if (level < minimum || level > maximum) {
+			return (ARCHIVE_WARN);
+		}
+		data->compression_level = level;
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_zstd_open(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (data->out.dst == NULL) {
+		size_t bs = ZSTD_CStreamOutSize(), bpb;
+		if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+			/* Buffer size should be a multiple number of
+			 * the of bytes per block for performance. */
+			bpb = archive_write_get_bytes_per_block(f->archive);
+			if (bpb > bs)
+				bs = bpb;
+			else if (bpb != 0)
+				bs -= bs % bpb;
+		}
+		data->out.size = bs;
+		data->out.pos = 0;
+		data->out.dst
+		    = (unsigned char *)malloc(data->out.size);
+		if (data->out.dst == NULL) {
+			archive_set_error(f->archive, ENOMEM,
+			    "Can't allocate data for compression buffer");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	f->write = archive_compressor_zstd_write;
+
+	if (ZSTD_isError(ZSTD_initCStream(data->cstream,
+	    data->compression_level))) {
+		archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing zstd compressor object");
+		return (ARCHIVE_FATAL);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
+    size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	int ret;
+
+	/* Update statistics */
+	data->total_in += length;
+
+	if ((ret = drive_compressor(f, data, 0, buff, length)) != ARCHIVE_OK)
+		return (ret);
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_zstd_close(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	/* Finish zstd frame */
+	return drive_compressor(f, data, 1, NULL, 0);
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+    struct private_data *data, int finishing, const void *src, size_t length)
+{
+	ZSTD_inBuffer in = (ZSTD_inBuffer) { src, length, 0 };
+
+	for (;;) {
+		if (data->out.pos == data->out.size) {
+			const int ret = __archive_write_filter(f->next_filter,
+			    data->out.dst, data->out.size);
+			if (ret != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			data->out.pos = 0;
+		}
+
+		/* If there's nothing to do, we're done. */
+		if (!finishing && in.pos == in.size)
+			return (ARCHIVE_OK);
+
+		{
+			const size_t zstdret = !finishing ?
+			    ZSTD_compressStream(data->cstream, &data->out, &in)
+			    : ZSTD_endStream(data->cstream, &data->out);
+
+			if (ZSTD_isError(zstdret)) {
+				archive_set_error(f->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Zstd compression failed: %s",
+				    ZSTD_getErrorName(zstdret));
+				return (ARCHIVE_FATAL);
+			}
+
+			/* If we're finishing, 0 means nothing left to flush */
+			if (finishing && zstdret == 0) {
+				const int ret = __archive_write_filter(f->next_filter,
+				    data->out.dst, data->out.pos);
+				return (ret);
+			}
+		}
+	}
+}
+
+#else /* HAVE_ZSTD_H && HAVE_LIBZSTD */
+
+static int
+archive_compressor_zstd_open(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+	struct archive_string as;
+	int r;
+
+	archive_string_init(&as);
+	/* --no-check matches library default */
+	archive_strcpy(&as, "zstd --no-check");
+
+	if (data->compression_level < CLEVEL_STD_MIN) {
+		struct archive_string as2;
+		archive_string_init(&as2);
+		archive_string_sprintf(&as2, " --fast=%d", -data->compression_level);
+		archive_string_concat(&as, &as2);
+		archive_string_free(&as2);
+	} else {
+		struct archive_string as2;
+		archive_string_init(&as2);
+		archive_string_sprintf(&as2, " -%d", data->compression_level);
+		archive_string_concat(&as, &as2);
+		archive_string_free(&as2);
+	}
+
+	if (data->compression_level > CLEVEL_STD_MAX) {
+		archive_strcat(&as, " --ultra");
+	}
+
+	f->write = archive_compressor_zstd_write;
+	r = __archive_write_program_open(f, data->pdata, as.s);
+	archive_string_free(&as);
+	return (r);
+}
+
+static int
+archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
+    size_t length)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_zstd_close(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	return __archive_write_program_close(f, data->pdata);
+}
+
+#endif /* HAVE_ZSTD_H && HAVE_LIBZSTD */
diff --git a/c/archive_write_disk_posix.c b/c/archive_write_disk_posix.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_disk_posix.c
@@ -0,0 +1,4540 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_ACL_H
+#include <sys/acl.h>
+#endif
+#ifdef HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+#if HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#elif HAVE_ATTR_XATTR_H
+#include <attr/xattr.h>
+#endif
+#ifdef HAVE_SYS_EA_H
+#include <sys/ea.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#ifdef HAVE_COPYFILE_H
+#include <copyfile.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#ifdef HAVE_LINUX_FS_H
+#include <linux/fs.h>	/* for Linux file flags */
+#endif
+/*
+ * Some Linux distributions have both linux/ext2_fs.h and ext2fs/ext2_fs.h.
+ * As the include guards don't agree, the order of include is important.
+ */
+#ifdef HAVE_LINUX_EXT2_FS_H
+#include <linux/ext2_fs.h>	/* for Linux file flags */
+#endif
+#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
+#include <ext2fs/ext2_fs.h>	/* Linux file flags, broken on Cygwin */
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.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_UTIME_H
+#include <utime.h>
+#endif
+#ifdef F_GETTIMES /* Tru64 specific */
+#include <sys/fcntl1.h>
+#endif
+
+/*
+ * Macro to cast st_mtime and time_t to an int64 so that 2 numbers can reliably be compared.
+ *
+ * It assumes that the input is an integer type of no more than 64 bits.
+ * If the number is less than zero, t must be a signed type, so it fits in
+ * int64_t. Otherwise, it's a nonnegative value so we can cast it to uint64_t
+ * without loss. But it could be a large unsigned value, so we have to clip it
+ * to INT64_MAX.*
+ */
+#define to_int64_time(t) \
+   ((t) < 0 ? (int64_t)(t) : (uint64_t)(t) > (uint64_t)INT64_MAX ? INT64_MAX : (int64_t)(t))
+
+#if __APPLE__
+#include <TargetConditionals.h>
+#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && HAVE_QUARANTINE_H
+#include <quarantine.h>
+#define HAVE_QUARANTINE 1
+#endif
+#endif
+
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+/* TODO: Support Mac OS 'quarantine' feature.  This is really just a
+ * standard tag to mark files that have been downloaded as "tainted".
+ * On Mac OS, we should mark the extracted files as tainted if the
+ * archive being read was tainted.  Windows has a similar feature; we
+ * should investigate ways to support this generically. */
+
+#include "archive.h"
+#include "archive_acl_private.h"
+#include "archive_string.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_disk_private.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+/* Ignore non-int O_NOFOLLOW constant. */
+/* gnulib's fcntl.h does this on AIX, but it seems practical everywhere */
+#if defined O_NOFOLLOW && !(INT_MIN <= O_NOFOLLOW && O_NOFOLLOW <= INT_MAX)
+#undef O_NOFOLLOW
+#endif
+
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
+
+#ifndef AT_FDCWD
+#define AT_FDCWD -100
+#endif
+
+struct fixup_entry {
+	struct fixup_entry	*next;
+	struct archive_acl	 acl;
+	mode_t			 mode;
+	int64_t			 atime;
+	int64_t                  birthtime;
+	int64_t			 mtime;
+	int64_t			 ctime;
+	unsigned long		 atime_nanos;
+	unsigned long            birthtime_nanos;
+	unsigned long		 mtime_nanos;
+	unsigned long		 ctime_nanos;
+	unsigned long		 fflags_set;
+	size_t			 mac_metadata_size;
+	void			*mac_metadata;
+	int			 fixup; /* bitmask of what needs fixing */
+	char			*name;
+};
+
+/*
+ * We use a bitmask to track which operations remain to be done for
+ * this file.  In particular, this helps us avoid unnecessary
+ * operations when it's possible to take care of one step as a
+ * side-effect of another.  For example, mkdir() can specify the mode
+ * for the newly-created object but symlink() cannot.  This means we
+ * can skip chmod() if mkdir() succeeded, but we must explicitly
+ * chmod() if we're trying to create a directory that already exists
+ * (mkdir() failed) or if we're restoring a symlink.  Similarly, we
+ * need to verify UID/GID before trying to restore SUID/SGID bits;
+ * that verification can occur explicitly through a stat() call or
+ * implicitly because of a successful chown() call.
+ */
+#define	TODO_MODE_FORCE		0x40000000
+#define	TODO_MODE_BASE		0x20000000
+#define	TODO_SUID		0x10000000
+#define	TODO_SUID_CHECK		0x08000000
+#define	TODO_SGID		0x04000000
+#define	TODO_SGID_CHECK		0x02000000
+#define	TODO_APPLEDOUBLE	0x01000000
+#define	TODO_MODE		(TODO_MODE_BASE|TODO_SUID|TODO_SGID)
+#define	TODO_TIMES		ARCHIVE_EXTRACT_TIME
+#define	TODO_OWNER		ARCHIVE_EXTRACT_OWNER
+#define	TODO_FFLAGS		ARCHIVE_EXTRACT_FFLAGS
+#define	TODO_ACLS		ARCHIVE_EXTRACT_ACL
+#define	TODO_XATTR		ARCHIVE_EXTRACT_XATTR
+#define	TODO_MAC_METADATA	ARCHIVE_EXTRACT_MAC_METADATA
+#define	TODO_HFS_COMPRESSION	ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED
+
+struct archive_write_disk {
+	struct archive	archive;
+
+	mode_t			 user_umask;
+	struct fixup_entry	*fixup_list;
+	struct fixup_entry	*current_fixup;
+	int64_t			 user_uid;
+	int			 skip_file_set;
+	int64_t			 skip_file_dev;
+	int64_t			 skip_file_ino;
+	time_t			 start_time;
+
+	int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid);
+	void  (*cleanup_gid)(void *private);
+	void			*lookup_gid_data;
+	int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid);
+	void  (*cleanup_uid)(void *private);
+	void			*lookup_uid_data;
+
+	/*
+	 * Full path of last file to satisfy symlink checks.
+	 */
+	struct archive_string	path_safe;
+
+	/*
+	 * Cached stat data from disk for the current entry.
+	 * If this is valid, pst points to st.  Otherwise,
+	 * pst is null.
+	 */
+	struct stat		 st;
+	struct stat		*pst;
+
+	/* Information about the object being restored right now. */
+	struct archive_entry	*entry; /* Entry being extracted. */
+	char			*name; /* Name of entry, possibly edited. */
+	struct archive_string	 _name_data; /* backing store for 'name' */
+	char			*tmpname; /* Temporary name * */
+	struct archive_string	 _tmpname_data; /* backing store for 'tmpname' */
+	/* Tasks remaining for this object. */
+	int			 todo;
+	/* Tasks deferred until end-of-archive. */
+	int			 deferred;
+	/* Options requested by the client. */
+	int			 flags;
+	/* Handle for the file we're restoring. */
+	int			 fd;
+	/* Current offset for writing data to the file. */
+	int64_t			 offset;
+	/* Last offset actually written to disk. */
+	int64_t			 fd_offset;
+	/* Total bytes actually written to files. */
+	int64_t			 total_bytes_written;
+	/* Maximum size of file, -1 if unknown. */
+	int64_t			 filesize;
+	/* Dir we were in before this restore; only for deep paths. */
+	int			 restore_pwd;
+	/* Mode we should use for this entry; affected by _PERM and umask. */
+	mode_t			 mode;
+	/* UID/GID to use in restoring this entry. */
+	int64_t			 uid;
+	int64_t			 gid;
+	/*
+	 * HFS+ Compression.
+	 */
+	/* Xattr "com.apple.decmpfs". */
+	uint32_t		 decmpfs_attr_size;
+	unsigned char		*decmpfs_header_p;
+	/* ResourceFork set options used for fsetxattr. */
+	int			 rsrc_xattr_options;
+	/* Xattr "com.apple.ResourceFork". */
+	unsigned char		*resource_fork;
+	size_t			 resource_fork_allocated_size;
+	unsigned int		 decmpfs_block_count;
+	uint32_t		*decmpfs_block_info;
+	/* Buffer for compressed data. */
+	unsigned char		*compressed_buffer;
+	size_t			 compressed_buffer_size;
+	size_t			 compressed_buffer_remaining;
+	/* The offset of the ResourceFork where compressed data will
+	 * be placed. */
+	uint32_t		 compressed_rsrc_position;
+	uint32_t		 compressed_rsrc_position_v;
+	/* Buffer for uncompressed data. */
+	char			*uncompressed_buffer;
+	size_t			 block_remaining_bytes;
+	size_t			 file_remaining_bytes;
+#ifdef HAVE_ZLIB_H
+	z_stream		 stream;
+	int			 stream_valid;
+	int			 decmpfs_compression_level;
+#endif
+};
+
+/*
+ * Default mode for dirs created automatically (will be modified by umask).
+ * Note that POSIX specifies 0777 for implicitly-created dirs, "modified
+ * by the process' file creation mask."
+ */
+#define	DEFAULT_DIR_MODE 0777
+/*
+ * Dir modes are restored in two steps:  During the extraction, the permissions
+ * in the archive are modified to match the following limits.  During
+ * the post-extract fixup pass, the permissions from the archive are
+ * applied.
+ */
+#define	MINIMUM_DIR_MODE 0700
+#define	MAXIMUM_DIR_MODE 0775
+
+/*
+ * Maximum uncompressed size of a decmpfs block.
+ */
+#define MAX_DECMPFS_BLOCK_SIZE	(64 * 1024)
+/*
+ * HFS+ compression type.
+ */
+#define CMP_XATTR		3/* Compressed data in xattr. */
+#define CMP_RESOURCE_FORK	4/* Compressed data in resource fork. */
+/*
+ * HFS+ compression resource fork.
+ */
+#define RSRC_H_SIZE	260	/* Base size of Resource fork header. */
+#define RSRC_F_SIZE	50	/* Size of Resource fork footer. */
+/* Size to write compressed data to resource fork. */
+#define COMPRESSED_W_SIZE	(64 * 1024)
+/* decmpfs definitions. */
+#define MAX_DECMPFS_XATTR_SIZE		3802
+#ifndef DECMPFS_XATTR_NAME
+#define DECMPFS_XATTR_NAME		"com.apple.decmpfs"
+#endif
+#define DECMPFS_MAGIC			0x636d7066
+#define DECMPFS_COMPRESSION_MAGIC	0
+#define DECMPFS_COMPRESSION_TYPE	4
+#define DECMPFS_UNCOMPRESSED_SIZE	8
+#define DECMPFS_HEADER_SIZE		16
+
+#define HFS_BLOCKS(s)	((s) >> 12)
+
+
+static int	la_opendirat(int, const char *);
+static int	la_mktemp(struct archive_write_disk *);
+static void	fsobj_error(int *, struct archive_string *, int, const char *,
+		    const char *);
+static int	check_symlinks_fsobj(char *, int *, struct archive_string *,
+		    int);
+static int	check_symlinks(struct archive_write_disk *);
+static int	create_filesystem_object(struct archive_write_disk *);
+static struct fixup_entry *current_fixup(struct archive_write_disk *,
+		    const char *pathname);
+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
+static void	edit_deep_directories(struct archive_write_disk *ad);
+#endif
+static int	cleanup_pathname_fsobj(char *, int *, struct archive_string *,
+		    int);
+static int	cleanup_pathname(struct archive_write_disk *);
+static int	create_dir(struct archive_write_disk *, char *);
+static int	create_parent_dir(struct archive_write_disk *, char *);
+static ssize_t	hfs_write_data_block(struct archive_write_disk *,
+		    const char *, size_t);
+static int	fixup_appledouble(struct archive_write_disk *, const char *);
+static int	older(struct stat *, struct archive_entry *);
+static int	restore_entry(struct archive_write_disk *);
+static int	set_mac_metadata(struct archive_write_disk *, const char *,
+				 const void *, size_t);
+static int	set_xattrs(struct archive_write_disk *);
+static int	clear_nochange_fflags(struct archive_write_disk *);
+static int	set_fflags(struct archive_write_disk *);
+static int	set_fflags_platform(struct archive_write_disk *, int fd,
+		    const char *name, mode_t mode,
+		    unsigned long fflags_set, unsigned long fflags_clear);
+static int	set_ownership(struct archive_write_disk *);
+static int	set_mode(struct archive_write_disk *, int mode);
+static int	set_time(int, int, const char *, time_t, long, time_t, long);
+static int	set_times(struct archive_write_disk *, int, int, const char *,
+		    time_t, long, time_t, long, time_t, long, time_t, long);
+static int	set_times_from_entry(struct archive_write_disk *);
+static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
+static ssize_t	write_data_block(struct archive_write_disk *,
+		    const char *, size_t);
+
+static struct archive_vtable *archive_write_disk_vtable(void);
+
+static int	_archive_write_disk_close(struct archive *);
+static int	_archive_write_disk_free(struct archive *);
+static int	_archive_write_disk_header(struct archive *,
+		    struct archive_entry *);
+static int64_t	_archive_write_disk_filter_bytes(struct archive *, int);
+static int	_archive_write_disk_finish_entry(struct archive *);
+static ssize_t	_archive_write_disk_data(struct archive *, const void *,
+		    size_t);
+static ssize_t	_archive_write_disk_data_block(struct archive *, const void *,
+		    size_t, int64_t);
+
+static int
+la_mktemp(struct archive_write_disk *a)
+{
+	int oerrno, fd;
+	mode_t mode;
+
+	archive_string_empty(&a->_tmpname_data);
+	archive_string_sprintf(&a->_tmpname_data, "%s.XXXXXX", a->name);
+	a->tmpname = a->_tmpname_data.s;
+
+	fd = __archive_mkstemp(a->tmpname);
+	if (fd == -1)
+		return -1;
+
+	mode = a->mode & 0777 & ~a->user_umask;
+	if (fchmod(fd, mode) == -1) {
+		oerrno = errno;
+		close(fd);
+		errno = oerrno;
+		return -1;
+	}
+	return fd;
+}
+
+static int
+la_opendirat(int fd, const char *path) {
+	const int flags = O_CLOEXEC
+#if defined(O_BINARY)
+	    | O_BINARY
+#endif
+#if defined(O_DIRECTORY)
+	    | O_DIRECTORY
+#endif
+#if defined(O_PATH)
+	    | O_PATH
+#elif defined(O_SEARCH)
+	    | O_SEARCH
+#elif defined(__FreeBSD__) && defined(O_EXEC)
+	    | O_EXEC
+#else
+	    | O_RDONLY
+#endif
+	    ;
+
+#if !defined(HAVE_OPENAT)
+	if (fd != AT_FDCWD) {
+		errno = ENOTSUP;
+		return (-1);
+	} else
+		return (open(path, flags));
+#else
+	return (openat(fd, path, flags));
+#endif
+}
+
+static int
+lazy_stat(struct archive_write_disk *a)
+{
+	if (a->pst != NULL) {
+		/* Already have stat() data available. */
+		return (ARCHIVE_OK);
+	}
+#ifdef HAVE_FSTAT
+	if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) {
+		a->pst = &a->st;
+		return (ARCHIVE_OK);
+	}
+#endif
+	/*
+	 * XXX At this point, symlinks should not be hit, otherwise
+	 * XXX a race occurred.  Do we want to check explicitly for that?
+	 */
+	if (lstat(a->name, &a->st) == 0) {
+		a->pst = &a->st;
+		return (ARCHIVE_OK);
+	}
+	archive_set_error(&a->archive, errno, "Couldn't stat file");
+	return (ARCHIVE_WARN);
+}
+
+static struct archive_vtable *
+archive_write_disk_vtable(void)
+{
+	static struct archive_vtable av;
+	static int inited = 0;
+
+	if (!inited) {
+		av.archive_close = _archive_write_disk_close;
+		av.archive_filter_bytes = _archive_write_disk_filter_bytes;
+		av.archive_free = _archive_write_disk_free;
+		av.archive_write_header = _archive_write_disk_header;
+		av.archive_write_finish_entry
+		    = _archive_write_disk_finish_entry;
+		av.archive_write_data = _archive_write_disk_data;
+		av.archive_write_data_block = _archive_write_disk_data_block;
+		inited = 1;
+	}
+	return (&av);
+}
+
+static int64_t
+_archive_write_disk_filter_bytes(struct archive *_a, int n)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	(void)n; /* UNUSED */
+	if (n == -1 || n == 0)
+		return (a->total_bytes_written);
+	return (-1);
+}
+
+
+int
+archive_write_disk_set_options(struct archive *_a, int flags)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+	a->flags = flags;
+	return (ARCHIVE_OK);
+}
+
+
+/*
+ * Extract this entry to disk.
+ *
+ * TODO: Validate hardlinks.  According to the standards, we're
+ * supposed to check each extracted hardlink and squawk if it refers
+ * to a file that we didn't restore.  I'm not entirely convinced this
+ * is a good idea, but more importantly: Is there any way to validate
+ * hardlinks without keeping a complete list of filenames from the
+ * entire archive?? Ugh.
+ *
+ */
+static int
+_archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	struct fixup_entry *fe;
+	int ret, r;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_write_disk_header");
+	archive_clear_error(&a->archive);
+	if (a->archive.state & ARCHIVE_STATE_DATA) {
+		r = _archive_write_disk_finish_entry(&a->archive);
+		if (r == ARCHIVE_FATAL)
+			return (r);
+	}
+
+	/* Set up for this particular entry. */
+	a->pst = NULL;
+	a->current_fixup = NULL;
+	a->deferred = 0;
+	if (a->entry) {
+		archive_entry_free(a->entry);
+		a->entry = NULL;
+	}
+	a->entry = archive_entry_clone(entry);
+	a->fd = -1;
+	a->fd_offset = 0;
+	a->offset = 0;
+	a->restore_pwd = -1;
+	a->uid = a->user_uid;
+	a->mode = archive_entry_mode(a->entry);
+	if (archive_entry_size_is_set(a->entry))
+		a->filesize = archive_entry_size(a->entry);
+	else
+		a->filesize = -1;
+	archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry));
+	a->name = a->_name_data.s;
+	archive_clear_error(&a->archive);
+
+	/*
+	 * Clean up the requested path.  This is necessary for correct
+	 * dir restores; the dir restore logic otherwise gets messed
+	 * up by nonsense like "dir/.".
+	 */
+	ret = cleanup_pathname(a);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+
+	/*
+	 * Query the umask so we get predictable mode settings.
+	 * This gets done on every call to _write_header in case the
+	 * user edits their umask during the extraction for some
+	 * reason.
+	 */
+	umask(a->user_umask = umask(0));
+
+	/* Figure out what we need to do for this entry. */
+	a->todo = TODO_MODE_BASE;
+	if (a->flags & ARCHIVE_EXTRACT_PERM) {
+		a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */
+		/*
+		 * SGID requires an extra "check" step because we
+		 * cannot easily predict the GID that the system will
+		 * assign.  (Different systems assign GIDs to files
+		 * based on a variety of criteria, including process
+		 * credentials and the gid of the enclosing
+		 * directory.)  We can only restore the SGID bit if
+		 * the file has the right GID, and we only know the
+		 * GID if we either set it (see set_ownership) or if
+		 * we've actually called stat() on the file after it
+		 * was restored.  Since there are several places at
+		 * which we might verify the GID, we need a TODO bit
+		 * to keep track.
+		 */
+		if (a->mode & S_ISGID)
+			a->todo |= TODO_SGID | TODO_SGID_CHECK;
+		/*
+		 * Verifying the SUID is simpler, but can still be
+		 * done in multiple ways, hence the separate "check" bit.
+		 */
+		if (a->mode & S_ISUID)
+			a->todo |= TODO_SUID | TODO_SUID_CHECK;
+	} else {
+		/*
+		 * User didn't request full permissions, so don't
+		 * restore SUID, SGID bits and obey umask.
+		 */
+		a->mode &= ~S_ISUID;
+		a->mode &= ~S_ISGID;
+		a->mode &= ~S_ISVTX;
+		a->mode &= ~a->user_umask;
+	}
+	if (a->flags & ARCHIVE_EXTRACT_OWNER)
+		a->todo |= TODO_OWNER;
+	if (a->flags & ARCHIVE_EXTRACT_TIME)
+		a->todo |= TODO_TIMES;
+	if (a->flags & ARCHIVE_EXTRACT_ACL) {
+#if ARCHIVE_ACL_DARWIN
+		/*
+		 * On MacOS, platform ACLs get stored in mac_metadata, too.
+		 * If we intend to extract mac_metadata and it is present
+		 * we skip extracting libarchive NFSv4 ACLs.
+		 */
+		size_t metadata_size;
+
+		if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 ||
+		    archive_entry_mac_metadata(a->entry,
+		    &metadata_size) == NULL || metadata_size == 0)
+#endif
+#if ARCHIVE_ACL_LIBRICHACL
+		/*
+		 * RichACLs are stored in an extended attribute.
+		 * If we intend to extract extended attributes and have this
+		 * attribute we skip extracting libarchive NFSv4 ACLs.
+		 */
+		short extract_acls = 1;
+		if (a->flags & ARCHIVE_EXTRACT_XATTR && (
+		    archive_entry_acl_types(a->entry) &
+		    ARCHIVE_ENTRY_ACL_TYPE_NFS4)) {
+			const char *attr_name;
+			const void *attr_value;
+			size_t attr_size;
+			int i = archive_entry_xattr_reset(a->entry);
+			while (i--) {
+				archive_entry_xattr_next(a->entry, &attr_name,
+				    &attr_value, &attr_size);
+				if (attr_name != NULL && attr_value != NULL &&
+				    attr_size > 0 && strcmp(attr_name,
+				    "trusted.richacl") == 0) {
+					extract_acls = 0;
+					break;
+				}
+			}
+		}
+		if (extract_acls)
+#endif
+#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL
+		{
+#endif
+		if (archive_entry_filetype(a->entry) == AE_IFDIR)
+			a->deferred |= TODO_ACLS;
+		else
+			a->todo |= TODO_ACLS;
+#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBRICHACL
+		}
+#endif
+	}
+	if (a->flags & ARCHIVE_EXTRACT_MAC_METADATA) {
+		if (archive_entry_filetype(a->entry) == AE_IFDIR)
+			a->deferred |= TODO_MAC_METADATA;
+		else
+			a->todo |= TODO_MAC_METADATA;
+	}
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
+	if ((a->flags & ARCHIVE_EXTRACT_NO_HFS_COMPRESSION) == 0) {
+		unsigned long set, clear;
+		archive_entry_fflags(a->entry, &set, &clear);
+		if ((set & ~clear) & UF_COMPRESSED) {
+			a->todo |= TODO_HFS_COMPRESSION;
+			a->decmpfs_block_count = (unsigned)-1;
+		}
+	}
+	if ((a->flags & ARCHIVE_EXTRACT_HFS_COMPRESSION_FORCED) != 0 &&
+	    (a->mode & AE_IFMT) == AE_IFREG && a->filesize > 0) {
+		a->todo |= TODO_HFS_COMPRESSION;
+		a->decmpfs_block_count = (unsigned)-1;
+	}
+	{
+		const char *p;
+
+		/* Check if the current file name is a type of the
+		 * resource fork file. */
+		p = strrchr(a->name, '/');
+		if (p == NULL)
+			p = a->name;
+		else
+			p++;
+		if (p[0] == '.' && p[1] == '_') {
+			/* Do not compress "._XXX" files. */
+			a->todo &= ~TODO_HFS_COMPRESSION;
+			if (a->filesize > 0)
+				a->todo |= TODO_APPLEDOUBLE;
+		}
+	}
+#endif
+
+	if (a->flags & ARCHIVE_EXTRACT_XATTR) {
+#if ARCHIVE_XATTR_DARWIN
+		/*
+		 * On MacOS, extended attributes get stored in mac_metadata,
+		 * too. If we intend to extract mac_metadata and it is present
+		 * we skip extracting extended attributes.
+		 */
+		size_t metadata_size;
+
+		if ((a->flags & ARCHIVE_EXTRACT_MAC_METADATA) == 0 ||
+		    archive_entry_mac_metadata(a->entry,
+		    &metadata_size) == NULL || metadata_size == 0)
+#endif
+		a->todo |= TODO_XATTR;
+	}
+	if (a->flags & ARCHIVE_EXTRACT_FFLAGS)
+		a->todo |= TODO_FFLAGS;
+	if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
+		ret = check_symlinks(a);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+	}
+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
+	/* If path exceeds PATH_MAX, shorten the path. */
+	edit_deep_directories(a);
+#endif
+
+	ret = restore_entry(a);
+
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
+	/*
+	 * Check if the filesystem the file is restoring on supports
+	 * HFS+ Compression. If not, cancel HFS+ Compression.
+	 */
+	if (a->todo | TODO_HFS_COMPRESSION) {
+		/*
+		 * NOTE: UF_COMPRESSED is ignored even if the filesystem
+		 * supports HFS+ Compression because the file should
+		 * have at least an extended attribute "com.apple.decmpfs"
+		 * before the flag is set to indicate that the file have
+		 * been compressed. If the filesystem does not support
+		 * HFS+ Compression the system call will fail.
+		 */
+		if (a->fd < 0 || fchflags(a->fd, UF_COMPRESSED) != 0)
+			a->todo &= ~TODO_HFS_COMPRESSION;
+	}
+#endif
+
+	/*
+	 * TODO: There are rumours that some extended attributes must
+	 * be restored before file data is written.  If this is true,
+	 * then we either need to write all extended attributes both
+	 * before and after restoring the data, or find some rule for
+	 * determining which must go first and which last.  Due to the
+	 * many ways people are using xattrs, this may prove to be an
+	 * intractable problem.
+	 */
+
+#ifdef HAVE_FCHDIR
+	/* If we changed directory above, restore it here. */
+	if (a->restore_pwd >= 0) {
+		r = fchdir(a->restore_pwd);
+		if (r != 0) {
+			archive_set_error(&a->archive, errno,
+			    "chdir() failure");
+			ret = ARCHIVE_FATAL;
+		}
+		close(a->restore_pwd);
+		a->restore_pwd = -1;
+	}
+#endif
+
+	/*
+	 * Fixup uses the unedited pathname from archive_entry_pathname(),
+	 * because it is relative to the base dir and the edited path
+	 * might be relative to some intermediate dir as a result of the
+	 * deep restore logic.
+	 */
+	if (a->deferred & TODO_MODE) {
+		fe = current_fixup(a, archive_entry_pathname(entry));
+		if (fe == NULL)
+			return (ARCHIVE_FATAL);
+		fe->fixup |= TODO_MODE_BASE;
+		fe->mode = a->mode;
+	}
+
+	if ((a->deferred & TODO_TIMES)
+		&& (archive_entry_mtime_is_set(entry)
+		    || archive_entry_atime_is_set(entry))) {
+		fe = current_fixup(a, archive_entry_pathname(entry));
+		if (fe == NULL)
+			return (ARCHIVE_FATAL);
+		fe->mode = a->mode;
+		fe->fixup |= TODO_TIMES;
+		if (archive_entry_atime_is_set(entry)) {
+			fe->atime = archive_entry_atime(entry);
+			fe->atime_nanos = archive_entry_atime_nsec(entry);
+		} else {
+			/* If atime is unset, use start time. */
+			fe->atime = a->start_time;
+			fe->atime_nanos = 0;
+		}
+		if (archive_entry_mtime_is_set(entry)) {
+			fe->mtime = archive_entry_mtime(entry);
+			fe->mtime_nanos = archive_entry_mtime_nsec(entry);
+		} else {
+			/* If mtime is unset, use start time. */
+			fe->mtime = a->start_time;
+			fe->mtime_nanos = 0;
+		}
+		if (archive_entry_birthtime_is_set(entry)) {
+			fe->birthtime = archive_entry_birthtime(entry);
+			fe->birthtime_nanos = archive_entry_birthtime_nsec(
+			    entry);
+		} else {
+			/* If birthtime is unset, use mtime. */
+			fe->birthtime = fe->mtime;
+			fe->birthtime_nanos = fe->mtime_nanos;
+		}
+	}
+
+	if (a->deferred & TODO_ACLS) {
+		fe = current_fixup(a, archive_entry_pathname(entry));
+		if (fe == NULL)
+			return (ARCHIVE_FATAL);
+		fe->fixup |= TODO_ACLS;
+		archive_acl_copy(&fe->acl, archive_entry_acl(entry));
+	}
+
+	if (a->deferred & TODO_MAC_METADATA) {
+		const void *metadata;
+		size_t metadata_size;
+		metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
+		if (metadata != NULL && metadata_size > 0) {
+			fe = current_fixup(a, archive_entry_pathname(entry));
+			if (fe == NULL)
+				return (ARCHIVE_FATAL);
+			fe->mac_metadata = malloc(metadata_size);
+			if (fe->mac_metadata != NULL) {
+				memcpy(fe->mac_metadata, metadata,
+				    metadata_size);
+				fe->mac_metadata_size = metadata_size;
+				fe->fixup |= TODO_MAC_METADATA;
+			}
+		}
+	}
+
+	if (a->deferred & TODO_FFLAGS) {
+		fe = current_fixup(a, archive_entry_pathname(entry));
+		if (fe == NULL)
+			return (ARCHIVE_FATAL);
+		fe->fixup |= TODO_FFLAGS;
+		/* TODO: Complete this.. defer fflags from below. */
+	}
+
+	/* We've created the object and are ready to pour data into it. */
+	if (ret >= ARCHIVE_WARN)
+		a->archive.state = ARCHIVE_STATE_DATA;
+	/*
+	 * If it's not open, tell our client not to try writing.
+	 * In particular, dirs, links, etc, don't get written to.
+	 */
+	if (a->fd < 0) {
+		archive_entry_set_size(entry, 0);
+		a->filesize = 0;
+	}
+
+	return (ret);
+}
+
+int
+archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file");
+	a->skip_file_set = 1;
+	a->skip_file_dev = d;
+	a->skip_file_ino = i;
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+write_data_block(struct archive_write_disk *a, const char *buff, size_t size)
+{
+	uint64_t start_size = size;
+	ssize_t bytes_written = 0;
+	ssize_t block_size = 0, bytes_to_write;
+
+	if (size == 0)
+		return (ARCHIVE_OK);
+
+	if (a->filesize == 0 || a->fd < 0) {
+		archive_set_error(&a->archive, 0,
+		    "Attempt to write to an empty file");
+		return (ARCHIVE_WARN);
+	}
+
+	if (a->flags & ARCHIVE_EXTRACT_SPARSE) {
+#if HAVE_STRUCT_STAT_ST_BLKSIZE
+		int r;
+		if ((r = lazy_stat(a)) != ARCHIVE_OK)
+			return (r);
+		block_size = a->pst->st_blksize;
+#else
+		/* XXX TODO XXX Is there a more appropriate choice here ? */
+		/* This needn't match the filesystem allocation size. */
+		block_size = 16*1024;
+#endif
+	}
+
+	/* If this write would run beyond the file size, truncate it. */
+	if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
+		start_size = size = (size_t)(a->filesize - a->offset);
+
+	/* Write the data. */
+	while (size > 0) {
+		if (block_size == 0) {
+			bytes_to_write = size;
+		} else {
+			/* We're sparsifying the file. */
+			const char *p, *end;
+			int64_t block_end;
+
+			/* Skip leading zero bytes. */
+			for (p = buff, end = buff + size; p < end; ++p) {
+				if (*p != '\0')
+					break;
+			}
+			a->offset += p - buff;
+			size -= p - buff;
+			buff = p;
+			if (size == 0)
+				break;
+
+			/* Calculate next block boundary after offset. */
+			block_end
+			    = (a->offset / block_size + 1) * block_size;
+
+			/* If the adjusted write would cross block boundary,
+			 * truncate it to the block boundary. */
+			bytes_to_write = size;
+			if (a->offset + bytes_to_write > block_end)
+				bytes_to_write = block_end - a->offset;
+		}
+		/* Seek if necessary to the specified offset. */
+		if (a->offset != a->fd_offset) {
+			if (lseek(a->fd, a->offset, SEEK_SET) < 0) {
+				archive_set_error(&a->archive, errno,
+				    "Seek failed");
+				return (ARCHIVE_FATAL);
+			}
+			a->fd_offset = a->offset;
+		}
+		bytes_written = write(a->fd, buff, bytes_to_write);
+		if (bytes_written < 0) {
+			archive_set_error(&a->archive, errno, "Write failed");
+			return (ARCHIVE_WARN);
+		}
+		buff += bytes_written;
+		size -= bytes_written;
+		a->total_bytes_written += bytes_written;
+		a->offset += bytes_written;
+		a->fd_offset = a->offset;
+	}
+	return (start_size - size);
+}
+
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\
+	&& defined(HAVE_ZLIB_H)
+
+/*
+ * Set UF_COMPRESSED file flag.
+ * This have to be called after hfs_write_decmpfs() because if the
+ * file does not have "com.apple.decmpfs" xattr the flag is ignored.
+ */
+static int
+hfs_set_compressed_fflag(struct archive_write_disk *a)
+{
+	int r;
+
+	if ((r = lazy_stat(a)) != ARCHIVE_OK)
+		return (r);
+
+	a->st.st_flags |= UF_COMPRESSED;
+	if (fchflags(a->fd, a->st.st_flags) != 0) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to set UF_COMPRESSED file flag");
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * HFS+ Compression decmpfs
+ *
+ *     +------------------------------+ +0
+ *     |      Magic(LE 4 bytes)       |
+ *     +------------------------------+
+ *     |      Type(LE 4 bytes)        |
+ *     +------------------------------+
+ *     | Uncompressed size(LE 8 bytes)|
+ *     +------------------------------+ +16
+ *     |                              |
+ *     |       Compressed data        |
+ *     |  (Placed only if Type == 3)  |
+ *     |                              |
+ *     +------------------------------+  +3802 = MAX_DECMPFS_XATTR_SIZE
+ *
+ *  Type is 3: decmpfs has compressed data.
+ *  Type is 4: Resource Fork has compressed data.
+ */
+/*
+ * Write "com.apple.decmpfs"
+ */
+static int
+hfs_write_decmpfs(struct archive_write_disk *a)
+{
+	int r;
+	uint32_t compression_type;
+
+	r = fsetxattr(a->fd, DECMPFS_XATTR_NAME, a->decmpfs_header_p,
+	    a->decmpfs_attr_size, 0, 0);
+	if (r < 0) {
+		archive_set_error(&a->archive, errno,
+		    "Cannot restore xattr:%s", DECMPFS_XATTR_NAME);
+		compression_type = archive_le32dec(
+		    &a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE]);
+		if (compression_type == CMP_RESOURCE_FORK)
+			fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME,
+			    XATTR_SHOWCOMPRESSION);
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * HFS+ Compression Resource Fork
+ *
+ *     +-----------------------------+
+ *     |     Header(260 bytes)       |
+ *     +-----------------------------+
+ *     |   Block count(LE 4 bytes)   |
+ *     +-----------------------------+  --+
+ * +-- |     Offset (LE 4 bytes)     |    |
+ * |   | [distance from Block count] |    | Block 0
+ * |   +-----------------------------+    |
+ * |   | Compressed size(LE 4 bytes) |    |
+ * |   +-----------------------------+  --+
+ * |   |                             |
+ * |   |      ..................     |
+ * |   |                             |
+ * |   +-----------------------------+  --+
+ * |   |     Offset (LE 4 bytes)     |    |
+ * |   +-----------------------------+    | Block (Block count -1)
+ * |   | Compressed size(LE 4 bytes) |    |
+ * +-> +-----------------------------+  --+
+ *     |   Compressed data(n bytes)  |  Block 0
+ *     +-----------------------------+
+ *     |                             |
+ *     |      ..................     |
+ *     |                             |
+ *     +-----------------------------+
+ *     |   Compressed data(n bytes)  |  Block (Block count -1)
+ *     +-----------------------------+
+ *     |      Footer(50 bytes)       |
+ *     +-----------------------------+
+ *
+ */
+/*
+ * Write the header of "com.apple.ResourceFork"
+ */
+static int
+hfs_write_resource_fork(struct archive_write_disk *a, unsigned char *buff,
+    size_t bytes, uint32_t position)
+{
+	int ret;
+
+	ret = fsetxattr(a->fd, XATTR_RESOURCEFORK_NAME, buff, bytes,
+	    position, a->rsrc_xattr_options);
+	if (ret < 0) {
+		archive_set_error(&a->archive, errno,
+		    "Cannot restore xattr: %s at %u pos %u bytes",
+		    XATTR_RESOURCEFORK_NAME,
+		    (unsigned)position,
+		    (unsigned)bytes);
+		return (ARCHIVE_WARN);
+	}
+	a->rsrc_xattr_options &= ~XATTR_CREATE;
+	return (ARCHIVE_OK);
+}
+
+static int
+hfs_write_compressed_data(struct archive_write_disk *a, size_t bytes_compressed)
+{
+	int ret;
+
+	ret = hfs_write_resource_fork(a, a->compressed_buffer,
+	    bytes_compressed, a->compressed_rsrc_position);
+	if (ret == ARCHIVE_OK)
+		a->compressed_rsrc_position += bytes_compressed;
+	return (ret);
+}
+
+static int
+hfs_write_resource_fork_header(struct archive_write_disk *a)
+{
+	unsigned char *buff;
+	uint32_t rsrc_bytes;
+	uint32_t rsrc_header_bytes;
+
+	/*
+	 * Write resource fork header + block info.
+	 */
+	buff = a->resource_fork;
+	rsrc_bytes = a->compressed_rsrc_position - RSRC_F_SIZE;
+	rsrc_header_bytes =
+		RSRC_H_SIZE +		/* Header base size. */
+		4 +			/* Block count. */
+		(a->decmpfs_block_count * 8);/* Block info */
+	archive_be32enc(buff, 0x100);
+	archive_be32enc(buff + 4, rsrc_bytes);
+	archive_be32enc(buff + 8, rsrc_bytes - 256);
+	archive_be32enc(buff + 12, 0x32);
+	memset(buff + 16, 0, 240);
+	archive_be32enc(buff + 256, rsrc_bytes - 260);
+	return hfs_write_resource_fork(a, buff, rsrc_header_bytes, 0);
+}
+
+static size_t
+hfs_set_resource_fork_footer(unsigned char *buff, size_t buff_size)
+{
+	static const char rsrc_footer[RSRC_F_SIZE] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 'c',  'm',
+		'p', 'f',   0x00, 0x00, 0x00, 0x0a, 0x00, 0x01,
+		0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00
+	};
+	if (buff_size < sizeof(rsrc_footer))
+		return (0);
+	memcpy(buff, rsrc_footer, sizeof(rsrc_footer));
+	return (sizeof(rsrc_footer));
+}
+
+static int
+hfs_reset_compressor(struct archive_write_disk *a)
+{
+	int ret;
+
+	if (a->stream_valid)
+		ret = deflateReset(&a->stream);
+	else
+		ret = deflateInit(&a->stream, a->decmpfs_compression_level);
+
+	if (ret != Z_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to initialize compressor");
+		return (ARCHIVE_FATAL);
+	} else
+		a->stream_valid = 1;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+hfs_decompress(struct archive_write_disk *a)
+{
+	uint32_t *block_info;
+	unsigned int block_count;
+	uint32_t data_pos, data_size;
+	ssize_t r;
+	ssize_t bytes_written, bytes_to_write;
+	unsigned char *b;
+
+	block_info = (uint32_t *)(a->resource_fork + RSRC_H_SIZE);
+	block_count = archive_le32dec(block_info++);
+	while (block_count--) {
+		data_pos = RSRC_H_SIZE + archive_le32dec(block_info++);
+		data_size = archive_le32dec(block_info++);
+		r = fgetxattr(a->fd, XATTR_RESOURCEFORK_NAME,
+		    a->compressed_buffer, data_size, data_pos, 0);
+		if (r != data_size)  {
+			archive_set_error(&a->archive,
+			    (r < 0)?errno:ARCHIVE_ERRNO_MISC,
+			    "Failed to read resource fork");
+			return (ARCHIVE_WARN);
+		}
+		if (a->compressed_buffer[0] == 0xff) {
+			bytes_to_write = data_size -1;
+			b = a->compressed_buffer + 1;
+		} else {
+			uLong dest_len = MAX_DECMPFS_BLOCK_SIZE;
+			int zr;
+
+			zr = uncompress((Bytef *)a->uncompressed_buffer,
+			    &dest_len, a->compressed_buffer, data_size);
+			if (zr != Z_OK) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Failed to decompress resource fork");
+				return (ARCHIVE_WARN);
+			}
+			bytes_to_write = dest_len;
+			b = (unsigned char *)a->uncompressed_buffer;
+		}
+		do {
+			bytes_written = write(a->fd, b, bytes_to_write);
+			if (bytes_written < 0) {
+				archive_set_error(&a->archive, errno,
+				    "Write failed");
+				return (ARCHIVE_WARN);
+			}
+			bytes_to_write -= bytes_written;
+			b += bytes_written;
+		} while (bytes_to_write > 0);
+	}
+	r = fremovexattr(a->fd, XATTR_RESOURCEFORK_NAME, 0);
+	if (r == -1)  {
+		archive_set_error(&a->archive, errno,
+		    "Failed to remove resource fork");
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+hfs_drive_compressor(struct archive_write_disk *a, const char *buff,
+    size_t size)
+{
+	unsigned char *buffer_compressed;
+	size_t bytes_compressed;
+	size_t bytes_used;
+	int ret;
+
+	ret = hfs_reset_compressor(a);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+
+	if (a->compressed_buffer == NULL) {
+		size_t block_size;
+
+		block_size = COMPRESSED_W_SIZE + RSRC_F_SIZE +
+		    + compressBound(MAX_DECMPFS_BLOCK_SIZE);
+		a->compressed_buffer = malloc(block_size);
+		if (a->compressed_buffer == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Resource Fork");
+			return (ARCHIVE_FATAL);
+		}
+		a->compressed_buffer_size = block_size;
+		a->compressed_buffer_remaining = block_size;
+	}
+
+	buffer_compressed = a->compressed_buffer +
+	    a->compressed_buffer_size - a->compressed_buffer_remaining;
+	a->stream.next_in = (Bytef *)(uintptr_t)(const void *)buff;
+	a->stream.avail_in = size;
+	a->stream.next_out = buffer_compressed;
+	a->stream.avail_out = a->compressed_buffer_remaining;
+	do {
+		ret = deflate(&a->stream, Z_FINISH);
+		switch (ret) {
+		case Z_OK:
+		case Z_STREAM_END:
+			break;
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Failed to compress data");
+			return (ARCHIVE_FAILED);
+		}
+	} while (ret == Z_OK);
+	bytes_compressed = a->compressed_buffer_remaining - a->stream.avail_out;
+
+	/*
+	 * If the compressed size is larger than the original size,
+	 * throw away compressed data, use uncompressed data instead.
+	 */
+	if (bytes_compressed > size) {
+		buffer_compressed[0] = 0xFF;/* uncompressed marker. */
+		memcpy(buffer_compressed + 1, buff, size);
+		bytes_compressed = size + 1;
+	}
+	a->compressed_buffer_remaining -= bytes_compressed;
+
+	/*
+	 * If the compressed size is smaller than MAX_DECMPFS_XATTR_SIZE
+	 * and the block count in the file is only one, store compressed
+	 * data to decmpfs xattr instead of the resource fork.
+	 */
+	if (a->decmpfs_block_count == 1 &&
+	    (a->decmpfs_attr_size + bytes_compressed)
+	      <= MAX_DECMPFS_XATTR_SIZE) {
+		archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE],
+		    CMP_XATTR);
+		memcpy(a->decmpfs_header_p + DECMPFS_HEADER_SIZE,
+		    buffer_compressed, bytes_compressed);
+		a->decmpfs_attr_size += bytes_compressed;
+		a->compressed_buffer_remaining = a->compressed_buffer_size;
+		/*
+		 * Finish HFS+ Compression.
+		 * - Write the decmpfs xattr.
+		 * - Set the UF_COMPRESSED file flag.
+		 */
+		ret = hfs_write_decmpfs(a);
+		if (ret == ARCHIVE_OK)
+			ret = hfs_set_compressed_fflag(a);
+		return (ret);
+	}
+
+	/* Update block info. */
+	archive_le32enc(a->decmpfs_block_info++,
+	    a->compressed_rsrc_position_v - RSRC_H_SIZE);
+	archive_le32enc(a->decmpfs_block_info++, bytes_compressed);
+	a->compressed_rsrc_position_v += bytes_compressed;
+
+	/*
+	 * Write the compressed data to the resource fork.
+	 */
+	bytes_used = a->compressed_buffer_size - a->compressed_buffer_remaining;
+	while (bytes_used >= COMPRESSED_W_SIZE) {
+		ret = hfs_write_compressed_data(a, COMPRESSED_W_SIZE);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+		bytes_used -= COMPRESSED_W_SIZE;
+		if (bytes_used > COMPRESSED_W_SIZE)
+			memmove(a->compressed_buffer,
+			    a->compressed_buffer + COMPRESSED_W_SIZE,
+			    bytes_used);
+		else
+			memcpy(a->compressed_buffer,
+			    a->compressed_buffer + COMPRESSED_W_SIZE,
+			    bytes_used);
+	}
+	a->compressed_buffer_remaining = a->compressed_buffer_size - bytes_used;
+
+	/*
+	 * If the current block is the last block, write the remaining
+	 * compressed data and the resource fork footer.
+	 */
+	if (a->file_remaining_bytes == 0) {
+		size_t rsrc_size;
+		int64_t bk;
+
+		/* Append the resource footer. */
+		rsrc_size = hfs_set_resource_fork_footer(
+		    a->compressed_buffer + bytes_used,
+		    a->compressed_buffer_remaining);
+		ret = hfs_write_compressed_data(a, bytes_used + rsrc_size);
+		a->compressed_buffer_remaining = a->compressed_buffer_size;
+
+		/* If the compressed size is not enough smaller than
+		 * the uncompressed size. cancel HFS+ compression.
+		 * TODO: study a behavior of ditto utility and improve
+		 * the condition to fall back into no HFS+ compression. */
+		bk = HFS_BLOCKS(a->compressed_rsrc_position);
+		bk += bk >> 7;
+		if (bk > HFS_BLOCKS(a->filesize))
+			return hfs_decompress(a);
+		/*
+		 * Write the resourcefork header.
+		 */
+		if (ret == ARCHIVE_OK)
+			ret = hfs_write_resource_fork_header(a);
+		/*
+		 * Finish HFS+ Compression.
+		 * - Write the decmpfs xattr.
+		 * - Set the UF_COMPRESSED file flag.
+		 */
+		if (ret == ARCHIVE_OK)
+			ret = hfs_write_decmpfs(a);
+		if (ret == ARCHIVE_OK)
+			ret = hfs_set_compressed_fflag(a);
+	}
+	return (ret);
+}
+
+static ssize_t
+hfs_write_decmpfs_block(struct archive_write_disk *a, const char *buff,
+    size_t size)
+{
+	const char *buffer_to_write;
+	size_t bytes_to_write;
+	int ret;
+
+	if (a->decmpfs_block_count == (unsigned)-1) {
+		void *new_block;
+		size_t new_size;
+		unsigned int block_count;
+
+		if (a->decmpfs_header_p == NULL) {
+			new_block = malloc(MAX_DECMPFS_XATTR_SIZE
+			    + sizeof(uint32_t));
+			if (new_block == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for decmpfs");
+				return (ARCHIVE_FATAL);
+			}
+			a->decmpfs_header_p = new_block;
+		}
+		a->decmpfs_attr_size = DECMPFS_HEADER_SIZE;
+		archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_MAGIC],
+		    DECMPFS_MAGIC);
+		archive_le32enc(&a->decmpfs_header_p[DECMPFS_COMPRESSION_TYPE],
+		    CMP_RESOURCE_FORK);
+		archive_le64enc(&a->decmpfs_header_p[DECMPFS_UNCOMPRESSED_SIZE],
+		    a->filesize);
+
+		/* Calculate a block count of the file. */
+		block_count =
+		    (a->filesize + MAX_DECMPFS_BLOCK_SIZE -1) /
+			MAX_DECMPFS_BLOCK_SIZE;
+		/*
+		 * Allocate buffer for resource fork.
+		 * Set up related pointers;
+		 */
+		new_size =
+		    RSRC_H_SIZE + /* header */
+		    4 + /* Block count */
+		    (block_count * sizeof(uint32_t) * 2) +
+		    RSRC_F_SIZE; /* footer */
+		if (new_size > a->resource_fork_allocated_size) {
+			new_block = realloc(a->resource_fork, new_size);
+			if (new_block == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for ResourceFork");
+				return (ARCHIVE_FATAL);
+			}
+			a->resource_fork_allocated_size = new_size;
+			a->resource_fork = new_block;
+		}
+
+		/* Allocate uncompressed buffer */
+		if (a->uncompressed_buffer == NULL) {
+			new_block = malloc(MAX_DECMPFS_BLOCK_SIZE);
+			if (new_block == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for decmpfs");
+				return (ARCHIVE_FATAL);
+			}
+			a->uncompressed_buffer = new_block;
+		}
+		a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE;
+		a->file_remaining_bytes = a->filesize;
+		a->compressed_buffer_remaining = a->compressed_buffer_size;
+
+		/*
+		 * Set up a resource fork.
+		 */
+		a->rsrc_xattr_options = XATTR_CREATE;
+		/* Get the position where we are going to set a bunch
+		 * of block info. */
+		a->decmpfs_block_info =
+		    (uint32_t *)(a->resource_fork + RSRC_H_SIZE);
+		/* Set the block count to the resource fork. */
+		archive_le32enc(a->decmpfs_block_info++, block_count);
+		/* Get the position where we are going to set compressed
+		 * data. */
+		a->compressed_rsrc_position =
+		    RSRC_H_SIZE + 4 + (block_count * 8);
+		a->compressed_rsrc_position_v = a->compressed_rsrc_position;
+		a->decmpfs_block_count = block_count;
+	}
+
+	/* Ignore redundant bytes. */
+	if (a->file_remaining_bytes == 0)
+		return ((ssize_t)size);
+
+	/* Do not overrun a block size. */
+	if (size > a->block_remaining_bytes)
+		bytes_to_write = a->block_remaining_bytes;
+	else
+		bytes_to_write = size;
+	/* Do not overrun the file size. */
+	if (bytes_to_write > a->file_remaining_bytes)
+		bytes_to_write = a->file_remaining_bytes;
+
+	/* For efficiency, if a copy length is full of the uncompressed
+	 * buffer size, do not copy writing data to it. */
+	if (bytes_to_write == MAX_DECMPFS_BLOCK_SIZE)
+		buffer_to_write = buff;
+	else {
+		memcpy(a->uncompressed_buffer +
+		    MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes,
+		    buff, bytes_to_write);
+		buffer_to_write = a->uncompressed_buffer;
+	}
+	a->block_remaining_bytes -= bytes_to_write;
+	a->file_remaining_bytes -= bytes_to_write;
+
+	if (a->block_remaining_bytes == 0 || a->file_remaining_bytes == 0) {
+		ret = hfs_drive_compressor(a, buffer_to_write,
+		    MAX_DECMPFS_BLOCK_SIZE - a->block_remaining_bytes);
+		if (ret < 0)
+			return (ret);
+		a->block_remaining_bytes = MAX_DECMPFS_BLOCK_SIZE;
+	}
+	/* Ignore redundant bytes. */
+	if (a->file_remaining_bytes == 0)
+		return ((ssize_t)size);
+	return (bytes_to_write);
+}
+
+static ssize_t
+hfs_write_data_block(struct archive_write_disk *a, const char *buff,
+    size_t size)
+{
+	uint64_t start_size = size;
+	ssize_t bytes_written = 0;
+	ssize_t bytes_to_write;
+
+	if (size == 0)
+		return (ARCHIVE_OK);
+
+	if (a->filesize == 0 || a->fd < 0) {
+		archive_set_error(&a->archive, 0,
+		    "Attempt to write to an empty file");
+		return (ARCHIVE_WARN);
+	}
+
+	/* If this write would run beyond the file size, truncate it. */
+	if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
+		start_size = size = (size_t)(a->filesize - a->offset);
+
+	/* Write the data. */
+	while (size > 0) {
+		bytes_to_write = size;
+		/* Seek if necessary to the specified offset. */
+		if (a->offset < a->fd_offset) {
+			/* Can't support backward move. */
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Seek failed");
+			return (ARCHIVE_FATAL);
+		} else if (a->offset > a->fd_offset) {
+			int64_t skip = a->offset - a->fd_offset;
+			char nullblock[1024];
+
+			memset(nullblock, 0, sizeof(nullblock));
+			while (skip > 0) {
+				if (skip > (int64_t)sizeof(nullblock))
+					bytes_written = hfs_write_decmpfs_block(
+					    a, nullblock, sizeof(nullblock));
+				else
+					bytes_written = hfs_write_decmpfs_block(
+					    a, nullblock, skip);
+				if (bytes_written < 0) {
+					archive_set_error(&a->archive, errno,
+					    "Write failed");
+					return (ARCHIVE_WARN);
+				}
+				skip -= bytes_written;
+			}
+
+			a->fd_offset = a->offset;
+		}
+		bytes_written =
+		    hfs_write_decmpfs_block(a, buff, bytes_to_write);
+		if (bytes_written < 0)
+			return (bytes_written);
+		buff += bytes_written;
+		size -= bytes_written;
+		a->total_bytes_written += bytes_written;
+		a->offset += bytes_written;
+		a->fd_offset = a->offset;
+	}
+	return (start_size - size);
+}
+#else
+static ssize_t
+hfs_write_data_block(struct archive_write_disk *a, const char *buff,
+    size_t size)
+{
+	return (write_data_block(a, buff, size));
+}
+#endif
+
+static ssize_t
+_archive_write_disk_data_block(struct archive *_a,
+    const void *buff, size_t size, int64_t offset)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	ssize_t r;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_DATA, "archive_write_data_block");
+
+	a->offset = offset;
+	if (a->todo & TODO_HFS_COMPRESSION)
+		r = hfs_write_data_block(a, buff, size);
+	else
+		r = write_data_block(a, buff, size);
+	if (r < ARCHIVE_OK)
+		return (r);
+	if ((size_t)r < size) {
+		archive_set_error(&a->archive, 0,
+		    "Too much data: Truncating file at %ju bytes",
+		    (uintmax_t)a->filesize);
+		return (ARCHIVE_WARN);
+	}
+#if ARCHIVE_VERSION_NUMBER < 3999000
+	return (ARCHIVE_OK);
+#else
+	return (size);
+#endif
+}
+
+static ssize_t
+_archive_write_disk_data(struct archive *_a, const void *buff, size_t size)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_DATA, "archive_write_data");
+
+	if (a->todo & TODO_HFS_COMPRESSION)
+		return (hfs_write_data_block(a, buff, size));
+	return (write_data_block(a, buff, size));
+}
+
+static int
+_archive_write_disk_finish_entry(struct archive *_a)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	int ret = ARCHIVE_OK;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_write_finish_entry");
+	if (a->archive.state & ARCHIVE_STATE_HEADER)
+		return (ARCHIVE_OK);
+	archive_clear_error(&a->archive);
+
+	/* Pad or truncate file to the right size. */
+	if (a->fd < 0) {
+		/* There's no file. */
+	} else if (a->filesize < 0) {
+		/* File size is unknown, so we can't set the size. */
+	} else if (a->fd_offset == a->filesize) {
+		/* Last write ended at exactly the filesize; we're done. */
+		/* Hopefully, this is the common case. */
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_ZLIB_H)
+	} else if (a->todo & TODO_HFS_COMPRESSION) {
+		char null_d[1024];
+		ssize_t r;
+
+		if (a->file_remaining_bytes)
+			memset(null_d, 0, sizeof(null_d));
+		while (a->file_remaining_bytes) {
+			if (a->file_remaining_bytes > sizeof(null_d))
+				r = hfs_write_data_block(
+				    a, null_d, sizeof(null_d));
+			else
+				r = hfs_write_data_block(
+				    a, null_d, a->file_remaining_bytes);
+			if (r < 0)
+				return ((int)r);
+		}
+#endif
+	} else {
+#if HAVE_FTRUNCATE
+		if (ftruncate(a->fd, a->filesize) == -1 &&
+		    a->filesize == 0) {
+			archive_set_error(&a->archive, errno,
+			    "File size could not be restored");
+			return (ARCHIVE_FAILED);
+		}
+#endif
+		/*
+		 * Not all platforms implement the XSI option to
+		 * extend files via ftruncate.  Stat() the file again
+		 * to see what happened.
+		 */
+		a->pst = NULL;
+		if ((ret = lazy_stat(a)) != ARCHIVE_OK)
+			return (ret);
+		/* We can use lseek()/write() to extend the file if
+		 * ftruncate didn't work or isn't available. */
+		if (a->st.st_size < a->filesize) {
+			const char nul = '\0';
+			if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) {
+				archive_set_error(&a->archive, errno,
+				    "Seek failed");
+				return (ARCHIVE_FATAL);
+			}
+			if (write(a->fd, &nul, 1) < 0) {
+				archive_set_error(&a->archive, errno,
+				    "Write to restore size failed");
+				return (ARCHIVE_FATAL);
+			}
+			a->pst = NULL;
+		}
+	}
+
+	/* Restore metadata. */
+
+	/*
+	 * This is specific to Mac OS X.
+	 * If the current file is an AppleDouble file, it should be
+	 * linked with the data fork file and remove it.
+	 */
+	if (a->todo & TODO_APPLEDOUBLE) {
+		int r2 = fixup_appledouble(a, a->name);
+		if (r2 == ARCHIVE_EOF) {
+			/* The current file has been successfully linked
+			 * with the data fork file and removed. So there
+			 * is nothing to do on the current file.  */
+			goto finish_metadata;
+		}
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * Look up the "real" UID only if we're going to need it.
+	 * TODO: the TODO_SGID condition can be dropped here, can't it?
+	 */
+	if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {
+		a->uid = archive_write_disk_uid(&a->archive,
+		    archive_entry_uname(a->entry),
+		    archive_entry_uid(a->entry));
+	}
+	/* Look up the "real" GID only if we're going to need it. */
+	/* TODO: the TODO_SUID condition can be dropped here, can't it? */
+	if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {
+		a->gid = archive_write_disk_gid(&a->archive,
+		    archive_entry_gname(a->entry),
+		    archive_entry_gid(a->entry));
+	 }
+
+	/*
+	 * Restore ownership before set_mode tries to restore suid/sgid
+	 * bits.  If we set the owner, we know what it is and can skip
+	 * a stat() call to examine the ownership of the file on disk.
+	 */
+	if (a->todo & TODO_OWNER) {
+		int r2 = set_ownership(a);
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * HYPOTHESIS:
+	 * If we're not root, we won't be setting any security
+	 * attributes that may be wiped by the set_mode() routine
+	 * below.  We also can't set xattr on non-owner-writable files,
+	 * which may be the state after set_mode(). Perform
+	 * set_xattrs() first based on these constraints.
+	 */
+	if (a->user_uid != 0 &&
+	    (a->todo & TODO_XATTR)) {
+		int r2 = set_xattrs(a);
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * set_mode must precede ACLs on systems such as Solaris and
+	 * FreeBSD where setting the mode implicitly clears extended ACLs
+	 */
+	if (a->todo & TODO_MODE) {
+		int r2 = set_mode(a, a->mode);
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * Security-related extended attributes (such as
+	 * security.capability on Linux) have to be restored last,
+	 * since they're implicitly removed by other file changes.
+	 * We do this last only when root.
+	 */
+	if (a->user_uid == 0 &&
+	    (a->todo & TODO_XATTR)) {
+		int r2 = set_xattrs(a);
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * Some flags prevent file modification; they must be restored after
+	 * file contents are written.
+	 */
+	if (a->todo & TODO_FFLAGS) {
+		int r2 = set_fflags(a);
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * Time must follow most other metadata;
+	 * otherwise atime will get changed.
+	 */
+	if (a->todo & TODO_TIMES) {
+		int r2 = set_times_from_entry(a);
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * Mac extended metadata includes ACLs.
+	 */
+	if (a->todo & TODO_MAC_METADATA) {
+		const void *metadata;
+		size_t metadata_size;
+		metadata = archive_entry_mac_metadata(a->entry, &metadata_size);
+		if (metadata != NULL && metadata_size > 0) {
+			int r2 = set_mac_metadata(a, archive_entry_pathname(
+			    a->entry), metadata, metadata_size);
+			if (r2 < ret) ret = r2;
+		}
+	}
+
+	/*
+	 * ACLs must be restored after timestamps because there are
+	 * ACLs that prevent attribute changes (including time).
+	 */
+	if (a->todo & TODO_ACLS) {
+		int r2;
+		r2 = archive_write_disk_set_acls(&a->archive, a->fd,
+		    archive_entry_pathname(a->entry),
+		    archive_entry_acl(a->entry),
+		    archive_entry_mode(a->entry));
+		if (r2 < ret) ret = r2;
+	}
+
+finish_metadata:
+	/* If there's an fd, we can close it now. */
+	if (a->fd >= 0) {
+		close(a->fd);
+		a->fd = -1;
+		if (a->tmpname) {
+			if (rename(a->tmpname, a->name) == -1) {
+				archive_set_error(&a->archive, errno,
+				    "Failed to rename temporary file");
+				ret = ARCHIVE_FAILED;
+				unlink(a->tmpname);
+			}
+			a->tmpname = NULL;
+		}
+	}
+	/* If there's an entry, we can release it now. */
+	archive_entry_free(a->entry);
+	a->entry = NULL;
+	a->archive.state = ARCHIVE_STATE_HEADER;
+	return (ret);
+}
+
+int
+archive_write_disk_set_group_lookup(struct archive *_a,
+    void *private_data,
+    la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid),
+    void (*cleanup_gid)(void *private))
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup");
+
+	if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL)
+		(a->cleanup_gid)(a->lookup_gid_data);
+
+	a->lookup_gid = lookup_gid;
+	a->cleanup_gid = cleanup_gid;
+	a->lookup_gid_data = private_data;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_user_lookup(struct archive *_a,
+    void *private_data,
+    int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid),
+    void (*cleanup_uid)(void *private))
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup");
+
+	if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL)
+		(a->cleanup_uid)(a->lookup_uid_data);
+
+	a->lookup_uid = lookup_uid;
+	a->cleanup_uid = cleanup_uid;
+	a->lookup_uid_data = private_data;
+	return (ARCHIVE_OK);
+}
+
+int64_t
+archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id)
+{
+       struct archive_write_disk *a = (struct archive_write_disk *)_a;
+       archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_write_disk_gid");
+       if (a->lookup_gid)
+               return (a->lookup_gid)(a->lookup_gid_data, name, id);
+       return (id);
+}
+ 
+int64_t
+archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_disk_uid");
+	if (a->lookup_uid)
+		return (a->lookup_uid)(a->lookup_uid_data, name, id);
+	return (id);
+}
+
+/*
+ * Create a new archive_write_disk object and initialize it with global state.
+ */
+struct archive *
+archive_write_disk_new(void)
+{
+	struct archive_write_disk *a;
+
+	a = (struct archive_write_disk *)calloc(1, sizeof(*a));
+	if (a == NULL)
+		return (NULL);
+	a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;
+	/* We're ready to write a header immediately. */
+	a->archive.state = ARCHIVE_STATE_HEADER;
+	a->archive.vtable = archive_write_disk_vtable();
+	a->start_time = time(NULL);
+	/* Query and restore the umask. */
+	umask(a->user_umask = umask(0));
+#ifdef HAVE_GETEUID
+	a->user_uid = geteuid();
+#endif /* HAVE_GETEUID */
+	if (archive_string_ensure(&a->path_safe, 512) == NULL) {
+		free(a);
+		return (NULL);
+	}
+#ifdef HAVE_ZLIB_H
+	a->decmpfs_compression_level = 5;
+#endif
+	return (&a->archive);
+}
+
+
+/*
+ * If pathname is longer than PATH_MAX, chdir to a suitable
+ * intermediate dir and edit the path down to a shorter suffix.  Note
+ * that this routine never returns an error; if the chdir() attempt
+ * fails for any reason, we just go ahead with the long pathname.  The
+ * object creation is likely to fail, but any error will get handled
+ * at that time.
+ */
+#if defined(HAVE_FCHDIR) && defined(PATH_MAX)
+static void
+edit_deep_directories(struct archive_write_disk *a)
+{
+	int ret;
+	char *tail = a->name;
+
+	/* If path is short, avoid the open() below. */
+	if (strlen(tail) < PATH_MAX)
+		return;
+
+	/* Try to record our starting dir. */
+	a->restore_pwd = la_opendirat(AT_FDCWD, ".");
+	__archive_ensure_cloexec_flag(a->restore_pwd);
+	if (a->restore_pwd < 0)
+		return;
+
+	/* As long as the path is too long... */
+	while (strlen(tail) >= PATH_MAX) {
+		/* Locate a dir prefix shorter than PATH_MAX. */
+		tail += PATH_MAX - 8;
+		while (tail > a->name && *tail != '/')
+			tail--;
+		/* Exit if we find a too-long path component. */
+		if (tail <= a->name)
+			return;
+		/* Create the intermediate dir and chdir to it. */
+		*tail = '\0'; /* Terminate dir portion */
+		ret = create_dir(a, a->name);
+		if (ret == ARCHIVE_OK && chdir(a->name) != 0)
+			ret = ARCHIVE_FAILED;
+		*tail = '/'; /* Restore the / we removed. */
+		if (ret != ARCHIVE_OK)
+			return;
+		tail++;
+		/* The chdir() succeeded; we've now shortened the path. */
+		a->name = tail;
+	}
+	return;
+}
+#endif
+
+/*
+ * The main restore function.
+ */
+static int
+restore_entry(struct archive_write_disk *a)
+{
+	int ret = ARCHIVE_OK, en;
+
+	if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {
+		/*
+		 * TODO: Fix this.  Apparently, there are platforms
+		 * that still allow root to hose the entire filesystem
+		 * by unlinking a dir.  The S_ISDIR() test above
+		 * prevents us from using unlink() here if the new
+		 * object is a dir, but that doesn't mean the old
+		 * object isn't a dir.
+		 */
+		if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+			(void)clear_nochange_fflags(a);
+		if (unlink(a->name) == 0) {
+			/* We removed it, reset cached stat. */
+			a->pst = NULL;
+		} else if (errno == ENOENT) {
+			/* File didn't exist, that's just as good. */
+		} else if (rmdir(a->name) == 0) {
+			/* It was a dir, but now it's gone. */
+			a->pst = NULL;
+		} else {
+			/* We tried, but couldn't get rid of it. */
+			archive_set_error(&a->archive, errno,
+			    "Could not unlink");
+			return(ARCHIVE_FAILED);
+		}
+	}
+
+	/* Try creating it first; if this fails, we'll try to recover. */
+	en = create_filesystem_object(a);
+
+	if ((en == ENOTDIR || en == ENOENT)
+	    && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {
+		/* If the parent dir doesn't exist, try creating it. */
+		create_parent_dir(a, a->name);
+		/* Now try to create the object again. */
+		en = create_filesystem_object(a);
+	}
+
+	if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) {
+		archive_set_error(&a->archive, en,
+		    "Hard-link target '%s' does not exist.",
+		    archive_entry_hardlink(a->entry));
+		return (ARCHIVE_FAILED);
+	}
+
+	if ((en == EISDIR || en == EEXIST)
+	    && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+		/* If we're not overwriting, we're done. */
+		if (S_ISDIR(a->mode)) {
+			/* Don't overwrite any settings on existing directories. */
+			a->todo = 0;
+		}
+		archive_entry_unset_size(a->entry);
+		return (ARCHIVE_OK);
+	}
+
+	/*
+	 * Some platforms return EISDIR if you call
+	 * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some
+	 * return EEXIST.  POSIX is ambiguous, requiring EISDIR
+	 * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)
+	 * on an existing item.
+	 */
+	if (en == EISDIR) {
+		/* A dir is in the way of a non-dir, rmdir it. */
+		if (rmdir(a->name) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't remove already-existing dir");
+			return (ARCHIVE_FAILED);
+		}
+		a->pst = NULL;
+		/* Try again. */
+		en = create_filesystem_object(a);
+	} else if (en == EEXIST) {
+		/*
+		 * We know something is in the way, but we don't know what;
+		 * we need to find out before we go any further.
+		 */
+		int r = 0;
+		/*
+		 * The SECURE_SYMLINKS logic has already removed a
+		 * symlink to a dir if the client wants that.  So
+		 * follow the symlink if we're creating a dir.
+		 */
+		if (S_ISDIR(a->mode))
+			r = la_stat(a->name, &a->st);
+		/*
+		 * If it's not a dir (or it's a broken symlink),
+		 * then don't follow it.
+		 */
+		if (r != 0 || !S_ISDIR(a->mode))
+			r = lstat(a->name, &a->st);
+		if (r != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't stat existing object");
+			return (ARCHIVE_FAILED);
+		}
+
+		/*
+		 * NO_OVERWRITE_NEWER doesn't apply to directories.
+		 */
+		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)
+		    &&  !S_ISDIR(a->st.st_mode)) {
+			if (!older(&(a->st), a->entry)) {
+				archive_entry_unset_size(a->entry);
+				return (ARCHIVE_OK);
+			}
+		}
+
+		/* If it's our archive, we're done. */
+		if (a->skip_file_set &&
+		    a->st.st_dev == (dev_t)a->skip_file_dev &&
+		    a->st.st_ino == (ino_t)a->skip_file_ino) {
+			archive_set_error(&a->archive, 0,
+			    "Refusing to overwrite archive");
+			return (ARCHIVE_FAILED);
+		}
+
+		if (!S_ISDIR(a->st.st_mode)) {
+			if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+				(void)clear_nochange_fflags(a);
+
+			if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) &&
+			    S_ISREG(a->st.st_mode)) {
+				/* Use a temporary file to extract */
+				if ((a->fd = la_mktemp(a)) == -1) {
+					archive_set_error(&a->archive, errno,
+					    "Can't create temporary file");
+					return ARCHIVE_FAILED;
+				}
+				a->pst = NULL;
+				en = 0;
+			} else {
+				/* A non-dir is in the way, unlink it. */
+				if (unlink(a->name) != 0) {
+					archive_set_error(&a->archive, errno,
+					    "Can't unlink already-existing "
+					    "object");
+					return (ARCHIVE_FAILED);
+				}
+				a->pst = NULL;
+				/* Try again. */
+				en = create_filesystem_object(a);
+			}
+		} else if (!S_ISDIR(a->mode)) {
+			/* A dir is in the way of a non-dir, rmdir it. */
+			if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+				(void)clear_nochange_fflags(a);
+			if (rmdir(a->name) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Can't replace existing directory with non-directory");
+				return (ARCHIVE_FAILED);
+			}
+			/* Try again. */
+			en = create_filesystem_object(a);
+		} else {
+			/*
+			 * There's a dir in the way of a dir.  Don't
+			 * waste time with rmdir()/mkdir(), just fix
+			 * up the permissions on the existing dir.
+			 * Note that we don't change perms on existing
+			 * dirs unless _EXTRACT_PERM is specified.
+			 */
+			if ((a->mode != a->st.st_mode)
+			    && (a->todo & TODO_MODE_FORCE))
+				a->deferred |= (a->todo & TODO_MODE);
+			/* Ownership doesn't need deferred fixup. */
+			en = 0; /* Forget the EEXIST. */
+		}
+	}
+
+	if (en) {
+		/* Everything failed; give up here. */
+		if ((&a->archive)->error == NULL)
+			archive_set_error(&a->archive, en, "Can't create '%s'",
+			    a->name);
+		return (ARCHIVE_FAILED);
+	}
+
+	a->pst = NULL; /* Cached stat data no longer valid. */
+	return (ret);
+}
+
+/*
+ * Returns 0 if creation succeeds, or else returns errno value from
+ * the failed system call.   Note:  This function should only ever perform
+ * a single system call.
+ */
+static int
+create_filesystem_object(struct archive_write_disk *a)
+{
+	/* Create the entry. */
+	const char *linkname;
+	mode_t final_mode, mode;
+	int r;
+	/* these for check_symlinks_fsobj */
+	char *linkname_copy;	/* non-const copy of linkname */
+	struct stat st;
+	struct archive_string error_string;
+	int error_number;
+
+	/* We identify hard/symlinks according to the link names. */
+	/* Since link(2) and symlink(2) don't handle modes, we're done here. */
+	linkname = archive_entry_hardlink(a->entry);
+	if (linkname != NULL) {
+#if !HAVE_LINK
+		return (EPERM);
+#else
+		archive_string_init(&error_string);
+		linkname_copy = strdup(linkname);
+		if (linkname_copy == NULL) {
+		    return (EPERM);
+		}
+		/*
+		 * TODO: consider using the cleaned-up path as the link
+		 * target?
+		 */
+		r = cleanup_pathname_fsobj(linkname_copy, &error_number,
+		    &error_string, a->flags);
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, error_number, "%s",
+			    error_string.s);
+			free(linkname_copy);
+			archive_string_free(&error_string);
+			/*
+			 * EPERM is more appropriate than error_number for our
+			 * callers
+			 */
+			return (EPERM);
+		}
+		r = check_symlinks_fsobj(linkname_copy, &error_number,
+		    &error_string, a->flags);
+		if (r != ARCHIVE_OK) {
+			archive_set_error(&a->archive, error_number, "%s",
+			    error_string.s);
+			free(linkname_copy);
+			archive_string_free(&error_string);
+			/*
+			 * EPERM is more appropriate than error_number for our
+			 * callers
+			 */
+			return (EPERM);
+		}
+		free(linkname_copy);
+		archive_string_free(&error_string);
+		/*
+		 * Unlinking and linking here is really not atomic,
+		 * but doing it right, would require us to construct
+		 * an mktemplink() function, and then use rename(2).
+		 */
+		if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES)
+			unlink(a->name);
+		r = link(linkname, a->name) ? errno : 0;
+		/*
+		 * New cpio and pax formats allow hardlink entries
+		 * to carry data, so we may have to open the file
+		 * for hardlink entries.
+		 *
+		 * If the hardlink was successfully created and
+		 * the archive doesn't have carry data for it,
+		 * consider it to be non-authoritative for meta data.
+		 * This is consistent with GNU tar and BSD pax.
+		 * If the hardlink does carry data, let the last
+		 * archive entry decide ownership.
+		 */
+		if (r == 0 && a->filesize <= 0) {
+			a->todo = 0;
+			a->deferred = 0;
+		} else if (r == 0 && a->filesize > 0) {
+#ifdef HAVE_LSTAT
+			r = lstat(a->name, &st);
+#else
+			r = la_stat(a->name, &st);
+#endif
+			if (r != 0)
+				r = errno;
+			else if ((st.st_mode & AE_IFMT) == AE_IFREG) {
+				a->fd = open(a->name, O_WRONLY | O_TRUNC |
+				    O_BINARY | O_CLOEXEC | O_NOFOLLOW);
+				__archive_ensure_cloexec_flag(a->fd);
+				if (a->fd < 0)
+					r = errno;
+			}
+		}
+		return (r);
+#endif
+	}
+	linkname = archive_entry_symlink(a->entry);
+	if (linkname != NULL) {
+#if HAVE_SYMLINK
+		/*
+		 * Unlinking and linking here is really not atomic,
+		 * but doing it right, would require us to construct
+		 * an mktempsymlink() function, and then use rename(2).
+		 */
+		if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES)
+			unlink(a->name);
+		return symlink(linkname, a->name) ? errno : 0;
+#else
+		return (EPERM);
+#endif
+	}
+
+	/*
+	 * The remaining system calls all set permissions, so let's
+	 * try to take advantage of that to avoid an extra chmod()
+	 * call.  (Recall that umask is set to zero right now!)
+	 */
+
+	/* Mode we want for the final restored object (w/o file type bits). */
+	final_mode = a->mode & 07777;
+	/*
+	 * The mode that will actually be restored in this step.  Note
+	 * that SUID, SGID, etc, require additional work to ensure
+	 * security, so we never restore them at this point.
+	 */
+	mode = final_mode & 0777 & ~a->user_umask;
+
+	/* 
+	 * Always create writable such that [f]setxattr() works if we're not
+	 * root.
+	 */
+	if (a->user_uid != 0 &&
+	    a->todo & (TODO_HFS_COMPRESSION | TODO_XATTR)) {
+		mode |= 0200;
+	}
+
+	switch (a->mode & AE_IFMT) {
+	default:
+		/* POSIX requires that we fall through here. */
+		/* FALLTHROUGH */
+	case AE_IFREG:
+		a->tmpname = NULL;
+		a->fd = open(a->name,
+		    O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, mode);
+		__archive_ensure_cloexec_flag(a->fd);
+		r = (a->fd < 0);
+		break;
+	case AE_IFCHR:
+#ifdef HAVE_MKNOD
+		/* Note: we use AE_IFCHR for the case label, and
+		 * S_IFCHR for the mknod() call.  This is correct.  */
+		r = mknod(a->name, mode | S_IFCHR,
+		    archive_entry_rdev(a->entry));
+		break;
+#else
+		/* TODO: Find a better way to warn about our inability
+		 * to restore a char device node. */
+		return (EINVAL);
+#endif /* HAVE_MKNOD */
+	case AE_IFBLK:
+#ifdef HAVE_MKNOD
+		r = mknod(a->name, mode | S_IFBLK,
+		    archive_entry_rdev(a->entry));
+		break;
+#else
+		/* TODO: Find a better way to warn about our inability
+		 * to restore a block device node. */
+		return (EINVAL);
+#endif /* HAVE_MKNOD */
+	case AE_IFDIR:
+		mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
+		r = mkdir(a->name, mode);
+		if (r == 0) {
+			/* Defer setting dir times. */
+			a->deferred |= (a->todo & TODO_TIMES);
+			a->todo &= ~TODO_TIMES;
+			/* Never use an immediate chmod(). */
+			/* We can't avoid the chmod() entirely if EXTRACT_PERM
+			 * because of SysV SGID inheritance. */
+			if ((mode != final_mode)
+			    || (a->flags & ARCHIVE_EXTRACT_PERM))
+				a->deferred |= (a->todo & TODO_MODE);
+			a->todo &= ~TODO_MODE;
+		}
+		break;
+	case AE_IFIFO:
+#ifdef HAVE_MKFIFO
+		r = mkfifo(a->name, mode);
+		break;
+#else
+		/* TODO: Find a better way to warn about our inability
+		 * to restore a fifo. */
+		return (EINVAL);
+#endif /* HAVE_MKFIFO */
+	}
+
+	/* All the system calls above set errno on failure. */
+	if (r)
+		return (errno);
+
+	/* If we managed to set the final mode, we've avoided a chmod(). */
+	if (mode == final_mode)
+		a->todo &= ~TODO_MODE;
+	return (0);
+}
+
+/*
+ * Cleanup function for archive_extract.  Mostly, this involves processing
+ * the fixup list, which is used to address a number of problems:
+ *   * Dir permissions might prevent us from restoring a file in that
+ *     dir, so we restore the dir with minimum 0700 permissions first,
+ *     then correct the mode at the end.
+ *   * Similarly, the act of restoring a file touches the directory
+ *     and changes the timestamp on the dir, so we have to touch-up dir
+ *     timestamps at the end as well.
+ *   * Some file flags can interfere with the restore by, for example,
+ *     preventing the creation of hardlinks to those files.
+ *   * Mac OS extended metadata includes ACLs, so must be deferred on dirs.
+ *
+ * Note that tar/cpio do not require that archives be in a particular
+ * order; there is no way to know when the last file has been restored
+ * within a directory, so there's no way to optimize the memory usage
+ * here by fixing up the directory any earlier than the
+ * end-of-archive.
+ *
+ * XXX TODO: Directory ACLs should be restored here, for the same
+ * reason we set directory perms here. XXX
+ */
+static int
+_archive_write_disk_close(struct archive *_a)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	struct fixup_entry *next, *p;
+	int fd, ret;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_write_disk_close");
+	ret = _archive_write_disk_finish_entry(&a->archive);
+
+	/* Sort dir list so directories are fixed up in depth-first order. */
+	p = sort_dir_list(a->fixup_list);
+
+	while (p != NULL) {
+		fd = -1;
+		a->pst = NULL; /* Mark stat cache as out-of-date. */
+		if (p->fixup &
+		    (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) {
+			fd = open(p->name,
+			    O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC);
+		}
+		if (p->fixup & TODO_TIMES) {
+			set_times(a, fd, p->mode, p->name,
+			    p->atime, p->atime_nanos,
+			    p->birthtime, p->birthtime_nanos,
+			    p->mtime, p->mtime_nanos,
+			    p->ctime, p->ctime_nanos);
+		}
+		if (p->fixup & TODO_MODE_BASE) {
+#ifdef HAVE_FCHMOD
+			if (fd >= 0)
+				fchmod(fd, p->mode);
+			else
+#endif
+			chmod(p->name, p->mode);
+		}
+		if (p->fixup & TODO_ACLS)
+			archive_write_disk_set_acls(&a->archive, fd,
+			    p->name, &p->acl, p->mode);
+		if (p->fixup & TODO_FFLAGS)
+			set_fflags_platform(a, fd, p->name,
+			    p->mode, p->fflags_set, 0);
+		if (p->fixup & TODO_MAC_METADATA)
+			set_mac_metadata(a, p->name, p->mac_metadata,
+					 p->mac_metadata_size);
+		next = p->next;
+		archive_acl_clear(&p->acl);
+		free(p->mac_metadata);
+		free(p->name);
+		if (fd >= 0)
+			close(fd);
+		free(p);
+		p = next;
+	}
+	a->fixup_list = NULL;
+	return (ret);
+}
+
+static int
+_archive_write_disk_free(struct archive *_a)
+{
+	struct archive_write_disk *a;
+	int ret;
+	if (_a == NULL)
+		return (ARCHIVE_OK);
+	archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free");
+	a = (struct archive_write_disk *)_a;
+	ret = _archive_write_disk_close(&a->archive);
+	archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL);
+	archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL);
+	archive_entry_free(a->entry);
+	archive_string_free(&a->_name_data);
+	archive_string_free(&a->_tmpname_data);
+	archive_string_free(&a->archive.error_string);
+	archive_string_free(&a->path_safe);
+	a->archive.magic = 0;
+	__archive_clean(&a->archive);
+	free(a->decmpfs_header_p);
+	free(a->resource_fork);
+	free(a->compressed_buffer);
+	free(a->uncompressed_buffer);
+#if defined(__APPLE__) && defined(UF_COMPRESSED) && defined(HAVE_SYS_XATTR_H)\
+	&& defined(HAVE_ZLIB_H)
+	if (a->stream_valid) {
+		switch (deflateEnd(&a->stream)) {
+		case Z_OK:
+			break;
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Failed to clean up compressor");
+			ret = ARCHIVE_FATAL;
+			break;
+		}
+	}
+#endif
+	free(a);
+	return (ret);
+}
+
+/*
+ * Simple O(n log n) merge sort to order the fixup list.  In
+ * particular, we want to restore dir timestamps depth-first.
+ */
+static struct fixup_entry *
+sort_dir_list(struct fixup_entry *p)
+{
+	struct fixup_entry *a, *b, *t;
+
+	if (p == NULL)
+		return (NULL);
+	/* A one-item list is already sorted. */
+	if (p->next == NULL)
+		return (p);
+
+	/* Step 1: split the list. */
+	t = p;
+	a = p->next->next;
+	while (a != NULL) {
+		/* Step a twice, t once. */
+		a = a->next;
+		if (a != NULL)
+			a = a->next;
+		t = t->next;
+	}
+	/* Now, t is at the mid-point, so break the list here. */
+	b = t->next;
+	t->next = NULL;
+	a = p;
+
+	/* Step 2: Recursively sort the two sub-lists. */
+	a = sort_dir_list(a);
+	b = sort_dir_list(b);
+
+	/* Step 3: Merge the returned lists. */
+	/* Pick the first element for the merged list. */
+	if (strcmp(a->name, b->name) > 0) {
+		t = p = a;
+		a = a->next;
+	} else {
+		t = p = b;
+		b = b->next;
+	}
+
+	/* Always put the later element on the list first. */
+	while (a != NULL && b != NULL) {
+		if (strcmp(a->name, b->name) > 0) {
+			t->next = a;
+			a = a->next;
+		} else {
+			t->next = b;
+			b = b->next;
+		}
+		t = t->next;
+	}
+
+	/* Only one list is non-empty, so just splice it on. */
+	if (a != NULL)
+		t->next = a;
+	if (b != NULL)
+		t->next = b;
+
+	return (p);
+}
+
+/*
+ * Returns a new, initialized fixup entry.
+ *
+ * TODO: Reduce the memory requirements for this list by using a tree
+ * structure rather than a simple list of names.
+ */
+static struct fixup_entry *
+new_fixup(struct archive_write_disk *a, const char *pathname)
+{
+	struct fixup_entry *fe;
+
+	fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry));
+	if (fe == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for a fixup");
+		return (NULL);
+	}
+	fe->next = a->fixup_list;
+	a->fixup_list = fe;
+	fe->fixup = 0;
+	fe->name = strdup(pathname);
+	return (fe);
+}
+
+/*
+ * Returns a fixup structure for the current entry.
+ */
+static struct fixup_entry *
+current_fixup(struct archive_write_disk *a, const char *pathname)
+{
+	if (a->current_fixup == NULL)
+		a->current_fixup = new_fixup(a, pathname);
+	return (a->current_fixup);
+}
+
+/* Error helper for new *_fsobj functions */
+static void
+fsobj_error(int *a_eno, struct archive_string *a_estr,
+    int err, const char *errstr, const char *path)
+{
+	if (a_eno)
+		*a_eno = err;
+	if (a_estr)
+		archive_string_sprintf(a_estr, "%s%s", errstr, path);
+}
+
+/*
+ * TODO: Someday, integrate this with the deep dir support; they both
+ * scan the path and both can be optimized by comparing against other
+ * recent paths.
+ */
+/*
+ * Checks the given path to see if any elements along it are symlinks.  Returns
+ * ARCHIVE_OK if there are none, otherwise puts an error in errmsg.
+ */
+static int
+check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
+    int flags)
+{
+#if !defined(HAVE_LSTAT) && \
+    !(defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT))
+	/* Platform doesn't have lstat, so we can't look for symlinks. */
+	(void)path; /* UNUSED */
+	(void)error_number; /* UNUSED */
+	(void)error_string; /* UNUSED */
+	(void)flags; /* UNUSED */
+	return (ARCHIVE_OK);
+#else
+	int res = ARCHIVE_OK;
+	char *tail;
+	char *head;
+	int last;
+	char c;
+	int r;
+	struct stat st;
+	int chdir_fd;
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+	int fd;
+#endif
+
+	/* Nothing to do here if name is empty */
+	if(path[0] == '\0')
+	    return (ARCHIVE_OK);
+
+	/*
+	 * Guard against symlink tricks.  Reject any archive entry whose
+	 * destination would be altered by a symlink.
+	 *
+	 * Walk the filename in chunks separated by '/'.  For each segment:
+	 *  - if it doesn't exist, continue
+	 *  - if it's symlink, abort or remove it
+	 *  - if it's a directory and it's not the last chunk, cd into it
+	 * As we go:
+	 *  head points to the current (relative) path
+	 *  tail points to the temporary \0 terminating the segment we're
+	 *      currently examining
+	 *  c holds what used to be in *tail
+	 *  last is 1 if this is the last tail
+	 */
+	chdir_fd = la_opendirat(AT_FDCWD, ".");
+	__archive_ensure_cloexec_flag(chdir_fd);
+	if (chdir_fd < 0) {
+		fsobj_error(a_eno, a_estr, errno,
+		    "Could not open ", path);
+		return (ARCHIVE_FATAL);
+	}
+	head = path;
+	tail = path;
+	last = 0;
+	/* TODO: reintroduce a safe cache here? */
+	/* Skip the root directory if the path is absolute. */
+	if(tail == path && tail[0] == '/')
+		++tail;
+	/* Keep going until we've checked the entire name.
+	 * head, tail, path all alias the same string, which is
+	 * temporarily zeroed at tail, so be careful restoring the
+	 * stashed (c=tail[0]) for error messages.
+	 * Exiting the loop with break is okay; continue is not.
+	 */
+	while (!last) {
+		/*
+		 * Skip the separator we just consumed, plus any adjacent ones
+		 */
+		while (*tail == '/')
+		    ++tail;
+		/* Skip the next path element. */
+		while (*tail != '\0' && *tail != '/')
+			++tail;
+		/* is this the last path component? */
+		last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0');
+		/* temporarily truncate the string here */
+		c = tail[0];
+		tail[0] = '\0';
+		/* Check that we haven't hit a symlink. */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+		r = fstatat(chdir_fd, head, &st, AT_SYMLINK_NOFOLLOW);
+#else
+		r = lstat(head, &st);
+#endif
+		if (r != 0) {
+			tail[0] = c;
+			/* We've hit a dir that doesn't exist; stop now. */
+			if (errno == ENOENT) {
+				break;
+			} else {
+				/*
+				 * Treat any other error as fatal - best to be
+				 * paranoid here.
+				 * Note: This effectively disables deep
+				 * directory support when security checks are
+				 * enabled. Otherwise, very long pathnames that
+				 * trigger an error here could evade the
+				 * sandbox.
+				 * TODO: We could do better, but it would
+				 * probably require merging the symlink checks
+				 * with the deep-directory editing.
+				 */
+				fsobj_error(a_eno, a_estr, errno,
+				    "Could not stat ", path);
+				res = ARCHIVE_FAILED;
+				break;
+			}
+		} else if (S_ISDIR(st.st_mode)) {
+			if (!last) {
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+				fd = la_opendirat(chdir_fd, head);
+				if (fd < 0)
+					r = -1;
+				else {
+					r = 0;
+					close(chdir_fd);
+					chdir_fd = fd;
+				}
+#else
+				r = chdir(head);
+#endif
+				if (r != 0) {
+					tail[0] = c;
+					fsobj_error(a_eno, a_estr, errno,
+					    "Could not chdir ", path);
+					res = (ARCHIVE_FATAL);
+					break;
+				}
+				/* Our view is now from inside this dir: */
+				head = tail + 1;
+			}
+		} else if (S_ISLNK(st.st_mode)) {
+			if (last) {
+				/*
+				 * Last element is symlink; remove it
+				 * so we can overwrite it with the
+				 * item being extracted.
+				 */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+				r = unlinkat(chdir_fd, head, 0);
+#else
+				r = unlink(head);
+#endif
+				if (r != 0) {
+					tail[0] = c;
+					fsobj_error(a_eno, a_estr, errno,
+					    "Could not remove symlink ",
+					    path);
+					res = ARCHIVE_FAILED;
+					break;
+				}
+				/*
+				 * Even if we did remove it, a warning
+				 * is in order.  The warning is silly,
+				 * though, if we're just replacing one
+				 * symlink with another symlink.
+				 */
+				tail[0] = c;
+				/*
+				 * FIXME:  not sure how important this is to
+				 * restore
+				 */
+				/*
+				if (!S_ISLNK(path)) {
+					fsobj_error(a_eno, a_estr, 0,
+					    "Removing symlink ", path);
+				}
+				*/
+				/* Symlink gone.  No more problem! */
+				res = ARCHIVE_OK;
+				break;
+			} else if (flags & ARCHIVE_EXTRACT_UNLINK) {
+				/* User asked us to remove problems. */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+				r = unlinkat(chdir_fd, head, 0);
+#else
+				r = unlink(head);
+#endif
+				if (r != 0) {
+					tail[0] = c;
+					fsobj_error(a_eno, a_estr, 0,
+					    "Cannot remove intervening "
+					    "symlink ", path);
+					res = ARCHIVE_FAILED;
+					break;
+				}
+				tail[0] = c;
+			} else if ((flags &
+			    ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) {
+				/*
+				 * We are not the last element and we want to
+				 * follow symlinks if they are a directory.
+				 * 
+				 * This is needed to extract hardlinks over
+				 * symlinks.
+				 */
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+				r = fstatat(chdir_fd, head, &st, 0);
+#else
+				r = la_stat(head, &st);
+#endif
+				if (r != 0) {
+					tail[0] = c;
+					if (errno == ENOENT) {
+						break;
+					} else {
+						fsobj_error(a_eno, a_estr,
+						    errno,
+						    "Could not stat ", path);
+						res = (ARCHIVE_FAILED);
+						break;
+					}
+				} else if (S_ISDIR(st.st_mode)) {
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+					fd = la_opendirat(chdir_fd, head);
+					if (fd < 0)
+						r = -1;
+					else {
+						r = 0;
+						close(chdir_fd);
+						chdir_fd = fd;
+					}
+#else
+					r = chdir(head);
+#endif
+					if (r != 0) {
+						tail[0] = c;
+						fsobj_error(a_eno, a_estr,
+						    errno,
+						    "Could not chdir ", path);
+						res = (ARCHIVE_FATAL);
+						break;
+					}
+					/*
+					 * Our view is now from inside
+					 * this dir:
+					 */
+					head = tail + 1;
+				} else {
+					tail[0] = c;
+					fsobj_error(a_eno, a_estr, 0,
+					    "Cannot extract through "
+					    "symlink ", path);
+					res = ARCHIVE_FAILED;
+					break;
+				}
+			} else {
+				tail[0] = c;
+				fsobj_error(a_eno, a_estr, 0,
+				    "Cannot extract through symlink ", path);
+				res = ARCHIVE_FAILED;
+				break;
+			}
+		}
+		/* be sure to always maintain this */
+		tail[0] = c;
+		if (tail[0] != '\0')
+			tail++; /* Advance to the next segment. */
+	}
+	/* Catches loop exits via break */
+	tail[0] = c;
+#if defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) && defined(HAVE_UNLINKAT)
+	/* If we operate with openat(), fstatat() and unlinkat() there was
+	 * no chdir(), so just close the fd */
+	if (chdir_fd >= 0)
+		close(chdir_fd);
+#elif HAVE_FCHDIR
+	/* If we changed directory above, restore it here. */
+	if (chdir_fd >= 0) {
+		r = fchdir(chdir_fd);
+		if (r != 0) {
+			fsobj_error(a_eno, a_estr, errno,
+			    "chdir() failure", "");
+		}
+		close(chdir_fd);
+		chdir_fd = -1;
+		if (r != 0) {
+			res = (ARCHIVE_FATAL);
+		}
+	}
+#endif
+	/* TODO: reintroduce a safe cache here? */
+	return res;
+#endif
+}
+
+/*
+ * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise
+ * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED}
+ */
+static int
+check_symlinks(struct archive_write_disk *a)
+{
+	struct archive_string error_string;
+	int error_number;
+	int rc;
+	archive_string_init(&error_string);
+	rc = check_symlinks_fsobj(a->name, &error_number, &error_string,
+	    a->flags);
+	if (rc != ARCHIVE_OK) {
+		archive_set_error(&a->archive, error_number, "%s",
+		    error_string.s);
+	}
+	archive_string_free(&error_string);
+	a->pst = NULL;	/* to be safe */
+	return rc;
+}
+
+
+#if defined(__CYGWIN__)
+/*
+ * 1. Convert a path separator from '\' to '/' .
+ *    We shouldn't check multibyte character directly because some
+ *    character-set have been using the '\' character for a part of
+ *    its multibyte character code.
+ * 2. Replace unusable characters in Windows with underscore('_').
+ * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx
+ */
+static void
+cleanup_pathname_win(char *path)
+{
+	wchar_t wc;
+	char *p;
+	size_t alen, l;
+	int mb, complete, utf8;
+
+	alen = 0;
+	mb = 0;
+	complete = 1;
+	utf8 = (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)? 1: 0;
+	for (p = path; *p != '\0'; p++) {
+		++alen;
+		if (*p == '\\') {
+			/* If previous byte is smaller than 128,
+			 * this is not second byte of multibyte characters,
+			 * so we can replace '\' with '/'. */
+			if (utf8 || !mb)
+				*p = '/';
+			else
+				complete = 0;/* uncompleted. */
+		} else if (*(unsigned char *)p > 127)
+			mb = 1;
+		else
+			mb = 0;
+		/* Rewrite the path name if its next character is unusable. */
+		if (*p == ':' || *p == '*' || *p == '?' || *p == '"' ||
+		    *p == '<' || *p == '>' || *p == '|')
+			*p = '_';
+	}
+	if (complete)
+		return;
+
+	/*
+	 * Convert path separator in wide-character.
+	 */
+	p = path;
+	while (*p != '\0' && alen) {
+		l = mbtowc(&wc, p, alen);
+		if (l == (size_t)-1) {
+			while (*p != '\0') {
+				if (*p == '\\')
+					*p = '/';
+				++p;
+			}
+			break;
+		}
+		if (l == 1 && wc == L'\\')
+			*p = '/';
+		p += l;
+		alen -= l;
+	}
+}
+#endif
+
+/*
+ * Canonicalize the pathname.  In particular, this strips duplicate
+ * '/' characters, '.' elements, and trailing '/'.  It also raises an
+ * error for an empty path, a trailing '..', (if _SECURE_NODOTDOT is
+ * set) any '..' in the path or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS
+ * is set) if the path is absolute.
+ */
+static int
+cleanup_pathname_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
+    int flags)
+{
+	char *dest, *src;
+	char separator = '\0';
+
+	dest = src = path;
+	if (*src == '\0') {
+		fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,
+		    "Invalid empty ", "pathname");
+		return (ARCHIVE_FAILED);
+	}
+
+#if defined(__CYGWIN__)
+	cleanup_pathname_win(path);
+#endif
+	/* Skip leading '/'. */
+	if (*src == '/') {
+		if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) {
+			fsobj_error(a_eno, a_estr, ARCHIVE_ERRNO_MISC,
+			    "Path is ", "absolute");
+			return (ARCHIVE_FAILED);
+		}
+
+		separator = *src++;
+	}
+
+	/* Scan the pathname one element at a time. */
+	for (;;) {
+		/* src points to first char after '/' */
+		if (src[0] == '\0') {
+			break;
+		} else if (src[0] == '/') {
+			/* Found '//', ignore second one. */
+			src++;
+			continue;
+		} else if (src[0] == '.') {
+			if (src[1] == '\0') {
+				/* Ignore trailing '.' */
+				break;
+			} else if (src[1] == '/') {
+				/* Skip './'. */
+				src += 2;
+				continue;
+			} else if (src[1] == '.') {
+				if (src[2] == '/' || src[2] == '\0') {
+					/* Conditionally warn about '..' */
+					if (flags
+					    & ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
+						fsobj_error(a_eno, a_estr,
+						    ARCHIVE_ERRNO_MISC,
+						    "Path contains ", "'..'");
+						return (ARCHIVE_FAILED);
+					}
+				}
+				/*
+				 * Note: Under no circumstances do we
+				 * remove '..' elements.  In
+				 * particular, restoring
+				 * '/foo/../bar/' should create the
+				 * 'foo' dir as a side-effect.
+				 */
+			}
+		}
+
+		/* Copy current element, including leading '/'. */
+		if (separator)
+			*dest++ = '/';
+		while (*src != '\0' && *src != '/') {
+			*dest++ = *src++;
+		}
+
+		if (*src == '\0')
+			break;
+
+		/* Skip '/' separator. */
+		separator = *src++;
+	}
+	/*
+	 * We've just copied zero or more path elements, not including the
+	 * final '/'.
+	 */
+	if (dest == path) {
+		/*
+		 * Nothing got copied.  The path must have been something
+		 * like '.' or '/' or './' or '/././././/./'.
+		 */
+		if (separator)
+			*dest++ = '/';
+		else
+			*dest++ = '.';
+	}
+	/* Terminate the result. */
+	*dest = '\0';
+	return (ARCHIVE_OK);
+}
+
+static int
+cleanup_pathname(struct archive_write_disk *a)
+{
+	struct archive_string error_string;
+	int error_number;
+	int rc;
+	archive_string_init(&error_string);
+	rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string,
+	    a->flags);
+	if (rc != ARCHIVE_OK) {
+		archive_set_error(&a->archive, error_number, "%s",
+		    error_string.s);
+	}
+	archive_string_free(&error_string);
+	return rc;
+}
+
+/*
+ * Create the parent directory of the specified path, assuming path
+ * is already in mutable storage.
+ */
+static int
+create_parent_dir(struct archive_write_disk *a, char *path)
+{
+	char *slash;
+	int r;
+
+	/* Remove tail element to obtain parent name. */
+	slash = strrchr(path, '/');
+	if (slash == NULL)
+		return (ARCHIVE_OK);
+	*slash = '\0';
+	r = create_dir(a, path);
+	*slash = '/';
+	return (r);
+}
+
+/*
+ * Create the specified dir, recursing to create parents as necessary.
+ *
+ * Returns ARCHIVE_OK if the path exists when we're done here.
+ * Otherwise, returns ARCHIVE_FAILED.
+ * Assumes path is in mutable storage; path is unchanged on exit.
+ */
+static int
+create_dir(struct archive_write_disk *a, char *path)
+{
+	struct stat st;
+	struct fixup_entry *le;
+	char *slash, *base;
+	mode_t mode_final, mode;
+	int r;
+
+	/* Check for special names and just skip them. */
+	slash = strrchr(path, '/');
+	if (slash == NULL)
+		base = path;
+	else
+		base = slash + 1;
+
+	if (base[0] == '\0' ||
+	    (base[0] == '.' && base[1] == '\0') ||
+	    (base[0] == '.' && base[1] == '.' && base[2] == '\0')) {
+		/* Don't bother trying to create null path, '.', or '..'. */
+		if (slash != NULL) {
+			*slash = '\0';
+			r = create_dir(a, path);
+			*slash = '/';
+			return (r);
+		}
+		return (ARCHIVE_OK);
+	}
+
+	/*
+	 * Yes, this should be stat() and not lstat().  Using lstat()
+	 * here loses the ability to extract through symlinks.  Also note
+	 * that this should not use the a->st cache.
+	 */
+	if (la_stat(path, &st) == 0) {
+		if (S_ISDIR(st.st_mode))
+			return (ARCHIVE_OK);
+		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+			archive_set_error(&a->archive, EEXIST,
+			    "Can't create directory '%s'", path);
+			return (ARCHIVE_FAILED);
+		}
+		if (unlink(path) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't create directory '%s': "
+			    "Conflicting file cannot be removed",
+			    path);
+			return (ARCHIVE_FAILED);
+		}
+	} else if (errno != ENOENT && errno != ENOTDIR) {
+		/* Stat failed? */
+		archive_set_error(&a->archive, errno,
+		    "Can't test directory '%s'", path);
+		return (ARCHIVE_FAILED);
+	} else if (slash != NULL) {
+		*slash = '\0';
+		r = create_dir(a, path);
+		*slash = '/';
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	/*
+	 * Mode we want for the final restored directory.  Per POSIX,
+	 * implicitly-created dirs must be created obeying the umask.
+	 * There's no mention whether this is different for privileged
+	 * restores (which the rest of this code handles by pretending
+	 * umask=0).  I've chosen here to always obey the user's umask for
+	 * implicit dirs, even if _EXTRACT_PERM was specified.
+	 */
+	mode_final = DEFAULT_DIR_MODE & ~a->user_umask;
+	/* Mode we want on disk during the restore process. */
+	mode = mode_final;
+	mode |= MINIMUM_DIR_MODE;
+	mode &= MAXIMUM_DIR_MODE;
+	if (mkdir(path, mode) == 0) {
+		if (mode != mode_final) {
+			le = new_fixup(a, path);
+			if (le == NULL)
+				return (ARCHIVE_FATAL);
+			le->fixup |=TODO_MODE_BASE;
+			le->mode = mode_final;
+		}
+		return (ARCHIVE_OK);
+	}
+
+	/*
+	 * Without the following check, a/b/../b/c/d fails at the
+	 * second visit to 'b', so 'd' can't be created.  Note that we
+	 * don't add it to the fixup list here, as it's already been
+	 * added.
+	 */
+	if (la_stat(path, &st) == 0 && S_ISDIR(st.st_mode))
+		return (ARCHIVE_OK);
+
+	archive_set_error(&a->archive, errno, "Failed to create dir '%s'",
+	    path);
+	return (ARCHIVE_FAILED);
+}
+
+/*
+ * Note: Although we can skip setting the user id if the desired user
+ * id matches the current user, we cannot skip setting the group, as
+ * many systems set the gid based on the containing directory.  So
+ * we have to perform a chown syscall if we want to set the SGID
+ * bit.  (The alternative is to stat() and then possibly chown(); it's
+ * more efficient to skip the stat() and just always chown().)  Note
+ * that a successful chown() here clears the TODO_SGID_CHECK bit, which
+ * allows set_mode to skip the stat() check for the GID.
+ */
+static int
+set_ownership(struct archive_write_disk *a)
+{
+#if !defined(__CYGWIN__) && !defined(__linux__)
+/*
+ * On Linux, a process may have the CAP_CHOWN capability.
+ * On Windows there is no 'root' user with uid 0.
+ * Elsewhere we can skip calling chown if we are not root and the desired
+ * user id does not match the current user.
+ */
+	if (a->user_uid != 0 && a->user_uid != a->uid) {
+		archive_set_error(&a->archive, errno,
+		    "Can't set UID=%jd", (intmax_t)a->uid);
+		return (ARCHIVE_WARN);
+	}
+#endif
+
+#ifdef HAVE_FCHOWN
+	/* If we have an fd, we can avoid a race. */
+	if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) {
+		/* We've set owner and know uid/gid are correct. */
+		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+		return (ARCHIVE_OK);
+	}
+#endif
+
+	/* We prefer lchown() but will use chown() if that's all we have. */
+	/* Of course, if we have neither, this will always fail. */
+#ifdef HAVE_LCHOWN
+	if (lchown(a->name, a->uid, a->gid) == 0) {
+		/* We've set owner and know uid/gid are correct. */
+		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+		return (ARCHIVE_OK);
+	}
+#elif HAVE_CHOWN
+	if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) {
+		/* We've set owner and know uid/gid are correct. */
+		a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+		return (ARCHIVE_OK);
+	}
+#endif
+
+	archive_set_error(&a->archive, errno,
+	    "Can't set user=%jd/group=%jd for %s",
+	    (intmax_t)a->uid, (intmax_t)a->gid, a->name);
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Note: Returns 0 on success, non-zero on failure.
+ */
+static int
+set_time(int fd, int mode, const char *name,
+    time_t atime, long atime_nsec,
+    time_t mtime, long mtime_nsec)
+{
+	/* Select the best implementation for this platform. */
+#if defined(HAVE_UTIMENSAT) && defined(HAVE_FUTIMENS)
+	/*
+	 * utimensat() and futimens() are defined in
+	 * POSIX.1-2008. They support ns resolution and setting times
+	 * on fds and symlinks.
+	 */
+	struct timespec ts[2];
+	(void)mode; /* UNUSED */
+	ts[0].tv_sec = atime;
+	ts[0].tv_nsec = atime_nsec;
+	ts[1].tv_sec = mtime;
+	ts[1].tv_nsec = mtime_nsec;
+	if (fd >= 0)
+		return futimens(fd, ts);
+	return utimensat(AT_FDCWD, name, ts, AT_SYMLINK_NOFOLLOW);
+
+#elif HAVE_UTIMES
+	/*
+	 * The utimes()-family functions support µs-resolution and
+	 * setting times fds and symlinks.  utimes() is documented as
+	 * LEGACY by POSIX, futimes() and lutimes() are not described
+	 * in POSIX.
+	 */
+	struct timeval times[2];
+
+	times[0].tv_sec = atime;
+	times[0].tv_usec = atime_nsec / 1000;
+	times[1].tv_sec = mtime;
+	times[1].tv_usec = mtime_nsec / 1000;
+
+#ifdef HAVE_FUTIMES
+	if (fd >= 0)
+		return (futimes(fd, times));
+#else
+	(void)fd; /* UNUSED */
+#endif
+#ifdef HAVE_LUTIMES
+	(void)mode; /* UNUSED */
+	return (lutimes(name, times));
+#else
+	if (S_ISLNK(mode))
+		return (0);
+	return (utimes(name, times));
+#endif
+
+#elif defined(HAVE_UTIME)
+	/*
+	 * utime() is POSIX-standard but only supports 1s resolution and
+	 * does not support fds or symlinks.
+	 */
+	struct utimbuf times;
+	(void)fd; /* UNUSED */
+	(void)name; /* UNUSED */
+	(void)atime_nsec; /* UNUSED */
+	(void)mtime_nsec; /* UNUSED */
+	times.actime = atime;
+	times.modtime = mtime;
+	if (S_ISLNK(mode))
+		return (ARCHIVE_OK);
+	return (utime(name, &times));
+
+#else
+	/*
+	 * We don't know how to set the time on this platform.
+	 */
+	(void)fd; /* UNUSED */
+	(void)mode; /* UNUSED */
+	(void)name; /* UNUSED */
+	(void)atime_nsec; /* UNUSED */
+	(void)mtime_nsec; /* UNUSED */
+	return (ARCHIVE_WARN);
+#endif
+}
+
+#ifdef F_SETTIMES
+static int
+set_time_tru64(int fd, int mode, const char *name,
+    time_t atime, long atime_nsec,
+    time_t mtime, long mtime_nsec,
+    time_t ctime, long ctime_nsec)
+{
+	struct attr_timbuf tstamp;
+	tstamp.atime.tv_sec = atime;
+	tstamp.mtime.tv_sec = mtime;
+	tstamp.ctime.tv_sec = ctime;
+#if defined (__hpux) && defined (__ia64)
+	tstamp.atime.tv_nsec = atime_nsec;
+	tstamp.mtime.tv_nsec = mtime_nsec;
+	tstamp.ctime.tv_nsec = ctime_nsec;
+#else
+	tstamp.atime.tv_usec = atime_nsec / 1000;
+	tstamp.mtime.tv_usec = mtime_nsec / 1000;
+	tstamp.ctime.tv_usec = ctime_nsec / 1000;
+#endif
+	return (fcntl(fd,F_SETTIMES,&tstamp));
+}
+#endif /* F_SETTIMES */
+
+static int
+set_times(struct archive_write_disk *a,
+    int fd, int mode, const char *name,
+    time_t atime, long atime_nanos,
+    time_t birthtime, long birthtime_nanos,
+    time_t mtime, long mtime_nanos,
+    time_t cctime, long ctime_nanos)
+{
+	/* Note: set_time doesn't use libarchive return conventions!
+	 * It uses syscall conventions.  So 0 here instead of ARCHIVE_OK. */
+	int r1 = 0, r2 = 0;
+
+#ifdef F_SETTIMES
+	 /*
+	 * on Tru64 try own fcntl first which can restore even the
+	 * ctime, fall back to default code path below if it fails
+	 * or if we are not running as root
+	 */
+	if (a->user_uid == 0 &&
+	    set_time_tru64(fd, mode, name,
+			   atime, atime_nanos, mtime,
+			   mtime_nanos, cctime, ctime_nanos) == 0) {
+		return (ARCHIVE_OK);
+	}
+#else /* Tru64 */
+	(void)cctime; /* UNUSED */
+	(void)ctime_nanos; /* UNUSED */
+#endif /* Tru64 */
+
+#ifdef HAVE_STRUCT_STAT_ST_BIRTHTIME
+	/*
+	 * If you have struct stat.st_birthtime, we assume BSD
+	 * birthtime semantics, in which {f,l,}utimes() updates
+	 * birthtime to earliest mtime.  So we set the time twice,
+	 * first using the birthtime, then using the mtime.  If
+	 * birthtime == mtime, this isn't necessary, so we skip it.
+	 * If birthtime > mtime, then this won't work, so we skip it.
+	 */
+	if (birthtime < mtime
+	    || (birthtime == mtime && birthtime_nanos < mtime_nanos))
+		r1 = set_time(fd, mode, name,
+			      atime, atime_nanos,
+			      birthtime, birthtime_nanos);
+#else
+	(void)birthtime; /* UNUSED */
+	(void)birthtime_nanos; /* UNUSED */
+#endif
+	r2 = set_time(fd, mode, name,
+		      atime, atime_nanos,
+		      mtime, mtime_nanos);
+	if (r1 != 0 || r2 != 0) {
+		archive_set_error(&a->archive, errno,
+				  "Can't restore time");
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+set_times_from_entry(struct archive_write_disk *a)
+{
+	time_t atime, birthtime, mtime, cctime;
+	long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec;
+
+	/* Suitable defaults. */
+	atime = birthtime = mtime = cctime = a->start_time;
+	atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0;
+
+	/* If no time was provided, we're done. */
+	if (!archive_entry_atime_is_set(a->entry)
+#if HAVE_STRUCT_STAT_ST_BIRTHTIME
+	    && !archive_entry_birthtime_is_set(a->entry)
+#endif
+	    && !archive_entry_mtime_is_set(a->entry))
+		return (ARCHIVE_OK);
+
+	if (archive_entry_atime_is_set(a->entry)) {
+		atime = archive_entry_atime(a->entry);
+		atime_nsec = archive_entry_atime_nsec(a->entry);
+	}
+	if (archive_entry_birthtime_is_set(a->entry)) {
+		birthtime = archive_entry_birthtime(a->entry);
+		birthtime_nsec = archive_entry_birthtime_nsec(a->entry);
+	}
+	if (archive_entry_mtime_is_set(a->entry)) {
+		mtime = archive_entry_mtime(a->entry);
+		mtime_nsec = archive_entry_mtime_nsec(a->entry);
+	}
+	if (archive_entry_ctime_is_set(a->entry)) {
+		cctime = archive_entry_ctime(a->entry);
+		ctime_nsec = archive_entry_ctime_nsec(a->entry);
+	}
+
+	return set_times(a, a->fd, a->mode, a->name,
+			 atime, atime_nsec,
+			 birthtime, birthtime_nsec,
+			 mtime, mtime_nsec,
+			 cctime, ctime_nsec);
+}
+
+static int
+set_mode(struct archive_write_disk *a, int mode)
+{
+	int r = ARCHIVE_OK;
+	int r2;
+	mode &= 07777; /* Strip off file type bits. */
+
+	if (a->todo & TODO_SGID_CHECK) {
+		/*
+		 * If we don't know the GID is right, we must stat()
+		 * to verify it.  We can't just check the GID of this
+		 * process, since systems sometimes set GID from
+		 * the enclosing dir or based on ACLs.
+		 */
+		if ((r = lazy_stat(a)) != ARCHIVE_OK)
+			return (r);
+		if (a->pst->st_gid != a->gid) {
+			mode &= ~ S_ISGID;
+			if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+				/*
+				 * This is only an error if you
+				 * requested owner restore.  If you
+				 * didn't, we'll try to restore
+				 * sgid/suid, but won't consider it a
+				 * problem if we can't.
+				 */
+				archive_set_error(&a->archive, -1,
+				    "Can't restore SGID bit");
+				r = ARCHIVE_WARN;
+			}
+		}
+		/* While we're here, double-check the UID. */
+		if (a->pst->st_uid != a->uid
+		    && (a->todo & TODO_SUID)) {
+			mode &= ~ S_ISUID;
+			if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+				archive_set_error(&a->archive, -1,
+				    "Can't restore SUID bit");
+				r = ARCHIVE_WARN;
+			}
+		}
+		a->todo &= ~TODO_SGID_CHECK;
+		a->todo &= ~TODO_SUID_CHECK;
+	} else if (a->todo & TODO_SUID_CHECK) {
+		/*
+		 * If we don't know the UID is right, we can just check
+		 * the user, since all systems set the file UID from
+		 * the process UID.
+		 */
+		if (a->user_uid != a->uid) {
+			mode &= ~ S_ISUID;
+			if (a->flags & ARCHIVE_EXTRACT_OWNER) {
+				archive_set_error(&a->archive, -1,
+				    "Can't make file SUID");
+				r = ARCHIVE_WARN;
+			}
+		}
+		a->todo &= ~TODO_SUID_CHECK;
+	}
+
+	if (S_ISLNK(a->mode)) {
+#ifdef HAVE_LCHMOD
+		/*
+		 * If this is a symlink, use lchmod().  If the
+		 * platform doesn't support lchmod(), just skip it.  A
+		 * platform that doesn't provide a way to set
+		 * permissions on symlinks probably ignores
+		 * permissions on symlinks, so a failure here has no
+		 * impact.
+		 */
+		if (lchmod(a->name, mode) != 0) {
+			switch (errno) {
+			case ENOTSUP:
+			case ENOSYS:
+#if ENOTSUP != EOPNOTSUPP
+			case EOPNOTSUPP:
+#endif
+				/*
+				 * if lchmod is defined but the platform
+				 * doesn't support it, silently ignore
+				 * error
+				 */
+				break;
+			default:
+				archive_set_error(&a->archive, errno,
+				    "Can't set permissions to 0%o", (int)mode);
+				r = ARCHIVE_WARN;
+			}
+		}
+#endif
+	} else if (!S_ISDIR(a->mode)) {
+		/*
+		 * If it's not a symlink and not a dir, then use
+		 * fchmod() or chmod(), depending on whether we have
+		 * an fd.  Dirs get their perms set during the
+		 * post-extract fixup, which is handled elsewhere.
+		 */
+#ifdef HAVE_FCHMOD
+		if (a->fd >= 0)
+			r2 = fchmod(a->fd, mode);
+		else
+#endif
+		/* If this platform lacks fchmod(), then
+		 * we'll just use chmod(). */
+		r2 = chmod(a->name, mode);
+
+		if (r2 != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't set permissions to 0%o", (int)mode);
+			r = ARCHIVE_WARN;
+		}
+	}
+	return (r);
+}
+
+static int
+set_fflags(struct archive_write_disk *a)
+{
+	struct fixup_entry *le;
+	unsigned long	set, clear;
+	int		r;
+	mode_t		mode = archive_entry_mode(a->entry);
+	/*
+	 * Make 'critical_flags' hold all file flags that can't be
+	 * immediately restored.  For example, on BSD systems,
+	 * SF_IMMUTABLE prevents hardlinks from being created, so
+	 * should not be set until after any hardlinks are created.  To
+	 * preserve some semblance of portability, this uses #ifdef
+	 * extensively.  Ugly, but it works.
+	 *
+	 * Yes, Virginia, this does create a security race.  It's mitigated
+	 * somewhat by the practice of creating dirs 0700 until the extract
+	 * is done, but it would be nice if we could do more than that.
+	 * People restoring critical file systems should be wary of
+	 * other programs that might try to muck with files as they're
+	 * being restored.
+	 */
+	const int	critical_flags = 0
+#ifdef SF_IMMUTABLE
+	    | SF_IMMUTABLE
+#endif
+#ifdef UF_IMMUTABLE
+	    | UF_IMMUTABLE
+#endif
+#ifdef SF_APPEND
+	    | SF_APPEND
+#endif
+#ifdef UF_APPEND
+	    | UF_APPEND
+#endif
+#if defined(FS_APPEND_FL)
+	    | FS_APPEND_FL
+#elif defined(EXT2_APPEND_FL)
+	    | EXT2_APPEND_FL
+#endif
+#if defined(FS_IMMUTABLE_FL)
+	    | FS_IMMUTABLE_FL
+#elif defined(EXT2_IMMUTABLE_FL)
+	    | EXT2_IMMUTABLE_FL
+#endif
+#ifdef FS_JOURNAL_DATA_FL
+	    | FS_JOURNAL_DATA_FL
+#endif
+	;
+
+	if (a->todo & TODO_FFLAGS) {
+		archive_entry_fflags(a->entry, &set, &clear);
+
+		/*
+		 * The first test encourages the compiler to eliminate
+		 * all of this if it's not necessary.
+		 */
+		if ((critical_flags != 0)  &&  (set & critical_flags)) {
+			le = current_fixup(a, a->name);
+			if (le == NULL)
+				return (ARCHIVE_FATAL);
+			le->fixup |= TODO_FFLAGS;
+			le->fflags_set = set;
+			/* Store the mode if it's not already there. */
+			if ((le->fixup & TODO_MODE) == 0)
+				le->mode = mode;
+		} else {
+			r = set_fflags_platform(a, a->fd,
+			    a->name, mode, set, clear);
+			if (r != ARCHIVE_OK)
+				return (r);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+clear_nochange_fflags(struct archive_write_disk *a)
+{
+	mode_t		mode = archive_entry_mode(a->entry);
+	const int nochange_flags = 0
+#ifdef SF_IMMUTABLE
+	    | SF_IMMUTABLE
+#endif
+#ifdef UF_IMMUTABLE
+	    | UF_IMMUTABLE
+#endif
+#ifdef SF_APPEND
+	    | SF_APPEND
+#endif
+#ifdef UF_APPEND
+	    | UF_APPEND
+#endif
+#ifdef EXT2_APPEND_FL
+	    | EXT2_APPEND_FL
+#endif
+#ifdef EXT2_IMMUTABLE_FL
+	    | EXT2_IMMUTABLE_FL
+#endif
+	;
+
+	return (set_fflags_platform(a, a->fd, a->name, mode, 0,
+	    nochange_flags));
+}
+
+
+#if ( defined(HAVE_LCHFLAGS) || defined(HAVE_CHFLAGS) || defined(HAVE_FCHFLAGS) ) && defined(HAVE_STRUCT_STAT_ST_FLAGS)
+/*
+ * BSD reads flags using stat() and sets them with one of {f,l,}chflags()
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+    mode_t mode, unsigned long set, unsigned long clear)
+{
+	int r;
+	const int sf_mask = 0
+#ifdef SF_APPEND
+	    | SF_APPEND
+#endif
+#ifdef SF_ARCHIVED
+	    | SF_ARCHIVED
+#endif
+#ifdef SF_IMMUTABLE
+	    | SF_IMMUTABLE
+#endif
+#ifdef SF_NOUNLINK
+	    | SF_NOUNLINK
+#endif
+	;
+	(void)mode; /* UNUSED */
+
+	if (set == 0  && clear == 0)
+		return (ARCHIVE_OK);
+
+	/*
+	 * XXX Is the stat here really necessary?  Or can I just use
+	 * the 'set' flags directly?  In particular, I'm not sure
+	 * about the correct approach if we're overwriting an existing
+	 * file that already has flags on it. XXX
+	 */
+	if ((r = lazy_stat(a)) != ARCHIVE_OK)
+		return (r);
+
+	a->st.st_flags &= ~clear;
+	a->st.st_flags |= set;
+
+	/* Only super-user may change SF_* flags */
+
+	if (a->user_uid != 0)
+		a->st.st_flags &= ~sf_mask;
+
+#ifdef HAVE_FCHFLAGS
+	/* If platform has fchflags() and we were given an fd, use it. */
+	if (fd >= 0 && fchflags(fd, a->st.st_flags) == 0)
+		return (ARCHIVE_OK);
+#endif
+	/*
+	 * If we can't use the fd to set the flags, we'll use the
+	 * pathname to set flags.  We prefer lchflags() but will use
+	 * chflags() if we must.
+	 */
+#ifdef HAVE_LCHFLAGS
+	if (lchflags(name, a->st.st_flags) == 0)
+		return (ARCHIVE_OK);
+#elif defined(HAVE_CHFLAGS)
+	if (S_ISLNK(a->st.st_mode)) {
+		archive_set_error(&a->archive, errno,
+		    "Can't set file flags on symlink.");
+		return (ARCHIVE_WARN);
+	}
+	if (chflags(name, a->st.st_flags) == 0)
+		return (ARCHIVE_OK);
+#endif
+	archive_set_error(&a->archive, errno,
+	    "Failed to set file flags");
+	return (ARCHIVE_WARN);
+}
+
+#elif (defined(FS_IOC_GETFLAGS) && defined(FS_IOC_SETFLAGS) && \
+       defined(HAVE_WORKING_FS_IOC_GETFLAGS)) || \
+      (defined(EXT2_IOC_GETFLAGS) && defined(EXT2_IOC_SETFLAGS) && \
+       defined(HAVE_WORKING_EXT2_IOC_GETFLAGS))
+/*
+ * Linux uses ioctl() to read and write file flags.
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+    mode_t mode, unsigned long set, unsigned long clear)
+{
+	int		 ret;
+	int		 myfd = fd;
+	int newflags, oldflags;
+	/*
+	 * Linux has no define for the flags that are only settable by
+	 * the root user.  This code may seem a little complex, but
+	 * there seem to be some Linux systems that lack these
+	 * defines. (?)  The code below degrades reasonably gracefully
+	 * if sf_mask is incomplete.
+	 */
+	const int sf_mask = 0
+#if defined(FS_IMMUTABLE_FL)
+	    | FS_IMMUTABLE_FL
+#elif defined(EXT2_IMMUTABLE_FL)
+	    | EXT2_IMMUTABLE_FL
+#endif
+#if defined(FS_APPEND_FL)
+	    | FS_APPEND_FL
+#elif defined(EXT2_APPEND_FL)
+	    | EXT2_APPEND_FL
+#endif
+#if defined(FS_JOURNAL_DATA_FL)
+	    | FS_JOURNAL_DATA_FL
+#endif
+	;
+
+	if (set == 0 && clear == 0)
+		return (ARCHIVE_OK);
+	/* Only regular files and dirs can have flags. */
+	if (!S_ISREG(mode) && !S_ISDIR(mode))
+		return (ARCHIVE_OK);
+
+	/* If we weren't given an fd, open it ourselves. */
+	if (myfd < 0) {
+		myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC);
+		__archive_ensure_cloexec_flag(myfd);
+	}
+	if (myfd < 0)
+		return (ARCHIVE_OK);
+
+	/*
+	 * XXX As above, this would be way simpler if we didn't have
+	 * to read the current flags from disk. XXX
+	 */
+	ret = ARCHIVE_OK;
+
+	/* Read the current file flags. */
+	if (ioctl(myfd,
+#ifdef FS_IOC_GETFLAGS
+	    FS_IOC_GETFLAGS,
+#else
+	    EXT2_IOC_GETFLAGS,
+#endif
+	    &oldflags) < 0)
+		goto fail;
+
+	/* Try setting the flags as given. */
+	newflags = (oldflags & ~clear) | set;
+	if (ioctl(myfd,
+#ifdef FS_IOC_SETFLAGS
+	    FS_IOC_SETFLAGS,
+#else
+	    EXT2_IOC_SETFLAGS,
+#endif
+	    &newflags) >= 0)
+		goto cleanup;
+	if (errno != EPERM)
+		goto fail;
+
+	/* If we couldn't set all the flags, try again with a subset. */
+	newflags &= ~sf_mask;
+	oldflags &= sf_mask;
+	newflags |= oldflags;
+	if (ioctl(myfd,
+#ifdef FS_IOC_SETFLAGS
+	    FS_IOC_SETFLAGS,
+#else
+	    EXT2_IOC_SETFLAGS,
+#endif
+	    &newflags) >= 0)
+		goto cleanup;
+
+	/* We couldn't set the flags, so report the failure. */
+fail:
+	archive_set_error(&a->archive, errno,
+	    "Failed to set file flags");
+	ret = ARCHIVE_WARN;
+cleanup:
+	if (fd < 0)
+		close(myfd);
+	return (ret);
+}
+
+#else
+
+/*
+ * Of course, some systems have neither BSD chflags() nor Linux' flags
+ * support through ioctl().
+ */
+static int
+set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
+    mode_t mode, unsigned long set, unsigned long clear)
+{
+	(void)a; /* UNUSED */
+	(void)fd; /* UNUSED */
+	(void)name; /* UNUSED */
+	(void)mode; /* UNUSED */
+	(void)set; /* UNUSED */
+	(void)clear; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+#endif /* __linux */
+
+#ifndef HAVE_COPYFILE_H
+/* Default is to simply drop Mac extended metadata. */
+static int
+set_mac_metadata(struct archive_write_disk *a, const char *pathname,
+		 const void *metadata, size_t metadata_size)
+{
+	(void)a; /* UNUSED */
+	(void)pathname; /* UNUSED */
+	(void)metadata; /* UNUSED */
+	(void)metadata_size; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+static int
+fixup_appledouble(struct archive_write_disk *a, const char *pathname)
+{
+	(void)a; /* UNUSED */
+	(void)pathname; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+#else
+
+/*
+ * On Mac OS, we use copyfile() to unpack the metadata and
+ * apply it to the target file.
+ */
+
+#if defined(HAVE_SYS_XATTR_H)
+static int
+copy_xattrs(struct archive_write_disk *a, int tmpfd, int dffd)
+{
+	ssize_t xattr_size;
+	char *xattr_names = NULL, *xattr_val = NULL;
+	int ret = ARCHIVE_OK, xattr_i;
+
+	xattr_size = flistxattr(tmpfd, NULL, 0, 0);
+	if (xattr_size == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to read metadata(xattr)");
+		ret = ARCHIVE_WARN;
+		goto exit_xattr;
+	}
+	xattr_names = malloc(xattr_size);
+	if (xattr_names == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for metadata(xattr)");
+		ret = ARCHIVE_FATAL;
+		goto exit_xattr;
+	}
+	xattr_size = flistxattr(tmpfd, xattr_names, xattr_size, 0);
+	if (xattr_size == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to read metadata(xattr)");
+		ret = ARCHIVE_WARN;
+		goto exit_xattr;
+	}
+	for (xattr_i = 0; xattr_i < xattr_size;
+	    xattr_i += strlen(xattr_names + xattr_i) + 1) {
+		char *xattr_val_saved;
+		ssize_t s;
+		int f;
+
+		s = fgetxattr(tmpfd, xattr_names + xattr_i, NULL, 0, 0, 0);
+		if (s == -1) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get metadata(xattr)");
+			ret = ARCHIVE_WARN;
+			goto exit_xattr;
+		}
+		xattr_val_saved = xattr_val;
+		xattr_val = realloc(xattr_val, s);
+		if (xattr_val == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Failed to get metadata(xattr)");
+			ret = ARCHIVE_WARN;
+			free(xattr_val_saved);
+			goto exit_xattr;
+		}
+		s = fgetxattr(tmpfd, xattr_names + xattr_i, xattr_val, s, 0, 0);
+		if (s == -1) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get metadata(xattr)");
+			ret = ARCHIVE_WARN;
+			goto exit_xattr;
+		}
+		f = fsetxattr(dffd, xattr_names + xattr_i, xattr_val, s, 0, 0);
+		if (f == -1) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to get metadata(xattr)");
+			ret = ARCHIVE_WARN;
+			goto exit_xattr;
+		}
+	}
+exit_xattr:
+	free(xattr_names);
+	free(xattr_val);
+	return (ret);
+}
+#endif
+
+static int
+copy_acls(struct archive_write_disk *a, int tmpfd, int dffd)
+{
+#ifndef HAVE_SYS_ACL_H
+	return 0;
+#else
+	acl_t acl, dfacl = NULL;
+	int acl_r, ret = ARCHIVE_OK;
+
+	acl = acl_get_fd(tmpfd);
+	if (acl == NULL) {
+		if (errno == ENOENT)
+			/* There are not any ACLs. */
+			return (ret);
+		archive_set_error(&a->archive, errno,
+		    "Failed to get metadata(acl)");
+		ret = ARCHIVE_WARN;
+		goto exit_acl;
+	}
+	dfacl = acl_dup(acl);
+	acl_r = acl_set_fd(dffd, dfacl);
+	if (acl_r == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to get metadata(acl)");
+		ret = ARCHIVE_WARN;
+		goto exit_acl;
+	}
+exit_acl:
+	if (acl)
+		acl_free(acl);
+	if (dfacl)
+		acl_free(dfacl);
+	return (ret);
+#endif
+}
+
+static int
+create_tempdatafork(struct archive_write_disk *a, const char *pathname)
+{
+	struct archive_string tmpdatafork;
+	int tmpfd;
+
+	archive_string_init(&tmpdatafork);
+	archive_strcpy(&tmpdatafork, "tar.md.XXXXXX");
+	tmpfd = mkstemp(tmpdatafork.s);
+	if (tmpfd < 0) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to mkstemp");
+		archive_string_free(&tmpdatafork);
+		return (-1);
+	}
+	if (copyfile(pathname, tmpdatafork.s, 0,
+	    COPYFILE_UNPACK | COPYFILE_NOFOLLOW
+	    | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to restore metadata");
+		close(tmpfd);
+		tmpfd = -1;
+	}
+	unlink(tmpdatafork.s);
+	archive_string_free(&tmpdatafork);
+	return (tmpfd);
+}
+
+static int
+copy_metadata(struct archive_write_disk *a, const char *metadata,
+    const char *datafork, int datafork_compressed)
+{
+	int ret = ARCHIVE_OK;
+
+	if (datafork_compressed) {
+		int dffd, tmpfd;
+
+		tmpfd = create_tempdatafork(a, metadata);
+		if (tmpfd == -1)
+			return (ARCHIVE_WARN);
+
+		/*
+		 * Do not open the data fork compressed by HFS+ compression
+		 * with at least a writing mode(O_RDWR or O_WRONLY). it
+		 * makes the data fork uncompressed.
+		 */
+		dffd = open(datafork, 0);
+		if (dffd == -1) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to open the data fork for metadata");
+			close(tmpfd);
+			return (ARCHIVE_WARN);
+		}
+
+#if defined(HAVE_SYS_XATTR_H)
+		ret = copy_xattrs(a, tmpfd, dffd);
+		if (ret == ARCHIVE_OK)
+#endif
+			ret = copy_acls(a, tmpfd, dffd);
+		close(tmpfd);
+		close(dffd);
+	} else {
+		if (copyfile(metadata, datafork, 0,
+		    COPYFILE_UNPACK | COPYFILE_NOFOLLOW
+		    | COPYFILE_ACL | COPYFILE_XATTR) < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Failed to restore metadata");
+			ret = ARCHIVE_WARN;
+		}
+	}
+	return (ret);
+}
+
+static int
+set_mac_metadata(struct archive_write_disk *a, const char *pathname,
+		 const void *metadata, size_t metadata_size)
+{
+	struct archive_string tmp;
+	ssize_t written;
+	int fd;
+	int ret = ARCHIVE_OK;
+
+	/* This would be simpler if copyfile() could just accept the
+	 * metadata as a block of memory; then we could sidestep this
+	 * silly dance of writing the data to disk just so that
+	 * copyfile() can read it back in again. */
+	archive_string_init(&tmp);
+	archive_strcpy(&tmp, pathname);
+	archive_strcat(&tmp, ".XXXXXX");
+	fd = mkstemp(tmp.s);
+
+	if (fd < 0) {
+		archive_set_error(&a->archive, errno,
+				  "Failed to restore metadata");
+		archive_string_free(&tmp);
+		return (ARCHIVE_WARN);
+	}
+	written = write(fd, metadata, metadata_size);
+	close(fd);
+	if ((size_t)written != metadata_size) {
+		archive_set_error(&a->archive, errno,
+				  "Failed to restore metadata");
+		ret = ARCHIVE_WARN;
+	} else {
+		int compressed;
+
+#if defined(UF_COMPRESSED)
+		if ((a->todo & TODO_HFS_COMPRESSION) != 0 &&
+		    (ret = lazy_stat(a)) == ARCHIVE_OK)
+			compressed = a->st.st_flags & UF_COMPRESSED;
+		else
+#endif
+			compressed = 0;
+		ret = copy_metadata(a, tmp.s, pathname, compressed);
+	}
+	unlink(tmp.s);
+	archive_string_free(&tmp);
+	return (ret);
+}
+
+static int
+fixup_appledouble(struct archive_write_disk *a, const char *pathname)
+{
+	char buff[8];
+	struct stat st;
+	const char *p;
+	struct archive_string datafork;
+	int fd = -1, ret = ARCHIVE_OK;
+
+	archive_string_init(&datafork);
+	/* Check if the current file name is a type of the resource
+	 * fork file. */
+	p = strrchr(pathname, '/');
+	if (p == NULL)
+		p = pathname;
+	else
+		p++;
+	if (p[0] != '.' || p[1] != '_')
+		goto skip_appledouble;
+
+	/*
+	 * Check if the data fork file exists.
+	 *
+	 * TODO: Check if this write disk object has handled it.
+	 */
+	archive_strncpy(&datafork, pathname, p - pathname);
+	archive_strcat(&datafork, p + 2);
+	if (lstat(datafork.s, &st) == -1 ||
+	    (st.st_mode & AE_IFMT) != AE_IFREG)
+		goto skip_appledouble;
+
+	/*
+	 * Check if the file is in the AppleDouble form.
+	 */
+	fd = open(pathname, O_RDONLY | O_BINARY | O_CLOEXEC);
+	__archive_ensure_cloexec_flag(fd);
+	if (fd == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to open a restoring file");
+		ret = ARCHIVE_WARN;
+		goto skip_appledouble;
+	}
+	if (read(fd, buff, 8) == -1) {
+		archive_set_error(&a->archive, errno,
+		    "Failed to read a restoring file");
+		close(fd);
+		ret = ARCHIVE_WARN;
+		goto skip_appledouble;
+	}
+	close(fd);
+	/* Check AppleDouble Magic Code. */
+	if (archive_be32dec(buff) != 0x00051607)
+		goto skip_appledouble;
+	/* Check AppleDouble Version. */
+	if (archive_be32dec(buff+4) != 0x00020000)
+		goto skip_appledouble;
+
+	ret = copy_metadata(a, pathname, datafork.s,
+#if defined(UF_COMPRESSED)
+	    st.st_flags & UF_COMPRESSED);
+#else
+	    0);
+#endif
+	if (ret == ARCHIVE_OK) {
+		unlink(pathname);
+		ret = ARCHIVE_EOF;
+	}
+skip_appledouble:
+	archive_string_free(&datafork);
+	return (ret);
+}
+#endif
+
+#if ARCHIVE_XATTR_LINUX || ARCHIVE_XATTR_DARWIN || ARCHIVE_XATTR_AIX
+/*
+ * Restore extended attributes -  Linux, Darwin and AIX implementations:
+ * AIX' ea interface is syntaxwise identical to the Linux xattr interface.
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+	struct archive_entry *entry = a->entry;
+	struct archive_string errlist;
+	int ret = ARCHIVE_OK;
+	int i = archive_entry_xattr_reset(entry);
+	short fail = 0;
+
+	archive_string_init(&errlist);
+
+	while (i--) {
+		const char *name;
+		const void *value;
+		size_t size;
+		int e;
+
+		archive_entry_xattr_next(entry, &name, &value, &size);
+
+		if (name == NULL)
+			continue;
+#if ARCHIVE_XATTR_LINUX
+		/* Linux: quietly skip POSIX.1e ACL extended attributes */
+		if (strncmp(name, "system.", 7) == 0 &&
+		   (strcmp(name + 7, "posix_acl_access") == 0 ||
+		    strcmp(name + 7, "posix_acl_default") == 0))
+			continue;
+		if (strncmp(name, "trusted.SGI_", 12) == 0 &&
+		   (strcmp(name + 12, "ACL_DEFAULT") == 0 ||
+		    strcmp(name + 12, "ACL_FILE") == 0))
+			continue;
+
+		/* Linux: xfsroot namespace is obsolete and unsupported */
+		if (strncmp(name, "xfsroot.", 8) == 0) {
+			fail = 1;
+			archive_strcat(&errlist, name);
+			archive_strappend_char(&errlist, ' ');
+			continue;
+		}
+#endif
+
+		if (a->fd >= 0) {
+#if ARCHIVE_XATTR_LINUX
+			e = fsetxattr(a->fd, name, value, size, 0);
+#elif ARCHIVE_XATTR_DARWIN
+			e = fsetxattr(a->fd, name, value, size, 0, 0);
+#elif ARCHIVE_XATTR_AIX
+			e = fsetea(a->fd, name, value, size, 0);
+#endif
+		} else {
+#if ARCHIVE_XATTR_LINUX
+			e = lsetxattr(archive_entry_pathname(entry),
+			    name, value, size, 0);
+#elif ARCHIVE_XATTR_DARWIN
+			e = setxattr(archive_entry_pathname(entry),
+			    name, value, size, 0, XATTR_NOFOLLOW);
+#elif ARCHIVE_XATTR_AIX
+			e = lsetea(archive_entry_pathname(entry),
+			    name, value, size, 0);
+#endif
+		}
+		if (e == -1) {
+			ret = ARCHIVE_WARN;
+			archive_strcat(&errlist, name);
+			archive_strappend_char(&errlist, ' ');
+			if (errno != ENOTSUP && errno != ENOSYS)
+				fail = 1;
+		}
+	}
+
+	if (ret == ARCHIVE_WARN) {
+		if (fail && errlist.length > 0) {
+			errlist.length--;
+			errlist.s[errlist.length] = '\0';
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Cannot restore extended attributes: %s",
+			    errlist.s);
+		} else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Cannot restore extended "
+			    "attributes on this file system.");
+	}
+
+	archive_string_free(&errlist);
+	return (ret);
+}
+#elif ARCHIVE_XATTR_FREEBSD
+/*
+ * Restore extended attributes -  FreeBSD implementation
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+	struct archive_entry *entry = a->entry;
+	struct archive_string errlist;
+	int ret = ARCHIVE_OK;
+	int i = archive_entry_xattr_reset(entry);
+	short fail = 0;
+
+	archive_string_init(&errlist);
+
+	while (i--) {
+		const char *name;
+		const void *value;
+		size_t size;
+		archive_entry_xattr_next(entry, &name, &value, &size);
+		if (name != NULL) {
+			int e;
+			int namespace;
+
+			if (strncmp(name, "user.", 5) == 0) {
+				/* "user." attributes go to user namespace */
+				name += 5;
+				namespace = EXTATTR_NAMESPACE_USER;
+			} else {
+				/* Other namespaces are unsupported */
+				archive_strcat(&errlist, name);
+				archive_strappend_char(&errlist, ' ');
+				fail = 1;
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+
+			if (a->fd >= 0) {
+				e = extattr_set_fd(a->fd, namespace, name,
+				    value, size);
+			} else {
+				e = extattr_set_link(
+				    archive_entry_pathname(entry), namespace,
+				    name, value, size);
+			}
+			if (e != (int)size) {
+				archive_strcat(&errlist, name);
+				archive_strappend_char(&errlist, ' ');
+				ret = ARCHIVE_WARN;
+				if (errno != ENOTSUP && errno != ENOSYS)
+					fail = 1;
+			}
+		}
+	}
+
+	if (ret == ARCHIVE_WARN) {
+		if (fail && errlist.length > 0) {
+			errlist.length--;
+			errlist.s[errlist.length] = '\0';
+
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Cannot restore extended attributes: %s",
+			    errlist.s);
+		} else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Cannot restore extended "
+			    "attributes on this file system.");
+	}
+
+	archive_string_free(&errlist);
+	return (ret);
+}
+#else
+/*
+ * Restore extended attributes - stub implementation for unsupported systems
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+	static int warning_done = 0;
+
+	/* If there aren't any extended attributes, then it's okay not
+	 * to extract them, otherwise, issue a single warning. */
+	if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) {
+		warning_done = 1;
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Cannot restore extended attributes on this system");
+		return (ARCHIVE_WARN);
+	}
+	/* Warning was already emitted; suppress further warnings. */
+	return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * Test if file on disk is older than entry.
+ */
+static int
+older(struct stat *st, struct archive_entry *entry)
+{
+	/* First, test the seconds and return if we have a definite answer. */
+	/* Definitely older. */
+	if (to_int64_time(st->st_mtime) < to_int64_time(archive_entry_mtime(entry)))
+		return (1);
+	/* Definitely younger. */
+	if (to_int64_time(st->st_mtime) > to_int64_time(archive_entry_mtime(entry)))
+		return (0);
+	/* If this platform supports fractional seconds, try those. */
+#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
+	/* Definitely older. */
+	if (st->st_mtimespec.tv_nsec < archive_entry_mtime_nsec(entry))
+		return (1);
+#elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+	/* Definitely older. */
+	if (st->st_mtim.tv_nsec < archive_entry_mtime_nsec(entry))
+		return (1);
+#elif HAVE_STRUCT_STAT_ST_MTIME_N
+	/* older. */
+	if (st->st_mtime_n < archive_entry_mtime_nsec(entry))
+		return (1);
+#elif HAVE_STRUCT_STAT_ST_UMTIME
+	/* older. */
+	if (st->st_umtime * 1000 < archive_entry_mtime_nsec(entry))
+		return (1);
+#elif HAVE_STRUCT_STAT_ST_MTIME_USEC
+	/* older. */
+	if (st->st_mtime_usec * 1000 < archive_entry_mtime_nsec(entry))
+		return (1);
+#else
+	/* This system doesn't have high-res timestamps. */
+#endif
+	/* Same age or newer, so not older. */
+	return (0);
+}
+
+#ifndef ARCHIVE_ACL_SUPPORT
+int
+archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode)
+{
+	(void)a; /* UNUSED */
+	(void)fd; /* UNUSED */
+	(void)name; /* UNUSED */
+	(void)abstract_acl; /* UNUSED */
+	(void)mode; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+#endif
+
+#endif /* !_WIN32 || __CYGWIN__ */
+
diff --git a/c/archive_write_disk_private.h b/c/archive_write_disk_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_write_disk_private.h
@@ -0,0 +1,45 @@
+/*-
+ * 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
+ *    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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_write_disk_private.h 201086 2009-12-28 02:17:53Z kientzle $
+ */
+
+#ifndef ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
+#define ARCHIVE_WRITE_DISK_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include "archive_platform_acl.h"
+#include "archive_acl_private.h"
+#include "archive_entry.h"
+
+struct archive_write_disk;
+
+int archive_write_disk_set_acls(struct archive *, int, const char *,
+    struct archive_acl *, __LA_MODE_T);
+
+#endif
diff --git a/c/archive_write_disk_set_standard_lookup.c b/c/archive_write_disk_set_standard_lookup.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_disk_set_standard_lookup.c
@@ -0,0 +1,263 @@
+/*-
+ * 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: head/lib/libarchive/archive_write_disk_set_standard_lookup.c 201083 2009-12-28 02:09:57Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.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 bucket {
+	char	*name;
+	int	 hash;
+	id_t	 id;
+};
+
+static const size_t cache_size = 127;
+static unsigned int	hash(const char *);
+static int64_t	lookup_gid(void *, const char *uname, int64_t);
+static int64_t	lookup_uid(void *, const char *uname, int64_t);
+static void	cleanup(void *);
+
+/*
+ * Installs functions that use getpwnam()/getgrnam()---along with
+ * a simple cache to accelerate such lookups---into the archive_write_disk
+ * object.  This is in a separate file because getpwnam()/getgrnam()
+ * can pull in a LOT of library code (including NIS/LDAP functions, which
+ * pull in DNS resolvers, etc).  This can easily top 500kB, which makes
+ * it inappropriate for some space-constrained applications.
+ *
+ * Applications that are size-sensitive may want to just use the
+ * real default functions (defined in archive_write_disk.c) that just
+ * use the uid/gid without the lookup.  Or define your own custom functions
+ * if you prefer.
+ *
+ * TODO: Replace these hash tables with simpler move-to-front LRU
+ * lists with a bounded size (128 items?).  The hash is a bit faster,
+ * but has a bad pathology in which it thrashes a single bucket.  Even
+ * walking a list of 128 items is a lot faster than calling
+ * getpwnam()!
+ */
+int
+archive_write_disk_set_standard_lookup(struct archive *a)
+{
+	struct bucket *ucache = calloc(cache_size, sizeof(struct bucket));
+	struct bucket *gcache = calloc(cache_size, sizeof(struct bucket));
+	if (ucache == NULL || gcache == NULL) {
+		free(ucache);
+		free(gcache);
+		return (ARCHIVE_FATAL);
+	}
+	archive_write_disk_set_group_lookup(a, gcache, lookup_gid, cleanup);
+	archive_write_disk_set_user_lookup(a, ucache, lookup_uid, cleanup);
+	return (ARCHIVE_OK);
+}
+
+static int64_t
+lookup_gid(void *private_data, const char *gname, int64_t gid)
+{
+	int h;
+	struct bucket *b;
+	struct bucket *gcache = (struct bucket *)private_data;
+
+	/* If no gname, just use the gid provided. */
+	if (gname == NULL || *gname == '\0')
+		return (gid);
+
+	/* Try to find gname in the cache. */
+	h = hash(gname);
+	b = &gcache[h % cache_size ];
+	if (b->name != NULL && b->hash == h && strcmp(gname, b->name) == 0)
+		return ((gid_t)b->id);
+
+	/* Free the cache slot for a new entry. */
+	free(b->name);
+	b->name = strdup(gname);
+	/* Note: If strdup fails, that's okay; we just won't cache. */
+	b->hash = h;
+#if HAVE_GRP_H
+#  if HAVE_GETGRNAM_R
+	{
+		char _buffer[128];
+		size_t bufsize = 128;
+		char *buffer = _buffer;
+		char *allocated = NULL;
+		struct group	grent, *result;
+		int r;
+
+		for (;;) {
+			result = &grent; /* Old getgrnam_r ignores last arg. */
+			r = getgrnam_r(gname, &grent, buffer, bufsize, &result);
+			if (r == 0)
+				break;
+			if (r != ERANGE)
+				break;
+			bufsize *= 2;
+			free(allocated);
+			allocated = malloc(bufsize);
+			if (allocated == NULL)
+				break;
+			buffer = allocated;
+		}
+		if (result != NULL)
+			gid = result->gr_gid;
+		free(allocated);
+	}
+#  else /* HAVE_GETGRNAM_R */
+	{
+		struct group *result;
+
+		result = getgrnam(gname);
+		if (result != NULL)
+			gid = result->gr_gid;
+	}
+#  endif /* HAVE_GETGRNAM_R */
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+	/* TODO: do a gname->gid lookup for Windows. */
+#else
+	#error No way to perform gid lookups on this platform
+#endif
+	b->id = (gid_t)gid;
+
+	return (gid);
+}
+
+static int64_t
+lookup_uid(void *private_data, const char *uname, int64_t uid)
+{
+	int h;
+	struct bucket *b;
+	struct bucket *ucache = (struct bucket *)private_data;
+
+	/* If no uname, just use the uid provided. */
+	if (uname == NULL || *uname == '\0')
+		return (uid);
+
+	/* Try to find uname in the cache. */
+	h = hash(uname);
+	b = &ucache[h % cache_size ];
+	if (b->name != NULL && b->hash == h && strcmp(uname, b->name) == 0)
+		return ((uid_t)b->id);
+
+	/* Free the cache slot for a new entry. */
+	free(b->name);
+	b->name = strdup(uname);
+	/* Note: If strdup fails, that's okay; we just won't cache. */
+	b->hash = h;
+#if HAVE_PWD_H
+#  if HAVE_GETPWNAM_R
+	{
+		char _buffer[128];
+		size_t bufsize = 128;
+		char *buffer = _buffer;
+		char *allocated = NULL;
+		struct passwd	pwent, *result;
+		int r;
+
+		for (;;) {
+			result = &pwent; /* Old getpwnam_r ignores last arg. */
+			r = getpwnam_r(uname, &pwent, buffer, bufsize, &result);
+			if (r == 0)
+				break;
+			if (r != ERANGE)
+				break;
+			bufsize *= 2;
+			free(allocated);
+			allocated = malloc(bufsize);
+			if (allocated == NULL)
+				break;
+			buffer = allocated;
+		}
+		if (result != NULL)
+			uid = result->pw_uid;
+		free(allocated);
+	}
+#  else /* HAVE_GETPWNAM_R */
+	{
+		struct passwd *result;
+
+		result = getpwnam(uname);
+		if (result != NULL)
+			uid = result->pw_uid;
+	}
+#endif	/* HAVE_GETPWNAM_R */
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+	/* TODO: do a uname->uid lookup for Windows. */
+#else
+	#error No way to look up uids on this platform
+#endif
+	b->id = (uid_t)uid;
+
+	return (uid);
+}
+
+static void
+cleanup(void *private)
+{
+	size_t i;
+	struct bucket *cache = (struct bucket *)private;
+
+	for (i = 0; i < cache_size; i++)
+		free(cache[i].name);
+	free(cache);
+}
+
+
+static unsigned int
+hash(const char *p)
+{
+	/* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
+	   as used by ELF for hashing function names. */
+	unsigned g, h = 0;
+	while (*p != '\0') {
+		h = (h << 4) + *p++;
+		if ((g = h & 0xF0000000) != 0) {
+			h ^= g >> 24;
+			h &= 0x0FFFFFFF;
+		}
+	}
+	return h;
+}
diff --git a/c/archive_write_disk_windows.c b/c/archive_write_disk_windows.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_disk_windows.c
@@ -0,0 +1,2834 @@
+/*-
+ * Copyright (c) 2003-2010 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD$");
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <winioctl.h>
+
+/* TODO: Support Mac OS 'quarantine' feature.  This is really just a
+ * standard tag to mark files that have been downloaded as "tainted".
+ * On Mac OS, we should mark the extracted files as tainted if the
+ * archive being read was tainted.  Windows has a similar feature; we
+ * should investigate ways to support this generically. */
+
+#include "archive.h"
+#include "archive_acl_private.h"
+#include "archive_string.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef IO_REPARSE_TAG_SYMLINK
+/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */
+#define	IO_REPARSE_TAG_SYMLINK 0xA000000CL
+#endif
+
+static BOOL SetFilePointerEx_perso(HANDLE hFile,
+                             LARGE_INTEGER liDistanceToMove,
+                             PLARGE_INTEGER lpNewFilePointer,
+                             DWORD dwMoveMethod)
+{
+	LARGE_INTEGER li;
+	li.QuadPart = liDistanceToMove.QuadPart;
+	li.LowPart = SetFilePointer(
+	    hFile, li.LowPart, &li.HighPart, dwMoveMethod);
+	if(lpNewFilePointer) {
+		lpNewFilePointer->QuadPart = li.QuadPart;
+	}
+	return li.LowPart != (DWORD)-1 || GetLastError() == NO_ERROR;
+}
+
+struct fixup_entry {
+	struct fixup_entry	*next;
+	struct archive_acl	 acl;
+	mode_t			 mode;
+	int64_t			 atime;
+	int64_t                  birthtime;
+	int64_t			 mtime;
+	int64_t			 ctime;
+	unsigned long		 atime_nanos;
+	unsigned long            birthtime_nanos;
+	unsigned long		 mtime_nanos;
+	unsigned long		 ctime_nanos;
+	unsigned long		 fflags_set;
+	int			 fixup; /* bitmask of what needs fixing */
+	wchar_t			*name;
+};
+
+/*
+ * We use a bitmask to track which operations remain to be done for
+ * this file.  In particular, this helps us avoid unnecessary
+ * operations when it's possible to take care of one step as a
+ * side-effect of another.  For example, mkdir() can specify the mode
+ * for the newly-created object but symlink() cannot.  This means we
+ * can skip chmod() if mkdir() succeeded, but we must explicitly
+ * chmod() if we're trying to create a directory that already exists
+ * (mkdir() failed) or if we're restoring a symlink.  Similarly, we
+ * need to verify UID/GID before trying to restore SUID/SGID bits;
+ * that verification can occur explicitly through a stat() call or
+ * implicitly because of a successful chown() call.
+ */
+#define	TODO_MODE_FORCE		0x40000000
+#define	TODO_MODE_BASE		0x20000000
+#define	TODO_SUID		0x10000000
+#define	TODO_SUID_CHECK		0x08000000
+#define	TODO_SGID		0x04000000
+#define	TODO_SGID_CHECK		0x02000000
+#define	TODO_MODE		(TODO_MODE_BASE|TODO_SUID|TODO_SGID)
+#define	TODO_TIMES		ARCHIVE_EXTRACT_TIME
+#define	TODO_OWNER		ARCHIVE_EXTRACT_OWNER
+#define	TODO_FFLAGS		ARCHIVE_EXTRACT_FFLAGS
+#define	TODO_ACLS		ARCHIVE_EXTRACT_ACL
+#define	TODO_XATTR		ARCHIVE_EXTRACT_XATTR
+#define	TODO_MAC_METADATA	ARCHIVE_EXTRACT_MAC_METADATA
+
+struct archive_write_disk {
+	struct archive	archive;
+
+	mode_t			 user_umask;
+	struct fixup_entry	*fixup_list;
+	struct fixup_entry	*current_fixup;
+	int64_t			 user_uid;
+	int			 skip_file_set;
+	int64_t			 skip_file_dev;
+	int64_t			 skip_file_ino;
+	time_t			 start_time;
+
+	int64_t (*lookup_gid)(void *private, const char *gname, int64_t gid);
+	void  (*cleanup_gid)(void *private);
+	void			*lookup_gid_data;
+	int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid);
+	void  (*cleanup_uid)(void *private);
+	void			*lookup_uid_data;
+
+	/*
+	 * Full path of last file to satisfy symlink checks.
+	 */
+	struct archive_wstring	path_safe;
+
+	/*
+	 * Cached stat data from disk for the current entry.
+	 * If this is valid, pst points to st.  Otherwise,
+	 * pst is null.
+	 */
+	BY_HANDLE_FILE_INFORMATION		 st;
+	BY_HANDLE_FILE_INFORMATION		*pst;
+
+	/* Information about the object being restored right now. */
+	struct archive_entry	*entry; /* Entry being extracted. */
+	wchar_t			*name; /* Name of entry, possibly edited. */
+	struct archive_wstring	 _name_data; /* backing store for 'name' */
+	wchar_t			*tmpname; /* Temporary name */
+	struct archive_wstring	_tmpname_data; /* backing store for 'tmpname' */
+	/* Tasks remaining for this object. */
+	int			 todo;
+	/* Tasks deferred until end-of-archive. */
+	int			 deferred;
+	/* Options requested by the client. */
+	int			 flags;
+	/* Handle for the file we're restoring. */
+	HANDLE		 fh;
+	/* Current offset for writing data to the file. */
+	int64_t			 offset;
+	/* Last offset actually written to disk. */
+	int64_t			 fd_offset;
+	/* Total bytes actually written to files. */
+	int64_t			 total_bytes_written;
+	/* Maximum size of file, -1 if unknown. */
+	int64_t			 filesize;
+	/* Dir we were in before this restore; only for deep paths. */
+	int			 restore_pwd;
+	/* Mode we should use for this entry; affected by _PERM and umask. */
+	mode_t			 mode;
+	/* UID/GID to use in restoring this entry. */
+	int64_t			 uid;
+	int64_t			 gid;
+};
+
+/*
+ * Default mode for dirs created automatically (will be modified by umask).
+ * Note that POSIX specifies 0777 for implicitly-created dirs, "modified
+ * by the process' file creation mask."
+ */
+#define	DEFAULT_DIR_MODE 0777
+/*
+ * Dir modes are restored in two steps:  During the extraction, the permissions
+ * in the archive are modified to match the following limits.  During
+ * the post-extract fixup pass, the permissions from the archive are
+ * applied.
+ */
+#define	MINIMUM_DIR_MODE 0700
+#define	MAXIMUM_DIR_MODE 0775
+
+static int	disk_unlink(const wchar_t *);
+static int	disk_rmdir(const wchar_t *);
+static int	check_symlinks(struct archive_write_disk *);
+static int	create_filesystem_object(struct archive_write_disk *);
+static struct fixup_entry *current_fixup(struct archive_write_disk *,
+		    const wchar_t *pathname);
+static int	cleanup_pathname(struct archive_write_disk *);
+static int	create_dir(struct archive_write_disk *, wchar_t *);
+static int	create_parent_dir(struct archive_write_disk *, wchar_t *);
+static int	la_chmod(const wchar_t *, mode_t);
+static int	la_mktemp(struct archive_write_disk *);
+static int	older(BY_HANDLE_FILE_INFORMATION *, struct archive_entry *);
+static int	permissive_name_w(struct archive_write_disk *);
+static int	restore_entry(struct archive_write_disk *);
+static int	set_acls(struct archive_write_disk *, HANDLE h,
+		    const wchar_t *, struct archive_acl *);
+static int	set_xattrs(struct archive_write_disk *);
+static int	clear_nochange_fflags(struct archive_write_disk *);
+static int	set_fflags(struct archive_write_disk *);
+static int	set_fflags_platform(const wchar_t *, unsigned long,
+		    unsigned long);
+static int	set_ownership(struct archive_write_disk *);
+static int	set_mode(struct archive_write_disk *, int mode);
+static int	set_times(struct archive_write_disk *, HANDLE, int,
+		    const wchar_t *, time_t, long, time_t, long, time_t,
+		    long, time_t, long);
+static int	set_times_from_entry(struct archive_write_disk *);
+static struct fixup_entry *sort_dir_list(struct fixup_entry *p);
+static ssize_t	write_data_block(struct archive_write_disk *,
+		    const char *, size_t);
+
+static struct archive_vtable *archive_write_disk_vtable(void);
+
+static int	_archive_write_disk_close(struct archive *);
+static int	_archive_write_disk_free(struct archive *);
+static int	_archive_write_disk_header(struct archive *,
+		    struct archive_entry *);
+static int64_t	_archive_write_disk_filter_bytes(struct archive *, int);
+static int	_archive_write_disk_finish_entry(struct archive *);
+static ssize_t	_archive_write_disk_data(struct archive *, const void *,
+		    size_t);
+static ssize_t	_archive_write_disk_data_block(struct archive *, const void *,
+		    size_t, int64_t);
+
+#define bhfi_dev(bhfi)	((bhfi)->dwVolumeSerialNumber)
+/* Treat FileIndex as i-node. We should remove a sequence number
+ * which is high-16-bits of nFileIndexHigh. */
+#define bhfi_ino(bhfi)	\
+	((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \
+    + (bhfi)->nFileIndexLow)
+#define bhfi_size(bhfi)	\
+    ((((int64_t)(bhfi)->nFileSizeHigh) << 32) + (bhfi)->nFileSizeLow)
+
+static int
+file_information(struct archive_write_disk *a, wchar_t *path,
+    BY_HANDLE_FILE_INFORMATION *st, mode_t *mode, int sim_lstat)
+{
+	HANDLE h;
+	int r;
+	DWORD flag = FILE_FLAG_BACKUP_SEMANTICS;
+	WIN32_FIND_DATAW	findData;
+
+	if (sim_lstat || mode != NULL) {
+		h = FindFirstFileW(path, &findData);
+		if (h == INVALID_HANDLE_VALUE &&
+		    GetLastError() == ERROR_INVALID_NAME) {
+			wchar_t *full;
+			full = __la_win_permissive_name_w(path);
+			h = FindFirstFileW(full, &findData);
+			free(full);
+		}
+		if (h == INVALID_HANDLE_VALUE) {
+			la_dosmaperr(GetLastError());
+			return (-1);
+		}
+		FindClose(h);
+	}
+
+	/* Is symlink file ? */
+	if (sim_lstat && 
+	    ((findData.dwFileAttributes
+		        & FILE_ATTRIBUTE_REPARSE_POINT) &&
+		(findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)))
+		flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+
+	h = CreateFileW(a->name, 0, 0, NULL,
+	    OPEN_EXISTING, flag, NULL);
+	if (h == INVALID_HANDLE_VALUE &&
+	    GetLastError() == ERROR_INVALID_NAME) {
+		wchar_t *full;
+		full = __la_win_permissive_name_w(path);
+		h = CreateFileW(full, 0, 0, NULL,
+		    OPEN_EXISTING, flag, NULL);
+		free(full);
+	}
+	if (h == INVALID_HANDLE_VALUE) {
+		la_dosmaperr(GetLastError());
+		return (-1);
+	}
+	r = GetFileInformationByHandle(h, st);
+	CloseHandle(h);
+	if (r == 0) {
+		la_dosmaperr(GetLastError());
+		return (-1);
+	}
+
+	if (mode == NULL)
+		return (0);
+
+	*mode = S_IRUSR | S_IRGRP | S_IROTH;
+	if ((st->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+		*mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+	if ((st->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+	    findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
+		*mode |= S_IFLNK;
+	else if (st->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+		*mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+	else {
+		const wchar_t *p;
+
+		*mode |= S_IFREG;
+		p = wcsrchr(path, L'.');
+		if (p != NULL && wcslen(p) == 4) {
+			switch (p[1]) {
+			case L'B': case L'b':
+				if ((p[2] == L'A' || p[2] == L'a' ) &&
+				    (p[3] == L'T' || p[3] == L't' ))
+					*mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+				break;
+			case L'C': case L'c':
+				if (((p[2] == L'M' || p[2] == L'm' ) &&
+				    (p[3] == L'D' || p[3] == L'd' )))
+					*mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+				break;
+			case L'E': case L'e':
+				if ((p[2] == L'X' || p[2] == L'x' ) &&
+				    (p[3] == L'E' || p[3] == L'e' ))
+					*mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+	return (0);
+}
+
+/* 
+ * Note: The path, for example, "aa/a/../b../c" will be converted to "aa/c"
+ * by GetFullPathNameW() W32 API, which __la_win_permissive_name_w uses.
+ * It means we cannot handle multiple dirs in one archive_entry.
+ * So we have to make the full-pathname in another way, which does not
+ * break "../" path string.
+ */
+static int
+permissive_name_w(struct archive_write_disk *a)
+{
+	wchar_t *wn, *wnp;
+	wchar_t *ws, *wsp;
+	DWORD l;
+
+	wnp = a->name;
+	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+	    wnp[2] == L'?' && wnp[3] == L'\\')
+		/* We have already a permissive name. */
+		return (0);
+
+	if (wnp[0] == L'\\' && wnp[1] == L'\\' &&
+		wnp[2] == L'.' && wnp[3] == L'\\') {
+		/* This is a device name */
+		if (((wnp[4] >= L'a' && wnp[4] <= L'z') ||
+		     (wnp[4] >= L'A' && wnp[4] <= L'Z')) &&
+			 wnp[5] == L':' && wnp[6] == L'\\') {
+			wnp[2] = L'?';/* Not device name. */
+			return (0);
+		}
+	}
+
+	/*
+	 * A full-pathname starting with a drive name like "C:\abc".
+	 */
+	if (((wnp[0] >= L'a' && wnp[0] <= L'z') ||
+	     (wnp[0] >= L'A' && wnp[0] <= L'Z')) &&
+		 wnp[1] == L':' && wnp[2] == L'\\') {
+		wn = _wcsdup(wnp);
+		if (wn == NULL)
+			return (-1);
+		archive_wstring_ensure(&(a->_name_data), 4 + wcslen(wn) + 1);
+		a->name = a->_name_data.s;
+		/* Prepend "\\?\" */
+		archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
+		archive_wstrcat(&(a->_name_data), wn);
+		free(wn);
+		return (0);
+	}
+
+	/*
+	 * A full-pathname pointing to a network drive
+	 * like "\\<server-name>\<share-name>\file". 
+	 */
+	if (wnp[0] == L'\\' && wnp[1] == L'\\' && wnp[2] != L'\\') {
+		const wchar_t *p = &wnp[2];
+
+		/* Skip server-name letters. */
+		while (*p != L'\\' && *p != L'\0')
+			++p;
+		if (*p == L'\\') {
+			const wchar_t *rp = ++p;
+			/* Skip share-name letters. */
+			while (*p != L'\\' && *p != L'\0')
+				++p;
+			if (*p == L'\\' && p != rp) {
+				/* Now, match patterns such as
+				 * "\\server-name\share-name\" */
+				wn = _wcsdup(wnp);
+				if (wn == NULL)
+					return (-1);
+				archive_wstring_ensure(&(a->_name_data),
+					8 + wcslen(wn) + 1);
+				a->name = a->_name_data.s;
+				/* Prepend "\\?\UNC\" */
+				archive_wstrncpy(&(a->_name_data),
+					L"\\\\?\\UNC\\", 8);
+				archive_wstrcat(&(a->_name_data), wn+2);
+				free(wn);
+				return (0);
+			}
+		}
+		return (0);
+	}
+
+	/*
+	 * Get current working directory.
+	 */
+	l = GetCurrentDirectoryW(0, NULL);
+	if (l == 0)
+		return (-1);
+	ws = malloc(l * sizeof(wchar_t));
+	l = GetCurrentDirectoryW(l, ws);
+	if (l == 0) {
+		free(ws);
+		return (-1);
+	}
+	wsp = ws;
+
+	/*
+	 * A full-pathname starting without a drive name like "\abc".
+	 */
+	if (wnp[0] == L'\\') {
+		wn = _wcsdup(wnp);
+		if (wn == NULL)
+			return (-1);
+		archive_wstring_ensure(&(a->_name_data),
+			4 + 2 + wcslen(wn) + 1);
+		a->name = a->_name_data.s;
+		/* Prepend "\\?\" and drive name. */
+		archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
+		archive_wstrncat(&(a->_name_data), wsp, 2);
+		archive_wstrcat(&(a->_name_data), wn);
+		free(wsp);
+		free(wn);
+		return (0);
+	}
+
+	wn = _wcsdup(wnp);
+	if (wn == NULL)
+		return (-1);
+	archive_wstring_ensure(&(a->_name_data), 4 + l + 1 + wcslen(wn) + 1);
+	a->name = a->_name_data.s;
+	/* Prepend "\\?\" and drive name if not already added. */
+	if (l > 3 && wsp[0] == L'\\' && wsp[1] == L'\\' &&
+		wsp[2] == L'?' && wsp[3] == L'\\')
+	{
+		archive_wstrncpy(&(a->_name_data), wsp, l);
+	}
+	else if (l > 2 && wsp[0] == L'\\' && wsp[1] == L'\\' && wsp[2] != L'\\')
+	{
+		archive_wstrncpy(&(a->_name_data), L"\\\\?\\UNC\\", 8);
+		archive_wstrncat(&(a->_name_data), wsp+2, l-2);
+	}
+	else
+	{
+		archive_wstrncpy(&(a->_name_data), L"\\\\?\\", 4);
+		archive_wstrncat(&(a->_name_data), wsp, l);
+	}
+	archive_wstrncat(&(a->_name_data), L"\\", 1);
+	archive_wstrcat(&(a->_name_data), wn);
+	a->name = a->_name_data.s;
+	free(wsp);
+	free(wn);
+	return (0);
+}
+
+static int
+la_chmod(const wchar_t *path, mode_t mode)
+{
+	DWORD attr;
+	BOOL r;
+	wchar_t *fullname;
+	int ret = 0;
+
+	fullname = NULL;
+	attr = GetFileAttributesW(path);
+	if (attr == (DWORD)-1 &&
+	    GetLastError() == ERROR_INVALID_NAME) {
+		fullname = __la_win_permissive_name_w(path);
+		attr = GetFileAttributesW(fullname);
+	}
+	if (attr == (DWORD)-1) {
+		la_dosmaperr(GetLastError());
+		ret = -1;
+		goto exit_chmode;
+	}
+	if (mode & _S_IWRITE)
+		attr &= ~FILE_ATTRIBUTE_READONLY;
+	else
+		attr |= FILE_ATTRIBUTE_READONLY;
+	if (fullname != NULL)
+		r = SetFileAttributesW(fullname, attr);
+	else
+		r = SetFileAttributesW(path, attr);
+	if (r == 0) {
+		la_dosmaperr(GetLastError());
+		ret = -1;
+	}
+exit_chmode:
+	free(fullname);
+	return (ret);
+}
+
+static int
+la_mktemp(struct archive_write_disk *a)
+{
+	int fd;
+	mode_t mode;
+
+	archive_wstring_empty(&(a->_tmpname_data));
+	archive_wstrcpy(&(a->_tmpname_data), a->name);
+	archive_wstrcat(&(a->_tmpname_data), L".XXXXXX");
+	a->tmpname = a->_tmpname_data.s;
+
+	fd = __archive_mkstemp(a->tmpname);
+	if (fd == -1)
+		return -1;
+
+	mode = a->mode & 0777 & ~a->user_umask;
+	if (la_chmod(a->tmpname, mode) == -1) {
+		la_dosmaperr(GetLastError());
+		_close(fd);
+		return -1;
+	}
+	return (fd);
+}
+
+static void *
+la_GetFunctionKernel32(const char *name)
+{
+	static HINSTANCE lib;
+	static int set;
+	if (!set) {
+		set = 1;
+		lib = LoadLibrary(TEXT("kernel32.dll"));
+	}
+	if (lib == NULL) {
+		fprintf(stderr, "Can't load kernel32.dll?!\n");
+		exit(1);
+	}
+	return (void *)GetProcAddress(lib, name);
+}
+
+static int
+la_CreateHardLinkW(wchar_t *linkname, wchar_t *target)
+{
+	static BOOLEAN (WINAPI *f)(LPWSTR, LPWSTR, LPSECURITY_ATTRIBUTES);
+	static int set;
+	BOOL ret;
+
+	if (!set) {
+		set = 1;
+		f = la_GetFunctionKernel32("CreateHardLinkW");
+	}
+	if (!f) {
+		errno = ENOTSUP;
+		return (0);
+	}
+	ret = (*f)(linkname, target, NULL);
+	if (!ret) {
+		/* Under windows 2000, it is necessary to remove
+		 * the "\\?\" prefix. */
+#define IS_UNC(name)	((name[0] == L'U' || name[0] == L'u') &&	\
+			 (name[1] == L'N' || name[1] == L'n') &&	\
+			 (name[2] == L'C' || name[2] == L'c') &&	\
+			 name[3] == L'\\')
+		if (!wcsncmp(linkname,L"\\\\?\\", 4)) {
+			linkname += 4;
+			if (IS_UNC(linkname))
+				linkname += 4;
+		}
+		if (!wcsncmp(target,L"\\\\?\\", 4)) {
+			target += 4;
+			if (IS_UNC(target))
+				target += 4;
+		}
+#undef IS_UNC
+		ret = (*f)(linkname, target, NULL);
+	}
+	return (ret);
+}
+
+/*
+ * Create file or directory symolic link
+ *
+ * If linktype is AE_SYMLINK_TYPE_UNDEFINED (or unknown), guess linktype from
+ * the link target
+ */
+static int
+la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target,
+    int linktype) {
+	static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD);
+	static int set;
+	wchar_t *ttarget, *p;
+	int len;
+	DWORD attrs = 0;
+	DWORD flags = 0;
+	DWORD newflags = 0;
+	BOOL ret = 0;
+
+	if (!set) {
+		set = 1;
+		f = la_GetFunctionKernel32("CreateSymbolicLinkW");
+	}
+	if (!f)
+		return (0);
+
+	len = wcslen(target);
+	if (len == 0) {
+		errno = EINVAL;
+		return(0);
+	}
+	/*
+	 * When writing path targets, we need to translate slashes
+	 * to backslashes
+	 */
+	ttarget = malloc((len + 1) * sizeof(wchar_t));
+	if (ttarget == NULL)
+		return(0);
+
+	p = ttarget;
+
+	while(*target != L'\0') {
+		if (*target == L'/')
+			*p = L'\\';
+		else
+			*p = *target;
+		target++;
+		p++;
+	}
+	*p = L'\0';
+
+	/*
+	 * In case of undefined symlink type we guess it from the target.
+	 * If the target equals ".", "..", ends with a backslash or a
+	 * backslash followed by "." or ".." we assume it is a directory
+	 * symlink. In all other cases we assume a file symlink.
+	 */
+	if (linktype != AE_SYMLINK_TYPE_FILE && (
+		linktype == AE_SYMLINK_TYPE_DIRECTORY ||
+		*(p - 1) == L'\\' || (*(p - 1) == L'.' && (
+	    len == 1 || *(p - 2) == L'\\' || ( *(p - 2) == L'.' && (
+	    len == 2 || *(p - 3) == L'\\')))))) {
+#if defined(SYMBOLIC_LINK_FLAG_DIRECTORY)
+		flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+#else
+		flags |= 0x1;
+#endif
+	}
+
+#if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
+	newflags = flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+#else
+	newflags = flags | 0x2;
+#endif
+
+	/*
+	 * Windows won't overwrite existing links
+	 */
+	attrs = GetFileAttributesW(linkname);
+	if (attrs != INVALID_FILE_ATTRIBUTES) {
+		if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+			disk_rmdir(linkname);
+		else
+			disk_unlink(linkname);
+	}
+
+	ret = (*f)(linkname, ttarget, newflags);
+	/*
+	 * Prior to Windows 10 calling CreateSymbolicLinkW() will fail
+	 * if SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is set
+	 */
+	if (!ret) {
+		ret = (*f)(linkname, ttarget, flags);
+	}
+	free(ttarget);
+	return (ret);
+}
+
+static int
+la_ftruncate(HANDLE handle, int64_t length)
+{
+	LARGE_INTEGER distance;
+
+	if (GetFileType(handle) != FILE_TYPE_DISK) {
+		errno = EBADF;
+		return (-1);
+	}
+	distance.QuadPart = length;
+	if (!SetFilePointerEx_perso(handle, distance, NULL, FILE_BEGIN)) {
+		la_dosmaperr(GetLastError());
+		return (-1);
+	}
+	if (!SetEndOfFile(handle)) {
+		la_dosmaperr(GetLastError());
+		return (-1);
+	}
+	return (0);
+}
+
+static int
+lazy_stat(struct archive_write_disk *a)
+{
+	if (a->pst != NULL) {
+		/* Already have stat() data available. */
+		return (ARCHIVE_OK);
+	}
+	if (a->fh != INVALID_HANDLE_VALUE &&
+	    GetFileInformationByHandle(a->fh, &a->st) == 0) {
+		a->pst = &a->st;
+		return (ARCHIVE_OK);
+	}
+
+	/*
+	 * XXX At this point, symlinks should not be hit, otherwise
+	 * XXX a race occurred.  Do we want to check explicitly for that?
+	 */
+	if (file_information(a, a->name, &a->st, NULL, 1) == 0) {
+		a->pst = &a->st;
+		return (ARCHIVE_OK);
+	}
+	archive_set_error(&a->archive, errno, "Couldn't stat file");
+	return (ARCHIVE_WARN);
+}
+
+static struct archive_vtable *
+archive_write_disk_vtable(void)
+{
+	static struct archive_vtable av;
+	static int inited = 0;
+
+	if (!inited) {
+		av.archive_close = _archive_write_disk_close;
+		av.archive_filter_bytes = _archive_write_disk_filter_bytes;
+		av.archive_free = _archive_write_disk_free;
+		av.archive_write_header = _archive_write_disk_header;
+		av.archive_write_finish_entry
+		    = _archive_write_disk_finish_entry;
+		av.archive_write_data = _archive_write_disk_data;
+		av.archive_write_data_block = _archive_write_disk_data_block;
+		inited = 1;
+	}
+	return (&av);
+}
+
+static int64_t
+_archive_write_disk_filter_bytes(struct archive *_a, int n)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	(void)n; /* UNUSED */
+	if (n == -1 || n == 0)
+		return (a->total_bytes_written);
+	return (-1);
+}
+
+
+int
+archive_write_disk_set_options(struct archive *_a, int flags)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+	a->flags = flags;
+	return (ARCHIVE_OK);
+}
+
+
+/*
+ * Extract this entry to disk.
+ *
+ * TODO: Validate hardlinks.  According to the standards, we're
+ * supposed to check each extracted hardlink and squawk if it refers
+ * to a file that we didn't restore.  I'm not entirely convinced this
+ * is a good idea, but more importantly: Is there any way to validate
+ * hardlinks without keeping a complete list of filenames from the
+ * entire archive?? Ugh.
+ *
+ */
+static int
+_archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	struct fixup_entry *fe;
+	int ret, r;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_write_disk_header");
+	archive_clear_error(&a->archive);
+	if (a->archive.state & ARCHIVE_STATE_DATA) {
+		r = _archive_write_disk_finish_entry(&a->archive);
+		if (r == ARCHIVE_FATAL)
+			return (r);
+	}
+
+	/* Set up for this particular entry. */
+	a->pst = NULL;
+	a->current_fixup = NULL;
+	a->deferred = 0;
+	archive_entry_free(a->entry);
+	a->entry = NULL;
+	a->entry = archive_entry_clone(entry);
+	a->fh = INVALID_HANDLE_VALUE;
+	a->fd_offset = 0;
+	a->offset = 0;
+	a->restore_pwd = -1;
+	a->uid = a->user_uid;
+	a->mode = archive_entry_mode(a->entry);
+	if (archive_entry_size_is_set(a->entry))
+		a->filesize = archive_entry_size(a->entry);
+	else
+		a->filesize = -1;
+	archive_wstrcpy(&(a->_name_data), archive_entry_pathname_w(a->entry));
+	a->name = a->_name_data.s;
+	archive_clear_error(&a->archive);
+
+	/*
+	 * Clean up the requested path.  This is necessary for correct
+	 * dir restores; the dir restore logic otherwise gets messed
+	 * up by nonsense like "dir/.".
+	 */
+	ret = cleanup_pathname(a);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+
+	/*
+	 * Generate a full-pathname and use it from here.
+	 */
+	if (permissive_name_w(a) < 0) {
+		errno = EINVAL;
+		return (ARCHIVE_FAILED);
+	}
+
+	/*
+	 * Query the umask so we get predictable mode settings.
+	 * This gets done on every call to _write_header in case the
+	 * user edits their umask during the extraction for some
+	 * reason.
+	 */
+	umask(a->user_umask = umask(0));
+
+	/* Figure out what we need to do for this entry. */
+	a->todo = TODO_MODE_BASE;
+	if (a->flags & ARCHIVE_EXTRACT_PERM) {
+		a->todo |= TODO_MODE_FORCE; /* Be pushy about permissions. */
+		/*
+		 * SGID requires an extra "check" step because we
+		 * cannot easily predict the GID that the system will
+		 * assign.  (Different systems assign GIDs to files
+		 * based on a variety of criteria, including process
+		 * credentials and the gid of the enclosing
+		 * directory.)  We can only restore the SGID bit if
+		 * the file has the right GID, and we only know the
+		 * GID if we either set it (see set_ownership) or if
+		 * we've actually called stat() on the file after it
+		 * was restored.  Since there are several places at
+		 * which we might verify the GID, we need a TODO bit
+		 * to keep track.
+		 */
+		if (a->mode & S_ISGID)
+			a->todo |= TODO_SGID | TODO_SGID_CHECK;
+		/*
+		 * Verifying the SUID is simpler, but can still be
+		 * done in multiple ways, hence the separate "check" bit.
+		 */
+		if (a->mode & S_ISUID)
+			a->todo |= TODO_SUID | TODO_SUID_CHECK;
+	} else {
+		/*
+		 * User didn't request full permissions, so don't
+		 * restore SUID, SGID bits and obey umask.
+		 */
+		a->mode &= ~S_ISUID;
+		a->mode &= ~S_ISGID;
+		a->mode &= ~S_ISVTX;
+		a->mode &= ~a->user_umask;
+	}
+#if 0
+	if (a->flags & ARCHIVE_EXTRACT_OWNER)
+		a->todo |= TODO_OWNER;
+#endif
+	if (a->flags & ARCHIVE_EXTRACT_TIME)
+		a->todo |= TODO_TIMES;
+	if (a->flags & ARCHIVE_EXTRACT_ACL) {
+		if (archive_entry_filetype(a->entry) == AE_IFDIR)
+			a->deferred |= TODO_ACLS;
+		else
+			a->todo |= TODO_ACLS;
+	}
+	if (a->flags & ARCHIVE_EXTRACT_XATTR)
+		a->todo |= TODO_XATTR;
+	if (a->flags & ARCHIVE_EXTRACT_FFLAGS)
+		a->todo |= TODO_FFLAGS;
+	if (a->flags & ARCHIVE_EXTRACT_SECURE_SYMLINKS) {
+		ret = check_symlinks(a);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+	}
+
+	ret = restore_entry(a);
+
+	/*
+	 * TODO: There are rumours that some extended attributes must
+	 * be restored before file data is written.  If this is true,
+	 * then we either need to write all extended attributes both
+	 * before and after restoring the data, or find some rule for
+	 * determining which must go first and which last.  Due to the
+	 * many ways people are using xattrs, this may prove to be an
+	 * intractable problem.
+	 */
+
+	/*
+	 * Fixup uses the unedited pathname from archive_entry_pathname(),
+	 * because it is relative to the base dir and the edited path
+	 * might be relative to some intermediate dir as a result of the
+	 * deep restore logic.
+	 */
+	if (a->deferred & TODO_MODE) {
+		fe = current_fixup(a, archive_entry_pathname_w(entry));
+		fe->fixup |= TODO_MODE_BASE;
+		fe->mode = a->mode;
+	}
+
+	if ((a->deferred & TODO_TIMES)
+		&& (archive_entry_mtime_is_set(entry)
+		    || archive_entry_atime_is_set(entry))) {
+		fe = current_fixup(a, archive_entry_pathname_w(entry));
+		fe->mode = a->mode;
+		fe->fixup |= TODO_TIMES;
+		if (archive_entry_atime_is_set(entry)) {
+			fe->atime = archive_entry_atime(entry);
+			fe->atime_nanos = archive_entry_atime_nsec(entry);
+		} else {
+			/* If atime is unset, use start time. */
+			fe->atime = a->start_time;
+			fe->atime_nanos = 0;
+		}
+		if (archive_entry_mtime_is_set(entry)) {
+			fe->mtime = archive_entry_mtime(entry);
+			fe->mtime_nanos = archive_entry_mtime_nsec(entry);
+		} else {
+			/* If mtime is unset, use start time. */
+			fe->mtime = a->start_time;
+			fe->mtime_nanos = 0;
+		}
+		if (archive_entry_birthtime_is_set(entry)) {
+			fe->birthtime = archive_entry_birthtime(entry);
+			fe->birthtime_nanos = archive_entry_birthtime_nsec(entry);
+		} else {
+			/* If birthtime is unset, use mtime. */
+			fe->birthtime = fe->mtime;
+			fe->birthtime_nanos = fe->mtime_nanos;
+		}
+	}
+
+	if (a->deferred & TODO_ACLS) {
+		fe = current_fixup(a, archive_entry_pathname_w(entry));
+		archive_acl_copy(&fe->acl, archive_entry_acl(entry));
+	}
+
+	if (a->deferred & TODO_FFLAGS) {
+		unsigned long set, clear;
+
+		fe = current_fixup(a, archive_entry_pathname_w(entry));
+		archive_entry_fflags(entry, &set, &clear);
+		fe->fflags_set = set;
+	}
+
+	/*
+	 * On Windows, A creating sparse file requires a special mark.
+	 */
+	if (a->fh != INVALID_HANDLE_VALUE &&
+	    archive_entry_sparse_count(entry) > 0) {
+		int64_t base = 0, offset, length;
+		int i, cnt = archive_entry_sparse_reset(entry);
+		int sparse = 0;
+
+		for (i = 0; i < cnt; i++) {
+			archive_entry_sparse_next(entry, &offset, &length);
+			if (offset - base >= 4096) {
+				sparse = 1;/* we have a hole. */
+				break;
+			}
+			base = offset + length;
+		}
+		if (sparse) {
+			DWORD dmy;
+			/* Mark this file as sparse. */
+			DeviceIoControl(a->fh, FSCTL_SET_SPARSE,
+			    NULL, 0, NULL, 0, &dmy, NULL);
+		}
+	}
+
+	/* We've created the object and are ready to pour data into it. */
+	if (ret >= ARCHIVE_WARN)
+		a->archive.state = ARCHIVE_STATE_DATA;
+	/*
+	 * If it's not open, tell our client not to try writing.
+	 * In particular, dirs, links, etc, don't get written to.
+	 */
+	if (a->fh == INVALID_HANDLE_VALUE) {
+		archive_entry_set_size(entry, 0);
+		a->filesize = 0;
+	}
+
+	return (ret);
+}
+
+int
+archive_write_disk_set_skip_file(struct archive *_a, la_int64_t d, la_int64_t i)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_skip_file");
+	a->skip_file_set = 1;
+	a->skip_file_dev = d;
+	a->skip_file_ino = i;
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+write_data_block(struct archive_write_disk *a, const char *buff, size_t size)
+{
+	OVERLAPPED ol;
+	uint64_t start_size = size;
+	DWORD bytes_written = 0;
+	ssize_t block_size = 0, bytes_to_write;
+
+	if (size == 0)
+		return (ARCHIVE_OK);
+
+	if (a->filesize == 0 || a->fh == INVALID_HANDLE_VALUE) {
+		archive_set_error(&a->archive, 0,
+		    "Attempt to write to an empty file");
+		return (ARCHIVE_WARN);
+	}
+
+	if (a->flags & ARCHIVE_EXTRACT_SPARSE) {
+		/* XXX TODO XXX Is there a more appropriate choice here ? */
+		/* This needn't match the filesystem allocation size. */
+		block_size = 16*1024;
+	}
+
+	/* If this write would run beyond the file size, truncate it. */
+	if (a->filesize >= 0 && (int64_t)(a->offset + size) > a->filesize)
+		start_size = size = (size_t)(a->filesize - a->offset);
+
+	/* Write the data. */
+	while (size > 0) {
+		if (block_size == 0) {
+			bytes_to_write = size;
+		} else {
+			/* We're sparsifying the file. */
+			const char *p, *end;
+			int64_t block_end;
+
+			/* Skip leading zero bytes. */
+			for (p = buff, end = buff + size; p < end; ++p) {
+				if (*p != '\0')
+					break;
+			}
+			a->offset += p - buff;
+			size -= p - buff;
+			buff = p;
+			if (size == 0)
+				break;
+
+			/* Calculate next block boundary after offset. */
+			block_end
+			    = (a->offset / block_size + 1) * block_size;
+
+			/* If the adjusted write would cross block boundary,
+			 * truncate it to the block boundary. */
+			bytes_to_write = size;
+			if (a->offset + bytes_to_write > block_end)
+				bytes_to_write = (DWORD)(block_end - a->offset);
+		}
+		memset(&ol, 0, sizeof(ol));
+		ol.Offset = (DWORD)(a->offset & 0xFFFFFFFF);
+		ol.OffsetHigh = (DWORD)(a->offset >> 32);
+		if (!WriteFile(a->fh, buff, (uint32_t)bytes_to_write,
+		    &bytes_written, &ol)) {
+			DWORD lasterr;
+
+			lasterr = GetLastError();
+			if (lasterr == ERROR_ACCESS_DENIED)
+				errno = EBADF;
+			else
+				la_dosmaperr(lasterr);
+			archive_set_error(&a->archive, errno, "Write failed");
+			return (ARCHIVE_WARN);
+		}
+		buff += bytes_written;
+		size -= bytes_written;
+		a->total_bytes_written += bytes_written;
+		a->offset += bytes_written;
+		a->fd_offset = a->offset;
+	}
+	return ((ssize_t)(start_size - size));
+}
+
+static ssize_t
+_archive_write_disk_data_block(struct archive *_a,
+    const void *buff, size_t size, int64_t offset)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	ssize_t r;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_DATA, "archive_write_data_block");
+
+	a->offset = offset;
+	r = write_data_block(a, buff, size);
+	if (r < ARCHIVE_OK)
+		return (r);
+	if ((size_t)r < size) {
+		archive_set_error(&a->archive, 0,
+		    "Write request too large");
+		return (ARCHIVE_WARN);
+	}
+#if ARCHIVE_VERSION_NUMBER < 3999000
+	return (ARCHIVE_OK);
+#else
+	return (size);
+#endif
+}
+
+static ssize_t
+_archive_write_disk_data(struct archive *_a, const void *buff, size_t size)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_DATA, "archive_write_data");
+
+	return (write_data_block(a, buff, size));
+}
+
+static int
+_archive_write_disk_finish_entry(struct archive *_a)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	int ret = ARCHIVE_OK;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_write_finish_entry");
+	if (a->archive.state & ARCHIVE_STATE_HEADER)
+		return (ARCHIVE_OK);
+	archive_clear_error(&a->archive);
+
+	/* Pad or truncate file to the right size. */
+	if (a->fh == INVALID_HANDLE_VALUE) {
+		/* There's no file. */
+	} else if (a->filesize < 0) {
+		/* File size is unknown, so we can't set the size. */
+	} else if (a->fd_offset == a->filesize) {
+		/* Last write ended at exactly the filesize; we're done. */
+		/* Hopefully, this is the common case. */
+	} else {
+		if (la_ftruncate(a->fh, a->filesize) == -1) {
+			archive_set_error(&a->archive, errno,
+			    "File size could not be restored");
+			return (ARCHIVE_FAILED);
+		}
+	}
+
+	/* Restore metadata. */
+
+	/*
+	 * Look up the "real" UID only if we're going to need it.
+	 * TODO: the TODO_SGID condition can be dropped here, can't it?
+	 */
+	if (a->todo & (TODO_OWNER | TODO_SUID | TODO_SGID)) {
+		a->uid = archive_write_disk_uid(&a->archive,
+		    archive_entry_uname(a->entry),
+		    archive_entry_uid(a->entry));
+	}
+	/* Look up the "real" GID only if we're going to need it. */
+	/* TODO: the TODO_SUID condition can be dropped here, can't it? */
+	if (a->todo & (TODO_OWNER | TODO_SGID | TODO_SUID)) {
+		a->gid = archive_write_disk_gid(&a->archive,
+		    archive_entry_gname(a->entry),
+		    archive_entry_gid(a->entry));
+	 }
+
+	/*
+	 * Restore ownership before set_mode tries to restore suid/sgid
+	 * bits.  If we set the owner, we know what it is and can skip
+	 * a stat() call to examine the ownership of the file on disk.
+	 */
+	if (a->todo & TODO_OWNER)
+		ret = set_ownership(a);
+
+	/*
+	 * set_mode must precede ACLs on systems such as Solaris and
+	 * FreeBSD where setting the mode implicitly clears extended ACLs
+	 */
+	if (a->todo & TODO_MODE) {
+		int r2 = set_mode(a, a->mode);
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * Security-related extended attributes (such as
+	 * security.capability on Linux) have to be restored last,
+	 * since they're implicitly removed by other file changes.
+	 */
+	if (a->todo & TODO_XATTR) {
+		int r2 = set_xattrs(a);
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * Some flags prevent file modification; they must be restored after
+	 * file contents are written.
+	 */
+	if (a->todo & TODO_FFLAGS) {
+		int r2 = set_fflags(a);
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * Time must follow most other metadata;
+	 * otherwise atime will get changed.
+	 */
+	if (a->todo & TODO_TIMES) {
+		int r2 = set_times_from_entry(a);
+		if (r2 < ret) ret = r2;
+	}
+
+	/*
+	 * ACLs must be restored after timestamps because there are
+	 * ACLs that prevent attribute changes (including time).
+	 */
+	if (a->todo & TODO_ACLS) {
+		int r2 = set_acls(a, a->fh,
+				  archive_entry_pathname_w(a->entry),
+				  archive_entry_acl(a->entry));
+		if (r2 < ret) ret = r2;
+	}
+
+	/* If there's an fd, we can close it now. */
+	if (a->fh != INVALID_HANDLE_VALUE) {
+		CloseHandle(a->fh);
+		a->fh = INVALID_HANDLE_VALUE;
+		if (a->tmpname) {
+			/* Windows does not support atomic rename */
+			disk_unlink(a->name);
+			if (_wrename(a->tmpname, a->name) != 0) {
+				la_dosmaperr(GetLastError());
+				archive_set_error(&a->archive, errno,
+				    "Failed to rename temporary file");
+				ret = ARCHIVE_FAILED;
+				disk_unlink(a->tmpname);
+			}
+			a->tmpname = NULL;
+		}
+	}
+	/* If there's an entry, we can release it now. */
+	archive_entry_free(a->entry);
+	a->entry = NULL;
+	a->archive.state = ARCHIVE_STATE_HEADER;
+	return (ret);
+}
+
+int
+archive_write_disk_set_group_lookup(struct archive *_a,
+    void *private_data,
+    la_int64_t (*lookup_gid)(void *private, const char *gname, la_int64_t gid),
+    void (*cleanup_gid)(void *private))
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_group_lookup");
+
+	if (a->cleanup_gid != NULL && a->lookup_gid_data != NULL)
+		(a->cleanup_gid)(a->lookup_gid_data);
+
+	a->lookup_gid = lookup_gid;
+	a->cleanup_gid = cleanup_gid;
+	a->lookup_gid_data = private_data;
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_disk_set_user_lookup(struct archive *_a,
+    void *private_data,
+    int64_t (*lookup_uid)(void *private, const char *uname, int64_t uid),
+    void (*cleanup_uid)(void *private))
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY, "archive_write_disk_set_user_lookup");
+
+	if (a->cleanup_uid != NULL && a->lookup_uid_data != NULL)
+		(a->cleanup_uid)(a->lookup_uid_data);
+
+	a->lookup_uid = lookup_uid;
+	a->cleanup_uid = cleanup_uid;
+	a->lookup_uid_data = private_data;
+	return (ARCHIVE_OK);
+}
+
+int64_t
+archive_write_disk_gid(struct archive *_a, const char *name, la_int64_t id)
+{
+       struct archive_write_disk *a = (struct archive_write_disk *)_a;
+       archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_write_disk_gid");
+       if (a->lookup_gid)
+               return (a->lookup_gid)(a->lookup_gid_data, name, id);
+       return (id);
+}
+ 
+int64_t
+archive_write_disk_uid(struct archive *_a, const char *name, la_int64_t id)
+{
+       struct archive_write_disk *a = (struct archive_write_disk *)_a;
+       archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+           ARCHIVE_STATE_ANY, "archive_write_disk_uid");
+       if (a->lookup_uid)
+               return (a->lookup_uid)(a->lookup_uid_data, name, id);
+       return (id);
+}
+
+/*
+ * Create a new archive_write_disk object and initialize it with global state.
+ */
+struct archive *
+archive_write_disk_new(void)
+{
+	struct archive_write_disk *a;
+
+	a = (struct archive_write_disk *)calloc(1, sizeof(*a));
+	if (a == NULL)
+		return (NULL);
+	a->archive.magic = ARCHIVE_WRITE_DISK_MAGIC;
+	/* We're ready to write a header immediately. */
+	a->archive.state = ARCHIVE_STATE_HEADER;
+	a->archive.vtable = archive_write_disk_vtable();
+	a->start_time = time(NULL);
+	/* Query and restore the umask. */
+	umask(a->user_umask = umask(0));
+	if (archive_wstring_ensure(&a->path_safe, 512) == NULL) {
+		free(a);
+		return (NULL);
+	}
+	return (&a->archive);
+}
+
+static int
+disk_unlink(const wchar_t *path)
+{
+	wchar_t *fullname;
+	int r;
+
+	r = _wunlink(path);
+	if (r != 0 && GetLastError() == ERROR_INVALID_NAME) {
+		fullname = __la_win_permissive_name_w(path);
+		r = _wunlink(fullname);
+		free(fullname);
+	}
+	return (r);
+}
+
+static int
+disk_rmdir(const wchar_t *path)
+{
+	wchar_t *fullname;
+	int r;
+
+	r = _wrmdir(path);
+	if (r != 0 && GetLastError() == ERROR_INVALID_NAME) {
+		fullname = __la_win_permissive_name_w(path);
+		r = _wrmdir(fullname);
+		free(fullname);
+	}
+	return (r);
+}
+
+/*
+ * The main restore function.
+ */
+static int
+restore_entry(struct archive_write_disk *a)
+{
+	int ret = ARCHIVE_OK, en;
+
+	if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {
+		/*
+		 * TODO: Fix this.  Apparently, there are platforms
+		 * that still allow root to hose the entire filesystem
+		 * by unlinking a dir.  The S_ISDIR() test above
+		 * prevents us from using unlink() here if the new
+		 * object is a dir, but that doesn't mean the old
+		 * object isn't a dir.
+		 */
+		if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+			(void)clear_nochange_fflags(a);
+		if (disk_unlink(a->name) == 0) {
+			/* We removed it, reset cached stat. */
+			a->pst = NULL;
+		} else if (errno == ENOENT) {
+			/* File didn't exist, that's just as good. */
+		} else if (disk_rmdir(a->name) == 0) {
+			/* It was a dir, but now it's gone. */
+			a->pst = NULL;
+		} else {
+			/* We tried, but couldn't get rid of it. */
+			archive_set_error(&a->archive, errno,
+			    "Could not unlink");
+			return(ARCHIVE_FAILED);
+		}
+	}
+
+	/* Try creating it first; if this fails, we'll try to recover. */
+	en = create_filesystem_object(a);
+
+	if ((en == ENOTDIR || en == ENOENT)
+	    && !(a->flags & ARCHIVE_EXTRACT_NO_AUTODIR)) {
+		wchar_t *full;
+		/* If the parent dir doesn't exist, try creating it. */
+		create_parent_dir(a, a->name);
+		/* Now try to create the object again. */
+		full = __la_win_permissive_name_w(a->name);
+		if (full == NULL) {
+			en = EINVAL;
+		} else {
+			/* Remove multiple directories such as "a/../b../c" */
+			archive_wstrcpy(&(a->_name_data), full);
+			a->name = a->_name_data.s;
+			free(full);
+			en = create_filesystem_object(a);
+		}
+	}
+
+	if ((en == ENOENT) && (archive_entry_hardlink(a->entry) != NULL)) {
+		archive_set_error(&a->archive, en,
+			"Hard-link target '%s' does not exist.",
+			archive_entry_hardlink(a->entry));
+		return (ARCHIVE_FAILED);
+	}
+
+	if ((en == EISDIR || en == EEXIST)
+	    && (a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+		/* If we're not overwriting, we're done. */
+		if (S_ISDIR(a->mode)) {
+			/* Don't overwrite any settings on existing directories. */
+			a->todo = 0;
+		}
+		archive_entry_unset_size(a->entry);
+		return (ARCHIVE_OK);
+	}
+
+	/*
+	 * Some platforms return EISDIR if you call
+	 * open(O_WRONLY | O_EXCL | O_CREAT) on a directory, some
+	 * return EEXIST.  POSIX is ambiguous, requiring EISDIR
+	 * for open(O_WRONLY) on a dir and EEXIST for open(O_EXCL | O_CREAT)
+	 * on an existing item.
+	 */
+	if (en == EISDIR) {
+		/* A dir is in the way of a non-dir, rmdir it. */
+		if (disk_rmdir(a->name) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't remove already-existing dir");
+			return (ARCHIVE_FAILED);
+		}
+		a->pst = NULL;
+		/* Try again. */
+		en = create_filesystem_object(a);
+	} else if (en == EEXIST) {
+		mode_t st_mode;
+		mode_t lst_mode;
+		BY_HANDLE_FILE_INFORMATION lst;
+		/*
+		 * We know something is in the way, but we don't know what;
+		 * we need to find out before we go any further.
+		 */
+		int r = 0;
+		int dirlnk = 0;
+
+		/*
+		 * The SECURE_SYMLINK logic has already removed a
+		 * symlink to a dir if the client wants that.  So
+		 * follow the symlink if we're creating a dir.
+		 * If it's not a dir (or it's a broken symlink),
+		 * then don't follow it.
+		 *
+		 * Windows distinguishes file and directory symlinks.
+		 * A file symlink may erroneously point to a directory
+		 * and a directory symlink to a file. Windows does not follow
+		 * such symlinks. We always need both source and target
+		 * information.
+		 */
+		r = file_information(a, a->name, &lst, &lst_mode, 1);
+		if (r != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't stat existing object");
+			return (ARCHIVE_FAILED);
+		} else if (S_ISLNK(lst_mode)) {
+			if (lst.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+				dirlnk = 1;
+			/* In case of a symlink we need target information */
+			r = file_information(a, a->name, &a->st, &st_mode, 0);
+			if (r != 0) {
+				a->st = lst;
+				st_mode = lst_mode;
+			}
+		} else {
+			a->st = lst;
+			st_mode = lst_mode;
+		}
+
+		/*
+		 * NO_OVERWRITE_NEWER doesn't apply to directories.
+		 */
+		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER)
+		    &&  !S_ISDIR(st_mode)) {
+			if (!older(&(a->st), a->entry)) {
+				archive_entry_unset_size(a->entry);
+				return (ARCHIVE_OK);
+			}
+		}
+
+		/* If it's our archive, we're done. */
+		if (a->skip_file_set &&
+		    bhfi_dev(&a->st) == a->skip_file_dev &&
+		    bhfi_ino(&a->st) == a->skip_file_ino) {
+			archive_set_error(&a->archive, 0,
+			    "Refusing to overwrite archive");
+			return (ARCHIVE_FAILED);
+		}
+
+		if (!S_ISDIR(st_mode)) {
+			if (a->flags &
+			    ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+				(void)clear_nochange_fflags(a);
+			}
+			if ((a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) &&
+				S_ISREG(st_mode)) {
+				int fd = la_mktemp(a);
+
+				if (fd == -1) {
+					la_dosmaperr(GetLastError());
+					archive_set_error(&a->archive, errno,
+					    "Can't create temporary file");
+					return (ARCHIVE_FAILED);
+				}
+				a->fh = (HANDLE)_get_osfhandle(fd);
+				if (a->fh == INVALID_HANDLE_VALUE) {
+					la_dosmaperr(GetLastError());
+					return (ARCHIVE_FAILED);
+				}
+				a->pst = NULL;
+				en = 0;
+			} else {
+				if (dirlnk) {
+					/* Edge case: dir symlink pointing
+					 * to a file */
+					if (disk_rmdir(a->name) != 0) {
+						archive_set_error(&a->archive,
+						    errno, "Can't unlink "
+						    "directory symlink");
+						return (ARCHIVE_FAILED);
+					}
+				} else {
+					if (disk_unlink(a->name) != 0) {
+						/* A non-dir is in the way,
+						 * unlink it. */
+						archive_set_error(&a->archive,
+						    errno, "Can't unlink "
+						    "already-existing object");
+						return (ARCHIVE_FAILED);
+					}
+				}
+				a->pst = NULL;
+				/* Try again. */
+				en = create_filesystem_object(a);
+			}
+		} else if (!S_ISDIR(a->mode)) {
+			/* A dir is in the way of a non-dir, rmdir it. */
+			if (a->flags & ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS)
+				(void)clear_nochange_fflags(a);
+			if (disk_rmdir(a->name) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Can't remove already-existing dir");
+				return (ARCHIVE_FAILED);
+			}
+			/* Try again. */
+			en = create_filesystem_object(a);
+		} else {
+			/*
+			 * There's a dir in the way of a dir.  Don't
+			 * waste time with rmdir()/mkdir(), just fix
+			 * up the permissions on the existing dir.
+			 * Note that we don't change perms on existing
+			 * dirs unless _EXTRACT_PERM is specified.
+			 */
+			if ((a->mode != st_mode)
+			    && (a->todo & TODO_MODE_FORCE))
+				a->deferred |= (a->todo & TODO_MODE);
+			/* Ownership doesn't need deferred fixup. */
+			en = 0; /* Forget the EEXIST. */
+		}
+	}
+
+	if (en) {
+		/* Everything failed; give up here. */
+		archive_set_error(&a->archive, en, "Can't create '%ls'",
+		    a->name);
+		return (ARCHIVE_FAILED);
+	}
+
+	a->pst = NULL; /* Cached stat data no longer valid. */
+	return (ret);
+}
+
+/*
+ * Returns 0 if creation succeeds, or else returns errno value from
+ * the failed system call.   Note:  This function should only ever perform
+ * a single system call.
+ */
+static int
+create_filesystem_object(struct archive_write_disk *a)
+{
+	/* Create the entry. */
+	const wchar_t *linkname;
+	wchar_t *fullname;
+	mode_t final_mode, mode;
+	int r;
+	DWORD attrs = 0;
+
+	/* We identify hard/symlinks according to the link names. */
+	/* Since link(2) and symlink(2) don't handle modes, we're done here. */
+	linkname = archive_entry_hardlink_w(a->entry);
+	if (linkname != NULL) {
+		wchar_t *linkfull, *namefull;
+
+		linkfull = __la_win_permissive_name_w(linkname);
+		namefull = __la_win_permissive_name_w(a->name);
+		if (linkfull == NULL || namefull == NULL) {
+			errno = EINVAL;
+			r = -1;
+		} else {
+			/*
+			 * Unlinking and linking here is really not atomic,
+			 * but doing it right, would require us to construct
+			 * an mktemplink() function, and then use _wrename().
+			 */
+			if (a->flags & ARCHIVE_EXTRACT_SAFE_WRITES) {
+				attrs = GetFileAttributesW(namefull);
+				if (attrs != INVALID_FILE_ATTRIBUTES) {
+					if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+						disk_rmdir(namefull);
+					else
+						disk_unlink(namefull);
+				}
+			}
+			r = la_CreateHardLinkW(namefull, linkfull);
+			if (r == 0) {
+				la_dosmaperr(GetLastError());
+				r = errno;
+			} else
+				r = 0;
+		}
+		/*
+		 * New cpio and pax formats allow hardlink entries
+		 * to carry data, so we may have to open the file
+		 * for hardlink entries.
+		 *
+		 * If the hardlink was successfully created and
+		 * the archive doesn't have carry data for it,
+		 * consider it to be non-authoritative for meta data.
+		 * This is consistent with GNU tar and BSD pax.
+		 * If the hardlink does carry data, let the last
+		 * archive entry decide ownership.
+		 */
+		if (r == 0 && a->filesize <= 0) {
+			a->todo = 0;
+			a->deferred = 0;
+		} else if (r == 0 && a->filesize > 0) {
+			a->fh = CreateFileW(namefull, GENERIC_WRITE, 0, NULL,
+			    TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+			if (a->fh == INVALID_HANDLE_VALUE) {
+				la_dosmaperr(GetLastError());
+				r = errno;
+			}
+		}
+		free(linkfull);
+		free(namefull);
+		return (r);
+	}
+	linkname = archive_entry_symlink_w(a->entry);
+	if (linkname != NULL) {
+		/*
+		 * Unlinking and linking here is really not atomic,
+		 * but doing it right, would require us to construct
+		 * an mktemplink() function, and then use _wrename().
+		 */
+		attrs = GetFileAttributesW(a->name);
+		if (attrs != INVALID_FILE_ATTRIBUTES) {
+			if (attrs & FILE_ATTRIBUTE_DIRECTORY)
+				disk_rmdir(a->name);
+			else
+				disk_unlink(a->name);
+		}
+#if HAVE_SYMLINK
+		return symlink(linkname, a->name) ? errno : 0;
+#else
+		errno = 0;
+		r = la_CreateSymbolicLinkW((const wchar_t *)a->name, linkname,
+		    archive_entry_symlink_type(a->entry));
+		if (r == 0) {
+			if (errno == 0)
+				la_dosmaperr(GetLastError());
+			r = errno;
+		} else
+			r = 0;
+		return (r);
+#endif
+	}
+
+	/*
+	 * The remaining system calls all set permissions, so let's
+	 * try to take advantage of that to avoid an extra chmod()
+	 * call.  (Recall that umask is set to zero right now!)
+	 */
+
+	/* Mode we want for the final restored object (w/o file type bits). */
+	final_mode = a->mode & 07777;
+	/*
+	 * The mode that will actually be restored in this step.  Note
+	 * that SUID, SGID, etc, require additional work to ensure
+	 * security, so we never restore them at this point.
+	 */
+	mode = final_mode & 0777 & ~a->user_umask;
+
+	switch (a->mode & AE_IFMT) {
+	default:
+		/* POSIX requires that we fall through here. */
+		/* FALLTHROUGH */
+	case AE_IFREG:
+		a->tmpname = NULL;
+		fullname = a->name;
+		/* O_WRONLY | O_CREAT | O_EXCL */
+		a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
+		    CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+		if (a->fh == INVALID_HANDLE_VALUE &&
+		    GetLastError() == ERROR_INVALID_NAME &&
+		    fullname == a->name) {
+			fullname = __la_win_permissive_name_w(a->name);
+			a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
+			    CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+		}
+		if (a->fh == INVALID_HANDLE_VALUE) {
+			if (GetLastError() == ERROR_ACCESS_DENIED) {
+				DWORD attr;
+				/* Simulate an errno of POSIX system. */
+				attr = GetFileAttributesW(fullname);
+				if (attr == (DWORD)-1)
+					la_dosmaperr(GetLastError());
+				else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+					errno = EISDIR;
+				else
+					errno = EACCES;
+			} else
+				la_dosmaperr(GetLastError());
+			r = 1;
+		} else
+			r = 0;
+		if (fullname != a->name)
+			free(fullname);
+		break;
+	case AE_IFCHR:
+	case AE_IFBLK:
+		/* TODO: Find a better way to warn about our inability
+		 * to restore a block device node. */
+		return (EINVAL);
+	case AE_IFDIR:
+		mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
+		fullname = a->name;
+		r = CreateDirectoryW(fullname, NULL);
+		if (r == 0 && GetLastError() == ERROR_INVALID_NAME &&
+			fullname == a->name) {
+			fullname = __la_win_permissive_name_w(a->name);
+			r = CreateDirectoryW(fullname, NULL);
+		}
+		if (r != 0) {
+			r = 0;
+			/* Defer setting dir times. */
+			a->deferred |= (a->todo & TODO_TIMES);
+			a->todo &= ~TODO_TIMES;
+			/* Never use an immediate chmod(). */
+			/* We can't avoid the chmod() entirely if EXTRACT_PERM
+			 * because of SysV SGID inheritance. */
+			if ((mode != final_mode)
+			    || (a->flags & ARCHIVE_EXTRACT_PERM))
+				a->deferred |= (a->todo & TODO_MODE);
+			a->todo &= ~TODO_MODE;
+		} else {
+			la_dosmaperr(GetLastError());
+			r = -1;
+		}
+		if (fullname != a->name)
+			free(fullname);
+		break;
+	case AE_IFIFO:
+		/* TODO: Find a better way to warn about our inability
+		 * to restore a fifo. */
+		return (EINVAL);
+	}
+
+	/* All the system calls above set errno on failure. */
+	if (r)
+		return (errno);
+
+	/* If we managed to set the final mode, we've avoided a chmod(). */
+	if (mode == final_mode)
+		a->todo &= ~TODO_MODE;
+	return (0);
+}
+
+/*
+ * Cleanup function for archive_extract.  Mostly, this involves processing
+ * the fixup list, which is used to address a number of problems:
+ *   * Dir permissions might prevent us from restoring a file in that
+ *     dir, so we restore the dir with minimum 0700 permissions first,
+ *     then correct the mode at the end.
+ *   * Similarly, the act of restoring a file touches the directory
+ *     and changes the timestamp on the dir, so we have to touch-up dir
+ *     timestamps at the end as well.
+ *   * Some file flags can interfere with the restore by, for example,
+ *     preventing the creation of hardlinks to those files.
+ *   * Mac OS extended metadata includes ACLs, so must be deferred on dirs.
+ *
+ * Note that tar/cpio do not require that archives be in a particular
+ * order; there is no way to know when the last file has been restored
+ * within a directory, so there's no way to optimize the memory usage
+ * here by fixing up the directory any earlier than the
+ * end-of-archive.
+ *
+ * XXX TODO: Directory ACLs should be restored here, for the same
+ * reason we set directory perms here. XXX
+ */
+static int
+_archive_write_disk_close(struct archive *_a)
+{
+	struct archive_write_disk *a = (struct archive_write_disk *)_a;
+	struct fixup_entry *next, *p;
+	int ret;
+
+	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+	    "archive_write_disk_close");
+	ret = _archive_write_disk_finish_entry(&a->archive);
+
+	/* Sort dir list so directories are fixed up in depth-first order. */
+	p = sort_dir_list(a->fixup_list);
+
+	while (p != NULL) {
+		a->pst = NULL; /* Mark stat cache as out-of-date. */
+		if (p->fixup & TODO_TIMES) {
+			set_times(a, INVALID_HANDLE_VALUE, p->mode, p->name,
+			    p->atime, p->atime_nanos,
+			    p->birthtime, p->birthtime_nanos,
+			    p->mtime, p->mtime_nanos,
+			    p->ctime, p->ctime_nanos);
+		}
+		if (p->fixup & TODO_MODE_BASE)
+			la_chmod(p->name, p->mode);
+		if (p->fixup & TODO_ACLS)
+			set_acls(a, INVALID_HANDLE_VALUE, p->name, &p->acl);
+		if (p->fixup & TODO_FFLAGS)
+			set_fflags_platform(p->name, p->fflags_set, 0);
+		next = p->next;
+		archive_acl_clear(&p->acl);
+		free(p->name);
+		free(p);
+		p = next;
+	}
+	a->fixup_list = NULL;
+	return (ret);
+}
+
+static int
+_archive_write_disk_free(struct archive *_a)
+{
+	struct archive_write_disk *a;
+	int ret;
+	if (_a == NULL)
+		return (ARCHIVE_OK);
+	archive_check_magic(_a, ARCHIVE_WRITE_DISK_MAGIC,
+	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_write_disk_free");
+	a = (struct archive_write_disk *)_a;
+	ret = _archive_write_disk_close(&a->archive);
+	archive_write_disk_set_group_lookup(&a->archive, NULL, NULL, NULL);
+	archive_write_disk_set_user_lookup(&a->archive, NULL, NULL, NULL);
+	archive_entry_free(a->entry);
+	archive_wstring_free(&a->_name_data);
+	archive_wstring_free(&a->_tmpname_data);
+	archive_string_free(&a->archive.error_string);
+	archive_wstring_free(&a->path_safe);
+	a->archive.magic = 0;
+	__archive_clean(&a->archive);
+	free(a);
+	return (ret);
+}
+
+/*
+ * Simple O(n log n) merge sort to order the fixup list.  In
+ * particular, we want to restore dir timestamps depth-first.
+ */
+static struct fixup_entry *
+sort_dir_list(struct fixup_entry *p)
+{
+	struct fixup_entry *a, *b, *t;
+
+	if (p == NULL)
+		return (NULL);
+	/* A one-item list is already sorted. */
+	if (p->next == NULL)
+		return (p);
+
+	/* Step 1: split the list. */
+	t = p;
+	a = p->next->next;
+	while (a != NULL) {
+		/* Step a twice, t once. */
+		a = a->next;
+		if (a != NULL)
+			a = a->next;
+		t = t->next;
+	}
+	/* Now, t is at the mid-point, so break the list here. */
+	b = t->next;
+	t->next = NULL;
+	a = p;
+
+	/* Step 2: Recursively sort the two sub-lists. */
+	a = sort_dir_list(a);
+	b = sort_dir_list(b);
+
+	/* Step 3: Merge the returned lists. */
+	/* Pick the first element for the merged list. */
+	if (wcscmp(a->name, b->name) > 0) {
+		t = p = a;
+		a = a->next;
+	} else {
+		t = p = b;
+		b = b->next;
+	}
+
+	/* Always put the later element on the list first. */
+	while (a != NULL && b != NULL) {
+		if (wcscmp(a->name, b->name) > 0) {
+			t->next = a;
+			a = a->next;
+		} else {
+			t->next = b;
+			b = b->next;
+		}
+		t = t->next;
+	}
+
+	/* Only one list is non-empty, so just splice it on. */
+	if (a != NULL)
+		t->next = a;
+	if (b != NULL)
+		t->next = b;
+
+	return (p);
+}
+
+/*
+ * Returns a new, initialized fixup entry.
+ *
+ * TODO: Reduce the memory requirements for this list by using a tree
+ * structure rather than a simple list of names.
+ */
+static struct fixup_entry *
+new_fixup(struct archive_write_disk *a, const wchar_t *pathname)
+{
+	struct fixup_entry *fe;
+
+	fe = (struct fixup_entry *)calloc(1, sizeof(struct fixup_entry));
+	if (fe == NULL)
+		return (NULL);
+	fe->next = a->fixup_list;
+	a->fixup_list = fe;
+	fe->fixup = 0;
+	fe->name = _wcsdup(pathname);
+	fe->fflags_set = 0;
+	return (fe);
+}
+
+/*
+ * Returns a fixup structure for the current entry.
+ */
+static struct fixup_entry *
+current_fixup(struct archive_write_disk *a, const wchar_t *pathname)
+{
+	if (a->current_fixup == NULL)
+		a->current_fixup = new_fixup(a, pathname);
+	return (a->current_fixup);
+}
+
+/*
+ * TODO: The deep-directory support bypasses this; disable deep directory
+ * support if we're doing symlink checks.
+ */
+/*
+ * TODO: Someday, integrate this with the deep dir support; they both
+ * scan the path and both can be optimized by comparing against other
+ * recent paths.
+ */
+static int
+check_symlinks(struct archive_write_disk *a)
+{
+	wchar_t *pn, *p;
+	wchar_t c;
+	int r;
+	BY_HANDLE_FILE_INFORMATION st;
+	mode_t st_mode;
+
+	/*
+	 * Guard against symlink tricks.  Reject any archive entry whose
+	 * destination would be altered by a symlink.
+	 */
+	/* Whatever we checked last time doesn't need to be re-checked. */
+	pn = a->name;
+	p = a->path_safe.s;
+	while ((*pn != '\0') && (*p == *pn))
+		++p, ++pn;
+	/* Skip leading backslashes */
+	while (*pn == '\\')
+		++pn;
+	c = pn[0];
+	/* Keep going until we've checked the entire name. */
+	while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) {
+		/* Skip the next path element. */
+		while (*pn != '\0' && *pn != '\\')
+			++pn;
+		c = pn[0];
+		pn[0] = '\0';
+		/* Check that we haven't hit a symlink. */
+		r = file_information(a, a->name, &st, &st_mode, 1);
+		if (r != 0) {
+			/* We've hit a dir that doesn't exist; stop now. */
+			if (errno == ENOENT)
+				break;
+		} else if (S_ISLNK(st_mode)) {
+			if (c == '\0') {
+				/*
+				 * Last element is a file or directory symlink.
+				 * Remove it so we can overwrite it with the
+				 * item being extracted.
+				 */
+				if (a->flags &
+				    ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+					(void)clear_nochange_fflags(a);
+				}
+				if (st.dwFileAttributes &
+				    FILE_ATTRIBUTE_DIRECTORY) {
+					r = disk_rmdir(a->name);
+				} else {
+					r = disk_unlink(a->name);
+				}
+				if (r) {
+					archive_set_error(&a->archive, errno,
+					    "Could not remove symlink %ls",
+					    a->name);
+					pn[0] = c;
+					return (ARCHIVE_FAILED);
+				}
+				a->pst = NULL;
+				/*
+				 * Even if we did remove it, a warning
+				 * is in order.  The warning is silly,
+				 * though, if we're just replacing one
+				 * symlink with another symlink.
+				 */
+				if (!S_ISLNK(a->mode)) {
+					archive_set_error(&a->archive, 0,
+					    "Removing symlink %ls",
+					    a->name);
+				}
+				/* Symlink gone.  No more problem! */
+				pn[0] = c;
+				return (0);
+			} else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
+				/* User asked us to remove problems. */
+				if (a->flags &
+				    ARCHIVE_EXTRACT_CLEAR_NOCHANGE_FFLAGS) {
+					(void)clear_nochange_fflags(a);
+				}
+				if (st.dwFileAttributes &
+				    FILE_ATTRIBUTE_DIRECTORY) {
+					r = disk_rmdir(a->name);
+				} else {
+					r = disk_unlink(a->name);
+				}
+				if (r != 0) {
+					archive_set_error(&a->archive, 0,
+					    "Cannot remove intervening "
+					    "symlink %ls", a->name);
+					pn[0] = c;
+					return (ARCHIVE_FAILED);
+				}
+				a->pst = NULL;
+			} else {
+				archive_set_error(&a->archive, 0,
+				    "Cannot extract through symlink %ls",
+				    a->name);
+				pn[0] = c;
+				return (ARCHIVE_FAILED);
+			}
+		}
+		pn[0] = c;
+		pn++;
+	}
+	pn[0] = c;
+	/* We've checked and/or cleaned the whole path, so remember it. */
+	archive_wstrcpy(&a->path_safe, a->name);
+	return (ARCHIVE_OK);
+}
+
+static int
+guidword(wchar_t *p, int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++) {
+		if ((*p >= L'0' && *p <= L'9') ||
+		    (*p >= L'a' && *p <= L'f') ||
+		    (*p >= L'A' && *p <= L'F'))
+			p++;
+		else
+			return (-1);
+	}
+	return (0);
+}
+
+/*
+ * Canonicalize the pathname.  In particular, this strips duplicate
+ * '\' characters, '.' elements, and trailing '\'.  It also raises an
+ * error for an empty path, a trailing '..' or (if _SECURE_NODOTDOT is
+ * set) any '..' in the path.
+ */
+static int
+cleanup_pathname(struct archive_write_disk *a)
+{
+	wchar_t *dest, *src, *p, *top;
+	wchar_t separator = L'\0';
+
+	p = a->name;
+	if (*p == L'\0') {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid empty pathname");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Replace '/' by '\' */
+	for (; *p != L'\0'; p++) {
+		if (*p == L'/')
+			*p = L'\\';
+	}
+	p = a->name;
+
+	/* Skip leading "\\.\" or "\\?\" or "\\?\UNC\" or
+	 * "\\?\Volume{GUID}\"
+	 * (absolute path prefixes used by Windows API) */
+	if (p[0] == L'\\' && p[1] == L'\\' &&
+	    (p[2] == L'.' || p[2] == L'?') && p[3] ==  L'\\')
+	{
+		/* A path begin with "\\?\UNC\" */
+		if (p[2] == L'?' &&
+		    (p[4] == L'U' || p[4] == L'u') &&
+		    (p[5] == L'N' || p[5] == L'n') &&
+		    (p[6] == L'C' || p[6] == L'c') &&
+		    p[7] == L'\\')
+			p += 8;
+		/* A path begin with "\\?\Volume{GUID}\" */
+		else if (p[2] == L'?' &&
+		    (p[4] == L'V' || p[4] == L'v') &&
+		    (p[5] == L'O' || p[5] == L'o') &&
+		    (p[6] == L'L' || p[6] == L'l') &&
+		    (p[7] == L'U' || p[7] == L'u') &&
+		    (p[8] == L'M' || p[8] == L'm') &&
+		    (p[9] == L'E' || p[9] == L'e') &&
+		    p[10] == L'{') {
+			if (guidword(p+11, 8) == 0 && p[19] == L'-' &&
+			    guidword(p+20, 4) == 0 && p[24] == L'-' &&
+			    guidword(p+25, 4) == 0 && p[29] == L'-' &&
+			    guidword(p+30, 4) == 0 && p[34] == L'-' &&
+			    guidword(p+35, 12) == 0 && p[47] == L'}' &&
+			    p[48] == L'\\')
+				p += 49;
+			else
+				p += 4;
+		/* A path begin with "\\.\PhysicalDriveX" */
+		} else if (p[2] == L'.' &&
+		    (p[4] == L'P' || p[4] == L'p') &&
+		    (p[5] == L'H' || p[5] == L'h') &&
+		    (p[6] == L'Y' || p[6] == L'y') &&
+		    (p[7] == L'S' || p[7] == L's') &&
+		    (p[8] == L'I' || p[8] == L'i') &&
+		    (p[9] == L'C' || p[9] == L'c') &&
+		    (p[9] == L'A' || p[9] == L'a') &&
+		    (p[9] == L'L' || p[9] == L'l') &&
+		    (p[9] == L'D' || p[9] == L'd') &&
+		    (p[9] == L'R' || p[9] == L'r') &&
+		    (p[9] == L'I' || p[9] == L'i') &&
+		    (p[9] == L'V' || p[9] == L'v') &&
+		    (p[9] == L'E' || p[9] == L'e') &&
+		    (p[10] >= L'0' && p[10] <= L'9') &&
+		    p[11] == L'\0') {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Path is a physical drive name");
+			return (ARCHIVE_FAILED);
+		} else
+			p += 4;
+	}
+
+	/* Skip leading drive letter from archives created
+	 * on Windows. */
+	if (((p[0] >= L'a' && p[0] <= L'z') ||
+	     (p[0] >= L'A' && p[0] <= L'Z')) &&
+		 p[1] == L':') {
+		if (p[2] == L'\0') {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Path is a drive name");
+			return (ARCHIVE_FAILED);
+		}
+		if (p[2] == L'\\')
+			p += 2;
+	}
+
+	top = dest = src = p;
+	/* Rewrite the path name if its character is a unusable. */
+	for (; *p != L'\0'; p++) {
+		if (*p == L':' || *p == L'*' || *p == L'?' || *p == L'"' ||
+		    *p == L'<' || *p == L'>' || *p == L'|')
+			*p = L'_';
+	}
+	/* Skip leading '\'. */
+	if (*src == L'\\')
+		separator = *src++;
+
+	/* Scan the pathname one element at a time. */
+	for (;;) {
+		/* src points to first char after '\' */
+		if (src[0] == L'\0') {
+			break;
+		} else if (src[0] == L'\\') {
+			/* Found '\\'('//'), ignore second one. */
+			src++;
+			continue;
+		} else if (src[0] == L'.') {
+			if (src[1] == L'\0') {
+				/* Ignore trailing '.' */
+				break;
+			} else if (src[1] == L'\\') {
+				/* Skip '.\'. */
+				src += 2;
+				continue;
+			} else if (src[1] == L'.') {
+				if (src[2] == L'\\' || src[2] == L'\0') {
+					/* Conditionally warn about '..' */
+					if (a->flags &
+					    ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
+						archive_set_error(&a->archive,
+						    ARCHIVE_ERRNO_MISC,
+						    "Path contains '..'");
+						return (ARCHIVE_FAILED);
+					}
+				}
+				/*
+				 * Note: Under no circumstances do we
+				 * remove '..' elements.  In
+				 * particular, restoring
+				 * '\foo\..\bar\' should create the
+				 * 'foo' dir as a side-effect.
+				 */
+			}
+		}
+
+		/* Copy current element, including leading '\'. */
+		if (separator)
+			*dest++ = L'\\';
+		while (*src != L'\0' && *src != L'\\') {
+			*dest++ = *src++;
+		}
+
+		if (*src == L'\0')
+			break;
+
+		/* Skip '\' separator. */
+		separator = *src++;
+	}
+	/*
+	 * We've just copied zero or more path elements, not including the
+	 * final '\'.
+	 */
+	if (dest == top) {
+		/*
+		 * Nothing got copied.  The path must have been something
+		 * like '.' or '\' or './' or '/././././/./'.
+		 */
+		if (separator)
+			*dest++ = L'\\';
+		else
+			*dest++ = L'.';
+	}
+	/* Terminate the result. */
+	*dest = L'\0';
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Create the parent directory of the specified path, assuming path
+ * is already in mutable storage.
+ */
+static int
+create_parent_dir(struct archive_write_disk *a, wchar_t *path)
+{
+	wchar_t *slash;
+	int r;
+
+	/* Remove tail element to obtain parent name. */
+	slash = wcsrchr(path, L'\\');
+	if (slash == NULL)
+		return (ARCHIVE_OK);
+	*slash = L'\0';
+	r = create_dir(a, path);
+	*slash = L'\\';
+	return (r);
+}
+
+/*
+ * Create the specified dir, recursing to create parents as necessary.
+ *
+ * Returns ARCHIVE_OK if the path exists when we're done here.
+ * Otherwise, returns ARCHIVE_FAILED.
+ * Assumes path is in mutable storage; path is unchanged on exit.
+ */
+static int
+create_dir(struct archive_write_disk *a, wchar_t *path)
+{
+	BY_HANDLE_FILE_INFORMATION st;
+	struct fixup_entry *le;
+	wchar_t *slash, *base, *full;
+	mode_t mode_final, mode, st_mode;
+	int r;
+
+	/* Check for special names and just skip them. */
+	slash = wcsrchr(path, L'\\');
+	if (slash == NULL)
+		base = path;
+	else
+		base = slash + 1;
+
+	if (base[0] == L'\0' ||
+	    (base[0] == L'.' && base[1] == L'\0') ||
+	    (base[0] == L'.' && base[1] == L'.' && base[2] == L'\0')) {
+		/* Don't bother trying to create null path, '.', or '..'. */
+		if (slash != NULL) {
+			*slash = L'\0';
+			r = create_dir(a, path);
+			*slash = L'\\';
+			return (r);
+		}
+		return (ARCHIVE_OK);
+	}
+
+	/*
+	 * Yes, this should be stat() and not lstat().  Using lstat()
+	 * here loses the ability to extract through symlinks.  Also note
+	 * that this should not use the a->st cache.
+	 */
+	if (file_information(a, path, &st, &st_mode, 0) == 0) {
+		if (S_ISDIR(st_mode))
+			return (ARCHIVE_OK);
+		if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) {
+			archive_set_error(&a->archive, EEXIST,
+			    "Can't create directory '%ls'", path);
+			return (ARCHIVE_FAILED);
+		}
+		if (disk_unlink(path) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't create directory '%ls': "
+			    "Conflicting file cannot be removed",
+			    path);
+			return (ARCHIVE_FAILED);
+		}
+	} else if (errno != ENOENT && errno != ENOTDIR) {
+		/* Stat failed? */
+		archive_set_error(&a->archive, errno,
+		    "Can't test directory '%ls'", path);
+		return (ARCHIVE_FAILED);
+	} else if (slash != NULL) {
+		*slash = '\0';
+		r = create_dir(a, path);
+		*slash = '\\';
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+
+	/*
+	 * Mode we want for the final restored directory.  Per POSIX,
+	 * implicitly-created dirs must be created obeying the umask.
+	 * There's no mention whether this is different for privileged
+	 * restores (which the rest of this code handles by pretending
+	 * umask=0).  I've chosen here to always obey the user's umask for
+	 * implicit dirs, even if _EXTRACT_PERM was specified.
+	 */
+	mode_final = DEFAULT_DIR_MODE & ~a->user_umask;
+	/* Mode we want on disk during the restore process. */
+	mode = mode_final;
+	mode |= MINIMUM_DIR_MODE;
+	mode &= MAXIMUM_DIR_MODE;
+	/*
+	 * Apply __la_win_permissive_name_w to path in order to
+	 * remove '../' path string.
+	 */
+	full = __la_win_permissive_name_w(path);
+	if (full == NULL)
+		errno = EINVAL;
+	else if (CreateDirectoryW(full, NULL) != 0) {
+		if (mode != mode_final) {
+			le = new_fixup(a, path);
+			le->fixup |=TODO_MODE_BASE;
+			le->mode = mode_final;
+		}
+		free(full);
+		return (ARCHIVE_OK);
+	} else {
+		la_dosmaperr(GetLastError());
+	}
+	free(full);
+
+	/*
+	 * Without the following check, a/b/../b/c/d fails at the
+	 * second visit to 'b', so 'd' can't be created.  Note that we
+	 * don't add it to the fixup list here, as it's already been
+	 * added.
+	 */
+	if (file_information(a, path, &st, &st_mode, 0) == 0 &&
+	    S_ISDIR(st_mode))
+		return (ARCHIVE_OK);
+
+	archive_set_error(&a->archive, errno, "Failed to create dir '%ls'",
+	    path);
+	return (ARCHIVE_FAILED);
+}
+
+/*
+ * Note: Although we can skip setting the user id if the desired user
+ * id matches the current user, we cannot skip setting the group, as
+ * many systems set the gid based on the containing directory.  So
+ * we have to perform a chown syscall if we want to set the SGID
+ * bit.  (The alternative is to stat() and then possibly chown(); it's
+ * more efficient to skip the stat() and just always chown().)  Note
+ * that a successful chown() here clears the TODO_SGID_CHECK bit, which
+ * allows set_mode to skip the stat() check for the GID.
+ */
+static int
+set_ownership(struct archive_write_disk *a)
+{
+/* unfortunately, on win32 there is no 'root' user with uid 0,
+   so we just have to try the chown and see if it works */
+
+	/* If we know we can't change it, don't bother trying. */
+	if (a->user_uid != 0  &&  a->user_uid != a->uid) {
+		archive_set_error(&a->archive, errno,
+		    "Can't set UID=%jd", (intmax_t)a->uid);
+		return (ARCHIVE_WARN);
+	}
+
+	archive_set_error(&a->archive, errno,
+	    "Can't set user=%jd/group=%jd for %ls",
+	    (intmax_t)a->uid, (intmax_t)a->gid, a->name);
+	return (ARCHIVE_WARN);
+}
+
+static int
+set_times(struct archive_write_disk *a,
+    HANDLE h, int mode, const wchar_t *name,
+    time_t atime, long atime_nanos,
+    time_t birthtime, long birthtime_nanos,
+    time_t mtime, long mtime_nanos,
+    time_t ctime_sec, long ctime_nanos)
+{
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+#define WINTIME(sec, nsec) ((Int32x32To64(sec, 10000000) + EPOC_TIME)\
+	 + (((nsec)/1000)*10))
+
+	HANDLE hw = 0;
+	ULARGE_INTEGER wintm;
+	FILETIME *pfbtime;
+	FILETIME fatime, fbtime, fmtime;
+
+	(void)ctime_sec; /* UNUSED */
+	(void)ctime_nanos; /* UNUSED */
+
+	if (h != INVALID_HANDLE_VALUE) {
+		hw = NULL;
+	} else {
+		wchar_t *ws;
+
+		if (S_ISLNK(mode))
+			return (ARCHIVE_OK);
+		ws = __la_win_permissive_name_w(name);
+		if (ws == NULL)
+			goto settimes_failed;
+		hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES,
+		    0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+		free(ws);
+		if (hw == INVALID_HANDLE_VALUE)
+			goto settimes_failed;
+		h = hw;
+	}
+
+	wintm.QuadPart = WINTIME(atime, atime_nanos);
+	fatime.dwLowDateTime = wintm.LowPart;
+	fatime.dwHighDateTime = wintm.HighPart;
+	wintm.QuadPart = WINTIME(mtime, mtime_nanos);
+	fmtime.dwLowDateTime = wintm.LowPart;
+	fmtime.dwHighDateTime = wintm.HighPart;
+	/*
+	 * SetFileTime() supports birthtime.
+	 */
+	if (birthtime > 0 || birthtime_nanos > 0) {
+		wintm.QuadPart = WINTIME(birthtime, birthtime_nanos);
+		fbtime.dwLowDateTime = wintm.LowPart;
+		fbtime.dwHighDateTime = wintm.HighPart;
+		pfbtime = &fbtime;
+	} else
+		pfbtime = NULL;
+	if (SetFileTime(h, pfbtime, &fatime, &fmtime) == 0)
+		goto settimes_failed;
+	CloseHandle(hw);
+	return (ARCHIVE_OK);
+
+settimes_failed:
+	CloseHandle(hw);
+	archive_set_error(&a->archive, EINVAL, "Can't restore time");
+	return (ARCHIVE_WARN);
+}
+
+static int
+set_times_from_entry(struct archive_write_disk *a)
+{
+	time_t atime, birthtime, mtime, ctime_sec;
+	long atime_nsec, birthtime_nsec, mtime_nsec, ctime_nsec;
+
+	/* Suitable defaults. */
+	atime = birthtime = mtime = ctime_sec = a->start_time;
+	atime_nsec = birthtime_nsec = mtime_nsec = ctime_nsec = 0;
+
+	/* If no time was provided, we're done. */
+	if (!archive_entry_atime_is_set(a->entry)
+	    && !archive_entry_birthtime_is_set(a->entry)
+	    && !archive_entry_mtime_is_set(a->entry))
+		return (ARCHIVE_OK);
+
+	if (archive_entry_atime_is_set(a->entry)) {
+		atime = archive_entry_atime(a->entry);
+		atime_nsec = archive_entry_atime_nsec(a->entry);
+	}
+	if (archive_entry_birthtime_is_set(a->entry)) {
+		birthtime = archive_entry_birthtime(a->entry);
+		birthtime_nsec = archive_entry_birthtime_nsec(a->entry);
+	}
+	if (archive_entry_mtime_is_set(a->entry)) {
+		mtime = archive_entry_mtime(a->entry);
+		mtime_nsec = archive_entry_mtime_nsec(a->entry);
+	}
+	if (archive_entry_ctime_is_set(a->entry)) {
+		ctime_sec = archive_entry_ctime(a->entry);
+		ctime_nsec = archive_entry_ctime_nsec(a->entry);
+	}
+
+	return set_times(a, a->fh, a->mode, a->name,
+			 atime, atime_nsec,
+			 birthtime, birthtime_nsec,
+			 mtime, mtime_nsec,
+			 ctime_sec, ctime_nsec);
+}
+
+static int
+set_mode(struct archive_write_disk *a, int mode)
+{
+	int r = ARCHIVE_OK;
+	mode &= 07777; /* Strip off file type bits. */
+
+	if (a->todo & TODO_SGID_CHECK) {
+		/*
+		 * If we don't know the GID is right, we must stat()
+		 * to verify it.  We can't just check the GID of this
+		 * process, since systems sometimes set GID from
+		 * the enclosing dir or based on ACLs.
+		 */
+		if ((r = lazy_stat(a)) != ARCHIVE_OK)
+			return (r);
+		if (0 != a->gid) {
+			mode &= ~ S_ISGID;
+		}
+		/* While we're here, double-check the UID. */
+		if (0 != a->uid
+		    && (a->todo & TODO_SUID)) {
+			mode &= ~ S_ISUID;
+		}
+		a->todo &= ~TODO_SGID_CHECK;
+		a->todo &= ~TODO_SUID_CHECK;
+	} else if (a->todo & TODO_SUID_CHECK) {
+		/*
+		 * If we don't know the UID is right, we can just check
+		 * the user, since all systems set the file UID from
+		 * the process UID.
+		 */
+		if (a->user_uid != a->uid) {
+			mode &= ~ S_ISUID;
+		}
+		a->todo &= ~TODO_SUID_CHECK;
+	}
+
+	if (S_ISLNK(a->mode)) {
+#ifdef HAVE_LCHMOD
+		/*
+		 * If this is a symlink, use lchmod().  If the
+		 * platform doesn't support lchmod(), just skip it.  A
+		 * platform that doesn't provide a way to set
+		 * permissions on symlinks probably ignores
+		 * permissions on symlinks, so a failure here has no
+		 * impact.
+		 */
+		if (lchmod(a->name, mode) != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't set permissions to 0%o", (int)mode);
+			r = ARCHIVE_WARN;
+		}
+#endif
+	} else if (!S_ISDIR(a->mode)) {
+		/*
+		 * If it's not a symlink and not a dir, then use
+		 * fchmod() or chmod(), depending on whether we have
+		 * an fd.  Dirs get their perms set during the
+		 * post-extract fixup, which is handled elsewhere.
+		 */
+#ifdef HAVE_FCHMOD
+		if (a->fd >= 0) {
+			if (fchmod(a->fd, mode) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Can't set permissions to 0%o", (int)mode);
+				r = ARCHIVE_WARN;
+			}
+		} else
+#endif
+			/* If this platform lacks fchmod(), then
+			 * we'll just use chmod(). */
+			if (la_chmod(a->name, mode) != 0) {
+				archive_set_error(&a->archive, errno,
+				    "Can't set permissions to 0%o", (int)mode);
+				r = ARCHIVE_WARN;
+			}
+	}
+	return (r);
+}
+
+static int set_fflags_platform(const wchar_t *name, unsigned long fflags_set,
+    unsigned long fflags_clear)
+{
+	DWORD oldflags, newflags;
+	wchar_t *fullname;
+
+	const DWORD settable_flags =
+	    FILE_ATTRIBUTE_ARCHIVE |
+	    FILE_ATTRIBUTE_HIDDEN |
+	    FILE_ATTRIBUTE_NORMAL |
+	    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED |
+	    FILE_ATTRIBUTE_OFFLINE |
+	    FILE_ATTRIBUTE_READONLY |
+	    FILE_ATTRIBUTE_SYSTEM |
+	    FILE_ATTRIBUTE_TEMPORARY;
+
+	oldflags = GetFileAttributesW(name);
+	if (oldflags == (DWORD)-1 &&
+	    GetLastError() == ERROR_INVALID_NAME) {
+		fullname = __la_win_permissive_name_w(name);
+		oldflags = GetFileAttributesW(fullname);
+	}
+	if (oldflags == (DWORD)-1) {
+		la_dosmaperr(GetLastError());
+		return (ARCHIVE_WARN);
+	}
+	newflags = ((oldflags & ~fflags_clear) | fflags_set) & settable_flags;
+	if(SetFileAttributesW(name, newflags) == 0)
+		return (ARCHIVE_WARN);
+	return (ARCHIVE_OK);
+}
+
+static int
+clear_nochange_fflags(struct archive_write_disk *a)
+{
+	return (set_fflags_platform(a->name, 0, FILE_ATTRIBUTE_READONLY));
+}
+
+static int
+set_fflags(struct archive_write_disk *a)
+{
+	unsigned long	set, clear;
+
+	if (a->todo & TODO_FFLAGS) {
+		archive_entry_fflags(a->entry, &set, &clear);
+		if (set == 0  && clear == 0)
+			return (ARCHIVE_OK);
+		return (set_fflags_platform(a->name, set, clear));
+
+        }
+	return (ARCHIVE_OK);
+}
+
+/* Default empty function body to satisfy mainline code. */
+static int
+set_acls(struct archive_write_disk *a, HANDLE h, const wchar_t *name,
+	 struct archive_acl *acl)
+{
+	(void)a; /* UNUSED */
+	(void)h; /* UNUSED */
+	(void)name; /* UNUSED */
+	(void)acl; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Restore extended attributes - stub implementation for unsupported systems
+ */
+static int
+set_xattrs(struct archive_write_disk *a)
+{
+	static int warning_done = 0;
+
+	/* If there aren't any extended attributes, then it's okay not
+	 * to extract them, otherwise, issue a single warning. */
+	if (archive_entry_xattr_count(a->entry) != 0 && !warning_done) {
+		warning_done = 1;
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Cannot restore extended attributes on this system");
+		return (ARCHIVE_WARN);
+	}
+	/* Warning was already emitted; suppress further warnings. */
+	return (ARCHIVE_OK);
+}
+
+static void
+fileTimeToUtc(const FILETIME *filetime, time_t *t, long *ns)
+{
+	ULARGE_INTEGER utc;
+
+	utc.HighPart = filetime->dwHighDateTime;
+	utc.LowPart  = filetime->dwLowDateTime;
+	if (utc.QuadPart >= EPOC_TIME) {
+		utc.QuadPart -= EPOC_TIME;
+		/* milli seconds base */
+		*t = (time_t)(utc.QuadPart / 10000000);
+		/* nano seconds base */
+		*ns = (long)(utc.QuadPart % 10000000) * 100;
+	} else {
+		*t = 0;
+		*ns = 0;
+	}
+}
+/*
+ * Test if file on disk is older than entry.
+ */
+static int
+older(BY_HANDLE_FILE_INFORMATION *st, struct archive_entry *entry)
+{
+	time_t sec;
+	long nsec;
+
+	fileTimeToUtc(&st->ftLastWriteTime, &sec, &nsec);
+	/* First, test the seconds and return if we have a definite answer. */
+	/* Definitely older. */
+	if (sec < archive_entry_mtime(entry))
+		return (1);
+	/* Definitely younger. */
+	if (sec > archive_entry_mtime(entry))
+		return (0);
+	if (nsec < archive_entry_mtime_nsec(entry))
+		return (1);
+	/* Same age or newer, so not older. */
+	return (0);
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
+
diff --git a/c/archive_write_open_fd.c b/c/archive_write_open_fd.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_open_fd.c
@@ -0,0 +1,144 @@
+/*-
+ * 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: head/lib/libarchive/archive_write_open_fd.c 201093 2009-12-28 02:28:44Z kientzle $");
+
+#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_IO_H
+#include <io.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 write_fd_data {
+	int		fd;
+};
+
+static int	file_close(struct archive *, void *);
+static int	file_open(struct archive *, void *);
+static ssize_t	file_write(struct archive *, void *, const void *buff, size_t);
+
+int
+archive_write_open_fd(struct archive *a, int fd)
+{
+	struct write_fd_data *mine;
+
+	mine = (struct write_fd_data *)malloc(sizeof(*mine));
+	if (mine == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		return (ARCHIVE_FATAL);
+	}
+	mine->fd = fd;
+#if defined(__CYGWIN__) || defined(_WIN32)
+	setmode(mine->fd, O_BINARY);
+#endif
+	return (archive_write_open(a, mine,
+		    file_open, file_write, file_close));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+	struct write_fd_data *mine;
+	struct stat st;
+
+	mine = (struct write_fd_data *)client_data;
+
+	if (fstat(mine->fd, &st) != 0) {
+		archive_set_error(a, errno, "Couldn't stat fd %d", mine->fd);
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * If this is a regular file, don't add it to itself.
+	 */
+	if (S_ISREG(st.st_mode))
+		archive_write_set_skip_file(a, st.st_dev, st.st_ino);
+
+	/*
+	 * If client hasn't explicitly set the last block handling,
+	 * then set it here.
+	 */
+	if (archive_write_get_bytes_in_last_block(a) < 0) {
+		/* If the output is a block or character device, fifo,
+		 * or stdout, pad the last block, otherwise leave it
+		 * unpadded. */
+		if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
+		    S_ISFIFO(st.st_mode) || (mine->fd == 1))
+			/* Last block will be fully padded. */
+			archive_write_set_bytes_in_last_block(a, 0);
+		else
+			archive_write_set_bytes_in_last_block(a, 1);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_write(struct archive *a, void *client_data, const void *buff, size_t length)
+{
+	struct write_fd_data	*mine;
+	ssize_t	bytesWritten;
+
+	mine = (struct write_fd_data *)client_data;
+	for (;;) {
+		bytesWritten = write(mine->fd, buff, length);
+		if (bytesWritten <= 0) {
+			if (errno == EINTR)
+				continue;
+			archive_set_error(a, errno, "Write error");
+			return (-1);
+		}
+		return (bytesWritten);
+	}
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+	struct write_fd_data	*mine = (struct write_fd_data *)client_data;
+
+	(void)a; /* UNUSED */
+	free(mine);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_write_open_file.c b/c/archive_write_open_file.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_open_file.c
@@ -0,0 +1,109 @@
+/*-
+ * 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_write_open_file.c,v 1.19 2007/01/09 08:05:56 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 write_FILE_data {
+	FILE		*f;
+};
+
+static int	file_close(struct archive *, void *);
+static int	file_open(struct archive *, void *);
+static ssize_t	file_write(struct archive *, void *, const void *buff, size_t);
+
+int
+archive_write_open_FILE(struct archive *a, FILE *f)
+{
+	struct write_FILE_data *mine;
+
+	mine = (struct write_FILE_data *)malloc(sizeof(*mine));
+	if (mine == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		return (ARCHIVE_FATAL);
+	}
+	mine->f = f;
+	return (archive_write_open(a, mine,
+		    file_open, file_write, file_close));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+	(void)a; /* UNUSED */
+	(void)client_data; /* UNUSED */
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_write(struct archive *a, void *client_data, const void *buff, size_t length)
+{
+	struct write_FILE_data	*mine;
+	size_t	bytesWritten;
+
+	mine = client_data;
+	for (;;) {
+		bytesWritten = fwrite(buff, 1, length, mine->f);
+		if (bytesWritten <= 0) {
+			if (errno == EINTR)
+				continue;
+			archive_set_error(a, errno, "Write error");
+			return (-1);
+		}
+		return (bytesWritten);
+	}
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+	struct write_FILE_data	*mine = client_data;
+
+	(void)a; /* UNUSED */
+	free(mine);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_write_open_filename.c b/c/archive_write_open_filename.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_open_filename.c
@@ -0,0 +1,253 @@
+/*-
+ * 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: head/lib/libarchive/archive_write_open_filename.c 191165 2009-04-17 00:39:35Z kientzle $");
+
+#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"
+#include "archive_private.h"
+#include "archive_string.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC	0
+#endif
+
+struct write_file_data {
+	int		fd;
+	struct archive_mstring filename;
+};
+
+static int	file_close(struct archive *, void *);
+static int	file_open(struct archive *, void *);
+static ssize_t	file_write(struct archive *, void *, const void *buff, size_t);
+static int	open_filename(struct archive *, int, const void *);
+
+int
+archive_write_open_file(struct archive *a, const char *filename)
+{
+	return (archive_write_open_filename(a, filename));
+}
+
+int
+archive_write_open_filename(struct archive *a, const char *filename)
+{
+
+	if (filename == NULL || filename[0] == '\0')
+		return (archive_write_open_fd(a, 1));
+
+	return (open_filename(a, 1, filename));
+}
+
+int
+archive_write_open_filename_w(struct archive *a, const wchar_t *filename)
+{
+
+	if (filename == NULL || filename[0] == L'\0')
+		return (archive_write_open_fd(a, 1));
+
+	return (open_filename(a, 0, filename));
+}
+
+static int
+open_filename(struct archive *a, int mbs_fn, const void *filename)
+{
+	struct write_file_data *mine;
+	int r;
+
+	mine = (struct write_file_data *)calloc(1, sizeof(*mine));
+	if (mine == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		return (ARCHIVE_FATAL);
+	}
+	if (mbs_fn)
+		r = archive_mstring_copy_mbs(&mine->filename, filename);
+	else
+		r = archive_mstring_copy_wcs(&mine->filename, filename);
+	if (r < 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(a, ENOMEM, "No memory");
+			return (ARCHIVE_FATAL);
+		}
+		if (mbs_fn)
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Can't convert '%s' to WCS",
+			    (const char *)filename);
+		else
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "Can't convert '%S' to MBS",
+			    (const wchar_t *)filename);
+		return (ARCHIVE_FAILED);
+	}
+	mine->fd = -1;
+	return (archive_write_open(a, mine,
+		file_open, file_write, file_close));
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+	int flags;
+	struct write_file_data *mine;
+	struct stat st;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	wchar_t *fullpath;
+#endif
+	const wchar_t *wcs;
+	const char *mbs;
+
+	mine = (struct write_file_data *)client_data;
+	flags = O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC;
+
+	/*
+	 * Open the file.
+	 */
+	mbs = NULL; wcs = NULL;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	if (archive_mstring_get_wcs(a, &mine->filename, &wcs) != 0) {
+		if (errno == ENOMEM)
+			archive_set_error(a, errno, "No memory");
+		else {
+			archive_mstring_get_mbs(a, &mine->filename, &mbs);
+			archive_set_error(a, errno,
+			    "Can't convert '%s' to WCS", mbs);
+		}
+		return (ARCHIVE_FATAL);
+	}
+	fullpath = __la_win_permissive_name_w(wcs);
+	if (fullpath != NULL) {
+		mine->fd = _wopen(fullpath, flags, 0666);
+		free(fullpath);
+	} else
+		mine->fd = _wopen(wcs, flags, 0666);
+#else
+	if (archive_mstring_get_mbs(a, &mine->filename, &mbs) != 0) {
+		if (errno == ENOMEM)
+			archive_set_error(a, errno, "No memory");
+		else {
+			archive_mstring_get_wcs(a, &mine->filename, &wcs);
+			archive_set_error(a, errno,
+			    "Can't convert '%S' to MBS", wcs);
+		}
+		return (ARCHIVE_FATAL);
+	}
+	mine->fd = open(mbs, flags, 0666);
+	__archive_ensure_cloexec_flag(mine->fd);
+#endif
+	if (mine->fd < 0) {
+		if (mbs != NULL)
+			archive_set_error(a, errno, "Failed to open '%s'", mbs);
+		else
+			archive_set_error(a, errno, "Failed to open '%S'", wcs);
+		return (ARCHIVE_FATAL);
+	}
+
+	if (fstat(mine->fd, &st) != 0) {
+		if (mbs != NULL)
+			archive_set_error(a, errno, "Couldn't stat '%s'", mbs);
+		else
+			archive_set_error(a, errno, "Couldn't stat '%S'", wcs);
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Set up default last block handling.
+	 */
+	if (archive_write_get_bytes_in_last_block(a) < 0) {
+		if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
+		    S_ISFIFO(st.st_mode))
+			/* Pad last block when writing to device or FIFO. */
+			archive_write_set_bytes_in_last_block(a, 0);
+		else
+			/* Don't pad last block otherwise. */
+			archive_write_set_bytes_in_last_block(a, 1);
+	}
+
+	/*
+	 * If the output file is a regular file, don't add it to
+	 * itself.  If it's a device file, it's okay to add the device
+	 * entry to the output archive.
+	 */
+	if (S_ISREG(st.st_mode))
+		archive_write_set_skip_file(a, st.st_dev, st.st_ino);
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+file_write(struct archive *a, void *client_data, const void *buff,
+    size_t length)
+{
+	struct write_file_data	*mine;
+	ssize_t	bytesWritten;
+
+	mine = (struct write_file_data *)client_data;
+	for (;;) {
+		bytesWritten = write(mine->fd, buff, length);
+		if (bytesWritten <= 0) {
+			if (errno == EINTR)
+				continue;
+			archive_set_error(a, errno, "Write error");
+			return (-1);
+		}
+		return (bytesWritten);
+	}
+}
+
+static int
+file_close(struct archive *a, void *client_data)
+{
+	struct write_file_data	*mine = (struct write_file_data *)client_data;
+
+	(void)a; /* UNUSED */
+
+	if (mine->fd >= 0)
+		close(mine->fd);
+
+	archive_mstring_clean(&mine->filename);
+	free(mine);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_write_open_memory.c b/c/archive_write_open_memory.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_open_memory.c
@@ -0,0 +1,113 @@
+/*-
+ * 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_write_open_memory.c,v 1.3 2007/01/09 08:05:56 kientzle Exp $");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+
+struct write_memory_data {
+	size_t	used;
+	size_t  size;
+	size_t * client_size;
+	unsigned char * buff;
+};
+
+static int	memory_write_close(struct archive *, void *);
+static int	memory_write_open(struct archive *, void *);
+static ssize_t	memory_write(struct archive *, void *, const void *buff, size_t);
+
+/*
+ * Client provides a pointer to a block of memory to receive
+ * the data.  The 'size' param both tells us the size of the
+ * client buffer and lets us tell the client the final size.
+ */
+int
+archive_write_open_memory(struct archive *a, void *buff, size_t buffSize, size_t *used)
+{
+	struct write_memory_data *mine;
+
+	mine = (struct write_memory_data *)calloc(1, sizeof(*mine));
+	if (mine == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		return (ARCHIVE_FATAL);
+	}
+	mine->buff = buff;
+	mine->size = buffSize;
+	mine->client_size = used;
+	return (archive_write_open(a, mine,
+		    memory_write_open, memory_write, memory_write_close));
+}
+
+static int
+memory_write_open(struct archive *a, void *client_data)
+{
+	struct write_memory_data *mine;
+	mine = client_data;
+	mine->used = 0;
+	if (mine->client_size != NULL)
+		*mine->client_size = mine->used;
+	/* Disable padding if it hasn't been set explicitly. */
+	if (-1 == archive_write_get_bytes_in_last_block(a))
+		archive_write_set_bytes_in_last_block(a, 1);
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Copy the data into the client buffer.
+ * Note that we update mine->client_size on every write.
+ * In particular, this means the client can follow exactly
+ * how much has been written into their buffer at any time.
+ */
+static ssize_t
+memory_write(struct archive *a, void *client_data, const void *buff, size_t length)
+{
+	struct write_memory_data *mine;
+	mine = client_data;
+
+	if (mine->used + length > mine->size) {
+		archive_set_error(a, ENOMEM, "Buffer exhausted");
+		return (ARCHIVE_FATAL);
+	}
+	memcpy(mine->buff + mine->used, buff, length);
+	mine->used += length;
+	if (mine->client_size != NULL)
+		*mine->client_size = mine->used;
+	return (length);
+}
+
+static int
+memory_write_close(struct archive *a, void *client_data)
+{
+	struct write_memory_data *mine;
+	(void)a; /* UNUSED */
+	mine = client_data;
+	free(mine);
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_write_private.h b/c/archive_write_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_write_private.h
@@ -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.
+ *
+ * $FreeBSD: head/lib/libarchive/archive_write_private.h 201155 2009-12-29 05:20:12Z kientzle $
+ */
+
+#ifndef ARCHIVE_WRITE_PRIVATE_H_INCLUDED
+#define ARCHIVE_WRITE_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include "archive.h"
+#include "archive_string.h"
+#include "archive_private.h"
+
+#define	ARCHIVE_WRITE_FILTER_STATE_NEW		1U
+#define	ARCHIVE_WRITE_FILTER_STATE_OPEN		2U
+#define	ARCHIVE_WRITE_FILTER_STATE_CLOSED	4U
+#define	ARCHIVE_WRITE_FILTER_STATE_FATAL	0x8000U
+
+struct archive_write;
+
+struct archive_write_filter {
+	int64_t bytes_written;
+	struct archive *archive; /* Associated archive. */
+	struct archive_write_filter *next_filter; /* Who I write to. */
+	int	(*options)(struct archive_write_filter *,
+	    const char *key, const char *value);
+	int	(*open)(struct archive_write_filter *);
+	int	(*write)(struct archive_write_filter *, const void *, size_t);
+	int	(*close)(struct archive_write_filter *);
+	int	(*free)(struct archive_write_filter *);
+	void	 *data;
+	const char *name;
+	int	  code;
+	int	  bytes_per_block;
+	int	  bytes_in_last_block;
+	int	  state;
+};
+
+#if ARCHIVE_VERSION < 4000000
+void __archive_write_filters_free(struct archive *);
+#endif
+
+struct archive_write_filter *__archive_write_allocate_filter(struct archive *);
+
+int __archive_write_output(struct archive_write *, const void *, size_t);
+int __archive_write_nulls(struct archive_write *, size_t);
+int __archive_write_filter(struct archive_write_filter *, const void *, size_t);
+
+struct archive_write {
+	struct archive	archive;
+
+	/* Dev/ino of the archive being written. */
+	int		  skip_file_set;
+	int64_t		  skip_file_dev;
+	int64_t		  skip_file_ino;
+
+	/* Utility:  Pointer to a block of nulls. */
+	const unsigned char	*nulls;
+	size_t			 null_length;
+
+	/* Callbacks to open/read/write/close archive stream. */
+	archive_open_callback	*client_opener;
+	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;
+
+	/*
+	 * First and last write filters in the pipeline.
+	 */
+	struct archive_write_filter *filter_first;
+	struct archive_write_filter *filter_last;
+
+	/*
+	 * Pointers to format-specific functions for writing.  They're
+	 * initialized by archive_write_set_format_XXX() calls.
+	 */
+	void	 *format_data;
+	const char *format_name;
+	int	(*format_init)(struct archive_write *);
+	int	(*format_options)(struct archive_write *,
+		    const char *key, const char *value);
+	int	(*format_finish_entry)(struct archive_write *);
+	int 	(*format_write_header)(struct archive_write *,
+		    struct archive_entry *);
+	ssize_t	(*format_write_data)(struct archive_write *,
+		    const void *buff, size_t);
+	int	(*format_close)(struct archive_write *);
+	int	(*format_free)(struct archive_write *);
+
+
+	/*
+	 * Encryption passphrase.
+	 */
+	char		*passphrase;
+	archive_passphrase_callback *passphrase_callback;
+	void		*passphrase_client_data;
+};
+
+/*
+ * Utility function to format a USTAR header into a buffer.  If
+ * "strict" is set, this tries to create the absolutely most portable
+ * version of a ustar header.  If "strict" is set to 0, then it will
+ * relax certain requirements.
+ *
+ * Generally, format-specific declarations don't belong in this
+ * header; this is a rare example of a function that is shared by
+ * two very similar formats (ustar and pax).
+ */
+int
+__archive_write_format_header_ustar(struct archive_write *, char buff[512],
+    struct archive_entry *, int tartype, int strict,
+    struct archive_string_conv *);
+
+struct archive_write_program_data;
+struct archive_write_program_data * __archive_write_program_allocate(const char *program_name);
+int	__archive_write_program_free(struct archive_write_program_data *);
+int	__archive_write_program_open(struct archive_write_filter *,
+	    struct archive_write_program_data *, const char *);
+int	__archive_write_program_close(struct archive_write_filter *,
+	    struct archive_write_program_data *);
+int	__archive_write_program_write(struct archive_write_filter *,
+	    struct archive_write_program_data *, const void *, size_t);
+
+/*
+ * Get a encryption passphrase.
+ */
+const char * __archive_write_get_passphrase(struct archive_write *a);
+#endif
diff --git a/c/archive_write_set_format.c b/c/archive_write_set_format.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format.c
@@ -0,0 +1,123 @@
+/*-
+ * 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: head/lib/libarchive/archive_write_set_format.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_write_set_format_private.h"
+
+/* A table that maps format codes to functions. */
+static const
+struct { int code; int (*setter)(struct archive *); } codes[] =
+{
+	{ ARCHIVE_FORMAT_7ZIP,		archive_write_set_format_7zip },
+	{ ARCHIVE_FORMAT_CPIO,		archive_write_set_format_cpio },
+	{ ARCHIVE_FORMAT_CPIO_POSIX,	archive_write_set_format_cpio },
+	{ ARCHIVE_FORMAT_CPIO_SVR4_NOCRC,	archive_write_set_format_cpio_newc },
+	{ ARCHIVE_FORMAT_ISO9660,	archive_write_set_format_iso9660 },
+	{ ARCHIVE_FORMAT_MTREE,		archive_write_set_format_mtree },
+	{ ARCHIVE_FORMAT_RAW,		archive_write_set_format_raw },
+	{ ARCHIVE_FORMAT_SHAR,		archive_write_set_format_shar },
+	{ ARCHIVE_FORMAT_SHAR_BASE,	archive_write_set_format_shar },
+	{ ARCHIVE_FORMAT_SHAR_DUMP,	archive_write_set_format_shar_dump },
+	{ ARCHIVE_FORMAT_TAR,	archive_write_set_format_pax_restricted },
+	{ ARCHIVE_FORMAT_TAR_GNUTAR,	archive_write_set_format_gnutar },
+	{ ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE, archive_write_set_format_pax },
+	{ ARCHIVE_FORMAT_TAR_PAX_RESTRICTED,
+				archive_write_set_format_pax_restricted },
+	{ ARCHIVE_FORMAT_TAR_USTAR,	archive_write_set_format_ustar },
+	{ ARCHIVE_FORMAT_WARC,		archive_write_set_format_warc },
+	{ ARCHIVE_FORMAT_XAR,		archive_write_set_format_xar },
+	{ ARCHIVE_FORMAT_ZIP,		archive_write_set_format_zip },
+	{ 0,		NULL }
+};
+
+int
+archive_write_set_format(struct archive *a, int code)
+{
+	int i;
+
+	for (i = 0; codes[i].code != 0; i++) {
+		if (code == codes[i].code)
+			return ((codes[i].setter)(a));
+	}
+
+	archive_set_error(a, EINVAL, "No such format");
+	return (ARCHIVE_FATAL);
+}
+
+void
+__archive_write_entry_filetype_unsupported(struct archive *a,
+    struct archive_entry *entry, const char *format)
+{
+	const char *name = NULL;
+
+	switch (archive_entry_filetype(entry)) {
+	/*
+	 * All formats should be able to archive regular files (AE_IFREG)
+	 */
+	case AE_IFDIR:
+		name = "directories";
+		break;
+	case AE_IFLNK:
+		name = "symbolic links";
+		break;
+	case AE_IFCHR:
+		name = "character devices";
+		break;
+	case AE_IFBLK:
+		name = "block devices";
+		break;
+	case AE_IFIFO:
+		name = "named pipes";
+		break;
+	case AE_IFSOCK:
+		name = "sockets";
+		break;
+	default:
+		break;
+	}
+
+	if (name != NULL) {
+		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "%s: %s format cannot archive %s",
+		    archive_entry_pathname(entry), format, name);
+	} else {
+		archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "%s: %s format cannot archive files with mode 0%lo",
+		    archive_entry_pathname(entry), format,
+		    (unsigned long)archive_entry_mode(entry));
+	}
+}
diff --git a/c/archive_write_set_format_7zip.c b/c/archive_write_set_format_7zip.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_7zip.c
@@ -0,0 +1,2317 @@
+/*-
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#ifndef HAVE_ZLIB_H
+#include "archive_crc32.h"
+#endif
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_ppmd7_private.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+/*
+ * Codec ID
+ */
+#define _7Z_COPY	0
+#define _7Z_LZMA1	0x030101
+#define _7Z_LZMA2	0x21
+#define _7Z_DEFLATE	0x040108
+#define _7Z_BZIP2	0x040202
+#define _7Z_PPMD	0x030401
+
+/*
+ * 7-Zip header property IDs.
+ */
+#define kEnd			0x00
+#define kHeader			0x01
+#define kArchiveProperties	0x02
+#define kAdditionalStreamsInfo	0x03
+#define kMainStreamsInfo	0x04
+#define kFilesInfo		0x05
+#define kPackInfo		0x06
+#define kUnPackInfo		0x07
+#define kSubStreamsInfo		0x08
+#define kSize			0x09
+#define kCRC			0x0A
+#define kFolder			0x0B
+#define kCodersUnPackSize	0x0C
+#define kNumUnPackStream	0x0D
+#define kEmptyStream		0x0E
+#define kEmptyFile		0x0F
+#define kAnti			0x10
+#define kName			0x11
+#define kCTime			0x12
+#define kATime			0x13
+#define kMTime			0x14
+#define kAttributes		0x15
+#define kEncodedHeader		0x17
+
+enum la_zaction {
+	ARCHIVE_Z_FINISH,
+	ARCHIVE_Z_RUN
+};
+
+/*
+ * A stream object of universal compressor.
+ */
+struct la_zstream {
+	const uint8_t		*next_in;
+	size_t			 avail_in;
+	uint64_t		 total_in;
+
+	uint8_t			*next_out;
+	size_t			 avail_out;
+	uint64_t		 total_out;
+
+	uint32_t		 prop_size;
+	uint8_t			*props;
+
+	int			 valid;
+	void			*real_stream;
+	int			 (*code) (struct archive *a,
+				    struct la_zstream *lastrm,
+				    enum la_zaction action);
+	int			 (*end)(struct archive *a,
+				    struct la_zstream *lastrm);
+};
+
+#define PPMD7_DEFAULT_ORDER	6
+#define PPMD7_DEFAULT_MEM_SIZE	(1 << 24)
+
+struct ppmd_stream {
+	int			 stat;
+	CPpmd7			 ppmd7_context;
+	CPpmd7z_RangeEnc	 range_enc;
+	IByteOut		 byteout;
+	uint8_t			*buff;
+	uint8_t			*buff_ptr;
+	uint8_t			*buff_end;
+	size_t			 buff_bytes;
+};
+
+struct coder {
+	unsigned		 codec;
+	size_t			 prop_size;
+	uint8_t			*props;
+};
+
+struct file {
+	struct archive_rb_node	 rbnode;
+
+	struct file		*next;
+	unsigned		 name_len;
+	uint8_t			*utf16name;/* UTF16-LE name. */
+	uint64_t		 size;
+	unsigned		 flg;
+#define MTIME_IS_SET	(1<<0)
+#define ATIME_IS_SET	(1<<1)
+#define CTIME_IS_SET	(1<<2)
+#define CRC32_IS_SET	(1<<3)
+#define HAS_STREAM	(1<<4)
+
+	struct {
+		time_t		 time;
+		long		 time_ns;
+	}			 times[3];
+#define MTIME 0
+#define ATIME 1
+#define CTIME 2
+
+	mode_t			 mode;
+	uint32_t		 crc32;
+
+	signed int		 dir:1;
+};
+
+struct _7zip {
+	int			 temp_fd;
+	uint64_t		 temp_offset;
+
+	struct file		*cur_file;
+	size_t			 total_number_entry;
+	size_t			 total_number_nonempty_entry;
+	size_t			 total_number_empty_entry;
+	size_t			 total_number_dir_entry;
+	size_t			 total_bytes_entry_name;
+	size_t			 total_number_time_defined[3];
+	uint64_t		 total_bytes_compressed;
+	uint64_t		 total_bytes_uncompressed;
+	uint64_t		 entry_bytes_remaining;
+	uint32_t		 entry_crc32;
+	uint32_t		 precode_crc32;
+	uint32_t		 encoded_crc32;
+	int			 crc32flg;
+#define	PRECODE_CRC32	1
+#define	ENCODED_CRC32	2
+
+	unsigned		 opt_compression;
+	int			 opt_compression_level;
+
+	struct la_zstream	 stream;
+	struct coder		 coder;
+
+	struct archive_string_conv *sconv;
+
+	/*
+	 * Compressed data buffer.
+	 */
+	unsigned char		 wbuff[512 * 20 * 6];
+	size_t			 wbuff_remaining;
+
+	/*
+	 * The list of the file entries which has its contents is used to
+	 * manage struct file objects.
+	 * We use 'next' (a member of struct file) to chain.
+	 */
+	struct {
+		struct file	*first;
+		struct file	**last;
+	}			 file_list, empty_list;
+	struct archive_rb_tree	 rbtree;/* for empty files */
+};
+
+static int	_7z_options(struct archive_write *,
+		    const char *, const char *);
+static int	_7z_write_header(struct archive_write *,
+		    struct archive_entry *);
+static ssize_t	_7z_write_data(struct archive_write *,
+		    const void *, size_t);
+static int	_7z_finish_entry(struct archive_write *);
+static int	_7z_close(struct archive_write *);
+static int	_7z_free(struct archive_write *);
+static int	file_cmp_node(const struct archive_rb_node *,
+		    const struct archive_rb_node *);
+static int	file_cmp_key(const struct archive_rb_node *, const void *);
+static int	file_new(struct archive_write *a, struct archive_entry *,
+		    struct file **);
+static void	file_free(struct file *);
+static void	file_register(struct _7zip *, struct file *);
+static void	file_register_empty(struct _7zip *, struct file *);
+static void	file_init_register(struct _7zip *);
+static void	file_init_register_empty(struct _7zip *);
+static void	file_free_register(struct _7zip *);
+static ssize_t	compress_out(struct archive_write *, const void *, size_t ,
+		    enum la_zaction);
+static int	compression_init_encoder_copy(struct archive *,
+		    struct la_zstream *);
+static int	compression_code_copy(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end_copy(struct archive *, struct la_zstream *);
+static int	compression_init_encoder_deflate(struct archive *,
+		    struct la_zstream *, int, int);
+#ifdef HAVE_ZLIB_H
+static int	compression_code_deflate(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end_deflate(struct archive *, struct la_zstream *);
+#endif
+static int	compression_init_encoder_bzip2(struct archive *,
+		    struct la_zstream *, int);
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int	compression_code_bzip2(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end_bzip2(struct archive *, struct la_zstream *);
+#endif
+static int	compression_init_encoder_lzma1(struct archive *,
+		    struct la_zstream *, int);
+static int	compression_init_encoder_lzma2(struct archive *,
+		    struct la_zstream *, int);
+#if defined(HAVE_LZMA_H)
+static int	compression_code_lzma(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end_lzma(struct archive *, struct la_zstream *);
+#endif
+static int	compression_init_encoder_ppmd(struct archive *,
+		    struct la_zstream *, unsigned, uint32_t);
+static int	compression_code_ppmd(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end_ppmd(struct archive *, struct la_zstream *);
+static int	_7z_compression_init_encoder(struct archive_write *, unsigned,
+		    int);
+static int	compression_code(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end(struct archive *,
+		    struct la_zstream *);
+static int	enc_uint64(struct archive_write *, uint64_t);
+static int	make_header(struct archive_write *, uint64_t, uint64_t,
+		    uint64_t, int, struct coder *);
+static int	make_streamsInfo(struct archive_write *, uint64_t, uint64_t,
+		    	uint64_t, int, struct coder *, int, uint32_t);
+
+int
+archive_write_set_format_7zip(struct archive *_a)
+{
+	static const struct archive_rb_tree_ops rb_ops = {
+		file_cmp_node, file_cmp_key
+	};
+	struct archive_write *a = (struct archive_write *)_a;
+	struct _7zip *zip;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_7zip");
+
+	/* If another format was already registered, unregister it. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	zip = calloc(1, sizeof(*zip));
+	if (zip == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate 7-Zip data");
+		return (ARCHIVE_FATAL);
+	}
+	zip->temp_fd = -1;
+	__archive_rb_tree_init(&(zip->rbtree), &rb_ops);
+	file_init_register(zip);
+	file_init_register_empty(zip);
+
+	/* Set default compression type and its level. */
+#if HAVE_LZMA_H
+	zip->opt_compression = _7Z_LZMA1;
+#elif defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+	zip->opt_compression = _7Z_BZIP2;
+#elif defined(HAVE_ZLIB_H)
+	zip->opt_compression = _7Z_DEFLATE;
+#else
+	zip->opt_compression = _7Z_COPY;
+#endif
+	zip->opt_compression_level = 6;
+
+	a->format_data = zip;
+
+	a->format_name = "7zip";
+	a->format_options = _7z_options;
+	a->format_write_header = _7z_write_header;
+	a->format_write_data = _7z_write_data;
+	a->format_finish_entry = _7z_finish_entry;
+	a->format_close = _7z_close;
+	a->format_free = _7z_free;
+	a->archive.archive_format = ARCHIVE_FORMAT_7ZIP;
+	a->archive.archive_format_name = "7zip";
+
+	return (ARCHIVE_OK);
+}
+
+static int
+_7z_options(struct archive_write *a, const char *key, const char *value)
+{
+	struct _7zip *zip;
+
+	zip = (struct _7zip *)a->format_data;
+
+	if (strcmp(key, "compression") == 0) {
+		const char *name = NULL;
+
+		if (value == NULL || strcmp(value, "copy") == 0 ||
+		    strcmp(value, "COPY") == 0 ||
+		    strcmp(value, "store") == 0 ||
+		    strcmp(value, "STORE") == 0)
+			zip->opt_compression = _7Z_COPY;
+		else if (strcmp(value, "deflate") == 0 ||
+		    strcmp(value, "DEFLATE") == 0)
+#if HAVE_ZLIB_H
+			zip->opt_compression = _7Z_DEFLATE;
+#else
+			name = "deflate";
+#endif
+		else if (strcmp(value, "bzip2") == 0 ||
+		    strcmp(value, "BZIP2") == 0)
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+			zip->opt_compression = _7Z_BZIP2;
+#else
+			name = "bzip2";
+#endif
+		else if (strcmp(value, "lzma1") == 0 ||
+		    strcmp(value, "LZMA1") == 0)
+#if HAVE_LZMA_H
+			zip->opt_compression = _7Z_LZMA1;
+#else
+			name = "lzma1";
+#endif
+		else if (strcmp(value, "lzma2") == 0 ||
+		    strcmp(value, "LZMA2") == 0)
+#if HAVE_LZMA_H
+			zip->opt_compression = _7Z_LZMA2;
+#else
+			name = "lzma2";
+#endif
+		else if (strcmp(value, "ppmd") == 0 ||
+		    strcmp(value, "PPMD") == 0 ||
+		    strcmp(value, "PPMd") == 0)
+			zip->opt_compression = _7Z_PPMD;
+		else {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "Unknown compression name: `%s'",
+			    value);
+			return (ARCHIVE_FAILED);
+		}
+		if (name != NULL) {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "`%s' compression not supported "
+			    "on this platform",
+			    name);
+			return (ARCHIVE_FAILED);
+		}
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "compression-level") == 0) {
+		if (value == NULL ||
+		    !(value[0] >= '0' && value[0] <= '9') ||
+		    value[1] != '\0') {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "Illegal value `%s'",
+			    value);
+			return (ARCHIVE_FAILED);
+		}
+		zip->opt_compression_level = value[0] - '0';
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+_7z_write_header(struct archive_write *a, struct archive_entry *entry)
+{
+	struct _7zip *zip;
+	struct file *file;
+	int r;
+
+	zip = (struct _7zip *)a->format_data;
+	zip->cur_file = NULL;
+	zip->entry_bytes_remaining = 0;
+
+	if (zip->sconv == NULL) {
+		zip->sconv = archive_string_conversion_to_charset(
+		    &a->archive, "UTF-16LE", 1);
+		if (zip->sconv == NULL)
+			return (ARCHIVE_FATAL);
+	}
+
+	r = file_new(a, entry, &file);
+	if (r < ARCHIVE_WARN) {
+		if (file != NULL)
+			file_free(file);
+		return (r);
+	}
+	if (file->size == 0 && file->dir) {
+		if (!__archive_rb_tree_insert_node(&(zip->rbtree),
+		    (struct archive_rb_node *)file)) {
+			/* We have already had the same file. */
+			file_free(file);
+			return (ARCHIVE_OK);
+		}
+	}
+
+	if (file->flg & MTIME_IS_SET)
+		zip->total_number_time_defined[MTIME]++;
+	if (file->flg & CTIME_IS_SET)
+		zip->total_number_time_defined[CTIME]++;
+	if (file->flg & ATIME_IS_SET)
+		zip->total_number_time_defined[ATIME]++;
+
+	zip->total_number_entry++;
+	zip->total_bytes_entry_name += file->name_len + 2;
+	if (file->size == 0) {
+		/* Count up the number of empty files. */
+		zip->total_number_empty_entry++;
+		if (file->dir)
+			zip->total_number_dir_entry++;
+		else
+			file_register_empty(zip, file);
+		return (r);
+	}
+
+	/*
+	 * Init compression.
+	 */
+	if ((zip->total_number_entry - zip->total_number_empty_entry) == 1) {
+		r = _7z_compression_init_encoder(a, zip->opt_compression,
+			zip->opt_compression_level);
+		if (r < 0) {
+			file_free(file);
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	/* Register a non-empty file. */
+	file_register(zip, file);
+
+	/*
+	 * Set the current file to cur_file to read its contents.
+	 */
+	zip->cur_file = file;
+
+
+	/* Save a offset of current file in temporary file. */
+	zip->entry_bytes_remaining = file->size;
+	zip->entry_crc32 = 0;
+
+	/*
+	 * Store a symbolic link name as file contents.
+	 */
+	if (archive_entry_filetype(entry) == AE_IFLNK) {
+		ssize_t bytes;
+		const void *p = (const void *)archive_entry_symlink(entry);
+		bytes = compress_out(a, p, (size_t)file->size, ARCHIVE_Z_RUN);
+		if (bytes < 0)
+			return ((int)bytes);
+		zip->entry_crc32 = crc32(zip->entry_crc32, p, (unsigned)bytes);
+		zip->entry_bytes_remaining -= bytes;
+	}
+
+	return (r);
+}
+
+/*
+ * Write data to a temporary file.
+ */
+static int
+write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+	struct _7zip *zip;
+	const unsigned char *p;
+	ssize_t ws;
+
+	zip = (struct _7zip *)a->format_data;
+
+	/*
+	 * Open a temporary file.
+	 */
+	if (zip->temp_fd == -1) {
+		zip->temp_offset = 0;
+		zip->temp_fd = __archive_mktemp(NULL);
+		if (zip->temp_fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't create temporary file");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	p = (const unsigned char *)buff;
+	while (s) {
+		ws = write(zip->temp_fd, p, s);
+		if (ws < 0) {
+			archive_set_error(&(a->archive), errno,
+			    "fwrite function failed");
+			return (ARCHIVE_FATAL);
+		}
+		s -= ws;
+		p += ws;
+		zip->temp_offset += ws;
+	}
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+compress_out(struct archive_write *a, const void *buff, size_t s,
+    enum la_zaction run)
+{
+	struct _7zip *zip = (struct _7zip *)a->format_data;
+	int r;
+
+	if (run == ARCHIVE_Z_FINISH && zip->stream.total_in == 0 && s == 0)
+		return (0);
+
+	if ((zip->crc32flg & PRECODE_CRC32) && s)
+		zip->precode_crc32 = crc32(zip->precode_crc32, buff,
+		    (unsigned)s);
+	zip->stream.next_in = (const unsigned char *)buff;
+	zip->stream.avail_in = s;
+	for (;;) {
+		/* Compress file data. */
+		r = compression_code(&(a->archive), &(zip->stream), run);
+		if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
+			return (ARCHIVE_FATAL);
+		if (zip->stream.avail_out == 0) {
+			if (write_to_temp(a, zip->wbuff, sizeof(zip->wbuff))
+			    != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			zip->stream.next_out = zip->wbuff;
+			zip->stream.avail_out = sizeof(zip->wbuff);
+			if (zip->crc32flg & ENCODED_CRC32)
+				zip->encoded_crc32 = crc32(zip->encoded_crc32,
+				    zip->wbuff, sizeof(zip->wbuff));
+			if (run == ARCHIVE_Z_FINISH && r != ARCHIVE_EOF)
+				continue;
+		}
+		if (zip->stream.avail_in == 0)
+			break;
+	}
+	if (run == ARCHIVE_Z_FINISH) {
+		uint64_t bytes = sizeof(zip->wbuff) - zip->stream.avail_out;
+		if (write_to_temp(a, zip->wbuff, (size_t)bytes) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		if ((zip->crc32flg & ENCODED_CRC32) && bytes)
+			zip->encoded_crc32 = crc32(zip->encoded_crc32,
+			    zip->wbuff, (unsigned)bytes);
+	}
+
+	return (s);
+}
+
+static ssize_t
+_7z_write_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct _7zip *zip;
+	ssize_t bytes;
+
+	zip = (struct _7zip *)a->format_data;
+
+	if (s > zip->entry_bytes_remaining)
+		s = (size_t)zip->entry_bytes_remaining;
+	if (s == 0 || zip->cur_file == NULL)
+		return (0);
+	bytes = compress_out(a, buff, s, ARCHIVE_Z_RUN);
+	if (bytes < 0)
+		return (bytes);
+	zip->entry_crc32 = crc32(zip->entry_crc32, buff, (unsigned)bytes);
+	zip->entry_bytes_remaining -= bytes;
+	return (bytes);
+}
+
+static int
+_7z_finish_entry(struct archive_write *a)
+{
+	struct _7zip *zip;
+	size_t s;
+	ssize_t r;
+
+	zip = (struct _7zip *)a->format_data;
+	if (zip->cur_file == NULL)
+		return (ARCHIVE_OK);
+
+	while (zip->entry_bytes_remaining > 0) {
+		s = (size_t)zip->entry_bytes_remaining;
+		if (s > a->null_length)
+			s = a->null_length;
+		r = _7z_write_data(a, a->nulls, s);
+		if (r < 0)
+			return ((int)r);
+	}
+	zip->total_bytes_compressed += zip->stream.total_in;
+	zip->total_bytes_uncompressed += zip->stream.total_out;
+	zip->cur_file->crc32 = zip->entry_crc32;
+	zip->cur_file = NULL;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+flush_wbuff(struct archive_write *a)
+{
+	struct _7zip *zip;
+	int r;
+	size_t s;
+
+	zip = (struct _7zip *)a->format_data;
+	s = sizeof(zip->wbuff) - zip->wbuff_remaining;
+	r = __archive_write_output(a, zip->wbuff, s);
+	if (r != ARCHIVE_OK)
+		return (r);
+	zip->wbuff_remaining = sizeof(zip->wbuff);
+	return (r);
+}
+
+static int
+copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
+{
+	struct _7zip *zip;
+	int r;
+
+	zip = (struct _7zip *)a->format_data;
+	if (zip->temp_offset > 0 &&
+	    lseek(zip->temp_fd, offset, SEEK_SET) < 0) {
+		archive_set_error(&(a->archive), errno, "lseek failed");
+		return (ARCHIVE_FATAL);
+	}
+	while (length) {
+		size_t rsize;
+		ssize_t rs;
+		unsigned char *wb;
+
+		if (length > zip->wbuff_remaining)
+			rsize = zip->wbuff_remaining;
+		else
+			rsize = (size_t)length;
+		wb = zip->wbuff + (sizeof(zip->wbuff) - zip->wbuff_remaining);
+		rs = read(zip->temp_fd, wb, rsize);
+		if (rs < 0) {
+			archive_set_error(&(a->archive), errno,
+			    "Can't read temporary file(%jd)",
+			    (intmax_t)rs);
+			return (ARCHIVE_FATAL);
+		}
+		if (rs == 0) {
+			archive_set_error(&(a->archive), 0,
+			    "Truncated 7-Zip archive");
+			return (ARCHIVE_FATAL);
+		}
+		zip->wbuff_remaining -= rs;
+		length -= rs;
+		if (zip->wbuff_remaining == 0) {
+			r = flush_wbuff(a);
+			if (r != ARCHIVE_OK)
+				return (r);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+_7z_close(struct archive_write *a)
+{
+	struct _7zip *zip;
+	unsigned char *wb;
+	uint64_t header_offset, header_size, header_unpacksize;
+	uint64_t length;
+	uint32_t header_crc32;
+	int r;
+
+	zip = (struct _7zip *)a->format_data;
+
+	if (zip->total_number_entry > 0) {
+		struct archive_rb_node *n;
+		uint64_t data_offset, data_size, data_unpacksize;
+		unsigned header_compression;
+
+		r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
+		if (r < 0)
+			return (r);
+		data_offset = 0;
+		data_size = zip->stream.total_out;
+		data_unpacksize = zip->stream.total_in;
+		zip->coder.codec = zip->opt_compression;
+		zip->coder.prop_size = zip->stream.prop_size;
+		zip->coder.props = zip->stream.props;
+		zip->stream.prop_size = 0;
+		zip->stream.props = NULL;
+		zip->total_number_nonempty_entry =
+		    zip->total_number_entry - zip->total_number_empty_entry;
+
+		/* Connect an empty file list. */
+		if (zip->empty_list.first != NULL) {
+			*zip->file_list.last = zip->empty_list.first;
+			zip->file_list.last = zip->empty_list.last;
+		}
+		/* Connect a directory file list. */
+		ARCHIVE_RB_TREE_FOREACH(n, &(zip->rbtree)) {
+			file_register(zip, (struct file *)n);
+		}
+
+		/*
+		 * NOTE: 7z command supports just LZMA1, LZMA2 and COPY for
+		 * the compression type for encoding the header.
+		 */
+#if HAVE_LZMA_H
+		header_compression = _7Z_LZMA1;
+		/* If the stored file is only one, do not encode the header.
+		 * This is the same way 7z command does. */
+		if (zip->total_number_entry == 1)
+			header_compression = _7Z_COPY;
+#else
+		header_compression = _7Z_COPY;
+#endif
+		r = _7z_compression_init_encoder(a, header_compression, 6);
+		if (r < 0)
+			return (r);
+		zip->crc32flg = PRECODE_CRC32;
+		zip->precode_crc32 = 0;
+		r = make_header(a, data_offset, data_size, data_unpacksize,
+			1, &(zip->coder));
+		if (r < 0)
+			return (r);
+		r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
+		if (r < 0)
+			return (r);
+		header_offset = data_offset + data_size;
+		header_size = zip->stream.total_out;
+		header_crc32 = zip->precode_crc32;
+		header_unpacksize = zip->stream.total_in;
+
+		if (header_compression != _7Z_COPY) {
+			/*
+			 * Encode the header in order to reduce the size
+			 * of the archive.
+			 */
+			free(zip->coder.props);
+			zip->coder.codec = header_compression;
+			zip->coder.prop_size = zip->stream.prop_size;
+			zip->coder.props = zip->stream.props;
+			zip->stream.prop_size = 0;
+			zip->stream.props = NULL;
+
+			r = _7z_compression_init_encoder(a, _7Z_COPY, 0);
+			if (r < 0)
+				return (r);
+			zip->crc32flg = ENCODED_CRC32;
+			zip->encoded_crc32 = 0;
+
+			/*
+			 * Make EncodedHeader.
+			 */
+			r = enc_uint64(a, kEncodedHeader);
+			if (r < 0)
+				return (r);
+			r = make_streamsInfo(a, header_offset, header_size,
+			      header_unpacksize, 1, &(zip->coder), 0,
+			      header_crc32);
+			if (r < 0)
+				return (r);
+			r = (int)compress_out(a, NULL, 0, ARCHIVE_Z_FINISH);
+			if (r < 0)
+				return (r);
+			header_offset = header_offset + header_size;
+			header_size = zip->stream.total_out;
+			header_crc32 = zip->encoded_crc32;
+		}
+		zip->crc32flg = 0;
+	} else {
+		header_offset = header_size = 0;
+		header_crc32 = 0;
+	}
+	
+	length = zip->temp_offset;
+
+	/*
+	 * Make the zip header on wbuff(write buffer).
+	 */
+	wb = zip->wbuff;
+	zip->wbuff_remaining = sizeof(zip->wbuff);
+	memcpy(&wb[0], "7z\xBC\xAF\x27\x1C", 6);
+	wb[6] = 0;/* Major version. */
+	wb[7] = 3;/* Minor version. */
+	archive_le64enc(&wb[12], header_offset);/* Next Header Offset */
+	archive_le64enc(&wb[20], header_size);/* Next Header Size */
+	archive_le32enc(&wb[28], header_crc32);/* Next Header CRC */
+	archive_le32enc(&wb[8], crc32(0, &wb[12], 20));/* Start Header CRC */
+	zip->wbuff_remaining -= 32;
+
+	/*
+	 * Read all file contents and an encoded header from the temporary
+	 * file and write out it.
+	 */
+	r = copy_out(a, 0, length);
+	if (r != ARCHIVE_OK)
+		return (r);
+	r = flush_wbuff(a);
+	return (r);
+}
+
+/*
+ * Encode 64 bits value into 7-Zip's encoded UINT64 value.
+ */
+static int
+enc_uint64(struct archive_write *a, uint64_t val)
+{
+	unsigned mask = 0x80;
+	uint8_t numdata[9];
+	int i;
+
+	numdata[0] = 0;
+	for (i = 1; i < (int)sizeof(numdata); i++) {
+		if (val < mask) {
+			numdata[0] |= (uint8_t)val;
+			break;
+		}
+		numdata[i] = (uint8_t)val;
+		val >>= 8;
+		numdata[0] |= mask;
+		mask >>= 1;
+	}
+	return ((int)compress_out(a, numdata, i, ARCHIVE_Z_RUN));
+}
+
+static int
+make_substreamsInfo(struct archive_write *a, struct coder *coders)
+{
+	struct _7zip *zip = (struct _7zip *)a->format_data;
+	struct file *file;
+	int r;
+
+	/*
+	 * Make SubStreamsInfo.
+	 */
+	r = enc_uint64(a, kSubStreamsInfo);
+	if (r < 0)
+		return (r);
+
+	if (zip->total_number_nonempty_entry > 1 && coders->codec != _7Z_COPY) {
+		/*
+		 * Make NumUnPackStream.
+		 */
+		r = enc_uint64(a, kNumUnPackStream);
+		if (r < 0)
+			return (r);
+
+		/* Write numUnpackStreams */
+		r = enc_uint64(a, zip->total_number_nonempty_entry);
+		if (r < 0)
+			return (r);
+
+		/*
+		 * Make kSize.
+		 */
+		r = enc_uint64(a, kSize);
+		if (r < 0)
+			return (r);
+		file = zip->file_list.first;
+		for (;file != NULL; file = file->next) {
+			if (file->next == NULL ||
+			    file->next->size == 0)
+				break;
+			r = enc_uint64(a, file->size);
+			if (r < 0)
+				return (r);
+		}
+	}
+
+	/*
+	 * Make CRC.
+	 */
+	r = enc_uint64(a, kCRC);
+	if (r < 0)
+		return (r);
+
+
+	/* All are defined */
+	r = enc_uint64(a, 1);
+	if (r < 0)
+		return (r);
+	file = zip->file_list.first;
+	for (;file != NULL; file = file->next) {
+		uint8_t crc[4];
+		if (file->size == 0)
+			break;
+		archive_le32enc(crc, file->crc32);
+		r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN);
+		if (r < 0)
+			return (r);
+	}
+
+	/* Write End. */
+	r = enc_uint64(a, kEnd);
+	if (r < 0)
+		return (r);
+	return (ARCHIVE_OK);
+}
+
+static int
+make_streamsInfo(struct archive_write *a, uint64_t offset, uint64_t pack_size,
+    uint64_t unpack_size, int num_coder, struct coder *coders, int substrm,
+    uint32_t header_crc)
+{
+	struct _7zip *zip = (struct _7zip *)a->format_data;
+	uint8_t codec_buff[8];
+	int numFolders, fi;
+	int codec_size;
+	int i, r;
+
+	if (coders->codec == _7Z_COPY)
+		numFolders = (int)zip->total_number_nonempty_entry;
+	else
+		numFolders = 1;
+
+	/*
+	 * Make PackInfo.
+	 */
+	r = enc_uint64(a, kPackInfo);
+	if (r < 0)
+		return (r);
+
+	/* Write PackPos. */
+	r = enc_uint64(a, offset);
+	if (r < 0)
+		return (r);
+
+	/* Write NumPackStreams. */
+	r = enc_uint64(a, numFolders);
+	if (r < 0)
+		return (r);
+
+	/* Make Size. */
+	r = enc_uint64(a, kSize);
+	if (r < 0)
+		return (r);
+
+	if (numFolders > 1) {
+		struct file *file = zip->file_list.first;
+		for (;file != NULL; file = file->next) {
+			if (file->size == 0)
+				break;
+			r = enc_uint64(a, file->size);
+			if (r < 0)
+				return (r);
+		}
+	} else {
+		/* Write size. */
+		r = enc_uint64(a, pack_size);
+		if (r < 0)
+			return (r);
+	}
+
+	r = enc_uint64(a, kEnd);
+	if (r < 0)
+		return (r);
+
+	/*
+	 * Make UnPackInfo.
+	 */
+	r = enc_uint64(a, kUnPackInfo);
+	if (r < 0)
+		return (r);
+
+	/*
+	 * Make Folder.
+	 */
+	r = enc_uint64(a, kFolder);
+	if (r < 0)
+		return (r);
+
+	/* Write NumFolders. */
+	r = enc_uint64(a, numFolders);
+	if (r < 0)
+		return (r);
+
+	/* Write External. */
+	r = enc_uint64(a, 0);
+	if (r < 0)
+		return (r);
+
+	for (fi = 0; fi < numFolders; fi++) {
+		/* Write NumCoders. */
+		r = enc_uint64(a, num_coder);
+		if (r < 0)
+			return (r);
+
+		for (i = 0; i < num_coder; i++) {
+			unsigned codec_id = coders[i].codec;
+
+			/* Write Codec flag. */
+			archive_be64enc(codec_buff, codec_id);
+			for (codec_size = 8; codec_size > 0; codec_size--) {
+				if (codec_buff[8 - codec_size])
+					break;
+			}
+			if (codec_size == 0)
+				codec_size = 1;
+			if (coders[i].prop_size)
+				r = enc_uint64(a, codec_size | 0x20);
+			else
+				r = enc_uint64(a, codec_size);
+			if (r < 0)
+				return (r);
+
+			/* Write Codec ID. */
+			codec_size &= 0x0f;
+			r = (int)compress_out(a, &codec_buff[8-codec_size],
+				codec_size, ARCHIVE_Z_RUN);
+			if (r < 0)
+				return (r);
+
+			if (coders[i].prop_size) {
+				/* Write Codec property size. */
+				r = enc_uint64(a, coders[i].prop_size);
+				if (r < 0)
+					return (r);
+
+				/* Write Codec properties. */
+				r = (int)compress_out(a, coders[i].props,
+					coders[i].prop_size, ARCHIVE_Z_RUN);
+				if (r < 0)
+					return (r);
+			}
+		}
+	}
+
+	/*
+	 * Make CodersUnPackSize.
+	 */
+	r = enc_uint64(a, kCodersUnPackSize);
+	if (r < 0)
+		return (r);
+
+	if (numFolders > 1) {
+		struct file *file = zip->file_list.first;
+		for (;file != NULL; file = file->next) {
+			if (file->size == 0)
+				break;
+			r = enc_uint64(a, file->size);
+			if (r < 0)
+				return (r);
+		}
+
+	} else {
+		/* Write UnPackSize. */
+		r = enc_uint64(a, unpack_size);
+		if (r < 0)
+			return (r);
+	}
+
+	if (!substrm) {
+		uint8_t crc[4];
+		/*
+		 * Make CRC.
+		 */
+		r = enc_uint64(a, kCRC);
+		if (r < 0)
+			return (r);
+
+		/* All are defined */
+		r = enc_uint64(a, 1);
+		if (r < 0)
+			return (r);
+		archive_le32enc(crc, header_crc);
+		r = (int)compress_out(a, crc, 4, ARCHIVE_Z_RUN);
+		if (r < 0)
+			return (r);
+	}
+
+	/* Write End. */
+	r = enc_uint64(a, kEnd);
+	if (r < 0)
+		return (r);
+
+	if (substrm) {
+		/*
+		 * Make SubStreamsInfo.
+		 */
+		r = make_substreamsInfo(a, coders);
+		if (r < 0)
+			return (r);
+	}
+
+
+	/* Write End. */
+	r = enc_uint64(a, kEnd);
+	if (r < 0)
+		return (r);
+
+	return (ARCHIVE_OK);
+}
+
+
+#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
+static uint64_t
+utcToFiletime(time_t t, long ns)
+{
+	uint64_t fileTime;
+
+	fileTime = t;
+	fileTime *= 10000000;
+	fileTime += ns / 100;
+	fileTime += EPOC_TIME;
+	return (fileTime);
+}
+
+static int
+make_time(struct archive_write *a, uint8_t type, unsigned flg, int ti)
+{
+	uint8_t filetime[8];
+	struct _7zip *zip = (struct _7zip *)a->format_data;
+	struct file *file;
+	int r;
+	uint8_t b, mask;
+
+	/*
+	 * Make Time Bools.
+	 */
+	if (zip->total_number_time_defined[ti] == zip->total_number_entry) {
+		/* Write Time Type. */
+		r = enc_uint64(a, type);
+		if (r < 0)
+			return (r);
+		/* Write EmptyStream Size. */
+		r = enc_uint64(a, 2 + zip->total_number_entry * 8);
+		if (r < 0)
+			return (r);
+		/* All are defined. */
+		r = enc_uint64(a, 1);
+		if (r < 0)
+			return (r);
+	} else {
+		if (zip->total_number_time_defined[ti] == 0)
+			return (ARCHIVE_OK);
+
+		/* Write Time Type. */
+		r = enc_uint64(a, type);
+		if (r < 0)
+			return (r);
+		/* Write EmptyStream Size. */
+		r = enc_uint64(a, 2 + ((zip->total_number_entry + 7) >> 3)
+			+ zip->total_number_time_defined[ti] * 8);
+		if (r < 0)
+			return (r);
+
+		/* All are not defined. */
+		r = enc_uint64(a, 0);
+		if (r < 0)
+			return (r);
+
+		b = 0;
+		mask = 0x80;
+		file = zip->file_list.first;
+		for (;file != NULL; file = file->next) {
+			if (file->flg & flg)
+				b |= mask;
+			mask >>= 1;
+			if (mask == 0) {
+				r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+				if (r < 0)
+					return (r);
+				mask = 0x80;
+				b = 0;
+			}
+		}
+		if (mask != 0x80) {
+			r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+			if (r < 0)
+				return (r);
+		}
+	}
+
+	/* External. */
+	r = enc_uint64(a, 0);
+	if (r < 0)
+		return (r);
+
+
+	/*
+	 * Make Times.
+	 */
+	file = zip->file_list.first;
+	for (;file != NULL; file = file->next) {
+		if ((file->flg & flg) == 0)
+			continue;
+		archive_le64enc(filetime, utcToFiletime(file->times[ti].time,
+			file->times[ti].time_ns));
+		r = (int)compress_out(a, filetime, 8, ARCHIVE_Z_RUN);
+		if (r < 0)
+			return (r);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
+make_header(struct archive_write *a, uint64_t offset, uint64_t pack_size,
+    uint64_t unpack_size, int codernum, struct coder *coders)
+{
+	struct _7zip *zip = (struct _7zip *)a->format_data;
+	struct file *file;
+	int r;
+	uint8_t b, mask;
+
+	/*
+	 * Make FilesInfo.
+	 */
+	r = enc_uint64(a, kHeader);
+	if (r < 0)
+		return (r);
+
+	/*
+	 * If there are empty files only, do not write MainStreamInfo.
+	 */
+	if (zip->total_number_nonempty_entry) {
+		/*
+		 * Make MainStreamInfo.
+		 */
+		r = enc_uint64(a, kMainStreamsInfo);
+		if (r < 0)
+			return (r);
+		r = make_streamsInfo(a, offset, pack_size, unpack_size,
+		      codernum, coders, 1, 0);
+		if (r < 0)
+			return (r);
+	}
+
+	/*
+	 * Make FilesInfo.
+	 */
+	r = enc_uint64(a, kFilesInfo);
+	if (r < 0)
+		return (r);
+
+	/* Write numFiles. */
+	r = enc_uint64(a, zip->total_number_entry);
+	if (r < 0)
+		return (r);
+
+	if (zip->total_number_empty_entry > 0) {
+		/* Make EmptyStream. */
+		r = enc_uint64(a, kEmptyStream);
+		if (r < 0)
+			return (r);
+
+		/* Write EmptyStream Size. */
+		r = enc_uint64(a, (zip->total_number_entry+7)>>3);
+		if (r < 0)
+			return (r);
+
+		b = 0;
+		mask = 0x80;
+		file = zip->file_list.first;
+		for (;file != NULL; file = file->next) {
+			if (file->size == 0)
+				b |= mask;
+			mask >>= 1;
+			if (mask == 0) {
+				r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+				if (r < 0)
+					return (r);
+				mask = 0x80;
+				b = 0;
+			}
+		}
+		if (mask != 0x80) {
+			r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+			if (r < 0)
+				return (r);
+		}
+	}
+
+	if (zip->total_number_empty_entry > zip->total_number_dir_entry) {
+		/* Make EmptyFile. */
+		r = enc_uint64(a, kEmptyFile);
+		if (r < 0)
+			return (r);
+
+		/* Write EmptyFile Size. */
+		r = enc_uint64(a, (zip->total_number_empty_entry + 7) >> 3);
+		if (r < 0)
+			return (r);
+
+		b = 0;
+		mask = 0x80;
+		file = zip->file_list.first;
+		for (;file != NULL; file = file->next) {
+			if (file->size)
+				continue;
+			if (!file->dir)
+				b |= mask;
+			mask >>= 1;
+			if (mask == 0) {
+				r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+				if (r < 0)
+					return (r);
+				mask = 0x80;
+				b = 0;
+			}
+		}
+		if (mask != 0x80) {
+			r = (int)compress_out(a, &b, 1, ARCHIVE_Z_RUN);
+			if (r < 0)
+				return (r);
+		}
+	}
+
+	/* Make Name. */
+	r = enc_uint64(a, kName);
+	if (r < 0)
+		return (r);
+
+	/* Write Name size. */
+	r = enc_uint64(a, zip->total_bytes_entry_name+1);
+	if (r < 0)
+		return (r);
+
+	/* Write dmy byte. */
+	r = enc_uint64(a, 0);
+	if (r < 0)
+		return (r);
+
+	file = zip->file_list.first;
+	for (;file != NULL; file = file->next) {
+		r = (int)compress_out(a, file->utf16name, file->name_len+2,
+			ARCHIVE_Z_RUN);
+		if (r < 0)
+			return (r);
+	}
+
+	/* Make MTime. */
+	r = make_time(a, kMTime, MTIME_IS_SET, MTIME);
+	if (r < 0)
+		return (r);
+
+	/* Make CTime. */
+	r = make_time(a, kCTime, CTIME_IS_SET, CTIME);
+	if (r < 0)
+		return (r);
+
+	/* Make ATime. */
+	r = make_time(a, kATime, ATIME_IS_SET, ATIME);
+	if (r < 0)
+		return (r);
+
+	/* Make Attributes. */
+	r = enc_uint64(a, kAttributes);
+	if (r < 0)
+		return (r);
+
+	/* Write Attributes size. */
+	r = enc_uint64(a, 2 + zip->total_number_entry * 4);
+	if (r < 0)
+		return (r);
+
+	/* Write "All Are Defined". */
+	r = enc_uint64(a, 1);
+	if (r < 0)
+		return (r);
+
+	/* Write dmy byte. */
+	r = enc_uint64(a, 0);
+	if (r < 0)
+		return (r);
+
+	file = zip->file_list.first;
+	for (;file != NULL; file = file->next) {
+		/*
+		 * High 16bits is unix mode.
+		 * Low 16bits is Windows attributes.
+		 */
+		uint32_t encattr, attr;
+		if (file->dir)
+			attr = 0x8010;
+		else
+			attr = 0x8020;
+		if ((file->mode & 0222) == 0)
+			attr |= 1;/* Read Only. */
+		attr |= ((uint32_t)file->mode) << 16;
+		archive_le32enc(&encattr, attr);
+		r = (int)compress_out(a, &encattr, 4, ARCHIVE_Z_RUN);
+		if (r < 0)
+			return (r);
+	}
+
+	/* Write End. */
+	r = enc_uint64(a, kEnd);
+	if (r < 0)
+		return (r);
+
+	/* Write End. */
+	r = enc_uint64(a, kEnd);
+	if (r < 0)
+		return (r);
+
+	return (ARCHIVE_OK);
+}
+
+
+static int
+_7z_free(struct archive_write *a)
+{
+	struct _7zip *zip = (struct _7zip *)a->format_data;
+
+	/* Close the temporary file. */
+	if (zip->temp_fd >= 0)
+		close(zip->temp_fd);
+
+	file_free_register(zip);
+	compression_end(&(a->archive), &(zip->stream));
+	free(zip->coder.props);
+	free(zip);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+file_cmp_node(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	const struct file *f1 = (const struct file *)n1;
+	const struct file *f2 = (const struct file *)n2;
+
+	if (f1->name_len == f2->name_len)
+		return (memcmp(f1->utf16name, f2->utf16name, f1->name_len));
+	return (f1->name_len > f2->name_len)?1:-1;
+}
+        
+static int
+file_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+	const struct file *f = (const struct file *)n;
+
+	return (f->name_len - *(const char *)key);
+}
+
+static int
+file_new(struct archive_write *a, struct archive_entry *entry,
+    struct file **newfile)
+{
+	struct _7zip *zip;
+	struct file *file;
+	const char *u16;
+	size_t u16len;
+	int ret = ARCHIVE_OK;
+
+	zip = (struct _7zip *)a->format_data;
+	*newfile = NULL;
+
+	file = calloc(1, sizeof(*file));
+	if (file == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (0 > archive_entry_pathname_l(entry, &u16, &u16len, zip->sconv)) {
+		if (errno == ENOMEM) {
+			free(file);
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for UTF-16LE");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "A filename cannot be converted to UTF-16LE;"
+		    "You should disable making Joliet extension");
+		ret = ARCHIVE_WARN;
+	}
+	file->utf16name = malloc(u16len + 2);
+	if (file->utf16name == NULL) {
+		free(file);
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for Name");
+		return (ARCHIVE_FATAL);
+	}
+	memcpy(file->utf16name, u16, u16len);
+	file->utf16name[u16len+0] = 0;
+	file->utf16name[u16len+1] = 0;
+	file->name_len = (unsigned)u16len;
+	file->mode = archive_entry_mode(entry);
+	if (archive_entry_filetype(entry) == AE_IFREG)
+		file->size = archive_entry_size(entry);
+	else
+		archive_entry_set_size(entry, 0);
+	if (archive_entry_filetype(entry) == AE_IFDIR)
+		file->dir = 1;
+	else if (archive_entry_filetype(entry) == AE_IFLNK)
+		file->size = strlen(archive_entry_symlink(entry));
+	if (archive_entry_mtime_is_set(entry)) {
+		file->flg |= MTIME_IS_SET;
+		file->times[MTIME].time = archive_entry_mtime(entry);
+		file->times[MTIME].time_ns = archive_entry_mtime_nsec(entry);
+	}
+	if (archive_entry_atime_is_set(entry)) {
+		file->flg |= ATIME_IS_SET;
+		file->times[ATIME].time = archive_entry_atime(entry);
+		file->times[ATIME].time_ns = archive_entry_atime_nsec(entry);
+	}
+	if (archive_entry_ctime_is_set(entry)) {
+		file->flg |= CTIME_IS_SET;
+		file->times[CTIME].time = archive_entry_ctime(entry);
+		file->times[CTIME].time_ns = archive_entry_ctime_nsec(entry);
+	}
+
+	*newfile = file;
+	return (ret);
+}
+
+static void
+file_free(struct file *file)
+{
+	free(file->utf16name);
+	free(file);
+}
+
+static void
+file_register(struct _7zip *zip, struct file *file)
+{
+	file->next = NULL;
+	*zip->file_list.last = file;
+	zip->file_list.last = &(file->next);
+}
+
+static void
+file_init_register(struct _7zip *zip)
+{
+	zip->file_list.first = NULL;
+	zip->file_list.last = &(zip->file_list.first);
+}
+
+static void
+file_free_register(struct _7zip *zip)
+{
+	struct file *file, *file_next;
+
+	file = zip->file_list.first;
+	while (file != NULL) {
+		file_next = file->next;
+		file_free(file);
+		file = file_next;
+	}
+}
+
+static void
+file_register_empty(struct _7zip *zip, struct file *file)
+{
+	file->next = NULL;
+	*zip->empty_list.last = file;
+	zip->empty_list.last = &(file->next);
+}
+
+static void
+file_init_register_empty(struct _7zip *zip)
+{
+	zip->empty_list.first = NULL;
+	zip->empty_list.last = &(zip->empty_list.first);
+}
+
+#if !defined(HAVE_ZLIB_H) || !defined(HAVE_BZLIB_H) ||\
+	 !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H)
+static int
+compression_unsupported_encoder(struct archive *a,
+    struct la_zstream *lastrm, const char *name)
+{
+
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
+	    "%s compression not supported on this platform", name);
+	lastrm->valid = 0;
+	lastrm->real_stream = NULL;
+	return (ARCHIVE_FAILED);
+}
+#endif
+
+/*
+ * _7_COPY compressor.
+ */
+static int
+compression_init_encoder_copy(struct archive *a, struct la_zstream *lastrm)
+{
+
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	lastrm->valid = 1;
+	lastrm->code = compression_code_copy;
+	lastrm->end = compression_end_copy;
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_code_copy(struct archive *a,
+    struct la_zstream *lastrm, enum la_zaction action)
+{
+	size_t bytes;
+
+	(void)a; /* UNUSED */
+	if (lastrm->avail_out > lastrm->avail_in)
+		bytes = lastrm->avail_in;
+	else
+		bytes = lastrm->avail_out;
+	if (bytes) {
+		memcpy(lastrm->next_out, lastrm->next_in, bytes);
+		lastrm->next_in += bytes;
+		lastrm->avail_in -= bytes;
+		lastrm->total_in += bytes;
+		lastrm->next_out += bytes;
+		lastrm->avail_out -= bytes;
+		lastrm->total_out += bytes;
+	}
+	if (action == ARCHIVE_Z_FINISH && lastrm->avail_in == 0)
+		return (ARCHIVE_EOF);
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_end_copy(struct archive *a, struct la_zstream *lastrm)
+{
+	(void)a; /* UNUSED */
+	lastrm->valid = 0;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * _7_DEFLATE compressor.
+ */
+#ifdef HAVE_ZLIB_H
+static int
+compression_init_encoder_deflate(struct archive *a,
+    struct la_zstream *lastrm, int level, int withheader)
+{
+	z_stream *strm;
+
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	strm = calloc(1, sizeof(*strm));
+	if (strm == NULL) {
+		archive_set_error(a, ENOMEM,
+		    "Can't allocate memory for gzip stream");
+		return (ARCHIVE_FATAL);
+	}
+	/* zlib.h is not const-correct, so we need this one bit
+	 * of ugly hackery to convert a const * pointer to
+	 * a non-const pointer. */
+	strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+	strm->avail_in = (uInt)lastrm->avail_in;
+	strm->total_in = (uLong)lastrm->total_in;
+	strm->next_out = lastrm->next_out;
+	strm->avail_out = (uInt)lastrm->avail_out;
+	strm->total_out = (uLong)lastrm->total_out;
+	if (deflateInit2(strm, level, Z_DEFLATED,
+	    (withheader)?15:-15,
+	    8, Z_DEFAULT_STRATEGY) != Z_OK) {
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library");
+		return (ARCHIVE_FATAL);
+	}
+	lastrm->real_stream = strm;
+	lastrm->valid = 1;
+	lastrm->code = compression_code_deflate;
+	lastrm->end = compression_end_deflate;
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_code_deflate(struct archive *a,
+    struct la_zstream *lastrm, enum la_zaction action)
+{
+	z_stream *strm;
+	int r;
+
+	strm = (z_stream *)lastrm->real_stream;
+	/* zlib.h is not const-correct, so we need this one bit
+	 * of ugly hackery to convert a const * pointer to
+	 * a non-const pointer. */
+	strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+	strm->avail_in = (uInt)lastrm->avail_in;
+	strm->total_in = (uLong)lastrm->total_in;
+	strm->next_out = lastrm->next_out;
+	strm->avail_out = (uInt)lastrm->avail_out;
+	strm->total_out = (uLong)lastrm->total_out;
+	r = deflate(strm,
+	    (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH);
+	lastrm->next_in = strm->next_in;
+	lastrm->avail_in = strm->avail_in;
+	lastrm->total_in = strm->total_in;
+	lastrm->next_out = strm->next_out;
+	lastrm->avail_out = strm->avail_out;
+	lastrm->total_out = strm->total_out;
+	switch (r) {
+	case Z_OK:
+		return (ARCHIVE_OK);
+	case Z_STREAM_END:
+		return (ARCHIVE_EOF);
+	default:
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "GZip compression failed:"
+		    " deflate() call returned status %d", r);
+		return (ARCHIVE_FATAL);
+	}
+}
+
+static int
+compression_end_deflate(struct archive *a, struct la_zstream *lastrm)
+{
+	z_stream *strm;
+	int r;
+
+	strm = (z_stream *)lastrm->real_stream;
+	r = deflateEnd(strm);
+	free(strm);
+	lastrm->real_stream = NULL;
+	lastrm->valid = 0;
+	if (r != Z_OK) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Failed to clean up compressor");
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+#else
+static int
+compression_init_encoder_deflate(struct archive *a,
+    struct la_zstream *lastrm, int level, int withheader)
+{
+
+	(void) level; /* UNUSED */
+	(void) withheader; /* UNUSED */
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	return (compression_unsupported_encoder(a, lastrm, "deflate"));
+}
+#endif
+
+/*
+ * _7_BZIP2 compressor.
+ */
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int
+compression_init_encoder_bzip2(struct archive *a,
+    struct la_zstream *lastrm, int level)
+{
+	bz_stream *strm;
+
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	strm = calloc(1, sizeof(*strm));
+	if (strm == NULL) {
+		archive_set_error(a, ENOMEM,
+		    "Can't allocate memory for bzip2 stream");
+		return (ARCHIVE_FATAL);
+	}
+	/* bzlib.h is not const-correct, so we need this one bit
+	 * of ugly hackery to convert a const * pointer to
+	 * a non-const pointer. */
+	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+	strm->avail_in = lastrm->avail_in;
+	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+	strm->next_out = (char *)lastrm->next_out;
+	strm->avail_out = lastrm->avail_out;
+	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+	if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library");
+		return (ARCHIVE_FATAL);
+	}
+	lastrm->real_stream = strm;
+	lastrm->valid = 1;
+	lastrm->code = compression_code_bzip2;
+	lastrm->end = compression_end_bzip2;
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_code_bzip2(struct archive *a,
+    struct la_zstream *lastrm, enum la_zaction action)
+{
+	bz_stream *strm;
+	int r;
+
+	strm = (bz_stream *)lastrm->real_stream;
+	/* bzlib.h is not const-correct, so we need this one bit
+	 * of ugly hackery to convert a const * pointer to
+	 * a non-const pointer. */
+	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+	strm->avail_in = lastrm->avail_in;
+	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+	strm->next_out = (char *)lastrm->next_out;
+	strm->avail_out = lastrm->avail_out;
+	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+	r = BZ2_bzCompress(strm,
+	    (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN);
+	lastrm->next_in = (const unsigned char *)strm->next_in;
+	lastrm->avail_in = strm->avail_in;
+	lastrm->total_in =
+	    (((uint64_t)(uint32_t)strm->total_in_hi32) << 32)
+	    + (uint64_t)(uint32_t)strm->total_in_lo32;
+	lastrm->next_out = (unsigned char *)strm->next_out;
+	lastrm->avail_out = strm->avail_out;
+	lastrm->total_out =
+	    (((uint64_t)(uint32_t)strm->total_out_hi32) << 32)
+	    + (uint64_t)(uint32_t)strm->total_out_lo32;
+	switch (r) {
+	case BZ_RUN_OK:     /* Non-finishing */
+	case BZ_FINISH_OK:  /* Finishing: There's more work to do */
+		return (ARCHIVE_OK);
+	case BZ_STREAM_END: /* Finishing: all done */
+		/* Only occurs in finishing case */
+		return (ARCHIVE_EOF);
+	default:
+		/* Any other return value indicates an error */
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Bzip2 compression failed:"
+		    " BZ2_bzCompress() call returned status %d", r);
+		return (ARCHIVE_FATAL);
+	}
+}
+
+static int
+compression_end_bzip2(struct archive *a, struct la_zstream *lastrm)
+{
+	bz_stream *strm;
+	int r;
+
+	strm = (bz_stream *)lastrm->real_stream;
+	r = BZ2_bzCompressEnd(strm);
+	free(strm);
+	lastrm->real_stream = NULL;
+	lastrm->valid = 0;
+	if (r != BZ_OK) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Failed to clean up compressor");
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+#else
+static int
+compression_init_encoder_bzip2(struct archive *a,
+    struct la_zstream *lastrm, int level)
+{
+
+	(void) level; /* UNUSED */
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	return (compression_unsupported_encoder(a, lastrm, "bzip2"));
+}
+#endif
+
+/*
+ * _7_LZMA1, _7_LZMA2 compressor.
+ */
+#if defined(HAVE_LZMA_H)
+static int
+compression_init_encoder_lzma(struct archive *a,
+    struct la_zstream *lastrm, int level, uint64_t filter_id)
+{
+	static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
+	lzma_stream *strm;
+	lzma_filter *lzmafilters;
+	lzma_options_lzma lzma_opt;
+	int r;
+
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2);
+	if (strm == NULL) {
+		archive_set_error(a, ENOMEM,
+		    "Can't allocate memory for lzma stream");
+		return (ARCHIVE_FATAL);
+	}
+	lzmafilters = (lzma_filter *)(strm+1);
+	if (level > 6)
+		level = 6;
+	if (lzma_lzma_preset(&lzma_opt, level)) {
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ENOMEM,
+		    "Internal error initializing compression library");
+		return (ARCHIVE_FATAL);
+	}
+	lzmafilters[0].id = filter_id;
+	lzmafilters[0].options = &lzma_opt;
+	lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+
+	r = lzma_properties_size(&(lastrm->prop_size), lzmafilters);
+	if (r != LZMA_OK) {
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "lzma_properties_size failed");
+		return (ARCHIVE_FATAL);
+	}
+	if (lastrm->prop_size) {
+		lastrm->props = malloc(lastrm->prop_size);
+		if (lastrm->props == NULL) {
+			free(strm);
+			lastrm->real_stream = NULL;
+			archive_set_error(a, ENOMEM,
+			    "Cannot allocate memory");
+			return (ARCHIVE_FATAL);
+		}
+		r = lzma_properties_encode(lzmafilters,  lastrm->props);
+		if (r != LZMA_OK) {
+			free(strm);
+			lastrm->real_stream = NULL;
+			archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			    "lzma_properties_encode failed");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	*strm = lzma_init_data;
+	r = lzma_raw_encoder(strm, lzmafilters);
+	switch (r) {
+	case LZMA_OK:
+		lastrm->real_stream = strm;
+		lastrm->valid = 1;
+		lastrm->code = compression_code_lzma;
+		lastrm->end = compression_end_lzma;
+		r = ARCHIVE_OK;
+		break;
+	case LZMA_MEM_ERROR:
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ENOMEM,
+		    "Internal error initializing compression library: "
+		    "Cannot allocate memory");
+		r =  ARCHIVE_FATAL;
+		break;
+        default:
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library: "
+		    "It's a bug in liblzma");
+		r =  ARCHIVE_FATAL;
+		break;
+	}
+	return (r);
+}
+
+static int
+compression_init_encoder_lzma1(struct archive *a,
+    struct la_zstream *lastrm, int level)
+{
+	return compression_init_encoder_lzma(a, lastrm, level,
+		    LZMA_FILTER_LZMA1);
+}
+
+static int
+compression_init_encoder_lzma2(struct archive *a,
+    struct la_zstream *lastrm, int level)
+{
+	return compression_init_encoder_lzma(a, lastrm, level,
+		    LZMA_FILTER_LZMA2);
+}
+
+static int
+compression_code_lzma(struct archive *a,
+    struct la_zstream *lastrm, enum la_zaction action)
+{
+	lzma_stream *strm;
+	int r;
+
+	strm = (lzma_stream *)lastrm->real_stream;
+	strm->next_in = lastrm->next_in;
+	strm->avail_in = lastrm->avail_in;
+	strm->total_in = lastrm->total_in;
+	strm->next_out = lastrm->next_out;
+	strm->avail_out = lastrm->avail_out;
+	strm->total_out = lastrm->total_out;
+	r = lzma_code(strm,
+	    (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN);
+	lastrm->next_in = strm->next_in;
+	lastrm->avail_in = strm->avail_in;
+	lastrm->total_in = strm->total_in;
+	lastrm->next_out = strm->next_out;
+	lastrm->avail_out = strm->avail_out;
+	lastrm->total_out = strm->total_out;
+	switch (r) {
+	case LZMA_OK:
+		/* Non-finishing case */
+		return (ARCHIVE_OK);
+	case LZMA_STREAM_END:
+		/* This return can only occur in finishing case. */
+		return (ARCHIVE_EOF);
+	case LZMA_MEMLIMIT_ERROR:
+		archive_set_error(a, ENOMEM,
+		    "lzma compression error:"
+		    " %ju MiB would have been needed",
+		    (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1)
+			/ (1024 * 1024)));
+		return (ARCHIVE_FATAL);
+	default:
+		/* Any other return value indicates an error */
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "lzma compression failed:"
+		    " lzma_code() call returned status %d", r);
+		return (ARCHIVE_FATAL);
+	}
+}
+
+static int
+compression_end_lzma(struct archive *a, struct la_zstream *lastrm)
+{
+	lzma_stream *strm;
+
+	(void)a; /* UNUSED */
+	strm = (lzma_stream *)lastrm->real_stream;
+	lzma_end(strm);
+	free(strm);
+	lastrm->valid = 0;
+	lastrm->real_stream = NULL;
+	return (ARCHIVE_OK);
+}
+#else
+static int
+compression_init_encoder_lzma1(struct archive *a,
+    struct la_zstream *lastrm, int level)
+{
+
+	(void) level; /* UNUSED */
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	return (compression_unsupported_encoder(a, lastrm, "lzma"));
+}
+static int
+compression_init_encoder_lzma2(struct archive *a,
+    struct la_zstream *lastrm, int level)
+{
+
+	(void) level; /* UNUSED */
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	return (compression_unsupported_encoder(a, lastrm, "lzma"));
+}
+#endif
+
+/*
+ * _7_PPMD compressor.
+ */
+static void
+ppmd_write(void *p, Byte b)
+{
+	struct archive_write *a = ((IByteOut *)p)->a;
+	struct _7zip *zip = (struct _7zip *)(a->format_data);
+	struct la_zstream *lastrm = &(zip->stream);
+	struct ppmd_stream *strm;
+
+	if (lastrm->avail_out) {
+		*lastrm->next_out++ = b;
+		lastrm->avail_out--;
+		lastrm->total_out++;
+		return;
+	}
+	strm = (struct ppmd_stream *)lastrm->real_stream;
+	if (strm->buff_ptr < strm->buff_end) {
+		*strm->buff_ptr++ = b;
+		strm->buff_bytes++;
+	}
+}
+
+static int
+compression_init_encoder_ppmd(struct archive *a,
+    struct la_zstream *lastrm, unsigned maxOrder, uint32_t msize)
+{
+	struct ppmd_stream *strm;
+	uint8_t *props;
+	int r;
+
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	strm = calloc(1, sizeof(*strm));
+	if (strm == NULL) {
+		archive_set_error(a, ENOMEM,
+		    "Can't allocate memory for PPMd");
+		return (ARCHIVE_FATAL);
+	}
+	strm->buff = malloc(32);
+	if (strm->buff == NULL) {
+		free(strm);
+		archive_set_error(a, ENOMEM,
+		    "Can't allocate memory for PPMd");
+		return (ARCHIVE_FATAL);
+	}
+	strm->buff_ptr = strm->buff;
+	strm->buff_end = strm->buff + 32;
+
+	props = malloc(1+4);
+	if (props == NULL) {
+		free(strm->buff);
+		free(strm);
+		archive_set_error(a, ENOMEM,
+		    "Coludn't allocate memory for PPMd");
+		return (ARCHIVE_FATAL);
+	}
+	props[0] = maxOrder;
+	archive_le32enc(props+1, msize);
+	__archive_ppmd7_functions.Ppmd7_Construct(&strm->ppmd7_context);
+	r = __archive_ppmd7_functions.Ppmd7_Alloc(
+		&strm->ppmd7_context, msize);
+	if (r == 0) {
+		free(strm->buff);
+		free(strm);
+		free(props);
+		archive_set_error(a, ENOMEM,
+		    "Coludn't allocate memory for PPMd");
+		return (ARCHIVE_FATAL);
+	}
+	__archive_ppmd7_functions.Ppmd7_Init(&(strm->ppmd7_context), maxOrder);
+	strm->byteout.a = (struct archive_write *)a;
+	strm->byteout.Write = ppmd_write;
+	strm->range_enc.Stream = &(strm->byteout);
+	__archive_ppmd7_functions.Ppmd7z_RangeEnc_Init(&(strm->range_enc));
+	strm->stat = 0;
+
+	lastrm->real_stream = strm;
+	lastrm->valid = 1;
+	lastrm->code = compression_code_ppmd;
+	lastrm->end = compression_end_ppmd;
+	lastrm->prop_size = 5;
+	lastrm->props = props;
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_code_ppmd(struct archive *a,
+    struct la_zstream *lastrm, enum la_zaction action)
+{
+	struct ppmd_stream *strm;
+
+	(void)a; /* UNUSED */
+
+	strm = (struct ppmd_stream *)lastrm->real_stream;
+
+	/* Copy encoded data if there are remaining bytes from previous call. */
+	if (strm->buff_bytes) {
+		uint8_t *p = strm->buff_ptr - strm->buff_bytes;
+		while (lastrm->avail_out && strm->buff_bytes) {
+			*lastrm->next_out++ = *p++;
+			lastrm->avail_out--;
+			lastrm->total_out++;
+			strm->buff_bytes--;
+		}
+		if (strm->buff_bytes)
+			return (ARCHIVE_OK);
+		if (strm->stat == 1)
+			return (ARCHIVE_EOF);
+		strm->buff_ptr = strm->buff;
+	}
+	while (lastrm->avail_in && lastrm->avail_out) {
+		__archive_ppmd7_functions.Ppmd7_EncodeSymbol(
+			&(strm->ppmd7_context), &(strm->range_enc),
+			*lastrm->next_in++);
+		lastrm->avail_in--;
+		lastrm->total_in++;
+	}
+	if (lastrm->avail_in == 0 && action == ARCHIVE_Z_FINISH) {
+		__archive_ppmd7_functions.Ppmd7z_RangeEnc_FlushData(
+			&(strm->range_enc));
+		strm->stat = 1;
+		/* Return EOF if there are no remaining bytes. */
+		if (strm->buff_bytes == 0)
+			return (ARCHIVE_EOF);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_end_ppmd(struct archive *a, struct la_zstream *lastrm)
+{
+	struct ppmd_stream *strm;
+
+	(void)a; /* UNUSED */
+
+	strm = (struct ppmd_stream *)lastrm->real_stream;
+	__archive_ppmd7_functions.Ppmd7_Free(&strm->ppmd7_context);
+	free(strm->buff);
+	free(strm);
+	lastrm->real_stream = NULL;
+	lastrm->valid = 0;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Universal compressor initializer.
+ */
+static int
+_7z_compression_init_encoder(struct archive_write *a, unsigned compression,
+    int compression_level)
+{
+	struct _7zip *zip;
+	int r;
+
+	zip = (struct _7zip *)a->format_data;
+	switch (compression) {
+	case _7Z_DEFLATE:
+		r = compression_init_encoder_deflate(
+		    &(a->archive), &(zip->stream),
+		    compression_level, 0);
+		break;
+	case _7Z_BZIP2:
+		r = compression_init_encoder_bzip2(
+		    &(a->archive), &(zip->stream),
+		    compression_level);
+		break;
+	case _7Z_LZMA1:
+		r = compression_init_encoder_lzma1(
+		    &(a->archive), &(zip->stream),
+		    compression_level);
+		break;
+	case _7Z_LZMA2:
+		r = compression_init_encoder_lzma2(
+		    &(a->archive), &(zip->stream),
+		    compression_level);
+		break;
+	case _7Z_PPMD:
+		r = compression_init_encoder_ppmd(
+		    &(a->archive), &(zip->stream),
+		    PPMD7_DEFAULT_ORDER, PPMD7_DEFAULT_MEM_SIZE);
+		break;
+	case _7Z_COPY:
+	default:
+		r = compression_init_encoder_copy(
+		    &(a->archive), &(zip->stream));
+		break;
+	}
+	if (r == ARCHIVE_OK) {
+		zip->stream.total_in = 0;
+		zip->stream.next_out = zip->wbuff;
+		zip->stream.avail_out = sizeof(zip->wbuff);
+		zip->stream.total_out = 0;
+	}
+
+	return (r);
+}
+
+static int
+compression_code(struct archive *a, struct la_zstream *lastrm,
+    enum la_zaction action)
+{
+	if (lastrm->valid)
+		return (lastrm->code(a, lastrm, action));
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_end(struct archive *a, struct la_zstream *lastrm)
+{
+	if (lastrm->valid) {
+		lastrm->prop_size = 0;
+		free(lastrm->props);
+		lastrm->props = NULL;
+		return (lastrm->end(a, lastrm));
+	}
+	return (ARCHIVE_OK);
+}
+
+
diff --git a/c/archive_write_set_format_ar.c b/c/archive_write_set_format_ar.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_ar.c
@@ -0,0 +1,570 @@
+/*-
+ * Copyright (c) 2007 Kai Wang
+ * Copyright (c) 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
+ *    in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_ar.c 201108 2009-12-28 03:28:21Z kientzle $");
+
+#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_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct ar_w {
+	uint64_t	 entry_bytes_remaining;
+	uint64_t	 entry_padding;
+	int		 is_strtab;
+	int		 has_strtab;
+	char		 wrote_global_header;
+	char		*strtab;
+};
+
+/*
+ * Define structure of the "ar" header.
+ */
+#define AR_name_offset 0
+#define AR_name_size 16
+#define AR_date_offset 16
+#define AR_date_size 12
+#define AR_uid_offset 28
+#define AR_uid_size 6
+#define AR_gid_offset 34
+#define AR_gid_size 6
+#define AR_mode_offset 40
+#define AR_mode_size 8
+#define AR_size_offset 48
+#define AR_size_size 10
+#define AR_fmag_offset 58
+#define AR_fmag_size 2
+
+static int		 archive_write_set_format_ar(struct archive_write *);
+static int		 archive_write_ar_header(struct archive_write *,
+			     struct archive_entry *);
+static ssize_t		 archive_write_ar_data(struct archive_write *,
+			     const void *buff, size_t s);
+static int		 archive_write_ar_free(struct archive_write *);
+static int		 archive_write_ar_close(struct archive_write *);
+static int		 archive_write_ar_finish_entry(struct archive_write *);
+static const char	*ar_basename(const char *path);
+static int		 format_octal(int64_t v, char *p, int s);
+static int		 format_decimal(int64_t v, char *p, int s);
+
+int
+archive_write_set_format_ar_bsd(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_ar_bsd");
+	r = archive_write_set_format_ar(a);
+	if (r == ARCHIVE_OK) {
+		a->archive.archive_format = ARCHIVE_FORMAT_AR_BSD;
+		a->archive.archive_format_name = "ar (BSD)";
+	}
+	return (r);
+}
+
+int
+archive_write_set_format_ar_svr4(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_ar_svr4");
+	r = archive_write_set_format_ar(a);
+	if (r == ARCHIVE_OK) {
+		a->archive.archive_format = ARCHIVE_FORMAT_AR_GNU;
+		a->archive.archive_format_name = "ar (GNU/SVR4)";
+	}
+	return (r);
+}
+
+/*
+ * Generic initialization.
+ */
+static int
+archive_write_set_format_ar(struct archive_write *a)
+{
+	struct ar_w *ar;
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	ar = (struct ar_w *)calloc(1, sizeof(*ar));
+	if (ar == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate ar data");
+		return (ARCHIVE_FATAL);
+	}
+	a->format_data = ar;
+
+	a->format_name = "ar";
+	a->format_write_header = archive_write_ar_header;
+	a->format_write_data = archive_write_ar_data;
+	a->format_close = archive_write_ar_close;
+	a->format_free = archive_write_ar_free;
+	a->format_finish_entry = archive_write_ar_finish_entry;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_header(struct archive_write *a, struct archive_entry *entry)
+{
+	int ret, append_fn;
+	char buff[60];
+	char *ss, *se;
+	struct ar_w *ar;
+	const char *pathname;
+	const char *filename;
+	int64_t size;
+
+	append_fn = 0;
+	ar = (struct ar_w *)a->format_data;
+	ar->is_strtab = 0;
+	filename = NULL;
+	size = archive_entry_size(entry);
+
+
+	/*
+	 * Reject files with empty name.
+	 */
+	pathname = archive_entry_pathname(entry);
+	if (pathname == NULL || *pathname == '\0') {
+		archive_set_error(&a->archive, EINVAL,
+		    "Invalid filename");
+		return (ARCHIVE_WARN);
+	}
+
+	/*
+	 * If we are now at the beginning of the archive,
+	 * we need first write the ar global header.
+	 */
+	if (!ar->wrote_global_header) {
+		__archive_write_output(a, "!<arch>\n", 8);
+		ar->wrote_global_header = 1;
+	}
+
+	memset(buff, ' ', 60);
+	memcpy(&buff[AR_fmag_offset], "`\n", 2);
+
+	if (strcmp(pathname, "/") == 0 ) {
+		/* Entry is archive symbol table in GNU format */
+		buff[AR_name_offset] = '/';
+		goto stat;
+	}
+	if (strcmp(pathname, "/SYM64/") == 0) {
+		/* Entry is archive symbol table in GNU 64-bit format */
+		memcpy(buff + AR_name_offset, "/SYM64/", 7);
+		goto stat;
+	}
+	if (strcmp(pathname, "__.SYMDEF") == 0) {
+		/* Entry is archive symbol table in BSD format */
+		memcpy(buff + AR_name_offset, "__.SYMDEF", 9);
+		goto stat;
+	}
+	if (strcmp(pathname, "//") == 0) {
+		/*
+		 * Entry is archive filename table, inform that we should
+		 * collect strtab in next _data call.
+		 */
+		ar->is_strtab = 1;
+		buff[AR_name_offset] = buff[AR_name_offset + 1] = '/';
+		/*
+		 * For archive string table, only ar_size field should
+		 * be set.
+		 */
+		goto size;
+	}
+
+	/*
+	 * Otherwise, entry is a normal archive member.
+	 * Strip leading paths from filenames, if any.
+	 */
+	if ((filename = ar_basename(pathname)) == NULL) {
+		/* Reject filenames with trailing "/" */
+		archive_set_error(&a->archive, EINVAL,
+		    "Invalid filename");
+		return (ARCHIVE_WARN);
+	}
+
+	if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) {
+		/*
+		 * SVR4/GNU variant use a "/" to mark then end of the filename,
+		 * make it possible to have embedded spaces in the filename.
+		 * So, the longest filename here (without extension) is
+		 * actually 15 bytes.
+		 */
+		if (strlen(filename) <= 15) {
+			memcpy(&buff[AR_name_offset],
+			    filename, strlen(filename));
+			buff[AR_name_offset + strlen(filename)] = '/';
+		} else {
+			/*
+			 * For filename longer than 15 bytes, GNU variant
+			 * makes use of a string table and instead stores the
+			 * offset of the real filename to in the ar_name field.
+			 * The string table should have been written before.
+			 */
+			if (ar->has_strtab <= 0) {
+				archive_set_error(&a->archive, EINVAL,
+				    "Can't find string table");
+				return (ARCHIVE_WARN);
+			}
+
+			se = (char *)malloc(strlen(filename) + 3);
+			if (se == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate filename buffer");
+				return (ARCHIVE_FATAL);
+			}
+
+			memcpy(se, filename, strlen(filename));
+			strcpy(se + strlen(filename), "/\n");
+
+			ss = strstr(ar->strtab, se);
+			free(se);
+
+			if (ss == NULL) {
+				archive_set_error(&a->archive, EINVAL,
+				    "Invalid string table");
+				return (ARCHIVE_WARN);
+			}
+
+			/*
+			 * GNU variant puts "/" followed by digits into
+			 * ar_name field. These digits indicates the real
+			 * filename string's offset to the string table.
+			 */
+			buff[AR_name_offset] = '/';
+			if (format_decimal(ss - ar->strtab,
+			    buff + AR_name_offset + 1,
+			    AR_name_size - 1)) {
+				archive_set_error(&a->archive, ERANGE,
+				    "string table offset too large");
+				return (ARCHIVE_WARN);
+			}
+		}
+	} else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) {
+		/*
+		 * BSD variant: for any file name which is more than
+		 * 16 chars or contains one or more embedded space(s), the
+		 * string "#1/" followed by the ASCII length of the name is
+		 * put into the ar_name field. The file size (stored in the
+		 * ar_size field) is incremented by the length of the name.
+		 * The name is then written immediately following the
+		 * archive header.
+		 */
+		if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) {
+			memcpy(&buff[AR_name_offset], filename, strlen(filename));
+			buff[AR_name_offset + strlen(filename)] = ' ';
+		}
+		else {
+			memcpy(buff + AR_name_offset, "#1/", 3);
+			if (format_decimal(strlen(filename),
+			    buff + AR_name_offset + 3,
+			    AR_name_size - 3)) {
+				archive_set_error(&a->archive, ERANGE,
+				    "File name too long");
+				return (ARCHIVE_WARN);
+			}
+			append_fn = 1;
+			size += strlen(filename);
+		}
+	}
+
+stat:
+	if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File modification time too large");
+		return (ARCHIVE_WARN);
+	}
+	if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric user ID too large");
+		return (ARCHIVE_WARN);
+	}
+	if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric group ID too large");
+		return (ARCHIVE_WARN);
+	}
+	if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric mode too large");
+		return (ARCHIVE_WARN);
+	}
+	/*
+	 * Sanity Check: A non-pseudo archive member should always be
+	 * a regular file.
+	 */
+	if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Regular file required for non-pseudo member");
+		return (ARCHIVE_WARN);
+	}
+
+size:
+	if (format_decimal(size, buff + AR_size_offset, AR_size_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File size out of range");
+		return (ARCHIVE_WARN);
+	}
+
+	ret = __archive_write_output(a, buff, 60);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+
+	ar->entry_bytes_remaining = size;
+	ar->entry_padding = ar->entry_bytes_remaining % 2;
+
+	if (append_fn > 0) {
+		ret = __archive_write_output(a, filename, strlen(filename));
+		if (ret != ARCHIVE_OK)
+			return (ret);
+		ar->entry_bytes_remaining -= strlen(filename);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_write_ar_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct ar_w *ar;
+	int ret;
+
+	ar = (struct ar_w *)a->format_data;
+	if (s > ar->entry_bytes_remaining)
+		s = (size_t)ar->entry_bytes_remaining;
+
+	if (ar->is_strtab > 0) {
+		if (ar->has_strtab > 0) {
+			archive_set_error(&a->archive, EINVAL,
+			    "More than one string tables exist");
+			return (ARCHIVE_WARN);
+		}
+
+		ar->strtab = (char *)malloc(s + 1);
+		if (ar->strtab == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate strtab buffer");
+			return (ARCHIVE_FATAL);
+		}
+		memcpy(ar->strtab, buff, s);
+		ar->strtab[s] = '\0';
+		ar->has_strtab = 1;
+	}
+
+	ret = __archive_write_output(a, buff, s);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+
+	ar->entry_bytes_remaining -= s;
+	return (s);
+}
+
+static int
+archive_write_ar_free(struct archive_write *a)
+{
+	struct ar_w *ar;
+
+	ar = (struct ar_w *)a->format_data;
+
+	if (ar == NULL)
+		return (ARCHIVE_OK);
+
+	if (ar->has_strtab > 0) {
+		free(ar->strtab);
+		ar->strtab = NULL;
+	}
+
+	free(ar);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_close(struct archive_write *a)
+{
+	struct ar_w *ar;
+	int ret;
+
+	/*
+	 * If we haven't written anything yet, we need to write
+	 * the ar global header now to make it a valid ar archive.
+	 */
+	ar = (struct ar_w *)a->format_data;
+	if (!ar->wrote_global_header) {
+		ar->wrote_global_header = 1;
+		ret = __archive_write_output(a, "!<arch>\n", 8);
+		return (ret);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ar_finish_entry(struct archive_write *a)
+{
+	struct ar_w *ar;
+	int ret;
+
+	ar = (struct ar_w *)a->format_data;
+
+	if (ar->entry_bytes_remaining != 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Entry remaining bytes larger than 0");
+		return (ARCHIVE_WARN);
+	}
+
+	if (ar->entry_padding == 0) {
+		return (ARCHIVE_OK);
+	}
+
+	if (ar->entry_padding != 1) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Padding wrong size: %ju should be 1 or 0",
+		    (uintmax_t)ar->entry_padding);
+		return (ARCHIVE_WARN);
+	}
+
+	ret = __archive_write_output(a, "\n", 1);
+	return (ret);
+}
+
+/*
+ * Format a number into the specified field using base-8.
+ * NB: This version is slightly different from the one in
+ * _ustar.c
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+	int len;
+	char *h;
+
+	len = s;
+	h = p;
+
+	/* Octal values can't be negative, so use 0. */
+	if (v < 0) {
+		while (len-- > 0)
+			*p++ = '0';
+		return (-1);
+	}
+
+	p += s;		/* Start at the end and work backwards. */
+	do {
+		*--p = (char)('0' + (v & 7));
+		v >>= 3;
+	} while (--s > 0 && v > 0);
+
+	if (v == 0) {
+		memmove(h, p, len - s);
+		p = h + len - s;
+		while (s-- > 0)
+			*p++ = ' ';
+		return (0);
+	}
+	/* If it overflowed, fill field with max value. */
+	while (len-- > 0)
+		*p++ = '7';
+
+	return (-1);
+}
+
+/*
+ * Format a number into the specified field using base-10.
+ */
+static int
+format_decimal(int64_t v, char *p, int s)
+{
+	int len;
+	char *h;
+
+	len = s;
+	h = p;
+
+	/* Negative values in ar header are meaningless, so use 0. */
+	if (v < 0) {
+		while (len-- > 0)
+			*p++ = '0';
+		return (-1);
+	}
+
+	p += s;
+	do {
+		*--p = (char)('0' + (v % 10));
+		v /= 10;
+	} while (--s > 0 && v > 0);
+
+	if (v == 0) {
+		memmove(h, p, len - s);
+		p = h + len - s;
+		while (s-- > 0)
+			*p++ = ' ';
+		return (0);
+	}
+	/* If it overflowed, fill field with max value. */
+	while (len-- > 0)
+		*p++ = '9';
+
+	return (-1);
+}
+
+static const char *
+ar_basename(const char *path)
+{
+	const char *endp, *startp;
+
+	endp = path + strlen(path) - 1;
+	/*
+	 * For filename with trailing slash(es), we return
+	 * NULL indicating an error.
+	 */
+	if (*endp == '/')
+		return (NULL);
+
+	/* Find the start of the base */
+	startp = endp;
+	while (startp > path && *(startp - 1) != '/')
+		startp--;
+	
+	return (startp);
+}
diff --git a/c/archive_write_set_format_by_name.c b/c/archive_write_set_format_by_name.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_by_name.c
@@ -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: head/lib/libarchive/archive_write_set_format_by_name.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps names to functions. */
+static const
+struct { const char *name; int (*setter)(struct archive *); } names[] =
+{
+	{ "7zip",	archive_write_set_format_7zip },
+	{ "ar",		archive_write_set_format_ar_bsd },
+	{ "arbsd",	archive_write_set_format_ar_bsd },
+	{ "argnu",	archive_write_set_format_ar_svr4 },
+	{ "arsvr4",	archive_write_set_format_ar_svr4 },
+	{ "bsdtar",	archive_write_set_format_pax_restricted },
+	{ "cd9660",	archive_write_set_format_iso9660 },
+	{ "cpio",	archive_write_set_format_cpio },
+	{ "gnutar",	archive_write_set_format_gnutar },
+	{ "iso",	archive_write_set_format_iso9660 },
+	{ "iso9660",	archive_write_set_format_iso9660 },
+	{ "mtree",	archive_write_set_format_mtree },
+	{ "mtree-classic",	archive_write_set_format_mtree_classic },
+	{ "newc",	archive_write_set_format_cpio_newc },
+	{ "odc",	archive_write_set_format_cpio },
+	{ "oldtar",	archive_write_set_format_v7tar },
+	{ "pax",	archive_write_set_format_pax },
+	{ "paxr",	archive_write_set_format_pax_restricted },
+	{ "posix",	archive_write_set_format_pax },
+	{ "raw",	archive_write_set_format_raw },
+	{ "rpax",	archive_write_set_format_pax_restricted },
+	{ "shar",	archive_write_set_format_shar },
+	{ "shardump",	archive_write_set_format_shar_dump },
+	{ "ustar",	archive_write_set_format_ustar },
+	{ "v7tar",	archive_write_set_format_v7tar },
+	{ "v7",		archive_write_set_format_v7tar },
+	{ "warc",	archive_write_set_format_warc },
+	{ "xar",	archive_write_set_format_xar },
+	{ "zip",	archive_write_set_format_zip },
+	{ NULL,		NULL }
+};
+
+int
+archive_write_set_format_by_name(struct archive *a, const char *name)
+{
+	int i;
+
+	for (i = 0; names[i].name != NULL; i++) {
+		if (strcmp(name, names[i].name) == 0)
+			return ((names[i].setter)(a));
+	}
+
+	archive_set_error(a, EINVAL, "No such format '%s'", name);
+	a->state = ARCHIVE_STATE_FATAL;
+	return (ARCHIVE_FATAL);
+}
diff --git a/c/archive_write_set_format_cpio.c b/c/archive_write_set_format_cpio.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_cpio.c
@@ -0,0 +1,500 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $");
+
+#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
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+static ssize_t	archive_write_cpio_data(struct archive_write *,
+		    const void *buff, size_t s);
+static int	archive_write_cpio_close(struct archive_write *);
+static int	archive_write_cpio_free(struct archive_write *);
+static int	archive_write_cpio_finish_entry(struct archive_write *);
+static int	archive_write_cpio_header(struct archive_write *,
+		    struct archive_entry *);
+static int	archive_write_cpio_options(struct archive_write *,
+		    const char *, const char *);
+static int	format_octal(int64_t, void *, int);
+static int64_t	format_octal_recursive(int64_t, char *, int);
+static int	write_header(struct archive_write *, struct archive_entry *);
+
+struct cpio {
+	uint64_t	  entry_bytes_remaining;
+
+	int64_t		  ino_next;
+
+	struct		 { int64_t old; int new;} *ino_list;
+	size_t		  ino_list_size;
+	size_t		  ino_list_next;
+
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv_default;
+	int		  init_default_conversion;
+};
+
+#define	c_magic_offset 0
+#define	c_magic_size 6
+#define	c_dev_offset 6
+#define	c_dev_size 6
+#define	c_ino_offset 12
+#define	c_ino_size 6
+#define	c_mode_offset 18
+#define	c_mode_size 6
+#define	c_uid_offset 24
+#define	c_uid_size 6
+#define	c_gid_offset 30
+#define	c_gid_size 6
+#define	c_nlink_offset 36
+#define	c_nlink_size 6
+#define	c_rdev_offset 42
+#define	c_rdev_size 6
+#define	c_mtime_offset 48
+#define	c_mtime_size 11
+#define	c_namesize_offset 59
+#define	c_namesize_size 6
+#define	c_filesize_offset 65
+#define	c_filesize_size 11
+
+/*
+ * Set output format to 'cpio' format.
+ */
+int
+archive_write_set_format_cpio(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct cpio *cpio;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_cpio");
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+	if (cpio == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+		return (ARCHIVE_FATAL);
+	}
+	a->format_data = cpio;
+	a->format_name = "cpio";
+	a->format_options = archive_write_cpio_options;
+	a->format_write_header = archive_write_cpio_header;
+	a->format_write_data = archive_write_cpio_data;
+	a->format_finish_entry = archive_write_cpio_finish_entry;
+	a->format_close = archive_write_cpio_close;
+	a->format_free = archive_write_cpio_free;
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
+	a->archive.archive_format_name = "POSIX cpio";
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_cpio_options(struct archive_write *a, const char *key,
+    const char *val)
+{
+	struct cpio *cpio = (struct cpio *)a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: hdrcharset option needs a character-set name",
+			    a->format_name);
+		else {
+			cpio->opt_sconv = archive_string_conversion_to_charset(
+			    &a->archive, val, 0);
+			if (cpio->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Ino values are as long as 64 bits on some systems; cpio format
+ * only allows 18 bits and relies on the ino values to identify hardlinked
+ * files.  So, we can't merely "hash" the ino numbers since collisions
+ * would corrupt the archive.  Instead, we generate synthetic ino values
+ * to store in the archive and maintain a map of original ino values to
+ * synthetic ones so we can preserve hardlink information.
+ *
+ * TODO: Make this more efficient.  It's not as bad as it looks (most
+ * files don't have any hardlinks and we don't do any work here for those),
+ * but it wouldn't be hard to do better.
+ *
+ * TODO: Work with dev/ino pairs here instead of just ino values.
+ */
+static int
+synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
+{
+	int64_t ino = archive_entry_ino64(entry);
+	int ino_new;
+	size_t i;
+
+	/*
+	 * If no index number was given, don't assign one.  In
+	 * particular, this handles the end-of-archive marker
+	 * correctly by giving it a zero index value.  (This is also
+	 * why we start our synthetic index numbers with one below.)
+	 */
+	if (ino == 0)
+		return (0);
+
+	/* Don't store a mapping if we don't need to. */
+	if (archive_entry_nlink(entry) < 2) {
+		return (int)(++cpio->ino_next);
+	}
+
+	/* Look up old ino; if we have it, this is a hardlink
+	 * and we reuse the same value. */
+	for (i = 0; i < cpio->ino_list_next; ++i) {
+		if (cpio->ino_list[i].old == ino)
+			return (cpio->ino_list[i].new);
+	}
+
+	/* Assign a new index number. */
+	ino_new = (int)(++cpio->ino_next);
+
+	/* Ensure space for the new mapping. */
+	if (cpio->ino_list_size <= cpio->ino_list_next) {
+		size_t newsize = cpio->ino_list_size < 512
+		    ? 512 : cpio->ino_list_size * 2;
+		void *newlist = realloc(cpio->ino_list,
+		    sizeof(cpio->ino_list[0]) * newsize);
+		if (newlist == NULL)
+			return (-1);
+
+		cpio->ino_list_size = newsize;
+		cpio->ino_list = newlist;
+	}
+
+	/* Record and return the new value. */
+	cpio->ino_list[cpio->ino_list_next].old = ino;
+	cpio->ino_list[cpio->ino_list_next].new = ino_new;
+	++cpio->ino_list_next;
+	return (ino_new);
+}
+
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a)
+{
+	struct cpio *cpio;
+	struct archive_string_conv *sconv;
+
+	cpio = (struct cpio *)a->format_data;
+	sconv = cpio->opt_sconv;
+	if (sconv == NULL) {
+		if (!cpio->init_default_conversion) {
+			cpio->sconv_default =
+			    archive_string_default_conversion_for_write(
+			      &(a->archive));
+			cpio->init_default_conversion = 1;
+		}
+		sconv = cpio->sconv_default;
+	}
+	return (sconv);
+}
+
+static int
+archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry)
+{
+	const char *path;
+	size_t len;
+
+	if (archive_entry_filetype(entry) == 0) {
+		archive_set_error(&a->archive, -1, "Filetype required");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
+	    && errno == ENOMEM) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for Pathname");
+		return (ARCHIVE_FATAL);
+	}
+	if (len == 0 || path == NULL || path[0] == '\0') {
+		archive_set_error(&a->archive, -1, "Pathname required");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) {
+		archive_set_error(&a->archive, -1, "Size required");
+		return (ARCHIVE_FAILED);
+	}
+	return write_header(a, entry);
+}
+
+static int
+write_header(struct archive_write *a, struct archive_entry *entry)
+{
+	struct cpio *cpio;
+	const char *p, *path;
+	int pathlength, ret, ret_final;
+	int64_t	ino;
+	char h[76];
+	struct archive_string_conv *sconv;
+	struct archive_entry *entry_main;
+	size_t len;
+
+	cpio = (struct cpio *)a->format_data;
+	ret_final = ARCHIVE_OK;
+	sconv = get_sconv(a);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* Make sure the path separators in pathname, hardlink and symlink
+	 * are all slash '/', not the Windows path separator '\'. */
+	entry_main = __la_win_entry_in_posix_pathseparator(entry);
+	if (entry_main == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate ustar data");
+		return(ARCHIVE_FATAL);
+	}
+	if (entry != entry_main)
+		entry = entry_main;
+	else
+		entry_main = NULL;
+#else
+	entry_main = NULL;
+#endif
+
+	ret = archive_entry_pathname_l(entry, &path, &len, sconv);
+	if (ret != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate pathname '%s' to %s",
+		    archive_entry_pathname(entry),
+		    archive_string_conversion_charset_name(sconv));
+		ret_final = ARCHIVE_WARN;
+	}
+	/* Include trailing null. */
+	pathlength = (int)len + 1;
+
+	memset(h, 0, sizeof(h));
+	format_octal(070707, h + c_magic_offset, c_magic_size);
+	format_octal(archive_entry_dev(entry), h + c_dev_offset, c_dev_size);
+
+	ino = synthesize_ino_value(cpio, entry);
+	if (ino < 0) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "No memory for ino translation table");
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	} else if (ino > 0777777) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Too many files for this cpio format");
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
+	format_octal(ino & 0777777, h + c_ino_offset, c_ino_size);
+
+	/* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
+	format_octal(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
+	format_octal(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
+	format_octal(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
+	format_octal(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
+	if (archive_entry_filetype(entry) == AE_IFBLK
+	    || archive_entry_filetype(entry) == AE_IFCHR)
+	    format_octal(archive_entry_dev(entry), h + c_rdev_offset, c_rdev_size);
+	else
+	    format_octal(0, h + c_rdev_offset, c_rdev_size);
+	format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
+	format_octal(pathlength, h + c_namesize_offset, c_namesize_size);
+
+	/* Non-regular files don't store bodies. */
+	if (archive_entry_filetype(entry) != AE_IFREG)
+		archive_entry_set_size(entry, 0);
+
+	/* Symlinks get the link written as the body of the entry. */
+	ret = archive_entry_symlink_l(entry, &p, &len, sconv);
+	if (ret != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate linkname '%s' to %s",
+		    archive_entry_symlink(entry),
+		    archive_string_conversion_charset_name(sconv));
+		ret_final = ARCHIVE_WARN;
+	}
+	if (len > 0 && p != NULL  &&  *p != '\0')
+		ret = format_octal(strlen(p), h + c_filesize_offset,
+		    c_filesize_size);
+	else
+		ret = format_octal(archive_entry_size(entry),
+		    h + c_filesize_offset, c_filesize_size);
+	if (ret) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File is too large for cpio format.");
+		ret_final = ARCHIVE_FAILED;
+		goto exit_write_header;
+	}
+
+	ret = __archive_write_output(a, h, sizeof(h));
+	if (ret != ARCHIVE_OK) {
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
+
+	ret = __archive_write_output(a, path, pathlength);
+	if (ret != ARCHIVE_OK) {
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
+
+	cpio->entry_bytes_remaining = archive_entry_size(entry);
+
+	/* Write the symlink now. */
+	if (p != NULL  &&  *p != '\0') {
+		ret = __archive_write_output(a, p, strlen(p));
+		if (ret != ARCHIVE_OK) {
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+	}
+exit_write_header:
+	archive_entry_free(entry_main);
+	return (ret_final);
+}
+
+static ssize_t
+archive_write_cpio_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct cpio *cpio;
+	int ret;
+
+	cpio = (struct cpio *)a->format_data;
+	if (s > cpio->entry_bytes_remaining)
+		s = (size_t)cpio->entry_bytes_remaining;
+
+	ret = __archive_write_output(a, buff, s);
+	cpio->entry_bytes_remaining -= s;
+	if (ret >= 0)
+		return (s);
+	else
+		return (ret);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_octal(int64_t v, void *p, int digits)
+{
+	int64_t	max;
+	int	ret;
+
+	max = (((int64_t)1) << (digits * 3)) - 1;
+	if (v >= 0  &&  v <= max) {
+	    format_octal_recursive(v, (char *)p, digits);
+	    ret = 0;
+	} else {
+	    format_octal_recursive(max, (char *)p, digits);
+	    ret = -1;
+	}
+	return (ret);
+}
+
+static int64_t
+format_octal_recursive(int64_t v, char *p, int s)
+{
+	if (s == 0)
+		return (v);
+	v = format_octal_recursive(v, p+1, s-1);
+	*p = '0' + ((char)v & 7);
+	return (v >> 3);
+}
+
+static int
+archive_write_cpio_close(struct archive_write *a)
+{
+	int er;
+	struct archive_entry *trailer;
+
+	trailer = archive_entry_new2(NULL);
+	/* nlink = 1 here for GNU cpio compat. */
+	archive_entry_set_nlink(trailer, 1);
+	archive_entry_set_size(trailer, 0);
+	archive_entry_set_pathname(trailer, "TRAILER!!!");
+	er = write_header(a, trailer);
+	archive_entry_free(trailer);
+	return (er);
+}
+
+static int
+archive_write_cpio_free(struct archive_write *a)
+{
+	struct cpio *cpio;
+
+	cpio = (struct cpio *)a->format_data;
+	free(cpio->ino_list);
+	free(cpio);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_cpio_finish_entry(struct archive_write *a)
+{
+	struct cpio *cpio;
+
+	cpio = (struct cpio *)a->format_data;
+	return (__archive_write_nulls(a,
+		(size_t)cpio->entry_bytes_remaining));
+}
diff --git a/c/archive_write_set_format_cpio_newc.c b/c/archive_write_set_format_cpio_newc.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_cpio_newc.c
@@ -0,0 +1,457 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2006 Rudolf Marek SYSGO s.r.o.
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_write_set_format_cpio_newc.c 201160 2009-12-29 05:41:57Z kientzle $");
+
+#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
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+static ssize_t	archive_write_newc_data(struct archive_write *,
+		    const void *buff, size_t s);
+static int	archive_write_newc_close(struct archive_write *);
+static int	archive_write_newc_free(struct archive_write *);
+static int	archive_write_newc_finish_entry(struct archive_write *);
+static int	archive_write_newc_header(struct archive_write *,
+		    struct archive_entry *);
+static int      archive_write_newc_options(struct archive_write *,
+		    const char *, const char *);
+static int	format_hex(int64_t, void *, int);
+static int64_t	format_hex_recursive(int64_t, char *, int);
+static int	write_header(struct archive_write *, struct archive_entry *);
+
+struct cpio {
+	uint64_t	  entry_bytes_remaining;
+	int		  padding;
+
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv_default;
+	int		  init_default_conversion;
+};
+
+#define	c_magic_offset 0
+#define	c_magic_size 6
+#define	c_ino_offset 6
+#define	c_ino_size 8
+#define	c_mode_offset 14
+#define	c_mode_size 8
+#define	c_uid_offset 22
+#define	c_uid_size 8
+#define	c_gid_offset 30
+#define	c_gid_size 8
+#define	c_nlink_offset 38
+#define	c_nlink_size 8
+#define	c_mtime_offset 46
+#define	c_mtime_size 8
+#define	c_filesize_offset 54
+#define	c_filesize_size 8
+#define	c_devmajor_offset 62
+#define	c_devmajor_size 8
+#define	c_devminor_offset 70
+#define	c_devminor_size 8
+#define	c_rdevmajor_offset 78
+#define	c_rdevmajor_size 8
+#define	c_rdevminor_offset 86
+#define	c_rdevminor_size 8
+#define	c_namesize_offset 94
+#define	c_namesize_size 8
+#define	c_checksum_offset 102
+#define	c_checksum_size 8
+#define	c_header_size 110
+
+/* Logic trick: difference between 'n' and next multiple of 4 */
+#define PAD4(n)	(3 & (1 + ~(n)))
+
+/*
+ * Set output format to 'cpio' format.
+ */
+int
+archive_write_set_format_cpio_newc(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct cpio *cpio;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_newc");
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	cpio = (struct cpio *)calloc(1, sizeof(*cpio));
+	if (cpio == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
+		return (ARCHIVE_FATAL);
+	}
+	a->format_data = cpio;
+	a->format_name = "cpio";
+	a->format_options = archive_write_newc_options;
+	a->format_write_header = archive_write_newc_header;
+	a->format_write_data = archive_write_newc_data;
+	a->format_finish_entry = archive_write_newc_finish_entry;
+	a->format_close = archive_write_newc_close;
+	a->format_free = archive_write_newc_free;
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_SVR4_NOCRC;
+	a->archive.archive_format_name = "SVR4 cpio nocrc";
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_newc_options(struct archive_write *a, const char *key,
+    const char *val)
+{
+	struct cpio *cpio = (struct cpio *)a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: hdrcharset option needs a character-set name",
+			    a->format_name);
+		else {
+			cpio->opt_sconv = archive_string_conversion_to_charset(
+			    &a->archive, val, 0);
+			if (cpio->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a)
+{
+	struct cpio *cpio;
+	struct archive_string_conv *sconv;
+
+	cpio = (struct cpio *)a->format_data;
+	sconv = cpio->opt_sconv;
+	if (sconv == NULL) {
+		if (!cpio->init_default_conversion) {
+			cpio->sconv_default =
+			    archive_string_default_conversion_for_write(
+			      &(a->archive));
+			cpio->init_default_conversion = 1;
+		}
+		sconv = cpio->sconv_default;
+	}
+	return (sconv);
+}
+
+static int
+archive_write_newc_header(struct archive_write *a, struct archive_entry *entry)
+{
+	const char *path;
+	size_t len;
+
+	if (archive_entry_filetype(entry) == 0) {
+		archive_set_error(&a->archive, -1, "Filetype required");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
+	    && errno == ENOMEM) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for Pathname");
+		return (ARCHIVE_FATAL);
+	}
+	if (len == 0 || path == NULL || path[0] == '\0') {
+		archive_set_error(&a->archive, -1, "Pathname required");
+		return (ARCHIVE_FAILED);
+	}
+
+	if (archive_entry_hardlink(entry) == NULL
+	    && (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0)) {
+		archive_set_error(&a->archive, -1, "Size required");
+		return (ARCHIVE_FAILED);
+	}
+	return write_header(a, entry);
+}
+
+static int
+write_header(struct archive_write *a, struct archive_entry *entry)
+{
+	int64_t ino;
+	struct cpio *cpio;
+	const char *p, *path;
+	int pathlength, ret, ret_final;
+	char h[c_header_size];
+	struct archive_string_conv *sconv;
+	struct archive_entry *entry_main;
+	size_t len;
+	int pad;
+
+	cpio = (struct cpio *)a->format_data;
+	ret_final = ARCHIVE_OK;
+	sconv = get_sconv(a);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* Make sure the path separators in pathname, hardlink and symlink
+	 * are all slash '/', not the Windows path separator '\'. */
+	entry_main = __la_win_entry_in_posix_pathseparator(entry);
+	if (entry_main == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate ustar data");
+		return(ARCHIVE_FATAL);
+	}
+	if (entry != entry_main)
+		entry = entry_main;
+	else
+		entry_main = NULL;
+#else
+	entry_main = NULL;
+#endif
+
+	ret = archive_entry_pathname_l(entry, &path, &len, sconv);
+	if (ret != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate pathname '%s' to %s",
+		    archive_entry_pathname(entry),
+		    archive_string_conversion_charset_name(sconv));
+		ret_final = ARCHIVE_WARN;
+	}
+	pathlength = (int)len + 1; /* Include trailing null. */
+
+	memset(h, 0, c_header_size);
+	format_hex(0x070701, h + c_magic_offset, c_magic_size);
+	format_hex(archive_entry_devmajor(entry), h + c_devmajor_offset,
+	    c_devmajor_size);
+	format_hex(archive_entry_devminor(entry), h + c_devminor_offset,
+	    c_devminor_size);
+
+	ino = archive_entry_ino64(entry);
+	if (ino > 0xffffffff) {
+		archive_set_error(&a->archive, ERANGE,
+		    "large inode number truncated");
+		ret_final = ARCHIVE_WARN;
+	}
+
+	/* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
+	format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size);
+	format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
+	format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
+	format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
+	format_hex(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
+	if (archive_entry_filetype(entry) == AE_IFBLK
+	    || archive_entry_filetype(entry) == AE_IFCHR) {
+	    format_hex(archive_entry_rdevmajor(entry), h + c_rdevmajor_offset, c_rdevmajor_size);
+	    format_hex(archive_entry_rdevminor(entry), h + c_rdevminor_offset, c_rdevminor_size);
+	} else {
+	    format_hex(0, h + c_rdevmajor_offset, c_rdevmajor_size);
+	    format_hex(0, h + c_rdevminor_offset, c_rdevminor_size);
+	}
+	format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
+	format_hex(pathlength, h + c_namesize_offset, c_namesize_size);
+	format_hex(0, h + c_checksum_offset, c_checksum_size);
+
+	/* Non-regular files don't store bodies. */
+	if (archive_entry_filetype(entry) != AE_IFREG)
+		archive_entry_set_size(entry, 0);
+
+	/* Symlinks get the link written as the body of the entry. */
+	ret = archive_entry_symlink_l(entry, &p, &len, sconv);
+	if (ret != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Likname");
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate linkname '%s' to %s",
+		    archive_entry_symlink(entry),
+		    archive_string_conversion_charset_name(sconv));
+		ret_final = ARCHIVE_WARN;
+	}
+	if (len > 0 && p != NULL  &&  *p != '\0')
+		ret = format_hex(strlen(p), h + c_filesize_offset,
+		    c_filesize_size);
+	else
+		ret = format_hex(archive_entry_size(entry),
+		    h + c_filesize_offset, c_filesize_size);
+	if (ret) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File is too large for this format.");
+		ret_final = ARCHIVE_FAILED;
+		goto exit_write_header;
+	}
+
+	ret = __archive_write_output(a, h, c_header_size);
+	if (ret != ARCHIVE_OK) {
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
+
+	/* Pad pathname to even length. */
+	ret = __archive_write_output(a, path, pathlength);
+	if (ret != ARCHIVE_OK) {
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
+	pad = PAD4(pathlength + c_header_size);
+	if (pad) {
+		ret = __archive_write_output(a, "\0\0\0", pad);
+		if (ret != ARCHIVE_OK) {
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+	}
+
+	cpio->entry_bytes_remaining = archive_entry_size(entry);
+	cpio->padding = (int)PAD4(cpio->entry_bytes_remaining);
+
+	/* Write the symlink now. */
+	if (p != NULL  &&  *p != '\0') {
+		ret = __archive_write_output(a, p, strlen(p));
+		if (ret != ARCHIVE_OK) {
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		pad = PAD4(strlen(p));
+		ret = __archive_write_output(a, "\0\0\0", pad);
+		if (ret != ARCHIVE_OK) {
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+	}
+exit_write_header:
+	archive_entry_free(entry_main);
+	return (ret_final);
+}
+
+static ssize_t
+archive_write_newc_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct cpio *cpio;
+	int ret;
+
+	cpio = (struct cpio *)a->format_data;
+	if (s > cpio->entry_bytes_remaining)
+		s = (size_t)cpio->entry_bytes_remaining;
+
+	ret = __archive_write_output(a, buff, s);
+	cpio->entry_bytes_remaining -= s;
+	if (ret >= 0)
+		return (s);
+	else
+		return (ret);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_hex(int64_t v, void *p, int digits)
+{
+	int64_t	max;
+	int	ret;
+
+	max = (((int64_t)1) << (digits * 4)) - 1;
+	if (v >= 0  &&  v <= max) {
+	    format_hex_recursive(v, (char *)p, digits);
+	    ret = 0;
+	} else {
+	    format_hex_recursive(max, (char *)p, digits);
+	    ret = -1;
+	}
+	return (ret);
+}
+
+static int64_t
+format_hex_recursive(int64_t v, char *p, int s)
+{
+	if (s == 0)
+		return (v);
+	v = format_hex_recursive(v, p+1, s-1);
+	*p = "0123456789abcdef"[v & 0xf];
+	return (v >> 4);
+}
+
+static int
+archive_write_newc_close(struct archive_write *a)
+{
+	int er;
+	struct archive_entry *trailer;
+
+	trailer = archive_entry_new();
+	archive_entry_set_nlink(trailer, 1);
+	archive_entry_set_size(trailer, 0);
+	archive_entry_set_pathname(trailer, "TRAILER!!!");
+	/* Bypass the required data checks. */
+	er = write_header(a, trailer);
+	archive_entry_free(trailer);
+	return (er);
+}
+
+static int
+archive_write_newc_free(struct archive_write *a)
+{
+	struct cpio *cpio;
+
+	cpio = (struct cpio *)a->format_data;
+	free(cpio);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_newc_finish_entry(struct archive_write *a)
+{
+	struct cpio *cpio;
+
+	cpio = (struct cpio *)a->format_data;
+	return (__archive_write_nulls(a,
+		(size_t)cpio->entry_bytes_remaining + cpio->padding));
+}
diff --git a/c/archive_write_set_format_filter_by_ext.c b/c/archive_write_set_format_filter_by_ext.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_filter_by_ext.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2015 Okhotnikov Kirill
+ * 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: head/lib/libarchive/archive_write_set_format_by_name.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+
+/* A table that maps names to functions. */
+static const
+struct { const char *name; int (*format)(struct archive *); int (*filter)(struct archive *);  } names[] =
+{
+	{ ".7z",	archive_write_set_format_7zip,            archive_write_add_filter_none},
+	{ ".zip",	archive_write_set_format_zip,             archive_write_add_filter_none},
+	{ ".jar",	archive_write_set_format_zip,             archive_write_add_filter_none},
+	{ ".cpio",	archive_write_set_format_cpio,            archive_write_add_filter_none},
+	{ ".iso",	archive_write_set_format_iso9660,         archive_write_add_filter_none},
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
+	{ ".a",	        archive_write_set_format_ar_bsd,          archive_write_add_filter_none},
+	{ ".ar",	archive_write_set_format_ar_bsd,          archive_write_add_filter_none},
+#else         
+	{ ".a",	        archive_write_set_format_ar_svr4,         archive_write_add_filter_none},
+	{ ".ar",	archive_write_set_format_ar_svr4,         archive_write_add_filter_none},
+#endif        
+	{ ".tar",	archive_write_set_format_pax_restricted,  archive_write_add_filter_none},
+	{ ".tgz",	archive_write_set_format_pax_restricted,  archive_write_add_filter_gzip},
+	{ ".tar.gz",	archive_write_set_format_pax_restricted,  archive_write_add_filter_gzip},
+	{ ".tar.bz2",	archive_write_set_format_pax_restricted,  archive_write_add_filter_bzip2},
+	{ ".tar.xz",	archive_write_set_format_pax_restricted,  archive_write_add_filter_xz},
+	{ NULL,		NULL,                             NULL }
+};
+
+static 
+int cmpsuff(const char *str, const char *suffix)
+{
+  size_t length_str, length_suffix;
+
+  if ((str == NULL) || (suffix == NULL))
+    return -1;
+
+  length_str = strlen(str);
+  length_suffix = strlen(suffix);
+
+  if (length_str >= length_suffix) {
+    return strcmp(str + (length_str - length_suffix), suffix);
+  } else {
+    return -1;
+  }
+}
+
+static int get_array_index(const char *name)
+{
+  int i;
+
+  for (i = 0; names[i].name != NULL; i++) 
+  {
+    if (cmpsuff(name, names[i].name) == 0)
+      return i;
+  }    
+  return -1;
+  
+}
+
+int
+archive_write_set_format_filter_by_ext(struct archive *a, const char *filename)
+{
+  int names_index = get_array_index(filename);
+  
+  if (names_index >= 0)
+  {  
+    int format_state = (names[names_index].format)(a);
+    if (format_state == ARCHIVE_OK)
+      return ((names[names_index].filter)(a));
+    else
+      return format_state;
+  }    
+
+  archive_set_error(a, EINVAL, "No such format '%s'", filename);
+  a->state = ARCHIVE_STATE_FATAL;
+  return (ARCHIVE_FATAL);
+}
+
+int
+archive_write_set_format_filter_by_ext_def(struct archive *a, const char *filename, const char * def_ext)
+{
+  int names_index = get_array_index(filename);
+  
+  if (names_index < 0)
+    names_index = get_array_index(def_ext);
+  
+  if (names_index >= 0)
+  {  
+    int format_state = (names[names_index].format)(a);
+    if (format_state == ARCHIVE_OK)
+      return ((names[names_index].filter)(a));
+    else
+      return format_state;
+  }    
+
+  archive_set_error(a, EINVAL, "No such format '%s'", filename);
+  a->state = ARCHIVE_STATE_FATAL;
+  return (ARCHIVE_FATAL);
+}
+
+
+
+
diff --git a/c/archive_write_set_format_gnutar.c b/c/archive_write_set_format_gnutar.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_gnutar.c
@@ -0,0 +1,755 @@
+/*-
+ * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Author: Jonas Gastal <jgastal@profusion.mobi>
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ *
+ * 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: head/lib/libarchive/archive_write_set_format_gnu_tar.c 191579 2009-04-27 18:35:03Z gastal $");
+
+
+#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
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct gnutar {
+	uint64_t	entry_bytes_remaining;
+	uint64_t	entry_padding;
+	const char *	linkname;
+	size_t		linkname_length;
+	const char *	pathname;
+	size_t		pathname_length;
+	const char *	uname;
+	size_t		uname_length;
+	const char *	gname;
+	size_t		gname_length;
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv_default;
+	int init_default_conversion;
+};
+
+/*
+ * Define structure of GNU tar header.
+ */
+#define	GNUTAR_name_offset 0
+#define	GNUTAR_name_size 100
+#define	GNUTAR_mode_offset 100
+#define	GNUTAR_mode_size 7
+#define	GNUTAR_mode_max_size 8
+#define	GNUTAR_uid_offset 108
+#define	GNUTAR_uid_size 7
+#define	GNUTAR_uid_max_size 8
+#define	GNUTAR_gid_offset 116
+#define	GNUTAR_gid_size 7
+#define	GNUTAR_gid_max_size 8
+#define	GNUTAR_size_offset 124
+#define	GNUTAR_size_size 11
+#define	GNUTAR_size_max_size 12
+#define	GNUTAR_mtime_offset 136
+#define	GNUTAR_mtime_size 11
+#define	GNUTAR_mtime_max_size 11
+#define	GNUTAR_checksum_offset 148
+#define	GNUTAR_checksum_size 8
+#define	GNUTAR_typeflag_offset 156
+#define	GNUTAR_typeflag_size 1
+#define	GNUTAR_linkname_offset 157
+#define	GNUTAR_linkname_size 100
+#define	GNUTAR_magic_offset 257
+#define	GNUTAR_magic_size 6
+#define	GNUTAR_version_offset 263
+#define	GNUTAR_version_size 2
+#define	GNUTAR_uname_offset 265
+#define	GNUTAR_uname_size 32
+#define	GNUTAR_gname_offset 297
+#define	GNUTAR_gname_size 32
+#define	GNUTAR_rdevmajor_offset 329
+#define	GNUTAR_rdevmajor_size 6
+#define	GNUTAR_rdevmajor_max_size 8
+#define	GNUTAR_rdevminor_offset 337
+#define	GNUTAR_rdevminor_size 6
+#define	GNUTAR_rdevminor_max_size 8
+
+/*
+ * A filled-in copy of the header for initialization.
+ */
+static const char template_header[] = {
+	/* name: 100 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,
+	/* Mode, null termination: 8 bytes */
+	'0','0','0','0','0','0', '0','\0',
+	/* uid, null termination: 8 bytes */
+	'0','0','0','0','0','0', '0','\0',
+	/* gid, null termination: 8 bytes */
+	'0','0','0','0','0','0', '0','\0',
+	/* size, space termination: 12 bytes */
+	'0','0','0','0','0','0','0','0','0','0','0', '\0',
+	/* mtime, space termination: 12 bytes */
+	'0','0','0','0','0','0','0','0','0','0','0', '\0',
+	/* Initial checksum value: 8 spaces */
+	' ',' ',' ',' ',' ',' ',' ',' ',
+	/* Typeflag: 1 byte */
+	'0',			/* '0' = regular file */
+	/* Linkname: 100 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,
+	/* Magic: 8 bytes */
+	'u','s','t','a','r',' ', ' ','\0',
+	/* Uname: 32 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	/* Gname: 32 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	/* rdevmajor + null padding: 8 bytes */
+	'\0','\0','\0','\0','\0','\0', '\0','\0',
+	/* rdevminor + null padding: 8 bytes */
+	'\0','\0','\0','\0','\0','\0', '\0','\0',
+	/* Padding: 167 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0
+};
+
+static int      archive_write_gnutar_options(struct archive_write *,
+		    const char *, const char *);
+static int	archive_format_gnutar_header(struct archive_write *, char h[512],
+		    struct archive_entry *, int tartype);
+static int      archive_write_gnutar_header(struct archive_write *,
+		    struct archive_entry *entry);
+static ssize_t	archive_write_gnutar_data(struct archive_write *a, const void *buff,
+		    size_t s);
+static int	archive_write_gnutar_free(struct archive_write *);
+static int	archive_write_gnutar_close(struct archive_write *);
+static int	archive_write_gnutar_finish_entry(struct archive_write *);
+static int	format_256(int64_t, char *, int);
+static int	format_number(int64_t, char *, int size, int maxsize);
+static int	format_octal(int64_t, char *, int);
+
+/*
+ * Set output format to 'GNU tar' format.
+ */
+int
+archive_write_set_format_gnutar(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct gnutar *gnutar;
+
+	gnutar = (struct gnutar *)calloc(1, sizeof(*gnutar));
+	if (gnutar == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate gnutar data");
+		return (ARCHIVE_FATAL);
+	}
+	a->format_data = gnutar;
+	a->format_name = "gnutar";
+	a->format_options = archive_write_gnutar_options;
+	a->format_write_header = archive_write_gnutar_header;
+	a->format_write_data = archive_write_gnutar_data;
+	a->format_close = archive_write_gnutar_close;
+	a->format_free = archive_write_gnutar_free;
+	a->format_finish_entry = archive_write_gnutar_finish_entry;
+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
+	a->archive.archive_format_name = "GNU tar";
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_gnutar_options(struct archive_write *a, const char *key,
+    const char *val)
+{
+	struct gnutar *gnutar = (struct gnutar *)a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: hdrcharset option needs a character-set name",
+			    a->format_name);
+		else {
+			gnutar->opt_sconv = archive_string_conversion_to_charset(
+			    &a->archive, val, 0);
+			if (gnutar->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_gnutar_close(struct archive_write *a)
+{
+	return (__archive_write_nulls(a, 512*2));
+}
+
+static int
+archive_write_gnutar_free(struct archive_write *a)
+{
+	struct gnutar *gnutar;
+
+	gnutar = (struct gnutar *)a->format_data;
+	free(gnutar);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_gnutar_finish_entry(struct archive_write *a)
+{
+	struct gnutar *gnutar;
+	int ret;
+
+	gnutar = (struct gnutar *)a->format_data;
+	ret = __archive_write_nulls(a, (size_t)
+	    (gnutar->entry_bytes_remaining + gnutar->entry_padding));
+	gnutar->entry_bytes_remaining = gnutar->entry_padding = 0;
+	return (ret);
+}
+
+static ssize_t
+archive_write_gnutar_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct gnutar *gnutar;
+	int ret;
+
+	gnutar = (struct gnutar *)a->format_data;
+	if (s > gnutar->entry_bytes_remaining)
+		s = (size_t)gnutar->entry_bytes_remaining;
+	ret = __archive_write_output(a, buff, s);
+	gnutar->entry_bytes_remaining -= s;
+	if (ret != ARCHIVE_OK)
+		return (ret);
+	return (s);
+}
+
+static int
+archive_write_gnutar_header(struct archive_write *a,
+     struct archive_entry *entry)
+{
+	char buff[512];
+	int r, ret, ret2 = ARCHIVE_OK;
+	int tartype;
+	struct gnutar *gnutar;
+	struct archive_string_conv *sconv;
+	struct archive_entry *entry_main;
+
+	gnutar = (struct gnutar *)a->format_data;
+
+	/* Setup default string conversion. */
+	if (gnutar->opt_sconv == NULL) {
+		if (!gnutar->init_default_conversion) {
+			gnutar->sconv_default =
+			    archive_string_default_conversion_for_write(
+				&(a->archive));
+			gnutar->init_default_conversion = 1;
+		}
+		sconv = gnutar->sconv_default;
+	} else
+		sconv = gnutar->opt_sconv;
+
+	/* Only regular files (not hardlinks) have data. */
+	if (archive_entry_hardlink(entry) != NULL ||
+	    archive_entry_symlink(entry) != NULL ||
+	    !(archive_entry_filetype(entry) == AE_IFREG))
+		archive_entry_set_size(entry, 0);
+
+	if (AE_IFDIR == archive_entry_filetype(entry)) {
+		const char *p;
+		size_t path_length;
+		/*
+		 * Ensure a trailing '/'.  Modify the entry so
+		 * the client sees the change.
+		 */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		const wchar_t *wp;
+
+		wp = archive_entry_pathname_w(entry);
+		if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+			struct archive_wstring ws;
+
+			archive_string_init(&ws);
+			path_length = wcslen(wp);
+			if (archive_wstring_ensure(&ws,
+			    path_length + 2) == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate ustar data");
+				archive_wstring_free(&ws);
+				return(ARCHIVE_FATAL);
+			}
+			/* Should we keep '\' ? */
+			if (wp[path_length -1] == L'\\')
+				path_length--;
+			archive_wstrncpy(&ws, wp, path_length);
+			archive_wstrappend_wchar(&ws, L'/');
+			archive_entry_copy_pathname_w(entry, ws.s);
+			archive_wstring_free(&ws);
+			p = NULL;
+		} else
+#endif
+			p = archive_entry_pathname(entry);
+		/*
+		 * On Windows, this is a backup operation just in
+		 * case getting WCS failed. On POSIX, this is a
+		 * normal operation.
+		 */
+		if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+			struct archive_string as;
+
+			archive_string_init(&as);
+			path_length = strlen(p);
+			if (archive_string_ensure(&as,
+			    path_length + 2) == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate ustar data");
+				archive_string_free(&as);
+				return(ARCHIVE_FATAL);
+			}
+#if defined(_WIN32) && !defined(__CYGWIN__)
+			/* NOTE: This might break the pathname
+			 * if the current code page is CP932 and
+			 * the pathname includes a character '\'
+			 * as a part of its multibyte pathname. */
+			if (p[strlen(p) -1] == '\\')
+				path_length--;
+			else
+#endif
+			archive_strncpy(&as, p, path_length);
+			archive_strappend_char(&as, '/');
+			archive_entry_copy_pathname(entry, as.s);
+			archive_string_free(&as);
+		}
+	}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* Make sure the path separators in pathname, hardlink and symlink
+	 * are all slash '/', not the Windows path separator '\'. */
+	entry_main = __la_win_entry_in_posix_pathseparator(entry);
+	if (entry_main == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate ustar data");
+		return(ARCHIVE_FATAL);
+	}
+	if (entry != entry_main)
+		entry = entry_main;
+	else
+		entry_main = NULL;
+#else
+	entry_main = NULL;
+#endif
+	r = archive_entry_pathname_l(entry, &(gnutar->pathname),
+	    &(gnutar->pathname_length), sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathame");
+			ret = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate pathname '%s' to %s",
+		    archive_entry_pathname(entry),
+		    archive_string_conversion_charset_name(sconv));
+		ret2 = ARCHIVE_WARN;
+	}
+	r = archive_entry_uname_l(entry, &(gnutar->uname),
+	    &(gnutar->uname_length), sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Uname");
+			ret = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate uname '%s' to %s",
+		    archive_entry_uname(entry),
+		    archive_string_conversion_charset_name(sconv));
+		ret2 = ARCHIVE_WARN;
+	}
+	r = archive_entry_gname_l(entry, &(gnutar->gname),
+	    &(gnutar->gname_length), sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Gname");
+			ret = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate gname '%s' to %s",
+		    archive_entry_gname(entry),
+		    archive_string_conversion_charset_name(sconv));
+		ret2 = ARCHIVE_WARN;
+	}
+
+	/* If linkname is longer than 100 chars we need to add a 'K' header. */
+	r = archive_entry_hardlink_l(entry, &(gnutar->linkname),
+	    &(gnutar->linkname_length), sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			ret = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate linkname '%s' to %s",
+		    archive_entry_hardlink(entry),
+		    archive_string_conversion_charset_name(sconv));
+		ret2 = ARCHIVE_WARN;
+	}
+	if (gnutar->linkname_length == 0) {
+		r = archive_entry_symlink_l(entry, &(gnutar->linkname),
+		    &(gnutar->linkname_length), sconv);
+		if (r != 0) {
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Linkname");
+				ret = ARCHIVE_FATAL;
+				goto exit_write_header;
+			}
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Can't translate linkname '%s' to %s",
+			    archive_entry_hardlink(entry),
+			    archive_string_conversion_charset_name(sconv));
+			ret2 = ARCHIVE_WARN;
+		}
+	}
+	if (gnutar->linkname_length > GNUTAR_linkname_size) {
+		size_t length = gnutar->linkname_length + 1;
+		struct archive_entry *temp = archive_entry_new2(&a->archive);
+
+		/* Uname/gname here don't really matter since no one reads them;
+		 * these are the values that GNU tar happens to use on FreeBSD. */
+		archive_entry_set_uname(temp, "root");
+		archive_entry_set_gname(temp, "wheel");
+
+		archive_entry_set_pathname(temp, "././@LongLink");
+		archive_entry_set_size(temp, length);
+		ret = archive_format_gnutar_header(a, buff, temp, 'K');
+		archive_entry_free(temp);
+		if (ret < ARCHIVE_WARN)
+			goto exit_write_header;
+		ret = __archive_write_output(a, buff, 512);
+		if (ret < ARCHIVE_WARN)
+			goto exit_write_header;
+		/* Write name and trailing null byte. */
+		ret = __archive_write_output(a, gnutar->linkname, length);
+		if (ret < ARCHIVE_WARN)
+			goto exit_write_header;
+		/* Pad to 512 bytes */
+		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
+		if (ret < ARCHIVE_WARN)
+			goto exit_write_header;
+	}
+
+	/* If pathname is longer than 100 chars we need to add an 'L' header. */
+	if (gnutar->pathname_length > GNUTAR_name_size) {
+		const char *pathname = gnutar->pathname;
+		size_t length = gnutar->pathname_length + 1;
+		struct archive_entry *temp = archive_entry_new2(&a->archive);
+
+		/* Uname/gname here don't really matter since no one reads them;
+		 * these are the values that GNU tar happens to use on FreeBSD. */
+		archive_entry_set_uname(temp, "root");
+		archive_entry_set_gname(temp, "wheel");
+
+		archive_entry_set_pathname(temp, "././@LongLink");
+		archive_entry_set_size(temp, length);
+		ret = archive_format_gnutar_header(a, buff, temp, 'L');
+		archive_entry_free(temp);
+		if (ret < ARCHIVE_WARN)
+			goto exit_write_header;
+		ret = __archive_write_output(a, buff, 512);
+		if(ret < ARCHIVE_WARN)
+			goto exit_write_header;
+		/* Write pathname + trailing null byte. */
+		ret = __archive_write_output(a, pathname, length);
+		if(ret < ARCHIVE_WARN)
+			goto exit_write_header;
+		/* Pad to multiple of 512 bytes. */
+		ret = __archive_write_nulls(a, 0x1ff & (-(ssize_t)length));
+		if (ret < ARCHIVE_WARN)
+			goto exit_write_header;
+	}
+
+	if (archive_entry_hardlink(entry) != NULL) {
+		tartype = '1';
+	} else
+		switch (archive_entry_filetype(entry)) {
+		case AE_IFREG: tartype = '0' ; break;
+		case AE_IFLNK: tartype = '2' ; break;
+		case AE_IFCHR: tartype = '3' ; break;
+		case AE_IFBLK: tartype = '4' ; break;
+		case AE_IFDIR: tartype = '5' ; break;
+		case AE_IFIFO: tartype = '6' ; break;
+		default: /* AE_IFSOCK and unknown */
+			__archive_write_entry_filetype_unsupported(
+                            &a->archive, entry, "gnutar");
+			ret = ARCHIVE_FAILED;
+			goto exit_write_header;
+		}
+
+	ret = archive_format_gnutar_header(a, buff, entry, tartype);
+	if (ret < ARCHIVE_WARN)
+		goto exit_write_header;
+	if (ret2 < ret)
+		ret = ret2;
+	ret2 = __archive_write_output(a, buff, 512);
+	if (ret2 < ARCHIVE_WARN) {
+		ret = ret2;
+		goto exit_write_header;
+	}
+	if (ret2 < ret)
+		ret = ret2;
+
+	gnutar->entry_bytes_remaining = archive_entry_size(entry);
+	gnutar->entry_padding = 0x1ff & (-(int64_t)gnutar->entry_bytes_remaining);
+exit_write_header:
+	archive_entry_free(entry_main);
+	return (ret);
+}
+
+static int
+archive_format_gnutar_header(struct archive_write *a, char h[512],
+    struct archive_entry *entry, int tartype)
+{
+	unsigned int checksum;
+	int i, ret;
+	size_t copy_length;
+	const char *p;
+	struct gnutar *gnutar;
+
+	gnutar = (struct gnutar *)a->format_data;
+
+	ret = 0;
+
+	/*
+	 * The "template header" already includes the signature,
+	 * various end-of-field markers, and other required elements.
+	 */
+	memcpy(h, &template_header, 512);
+
+	/*
+	 * Because the block is already null-filled, and strings
+	 * are allowed to exactly fill their destination (without null),
+	 * I use memcpy(dest, src, strlen()) here a lot to copy strings.
+	 */
+
+	if (tartype == 'K' || tartype == 'L') {
+		p = archive_entry_pathname(entry);
+		copy_length = strlen(p);
+	} else {
+		p = gnutar->pathname;
+		copy_length = gnutar->pathname_length;
+	}
+	if (copy_length > GNUTAR_name_size)
+		copy_length = GNUTAR_name_size;
+	memcpy(h + GNUTAR_name_offset, p, copy_length);
+
+	if ((copy_length = gnutar->linkname_length) > 0) {
+		if (copy_length > GNUTAR_linkname_size)
+			copy_length = GNUTAR_linkname_size;
+		memcpy(h + GNUTAR_linkname_offset, gnutar->linkname,
+		    copy_length);
+	}
+
+	/* TODO: How does GNU tar handle unames longer than GNUTAR_uname_size? */
+	if (tartype == 'K' || tartype == 'L') {
+		p = archive_entry_uname(entry);
+		copy_length = strlen(p);
+	} else {
+		p = gnutar->uname;
+		copy_length = gnutar->uname_length;
+	}
+	if (copy_length > 0) {
+		if (copy_length > GNUTAR_uname_size)
+			copy_length = GNUTAR_uname_size;
+		memcpy(h + GNUTAR_uname_offset, p, copy_length);
+	}
+
+	/* TODO: How does GNU tar handle gnames longer than GNUTAR_gname_size? */
+	if (tartype == 'K' || tartype == 'L') {
+		p = archive_entry_gname(entry);
+		copy_length = strlen(p);
+	} else {
+		p = gnutar->gname;
+		copy_length = gnutar->gname_length;
+	}
+	if (copy_length > 0) {
+		if (strlen(p) > GNUTAR_gname_size)
+			copy_length = GNUTAR_gname_size;
+		memcpy(h + GNUTAR_gname_offset, p, copy_length);
+	}
+
+	/* By truncating the mode here, we ensure it always fits. */
+	format_octal(archive_entry_mode(entry) & 07777,
+	    h + GNUTAR_mode_offset, GNUTAR_mode_size);
+
+	/* GNU tar supports base-256 here, so should never overflow. */
+	if (format_number(archive_entry_uid(entry), h + GNUTAR_uid_offset,
+		GNUTAR_uid_size, GNUTAR_uid_max_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric user ID %jd too large",
+		    (intmax_t)archive_entry_uid(entry));
+		ret = ARCHIVE_FAILED;
+	}
+
+	/* GNU tar supports base-256 here, so should never overflow. */
+	if (format_number(archive_entry_gid(entry), h + GNUTAR_gid_offset,
+		GNUTAR_gid_size, GNUTAR_gid_max_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric group ID %jd too large",
+		    (intmax_t)archive_entry_gid(entry));
+		ret = ARCHIVE_FAILED;
+	}
+
+	/* GNU tar supports base-256 here, so should never overflow. */
+	if (format_number(archive_entry_size(entry), h + GNUTAR_size_offset,
+		GNUTAR_size_size, GNUTAR_size_max_size)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File size out of range");
+		ret = ARCHIVE_FAILED;
+	}
+
+	/* Shouldn't overflow before 2106, since mtime field is 33 bits. */
+	format_octal(archive_entry_mtime(entry),
+	    h + GNUTAR_mtime_offset, GNUTAR_mtime_size);
+
+	if (archive_entry_filetype(entry) == AE_IFBLK
+	    || archive_entry_filetype(entry) == AE_IFCHR) {
+		if (format_octal(archive_entry_rdevmajor(entry),
+		    h + GNUTAR_rdevmajor_offset,
+			GNUTAR_rdevmajor_size)) {
+			archive_set_error(&a->archive, ERANGE,
+			    "Major device number too large");
+			ret = ARCHIVE_FAILED;
+		}
+
+		if (format_octal(archive_entry_rdevminor(entry),
+		    h + GNUTAR_rdevminor_offset,
+			GNUTAR_rdevminor_size)) {
+			archive_set_error(&a->archive, ERANGE,
+			    "Minor device number too large");
+			ret = ARCHIVE_FAILED;
+		}
+	}
+
+	h[GNUTAR_typeflag_offset] = tartype;
+
+	checksum = 0;
+	for (i = 0; i < 512; i++)
+		checksum += 255 & (unsigned int)h[i];
+	h[GNUTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */
+	/* h[GNUTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */
+	format_octal(checksum, h + GNUTAR_checksum_offset, 6);
+	return (ret);
+}
+
+/*
+ * Format a number into a field, falling back to base-256 if necessary.
+ */
+static int
+format_number(int64_t v, char *p, int s, int maxsize)
+{
+	int64_t limit = ((int64_t)1 << (s*3));
+
+	if (v < limit)
+		return (format_octal(v, p, s));
+	return (format_256(v, p, maxsize));
+}
+
+/*
+ * Format a number into the specified field using base-256.
+ */
+static int
+format_256(int64_t v, char *p, int s)
+{
+	p += s;
+	while (s-- > 0) {
+		*--p = (char)(v & 0xff);
+		v >>= 8;
+	}
+	*p |= 0x80; /* Set the base-256 marker bit. */
+	return (0);
+}
+
+/*
+ * Format a number into the specified field using octal.
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+	int len = s;
+
+	/* Octal values can't be negative, so use 0. */
+	if (v < 0)
+		v = 0;
+
+	p += s;		/* Start at the end and work backwards. */
+	while (s-- > 0) {
+		*--p = (char)('0' + (v & 7));
+		v >>= 3;
+	}
+
+	if (v == 0)
+		return (0);
+
+	/* If it overflowed, fill field with max value. */
+	while (len-- > 0)
+		*p++ = '7';
+
+	return (-1);
+}
diff --git a/c/archive_write_set_format_iso9660.c b/c/archive_write_set_format_iso9660.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_iso9660.c
@@ -0,0 +1,8162 @@
+/*-
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * 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"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_write_private.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#define getuid()			0
+#define getgid()			0
+#endif
+
+/*#define DEBUG 1*/
+#ifdef DEBUG
+/* To compare to the ISO image file made by mkisofs. */
+#define COMPAT_MKISOFS		1
+#endif
+
+#define LOGICAL_BLOCK_BITS			11
+#define LOGICAL_BLOCK_SIZE			2048
+#define PATH_TABLE_BLOCK_SIZE			4096
+
+#define SYSTEM_AREA_BLOCK			16
+#define PRIMARY_VOLUME_DESCRIPTOR_BLOCK 	1
+#define SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK 	1
+#define BOOT_RECORD_DESCRIPTOR_BLOCK	 	1
+#define VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK	1
+#define NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK	1
+#define RRIP_ER_BLOCK				1
+#define PADDING_BLOCK				150
+
+#define FD_1_2M_SIZE		(1024 * 1200)
+#define FD_1_44M_SIZE		(1024 * 1440)
+#define FD_2_88M_SIZE		(1024 * 2880)
+#define MULTI_EXTENT_SIZE	(ARCHIVE_LITERAL_LL(1) << 32)	/* 4Gi bytes. */
+#define MAX_DEPTH		8
+#define RR_CE_SIZE		28		/* SUSP "CE" extension size */
+
+#define FILE_FLAG_EXISTENCE	0x01
+#define FILE_FLAG_DIRECTORY	0x02
+#define FILE_FLAG_ASSOCIATED	0x04
+#define FILE_FLAG_RECORD	0x08
+#define FILE_FLAG_PROTECTION	0x10
+#define FILE_FLAG_MULTI_EXTENT	0x80
+
+static const char rrip_identifier[] =
+	"RRIP_1991A";
+static const char rrip_descriptor[] =
+	"THE ROCK RIDGE INTERCHANGE PROTOCOL PROVIDES SUPPORT FOR "
+	"POSIX FILE SYSTEM SEMANTICS";
+static const char rrip_source[] =
+	"PLEASE CONTACT DISC PUBLISHER FOR SPECIFICATION SOURCE.  "
+	"SEE PUBLISHER IDENTIFIER IN PRIMARY VOLUME DESCRIPTOR FOR "
+	"CONTACT INFORMATION.";
+#define RRIP_ER_ID_SIZE		(sizeof(rrip_identifier)-1)
+#define RRIP_ER_DSC_SIZE	(sizeof(rrip_descriptor)-1)
+#define RRIP_ER_SRC_SIZE	(sizeof(rrip_source)-1)
+#define RRIP_ER_SIZE		(8 + RRIP_ER_ID_SIZE + \
+				RRIP_ER_DSC_SIZE + RRIP_ER_SRC_SIZE)
+
+static const unsigned char zisofs_magic[8] = {
+	0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07
+};
+
+#define ZF_HEADER_SIZE	16	/* zisofs header size. */
+#define ZF_LOG2_BS	15	/* log2 block size; 32K bytes. */
+#define ZF_BLOCK_SIZE	(1UL << ZF_LOG2_BS)
+
+/*
+ * Manage extra records.
+ */
+struct extr_rec {
+	int		 location;
+	int		 offset;
+	unsigned char	 buf[LOGICAL_BLOCK_SIZE];
+	struct extr_rec	*next;
+};
+
+struct ctl_extr_rec {
+	int		 use_extr;
+	unsigned char	*bp;
+	struct isoent	*isoent;
+	unsigned char	*ce_ptr;
+	int		 cur_len;
+	int		 dr_len;
+	int		 limit;
+	int		 extr_off;
+	int		 extr_loc;
+};
+#define DR_SAFETY	RR_CE_SIZE
+#define DR_LIMIT	(254 - DR_SAFETY)
+
+/*
+ * The relation of struct isofile and isoent and archive_entry.
+ *
+ * Primary volume tree  --> struct isoent
+ *                                |
+ *                                v
+ *                          struct isofile --> archive_entry
+ *                                ^
+ *                                |
+ * Joliet volume tree   --> struct isoent
+ *
+ * struct isoent has specific information for volume.
+ */
+
+struct isofile {
+	/* Used for managing struct isofile list. */
+	struct isofile		*allnext;
+	struct isofile		*datanext;
+	/* Used for managing a hardlinked struct isofile list. */
+	struct isofile		*hlnext;
+	struct isofile		*hardlink_target;
+
+	struct archive_entry	*entry;
+
+	/*
+	 * Used for making a directory tree.
+	 */
+	struct archive_string	 parentdir;
+	struct archive_string	 basename;
+	struct archive_string	 basename_utf16;
+	struct archive_string	 symlink;
+	int			 dircnt;	/* The number of elements of
+						 * its parent directory */
+
+	/*
+	 * Used for a Directory Record.
+	 */
+	struct content {
+		int64_t		 offset_of_temp;
+		int64_t		 size;
+		int		 blocks;
+		uint32_t 	 location;
+		/*
+		 * One extent equals one content.
+		 * If this entry has multi extent, `next' variable points
+		 * next content data.
+		 */
+		struct content	*next;		/* next content	*/
+	} content, *cur_content;
+	int			 write_content;
+
+	enum {
+		NO = 0,
+		BOOT_CATALOG,
+		BOOT_IMAGE
+	} boot;
+
+	/*
+	 * Used for a zisofs.
+	 */
+	struct {
+		unsigned char	 header_size;
+		unsigned char	 log2_bs;
+		uint32_t	 uncompressed_size;
+	} zisofs;
+};
+
+struct isoent {
+	/* Keep `rbnode' at the first member of struct isoent. */
+	struct archive_rb_node	 rbnode;
+
+	struct isofile		*file;
+
+	struct isoent		*parent;
+	/* A list of children.(use chnext) */
+	struct {
+		struct isoent	*first;
+		struct isoent	**last;
+		int		 cnt;
+	}			 children;
+	struct archive_rb_tree	 rbtree;
+
+	/* A list of sub directories.(use drnext) */
+	struct {
+		struct isoent	*first;
+		struct isoent	**last;
+		int		 cnt;
+	}			 subdirs;
+	/* A sorted list of sub directories. */
+	struct isoent		**children_sorted;
+	/* Used for managing struct isoent list. */
+	struct isoent		*chnext;
+	struct isoent		*drnext;
+	struct isoent		*ptnext;
+
+	/*
+	 * Used for making a Directory Record.
+	 */
+	int			 dir_number;
+	struct {
+		int		 vd;
+		int		 self;
+		int		 parent;
+		int		 normal;
+	}			 dr_len;
+	uint32_t 		 dir_location;
+	int			 dir_block;
+
+	/*
+	 * Identifier:
+	 *   on primary, ISO9660 file/directory name.
+	 *   on joliet, UCS2 file/directory name.
+	 * ext_off   : offset of identifier extension.
+	 * ext_len   : length of identifier extension.
+	 * id_len    : byte size of identifier.
+	 *   on primary, this is ext_off + ext_len + version length.
+	 *   on joliet, this is ext_off + ext_len.
+	 * mb_len    : length of multibyte-character of identifier.
+	 *   on primary, mb_len and id_len are always the same.
+	 *   on joliet, mb_len and id_len are different.
+	 */
+	char			*identifier;
+	int			 ext_off;
+	int			 ext_len;
+	int			 id_len;
+	int			 mb_len;
+
+	/*
+	 * Used for making a Rockridge extension.
+	 * This is a part of Directory Records.
+	 */
+	struct isoent		*rr_parent;
+	struct isoent		*rr_child;
+
+	/* Extra Record.(which we call in this source file)
+	 * A maximum size of the Directory Record is 254.
+	 * so, if generated RRIP data of a file cannot into a Directory
+	 * Record because of its size, that surplus data relocate this
+	 * Extra Record.
+	 */
+	struct {
+		struct extr_rec	*first;
+		struct extr_rec	**last;
+		struct extr_rec	*current;
+	}			 extr_rec_list;
+
+	signed int		 virtual:1;
+	/* If set to one, this file type is a directory.
+	 * A convenience flag to be used as
+	 * "archive_entry_filetype(isoent->file->entry) == AE_IFDIR".
+	 */
+	signed int		 dir:1;
+};
+
+struct hardlink {
+	struct archive_rb_node	 rbnode;
+	int			 nlink;
+	struct {
+		struct isofile	*first;
+		struct isofile	**last;
+	}			 file_list;
+};
+
+/*
+ * ISO writer options
+ */
+struct iso_option {
+	/*
+	 * Usage  : abstract-file=<value>
+	 * Type   : string, max 37 bytes
+	 * Default: Not specified
+	 * COMPAT : mkisofs -abstract <value>
+	 *
+	 * Specifies Abstract Filename.
+	 * This file shall be described in the Root Directory
+	 * and containing a abstract statement.
+	 */
+	unsigned int	 abstract_file:1;
+#define OPT_ABSTRACT_FILE_DEFAULT	0	/* Not specified */
+#define ABSTRACT_FILE_SIZE		37
+
+	/*
+	 * Usage  : application-id=<value>
+	 * Type   : string, max 128 bytes
+	 * Default: Not specified
+	 * COMPAT : mkisofs -A/-appid <value>.
+	 *
+	 * Specifies Application Identifier.
+	 * If the first byte is set to '_'(5F), the remaining
+	 * bytes of this option shall specify an identifier
+	 * for a file containing the identification of the
+	 * application.
+	 * This file shall be described in the Root Directory.
+	 */
+	unsigned int	 application_id:1;
+#define OPT_APPLICATION_ID_DEFAULT	0	/* Use default identifier */
+#define APPLICATION_IDENTIFIER_SIZE	128
+
+	/*
+	 * Usage : !allow-vernum
+	 * Type  : boolean
+	 * Default: Enabled
+	 *	  : Violates the ISO9660 standard if disable.
+	 * COMPAT: mkisofs -N
+	 *
+	 * Allow filenames to use version numbers.
+	 */
+	unsigned int	 allow_vernum:1;
+#define OPT_ALLOW_VERNUM_DEFAULT	1	/* Enabled */
+
+	/*
+	 * Usage  : biblio-file=<value>
+	 * Type   : string, max 37 bytes
+	 * Default: Not specified
+	 * COMPAT : mkisofs -biblio <value>
+	 *
+	 * Specifies Bibliographic Filename.
+	 * This file shall be described in the Root Directory
+	 * and containing bibliographic records.
+	 */
+	unsigned int	 biblio_file:1;
+#define OPT_BIBLIO_FILE_DEFAULT		0	/* Not specified */
+#define BIBLIO_FILE_SIZE		37
+
+	/*
+	 * Usage  : boot=<value>
+	 * Type   : string
+	 * Default: Not specified
+	 * COMPAT : mkisofs -b/-eltorito-boot <value>
+	 *
+	 * Specifies "El Torito" boot image file to make
+	 * a bootable CD.
+	 */
+	unsigned int	 boot:1;
+#define OPT_BOOT_DEFAULT		0	/* Not specified */
+
+	/*
+	 * Usage  : boot-catalog=<value>
+	 * Type   : string
+	 * Default: "boot.catalog"
+	 * COMPAT : mkisofs -c/-eltorito-catalog <value>
+	 *
+	 * Specifies a fullpath of El Torito boot catalog.
+	 */
+	unsigned int	 boot_catalog:1;
+#define OPT_BOOT_CATALOG_DEFAULT	0	/* Not specified */
+
+	/*
+	 * Usage  : boot-info-table
+	 * Type   : boolean
+	 * Default: Disabled
+	 * COMPAT : mkisofs -boot-info-table
+	 *
+	 * Modify the boot image file specified by `boot'
+	 * option; ISO writer stores boot file information
+	 * into the boot file in ISO image at offset 8
+	 * through offset 64.
+	 */
+	unsigned int	 boot_info_table:1;
+#define OPT_BOOT_INFO_TABLE_DEFAULT	0	/* Disabled */
+
+	/*
+	 * Usage  : boot-load-seg=<value>
+	 * Type   : hexadecimal
+	 * Default: Not specified
+	 * COMPAT : mkisofs -boot-load-seg <value>
+	 *
+	 * Specifies a load segment for boot image.
+	 * This is used with no-emulation mode.
+	 */
+	unsigned int	 boot_load_seg:1;
+#define OPT_BOOT_LOAD_SEG_DEFAULT	0	/* Not specified */
+
+	/*
+	 * Usage  : boot-load-size=<value>
+	 * Type   : decimal
+	 * Default: Not specified
+	 * COMPAT : mkisofs -boot-load-size <value>
+	 *
+	 * Specifies a sector count for boot image.
+	 * This is used with no-emulation mode.
+	 */
+	unsigned int	 boot_load_size:1;
+#define OPT_BOOT_LOAD_SIZE_DEFAULT	0	/* Not specified */
+
+	/*
+	 * Usage  : boot-type=<boot-media-type>
+	 *        : 'no-emulation' : 'no emulation' image
+	 *        :           'fd' : floppy disk image
+	 *        :    'hard-disk' : hard disk image
+	 * Type   : string
+	 * Default: Auto detect
+	 *        : We check a size of boot image;
+	 *        : If the size is just 1.22M/1.44M/2.88M,
+	 *        : we assume boot_type is 'fd';
+	 *        : otherwise boot_type is 'no-emulation'.
+	 * COMPAT :
+	 *    boot=no-emulation
+	 *	mkisofs -no-emul-boot
+	 *    boot=fd
+	 *	This is a default on the mkisofs.
+	 *    boot=hard-disk
+	 *	mkisofs -hard-disk-boot
+	 *
+	 * Specifies a type of "El Torito" boot image.
+	 */
+	unsigned int	 boot_type:2;
+#define OPT_BOOT_TYPE_AUTO		0	/* auto detect		  */
+#define OPT_BOOT_TYPE_NO_EMU		1	/* ``no emulation'' image */
+#define OPT_BOOT_TYPE_FD		2	/* floppy disk image	  */
+#define OPT_BOOT_TYPE_HARD_DISK		3	/* hard disk image	  */
+#define OPT_BOOT_TYPE_DEFAULT		OPT_BOOT_TYPE_AUTO
+
+	/*
+	 * Usage  : compression-level=<value>
+	 * Type   : decimal
+	 * Default: Not specified
+	 * COMPAT : NONE
+	 *
+	 * Specifies compression level for option zisofs=direct.
+	 */
+	unsigned int	 compression_level:1;
+#define OPT_COMPRESSION_LEVEL_DEFAULT	0	/* Not specified */
+
+	/*
+	 * Usage  : copyright-file=<value>
+	 * Type   : string, max 37 bytes
+	 * Default: Not specified
+	 * COMPAT : mkisofs -copyright <value>
+	 *
+	 * Specifies Copyright Filename.
+	 * This file shall be described in the Root Directory
+	 * and containing a copyright statement.
+	 */
+	unsigned int	 copyright_file:1;
+#define OPT_COPYRIGHT_FILE_DEFAULT	0	/* Not specified */
+#define COPYRIGHT_FILE_SIZE		37
+
+	/*
+	 * Usage  : gid=<value>
+	 * Type   : decimal
+	 * Default: Not specified
+	 * COMPAT : mkisofs -gid <value>
+	 *
+	 * Specifies a group id to rewrite the group id of all files.
+	 */
+	unsigned int	 gid:1;
+#define OPT_GID_DEFAULT			0	/* Not specified */
+
+	/*
+	 * Usage  : iso-level=[1234]
+	 * Type   : decimal
+	 * Default: 1
+	 * COMPAT : mkisofs -iso-level <value>
+	 *
+	 * Specifies ISO9600 Level.
+	 * Level 1: [DEFAULT]
+	 *   - limits each file size less than 4Gi bytes;
+	 *   - a File Name shall not contain more than eight
+	 *     d-characters or eight d1-characters;
+	 *   - a File Name Extension shall not contain more than
+	 *     three d-characters or three d1-characters;
+	 *   - a Directory Identifier shall not contain more
+	 *     than eight d-characters or eight d1-characters.
+	 * Level 2:
+	 *   - limits each file size less than 4Giga bytes;
+	 *   - a File Name shall not contain more than thirty
+	 *     d-characters or thirty d1-characters;
+	 *   - a File Name Extension shall not contain more than
+	 *     thirty d-characters or thirty d1-characters;
+	 *   - a Directory Identifier shall not contain more
+	 *     than thirty-one d-characters or thirty-one
+	 *     d1-characters.
+	 * Level 3:
+	 *   - no limit of file size; use multi extent.
+	 * Level 4:
+	 *   - this level 4 simulates mkisofs option
+	 *     '-iso-level 4';
+	 *   - crate a enhanced volume as mkisofs doing;
+	 *   - allow a File Name to have leading dot;
+	 *   - allow a File Name to have all ASCII letters;
+	 *   - allow a File Name to have multiple dots;
+	 *   - allow more then 8 depths of directory trees;
+	 *   - disable a version number to a File Name;
+	 *   - disable a forced period to the tail of a File Name;
+	 *   - the maximum length of files and directories is raised to 193.
+	 *     if rockridge option is disabled, raised to 207.
+	 */
+	unsigned int	 iso_level:3;
+#define OPT_ISO_LEVEL_DEFAULT		1	/* ISO Level 1 */
+
+	/*
+	 * Usage  : joliet[=long]
+	 *        : !joliet
+	 *        :   Do not generate Joliet Volume and Records.
+	 *        : joliet [DEFAULT]
+	 *        :   Generates Joliet Volume and Directory Records.
+	 *        :   [COMPAT: mkisofs -J/-joliet]
+	 *        : joliet=long
+	 *        :   The joliet filenames are up to 103 Unicode
+	 *        :   characters.
+	 *        :   This option breaks the Joliet specification.
+	 *        :   [COMPAT: mkisofs -J -joliet-long]
+	 * Type   : boolean/string
+	 * Default: Enabled
+	 * COMPAT : mkisofs -J / -joliet-long
+	 *
+	 * Generates Joliet Volume and Directory Records.
+	 */
+	unsigned int	 joliet:2;
+#define OPT_JOLIET_DISABLE		0	/* Not generate Joliet Records. */
+#define OPT_JOLIET_ENABLE		1	/* Generate Joliet Records.  */
+#define OPT_JOLIET_LONGNAME		2	/* Use long joliet filenames.*/
+#define OPT_JOLIET_DEFAULT		OPT_JOLIET_ENABLE
+
+	/*
+	 * Usage  : !limit-depth
+	 * Type   : boolean
+	 * Default: Enabled
+	 *	  : Violates the ISO9660 standard if disable.
+	 * COMPAT : mkisofs -D/-disable-deep-relocation
+	 *
+	 * The number of levels in hierarchy cannot exceed eight.
+	 */
+	unsigned int	 limit_depth:1;
+#define OPT_LIMIT_DEPTH_DEFAULT		1	/* Enabled */
+
+	/*
+	 * Usage  : !limit-dirs
+	 * Type   : boolean
+	 * Default: Enabled
+	 *	  : Violates the ISO9660 standard if disable.
+	 * COMPAT : mkisofs -no-limit-pathtables
+	 *
+	 * Limits the number of directories less than 65536 due
+	 * to the size of the Parent Directory Number of Path
+	 * Table.
+	 */
+	unsigned int	 limit_dirs:1;
+#define OPT_LIMIT_DIRS_DEFAULT		1	/* Enabled */
+
+	/*
+	 * Usage  : !pad
+	 * Type   : boolean
+	 * Default: Enabled
+	 * COMPAT : -pad/-no-pad
+	 *
+	 * Pads the end of the ISO image by null of 300Ki bytes.
+	 */
+	unsigned int	 pad:1;
+#define OPT_PAD_DEFAULT			1	/* Enabled */
+
+	/*
+	 * Usage  : publisher=<value>
+	 * Type   : string, max 128 bytes
+	 * Default: Not specified
+	 * COMPAT : mkisofs -publisher <value>
+	 *
+	 * Specifies Publisher Identifier.
+	 * If the first byte is set to '_'(5F), the remaining
+	 * bytes of this option shall specify an identifier
+	 * for a file containing the identification of the user.
+	 * This file shall be described in the Root Directory.
+	 */
+	unsigned int	 publisher:1;
+#define OPT_PUBLISHER_DEFAULT		0	/* Not specified */
+#define PUBLISHER_IDENTIFIER_SIZE	128
+
+	/*
+	 * Usage  : rockridge
+	 *        : !rockridge
+	 *        :    disable to generate SUSP and RR records.
+	 *        : rockridge
+	 *        :    the same as 'rockridge=useful'.
+	 *        : rockridge=strict
+	 *        :    generate SUSP and RR records.
+	 *        :    [COMPAT: mkisofs -R]
+	 *        : rockridge=useful [DEFAULT]
+	 *        :    generate SUSP and RR records.
+	 *        :    [COMPAT: mkisofs -r]
+	 *        :    NOTE  Our rockridge=useful option does not set a zero
+	 *        :          to uid and gid, you should use application
+	 *        :          option such as --gid,--gname,--uid and --uname
+	 *        :          bsdtar options instead.
+	 * Type   : boolean/string
+	 * Default: Enabled as rockridge=useful
+	 * COMPAT : mkisofs -r / -R
+	 *
+	 * Generates SUSP and RR records.
+	 */
+	unsigned int	 rr:2;
+#define OPT_RR_DISABLED			0
+#define OPT_RR_STRICT			1
+#define OPT_RR_USEFUL			2
+#define OPT_RR_DEFAULT			OPT_RR_USEFUL
+
+	/*
+	 * Usage  : volume-id=<value>
+	 * Type   : string, max 32 bytes
+	 * Default: Not specified
+	 * COMPAT : mkisofs -V <value>
+	 *
+	 * Specifies Volume Identifier.
+	 */
+	unsigned int	 volume_id:1;
+#define OPT_VOLUME_ID_DEFAULT		0	/* Use default identifier */
+#define VOLUME_IDENTIFIER_SIZE		32
+
+	/*
+	 * Usage  : !zisofs [DEFAULT] 
+	 *        :    Disable to generate RRIP 'ZF' extension.
+	 *        : zisofs
+	 *        :    Make files zisofs file and generate RRIP 'ZF'
+ 	 *        :    extension. So you do not need mkzftree utility
+	 *        :    for making zisofs.
+	 *        :    When the file size is less than one Logical Block
+	 *        :    size, that file will not zisofs'ed since it does
+	 *        :    reduce an ISO-image size.
+	 *        :
+	 *        :    When you specify option 'boot=<boot-image>', that
+	 *        :    'boot-image' file won't be converted to zisofs file.
+	 * Type   : boolean
+	 * Default: Disabled
+	 *
+	 * Generates RRIP 'ZF' System Use Entry.
+	 */
+	unsigned int	 zisofs:1;
+#define OPT_ZISOFS_DISABLED		0
+#define OPT_ZISOFS_DIRECT		1
+#define OPT_ZISOFS_DEFAULT		OPT_ZISOFS_DISABLED
+
+};
+
+struct iso9660 {
+	/* The creation time of ISO image. */
+	time_t			 birth_time;
+	/* A file stream of a temporary file, which file contents
+	 * save to until ISO image can be created. */
+	int			 temp_fd;
+
+	struct isofile		*cur_file;
+	struct isoent		*cur_dirent;
+	struct archive_string	 cur_dirstr;
+	uint64_t		 bytes_remaining;
+	int			 need_multi_extent;
+
+	/* Temporary string buffer for Joliet extension. */ 
+	struct archive_string	 utf16be;
+	struct archive_string	 mbs;
+
+	struct archive_string_conv *sconv_to_utf16be;
+	struct archive_string_conv *sconv_from_utf16be;
+
+	/* A list of all of struct isofile entries. */
+	struct {
+		struct isofile	*first;
+		struct isofile	**last;
+	}			 all_file_list;
+
+	/* A list of struct isofile entries which have its
+	 * contents and are not a directory, a hardlinked file
+	 * and a symlink file. */
+	struct {
+		struct isofile	*first;
+		struct isofile	**last;
+	}			 data_file_list;
+
+	/* Used for managing to find hardlinking files. */
+	struct archive_rb_tree	 hardlink_rbtree;
+
+	/* Used for making the Path Table Record. */
+	struct vdd {
+		/* the root of entry tree. */
+		struct isoent	*rootent;
+		enum vdd_type {
+			VDD_PRIMARY,
+			VDD_JOLIET,
+			VDD_ENHANCED
+		} vdd_type;
+
+		struct path_table {
+			struct isoent		*first;
+			struct isoent		**last;
+			struct isoent		**sorted;
+			int			 cnt;
+		} *pathtbl;
+		int				 max_depth;
+
+		int		 path_table_block;
+		int		 path_table_size;
+		int		 location_type_L_path_table;
+		int		 location_type_M_path_table;
+		int		 total_dir_block;
+	} primary, joliet;
+
+	/* Used for making a Volume Descriptor. */
+	int			 volume_space_size;
+	int			 volume_sequence_number;
+	int			 total_file_block;
+	struct archive_string	 volume_identifier;
+	struct archive_string	 publisher_identifier;
+	struct archive_string	 data_preparer_identifier;
+	struct archive_string	 application_identifier;
+	struct archive_string	 copyright_file_identifier;
+	struct archive_string	 abstract_file_identifier;
+	struct archive_string	 bibliographic_file_identifier;
+
+	/* Used for making rockridge extensions. */
+	int			 location_rrip_er;
+
+	/* Used for making zisofs. */
+	struct {
+		signed int	 detect_magic:1;
+		signed int	 making:1;
+		signed int	 allzero:1;
+		unsigned char	 magic_buffer[64];
+		int		 magic_cnt;
+
+#ifdef HAVE_ZLIB_H
+		/*
+		 * Copy a compressed file to iso9660.zisofs.temp_fd
+		 * and also copy a uncompressed file(original file) to
+		 * iso9660.temp_fd . If the number of logical block
+		 * of the compressed file is less than the number of
+		 * logical block of the uncompressed file, use it and
+		 * remove the copy of the uncompressed file.
+		 * but if not, we use uncompressed file and remove
+		 * the copy of the compressed file.
+		 */
+		uint32_t	*block_pointers;
+		size_t		 block_pointers_allocated;
+		int		 block_pointers_cnt;
+		int		 block_pointers_idx;
+		int64_t		 total_size;
+		int64_t		 block_offset;
+
+		z_stream	 stream;
+		int		 stream_valid;
+		int64_t		 remaining;
+		int		 compression_level;
+#endif
+	} zisofs;
+
+	struct isoent		*directories_too_deep;
+	int			 dircnt_max;
+
+	/* Write buffer. */
+#define wb_buffmax()	(LOGICAL_BLOCK_SIZE * 32)
+#define wb_remaining(a)	(((struct iso9660 *)(a)->format_data)->wbuff_remaining)
+#define wb_offset(a)	(((struct iso9660 *)(a)->format_data)->wbuff_offset \
+		+ wb_buffmax() - wb_remaining(a))
+	unsigned char		 wbuff[LOGICAL_BLOCK_SIZE * 32];
+	size_t			 wbuff_remaining;
+	enum {
+		WB_TO_STREAM,
+		WB_TO_TEMP
+	} 			 wbuff_type;
+	int64_t			 wbuff_offset;
+	int64_t			 wbuff_written;
+	int64_t			 wbuff_tail;
+
+	/* 'El Torito' boot data. */
+	struct {
+		/* boot catalog file */
+		struct archive_string	 catalog_filename;
+		struct isoent		*catalog;
+		/* boot image file */
+		struct archive_string	 boot_filename;
+		struct isoent		*boot;
+
+		unsigned char		 platform_id;
+#define BOOT_PLATFORM_X86	0
+#define BOOT_PLATFORM_PPC	1
+#define BOOT_PLATFORM_MAC	2
+		struct archive_string	 id;
+		unsigned char		 media_type;
+#define BOOT_MEDIA_NO_EMULATION		0
+#define BOOT_MEDIA_1_2M_DISKETTE	1
+#define BOOT_MEDIA_1_44M_DISKETTE	2
+#define BOOT_MEDIA_2_88M_DISKETTE	3
+#define BOOT_MEDIA_HARD_DISK		4
+		unsigned char		 system_type;
+		uint16_t		 boot_load_seg;
+		uint16_t		 boot_load_size;
+#define BOOT_LOAD_SIZE		4
+	} el_torito;
+
+	struct iso_option	 opt;
+};
+
+/*
+ * Types of Volume Descriptor
+ */
+enum VD_type {
+	VDT_BOOT_RECORD=0,	/* Boot Record Volume Descriptor 	*/
+	VDT_PRIMARY=1,		/* Primary Volume Descriptor		*/
+	VDT_SUPPLEMENTARY=2,	/* Supplementary Volume Descriptor	*/
+	VDT_TERMINATOR=255	/* Volume Descriptor Set Terminator	*/
+};
+
+/*
+ * Types of Directory Record
+ */
+enum dir_rec_type {
+	DIR_REC_VD,		/* Stored in Volume Descriptor.	*/
+	DIR_REC_SELF,		/* Stored as Current Directory.	*/
+	DIR_REC_PARENT,		/* Stored as Parent Directory.	*/
+	DIR_REC_NORMAL 		/* Stored as Child.		*/
+};
+
+/*
+ * Kinds of Volume Descriptor Character
+ */
+enum vdc {
+	VDC_STD,
+	VDC_LOWERCASE,
+	VDC_UCS2,
+	VDC_UCS2_DIRECT
+};
+
+/*
+ * IDentifier Resolver.
+ * Used for resolving duplicated filenames.
+ */
+struct idr {
+	struct idrent {
+		struct archive_rb_node	rbnode;
+		/* Used in wait_list. */
+		struct idrent		*wnext;
+		struct idrent		*avail;
+
+		struct isoent		*isoent;
+		int			 weight;
+		int			 noff;
+		int			 rename_num;
+	} *idrent_pool;
+
+	struct archive_rb_tree		 rbtree;
+
+	struct {
+		struct idrent		*first;
+		struct idrent		**last;
+	} wait_list;
+
+	int				 pool_size;
+	int				 pool_idx;
+	int				 num_size;
+	int				 null_size;
+
+	char				 char_map[0x80];
+};
+
+enum char_type {
+	A_CHAR,
+	D_CHAR
+};
+
+
+static int	iso9660_options(struct archive_write *,
+		    const char *, const char *);
+static int	iso9660_write_header(struct archive_write *,
+		    struct archive_entry *);
+static ssize_t	iso9660_write_data(struct archive_write *,
+		    const void *, size_t);
+static int	iso9660_finish_entry(struct archive_write *);
+static int	iso9660_close(struct archive_write *);
+static int	iso9660_free(struct archive_write *);
+
+static void	get_system_identitier(char *, size_t);
+static void	set_str(unsigned char *, const char *, size_t, char,
+		    const char *);
+static inline int joliet_allowed_char(unsigned char, unsigned char);
+static int	set_str_utf16be(struct archive_write *, unsigned char *,
+			const char *, size_t, uint16_t, enum vdc);
+static int	set_str_a_characters_bp(struct archive_write *,
+			unsigned char *, int, int, const char *, enum vdc);
+static int	set_str_d_characters_bp(struct archive_write *,
+			unsigned char *, int, int, const char *, enum  vdc);
+static void	set_VD_bp(unsigned char *, enum VD_type, unsigned char);
+static inline void set_unused_field_bp(unsigned char *, int, int);
+
+static unsigned char *extra_open_record(unsigned char *, int,
+		    struct isoent *, struct ctl_extr_rec *);
+static void	extra_close_record(struct ctl_extr_rec *, int);
+static unsigned char * extra_next_record(struct ctl_extr_rec *, int);
+static unsigned char *extra_get_record(struct isoent *, int *, int *, int *);
+static void	extra_tell_used_size(struct ctl_extr_rec *, int);
+static int	extra_setup_location(struct isoent *, int);
+static int	set_directory_record_rr(unsigned char *, int,
+		    struct isoent *, struct iso9660 *, enum dir_rec_type);
+static int	set_directory_record(unsigned char *, size_t,
+		    struct isoent *, struct iso9660 *, enum dir_rec_type,
+		    enum vdd_type);
+static inline int get_dir_rec_size(struct iso9660 *, struct isoent *,
+		    enum dir_rec_type, enum vdd_type);
+static inline unsigned char *wb_buffptr(struct archive_write *);
+static int	wb_write_out(struct archive_write *);
+static int	wb_consume(struct archive_write *, size_t);
+#ifdef HAVE_ZLIB_H
+static int	wb_set_offset(struct archive_write *, int64_t);
+#endif
+static int	write_null(struct archive_write *, size_t);
+static int	write_VD_terminator(struct archive_write *);
+static int	set_file_identifier(unsigned char *, int, int, enum vdc,
+		    struct archive_write *, struct vdd *,
+		    struct archive_string *, const char *, int,
+		    enum char_type);
+static int	write_VD(struct archive_write *, struct vdd *);
+static int	write_VD_boot_record(struct archive_write *);
+static int	write_information_block(struct archive_write *);
+static int	write_path_table(struct archive_write *, int,
+		    struct vdd *);
+static int	write_directory_descriptors(struct archive_write *,
+		    struct vdd *);
+static int	write_file_descriptors(struct archive_write *);
+static int	write_rr_ER(struct archive_write *);
+static void	calculate_path_table_size(struct vdd *);
+
+static void	isofile_init_entry_list(struct iso9660 *);
+static void	isofile_add_entry(struct iso9660 *, struct isofile *);
+static void	isofile_free_all_entries(struct iso9660 *);
+static void	isofile_init_entry_data_file_list(struct iso9660 *);
+static void	isofile_add_data_file(struct iso9660 *, struct isofile *);
+static struct isofile * isofile_new(struct archive_write *,
+		    struct archive_entry *);
+static void	isofile_free(struct isofile *);
+static int	isofile_gen_utility_names(struct archive_write *,
+		    struct isofile *);
+static int	isofile_register_hardlink(struct archive_write *,
+		    struct isofile *);
+static void	isofile_connect_hardlink_files(struct iso9660 *);
+static void	isofile_init_hardlinks(struct iso9660 *);
+static void	isofile_free_hardlinks(struct iso9660 *);
+
+static struct isoent *isoent_new(struct isofile *);
+static int	isoent_clone_tree(struct archive_write *,
+		    struct isoent **, struct isoent *);
+static void	_isoent_free(struct isoent *isoent);
+static void	isoent_free_all(struct isoent *);
+static struct isoent * isoent_create_virtual_dir(struct archive_write *,
+		    struct iso9660 *, const char *);
+static int	isoent_cmp_node(const struct archive_rb_node *,
+		    const struct archive_rb_node *);
+static int	isoent_cmp_key(const struct archive_rb_node *,
+		    const void *);
+static int	isoent_add_child_head(struct isoent *, struct isoent *);
+static int	isoent_add_child_tail(struct isoent *, struct isoent *);
+static void	isoent_remove_child(struct isoent *, struct isoent *);
+static void	isoent_setup_directory_location(struct iso9660 *,
+		    int, struct vdd *);
+static void	isoent_setup_file_location(struct iso9660 *, int);
+static int	get_path_component(char *, size_t, const char *);
+static int	isoent_tree(struct archive_write *, struct isoent **);
+static struct isoent *isoent_find_child(struct isoent *, const char *);
+static struct isoent *isoent_find_entry(struct isoent *, const char *);
+static void	idr_relaxed_filenames(char *);
+static void	idr_init(struct iso9660 *, struct vdd *, struct idr *);
+static void	idr_cleanup(struct idr *);
+static int	idr_ensure_poolsize(struct archive_write *, struct idr *,
+		    int);
+static int	idr_start(struct archive_write *, struct idr *,
+		    int, int, int, int, const struct archive_rb_tree_ops *);
+static void	idr_register(struct idr *, struct isoent *, int,
+		    int);
+static void	idr_extend_identifier(struct idrent *, int, int);
+static void	idr_resolve(struct idr *, void (*)(unsigned char *, int));
+static void	idr_set_num(unsigned char *, int);
+static void	idr_set_num_beutf16(unsigned char *, int);
+static int	isoent_gen_iso9660_identifier(struct archive_write *,
+		    struct isoent *, struct idr *);
+static int	isoent_gen_joliet_identifier(struct archive_write *,
+		    struct isoent *, struct idr *);
+static int	isoent_cmp_iso9660_identifier(const struct isoent *,
+		    const struct isoent *);
+static int	isoent_cmp_node_iso9660(const struct archive_rb_node *,
+		    const struct archive_rb_node *);
+static int	isoent_cmp_key_iso9660(const struct archive_rb_node *,
+		    const void *);
+static int	isoent_cmp_joliet_identifier(const struct isoent *,
+		    const struct isoent *);
+static int	isoent_cmp_node_joliet(const struct archive_rb_node *,
+		    const struct archive_rb_node *);
+static int	isoent_cmp_key_joliet(const struct archive_rb_node *,
+		    const void *);
+static inline void path_table_add_entry(struct path_table *, struct isoent *);
+static inline struct isoent * path_table_last_entry(struct path_table *);
+static int	isoent_make_path_table(struct archive_write *);
+static int	isoent_find_out_boot_file(struct archive_write *,
+		    struct isoent *);
+static int	isoent_create_boot_catalog(struct archive_write *,
+		    struct isoent *);
+static size_t	fd_boot_image_size(int);
+static int	make_boot_catalog(struct archive_write *);
+static int	setup_boot_information(struct archive_write *);
+
+static int	zisofs_init(struct archive_write *, struct isofile *);
+static void	zisofs_detect_magic(struct archive_write *,
+		    const void *, size_t);
+static int	zisofs_write_to_temp(struct archive_write *,
+		    const void *, size_t);
+static int	zisofs_finish_entry(struct archive_write *);
+static int	zisofs_rewind_boot_file(struct archive_write *);
+static int	zisofs_free(struct archive_write *);
+
+int
+archive_write_set_format_iso9660(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct iso9660 *iso9660;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_iso9660");
+
+	/* If another format was already registered, unregister it. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	iso9660 = calloc(1, sizeof(*iso9660));
+	if (iso9660 == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate iso9660 data");
+		return (ARCHIVE_FATAL);
+	}
+	iso9660->birth_time = 0;
+	iso9660->temp_fd = -1;
+	iso9660->cur_file = NULL;
+	iso9660->primary.max_depth = 0;
+	iso9660->primary.vdd_type = VDD_PRIMARY;
+	iso9660->primary.pathtbl = NULL;
+	iso9660->joliet.rootent = NULL;
+	iso9660->joliet.max_depth = 0;
+	iso9660->joliet.vdd_type = VDD_JOLIET;
+	iso9660->joliet.pathtbl = NULL;
+	isofile_init_entry_list(iso9660);
+	isofile_init_entry_data_file_list(iso9660);
+	isofile_init_hardlinks(iso9660);
+	iso9660->directories_too_deep = NULL;
+	iso9660->dircnt_max = 1;
+	iso9660->wbuff_remaining = wb_buffmax();
+	iso9660->wbuff_type = WB_TO_TEMP;
+	iso9660->wbuff_offset = 0;
+	iso9660->wbuff_written = 0;
+	iso9660->wbuff_tail = 0;
+	archive_string_init(&(iso9660->utf16be));
+	archive_string_init(&(iso9660->mbs));
+
+	/*
+	 * Init Identifiers used for PVD and SVD.
+	 */
+	archive_string_init(&(iso9660->volume_identifier));
+	archive_strcpy(&(iso9660->volume_identifier), "CDROM");
+	archive_string_init(&(iso9660->publisher_identifier));
+	archive_string_init(&(iso9660->data_preparer_identifier));
+	archive_string_init(&(iso9660->application_identifier));
+	archive_strcpy(&(iso9660->application_identifier),
+	    archive_version_string());
+	archive_string_init(&(iso9660->copyright_file_identifier));
+	archive_string_init(&(iso9660->abstract_file_identifier));
+	archive_string_init(&(iso9660->bibliographic_file_identifier));
+
+	/*
+	 * Init El Torito bootable CD variables.
+	 */
+	archive_string_init(&(iso9660->el_torito.catalog_filename));
+	iso9660->el_torito.catalog = NULL;
+	/* Set default file name of boot catalog  */
+	archive_strcpy(&(iso9660->el_torito.catalog_filename),
+	    "boot.catalog");
+	archive_string_init(&(iso9660->el_torito.boot_filename));
+	iso9660->el_torito.boot = NULL;
+	iso9660->el_torito.platform_id = BOOT_PLATFORM_X86;
+	archive_string_init(&(iso9660->el_torito.id));
+	iso9660->el_torito.boot_load_seg = 0;
+	iso9660->el_torito.boot_load_size = BOOT_LOAD_SIZE;
+
+	/*
+	 * Init zisofs variables.
+	 */
+#ifdef HAVE_ZLIB_H
+	iso9660->zisofs.block_pointers = NULL;
+	iso9660->zisofs.block_pointers_allocated = 0;
+	iso9660->zisofs.stream_valid = 0;
+	iso9660->zisofs.compression_level = 9;
+	memset(&(iso9660->zisofs.stream), 0,
+	    sizeof(iso9660->zisofs.stream));
+#endif
+
+	/*
+	 * Set default value of iso9660 options.
+	 */
+	iso9660->opt.abstract_file = OPT_ABSTRACT_FILE_DEFAULT;
+	iso9660->opt.application_id = OPT_APPLICATION_ID_DEFAULT;
+	iso9660->opt.allow_vernum = OPT_ALLOW_VERNUM_DEFAULT;
+	iso9660->opt.biblio_file = OPT_BIBLIO_FILE_DEFAULT;
+	iso9660->opt.boot = OPT_BOOT_DEFAULT;
+	iso9660->opt.boot_catalog = OPT_BOOT_CATALOG_DEFAULT;
+	iso9660->opt.boot_info_table = OPT_BOOT_INFO_TABLE_DEFAULT;
+	iso9660->opt.boot_load_seg = OPT_BOOT_LOAD_SEG_DEFAULT;
+	iso9660->opt.boot_load_size = OPT_BOOT_LOAD_SIZE_DEFAULT;
+	iso9660->opt.boot_type = OPT_BOOT_TYPE_DEFAULT;
+	iso9660->opt.compression_level = OPT_COMPRESSION_LEVEL_DEFAULT;
+	iso9660->opt.copyright_file = OPT_COPYRIGHT_FILE_DEFAULT;
+	iso9660->opt.iso_level = OPT_ISO_LEVEL_DEFAULT;
+	iso9660->opt.joliet = OPT_JOLIET_DEFAULT;
+	iso9660->opt.limit_depth = OPT_LIMIT_DEPTH_DEFAULT;
+	iso9660->opt.limit_dirs = OPT_LIMIT_DIRS_DEFAULT;
+	iso9660->opt.pad = OPT_PAD_DEFAULT;
+	iso9660->opt.publisher = OPT_PUBLISHER_DEFAULT;
+	iso9660->opt.rr = OPT_RR_DEFAULT;
+	iso9660->opt.volume_id = OPT_VOLUME_ID_DEFAULT;
+	iso9660->opt.zisofs = OPT_ZISOFS_DEFAULT;
+
+	/* Create the root directory. */
+	iso9660->primary.rootent =
+	    isoent_create_virtual_dir(a, iso9660, "");
+	if (iso9660->primary.rootent == NULL) {
+		free(iso9660);
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	iso9660->primary.rootent->parent = iso9660->primary.rootent;
+	iso9660->cur_dirent = iso9660->primary.rootent;
+	archive_string_init(&(iso9660->cur_dirstr));
+	archive_string_ensure(&(iso9660->cur_dirstr), 1);
+	iso9660->cur_dirstr.s[0] = 0;
+	iso9660->sconv_to_utf16be = NULL;
+	iso9660->sconv_from_utf16be = NULL;
+
+	a->format_data = iso9660;
+	a->format_name = "iso9660";
+	a->format_options = iso9660_options;
+	a->format_write_header = iso9660_write_header;
+	a->format_write_data = iso9660_write_data;
+	a->format_finish_entry = iso9660_finish_entry;
+	a->format_close = iso9660_close;
+	a->format_free = iso9660_free;
+	a->archive.archive_format = ARCHIVE_FORMAT_ISO9660;
+	a->archive.archive_format_name = "ISO9660";
+
+	return (ARCHIVE_OK);
+}
+
+static int
+get_str_opt(struct archive_write *a, struct archive_string *s,
+    size_t maxsize, const char *key, const char *value)
+{
+
+	if (strlen(value) > maxsize) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Value is longer than %zu characters "
+		    "for option ``%s''", maxsize, key);
+		return (ARCHIVE_FATAL);
+	}
+	archive_strcpy(s, value);
+	return (ARCHIVE_OK);
+}
+
+static int
+get_num_opt(struct archive_write *a, int *num, int high, int low,
+    const char *key, const char *value)
+{
+	const char *p = value;
+	int data = 0;
+	int neg = 0;
+
+	if (p == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid value(empty) for option ``%s''", key);
+		return (ARCHIVE_FATAL);
+	}
+	if (*p == '-') {
+		neg = 1;
+		p++;
+	}
+	while (*p) {
+		if (*p >= '0' && *p <= '9')
+			data = data * 10 + *p - '0';
+		else {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Invalid value for option ``%s''", key);
+			return (ARCHIVE_FATAL);
+		}
+		if (data > high) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Invalid value(over %d) for "
+			    "option ``%s''", high, key);
+			return (ARCHIVE_FATAL);
+		}
+		if (data < low) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Invalid value(under %d) for "
+			    "option ``%s''", low, key);
+			return (ARCHIVE_FATAL);
+		}
+		p++;
+	}
+	if (neg)
+		data *= -1;
+	*num = data;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+iso9660_options(struct archive_write *a, const char *key, const char *value)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	const char *p;
+	int r;
+
+	switch (key[0]) {
+	case 'a':
+		if (strcmp(key, "abstract-file") == 0) {
+			r = get_str_opt(a,
+			    &(iso9660->abstract_file_identifier),
+			    ABSTRACT_FILE_SIZE, key, value);
+			iso9660->opt.abstract_file = r == ARCHIVE_OK;
+			return (r);
+		}
+		if (strcmp(key, "application-id") == 0) {
+			r = get_str_opt(a,
+			    &(iso9660->application_identifier),
+			    APPLICATION_IDENTIFIER_SIZE, key, value);
+			iso9660->opt.application_id = r == ARCHIVE_OK;
+			return (r);
+		}
+		if (strcmp(key, "allow-vernum") == 0) {
+			iso9660->opt.allow_vernum = value != NULL;
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'b':
+		if (strcmp(key, "biblio-file") == 0) {
+			r = get_str_opt(a,
+			    &(iso9660->bibliographic_file_identifier),
+			    BIBLIO_FILE_SIZE, key, value);
+			iso9660->opt.biblio_file = r == ARCHIVE_OK;
+			return (r);
+		}
+		if (strcmp(key, "boot") == 0) {
+			if (value == NULL)
+				iso9660->opt.boot = 0;
+			else {
+				iso9660->opt.boot = 1;
+				archive_strcpy(
+				    &(iso9660->el_torito.boot_filename),
+				    value);
+			}
+			return (ARCHIVE_OK);
+		}
+		if (strcmp(key, "boot-catalog") == 0) {
+			r = get_str_opt(a,
+			    &(iso9660->el_torito.catalog_filename),
+			    1024, key, value);
+			iso9660->opt.boot_catalog = r == ARCHIVE_OK;
+			return (r);
+		}
+		if (strcmp(key, "boot-info-table") == 0) {
+			iso9660->opt.boot_info_table = value != NULL;
+			return (ARCHIVE_OK);
+		}
+		if (strcmp(key, "boot-load-seg") == 0) {
+			uint32_t seg;
+
+			iso9660->opt.boot_load_seg = 0;
+			if (value == NULL)
+				goto invalid_value;
+			seg = 0;
+			p = value;
+			if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X'))
+				p += 2;
+			while (*p) {
+				if (seg)
+					seg <<= 4;
+				if (*p >= 'A' && *p <= 'F')
+					seg += *p - 'A' + 0x0a;
+				else if (*p >= 'a' && *p <= 'f')
+					seg += *p - 'a' + 0x0a;
+				else if (*p >= '0' && *p <= '9')
+					seg += *p - '0';
+				else
+					goto invalid_value;
+				if (seg > 0xffff) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_MISC,
+					    "Invalid value(over 0xffff) for "
+					    "option ``%s''", key);
+					return (ARCHIVE_FATAL);
+				}
+				p++;
+			}
+			iso9660->el_torito.boot_load_seg = (uint16_t)seg;
+			iso9660->opt.boot_load_seg = 1;
+			return (ARCHIVE_OK);
+		}
+		if (strcmp(key, "boot-load-size") == 0) {
+			int num = 0;
+			r = get_num_opt(a, &num, 0xffff, 1, key, value);
+			iso9660->opt.boot_load_size = r == ARCHIVE_OK;
+			if (r != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			iso9660->el_torito.boot_load_size = (uint16_t)num;
+			return (ARCHIVE_OK);
+		}
+		if (strcmp(key, "boot-type") == 0) {
+			if (value == NULL)
+				goto invalid_value;
+			if (strcmp(value, "no-emulation") == 0)
+				iso9660->opt.boot_type = OPT_BOOT_TYPE_NO_EMU;
+			else if (strcmp(value, "fd") == 0)
+				iso9660->opt.boot_type = OPT_BOOT_TYPE_FD;
+			else if (strcmp(value, "hard-disk") == 0)
+				iso9660->opt.boot_type = OPT_BOOT_TYPE_HARD_DISK;
+			else
+				goto invalid_value;
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'c':
+		if (strcmp(key, "compression-level") == 0) {
+#ifdef HAVE_ZLIB_H
+			if (value == NULL ||
+			    !(value[0] >= '0' && value[0] <= '9') ||
+			    value[1] != '\0')
+				goto invalid_value;
+                	iso9660->zisofs.compression_level = value[0] - '0';
+			iso9660->opt.compression_level = 1;
+                	return (ARCHIVE_OK);
+#else
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "Option ``%s'' "
+			    "is not supported on this platform.", key);
+			return (ARCHIVE_FATAL);
+#endif
+		}
+		if (strcmp(key, "copyright-file") == 0) {
+			r = get_str_opt(a,
+			    &(iso9660->copyright_file_identifier),
+			    COPYRIGHT_FILE_SIZE, key, value);
+			iso9660->opt.copyright_file = r == ARCHIVE_OK;
+			return (r);
+		}
+#ifdef DEBUG
+		/* Specifies Volume creation date and time;
+		 * year(4),month(2),day(2),hour(2),minute(2),second(2).
+		 * e.g. "20090929033757"
+		 */
+		if (strcmp(key, "creation") == 0) {
+			struct tm tm;
+			char buf[5];
+
+			p = value;
+			if (p == NULL || strlen(p) < 14)
+				goto invalid_value;
+			memset(&tm, 0, sizeof(tm));
+			memcpy(buf, p, 4); buf[4] = '\0'; p += 4;
+			tm.tm_year = strtol(buf, NULL, 10) - 1900;
+			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+			tm.tm_mon = strtol(buf, NULL, 10) - 1;
+			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+			tm.tm_mday = strtol(buf, NULL, 10);
+			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+			tm.tm_hour = strtol(buf, NULL, 10);
+			memcpy(buf, p, 2); buf[2] = '\0'; p += 2;
+			tm.tm_min = strtol(buf, NULL, 10);
+			memcpy(buf, p, 2); buf[2] = '\0';
+			tm.tm_sec = strtol(buf, NULL, 10);
+			iso9660->birth_time = mktime(&tm);
+			return (ARCHIVE_OK);
+		}
+#endif
+		break;
+	case 'i':
+		if (strcmp(key, "iso-level") == 0) {
+			if (value != NULL && value[1] == '\0' &&
+			    (value[0] >= '1' && value[0] <= '4')) {
+				iso9660->opt.iso_level = value[0]-'0';
+				return (ARCHIVE_OK);
+			}
+			goto invalid_value;
+		}
+		break;
+	case 'j':
+		if (strcmp(key, "joliet") == 0) {
+			if (value == NULL)
+				iso9660->opt.joliet = OPT_JOLIET_DISABLE;
+			else if (strcmp(value, "1") == 0)
+				iso9660->opt.joliet = OPT_JOLIET_ENABLE;
+			else if (strcmp(value, "long") == 0)
+				iso9660->opt.joliet = OPT_JOLIET_LONGNAME;
+			else
+				goto invalid_value;
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'l':
+		if (strcmp(key, "limit-depth") == 0) {
+			iso9660->opt.limit_depth = value != NULL;
+			return (ARCHIVE_OK);
+		}
+		if (strcmp(key, "limit-dirs") == 0) {
+			iso9660->opt.limit_dirs = value != NULL;
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'p':
+		if (strcmp(key, "pad") == 0) {
+			iso9660->opt.pad = value != NULL;
+			return (ARCHIVE_OK);
+		}
+		if (strcmp(key, "publisher") == 0) {
+			r = get_str_opt(a,
+			    &(iso9660->publisher_identifier),
+			    PUBLISHER_IDENTIFIER_SIZE, key, value);
+			iso9660->opt.publisher = r == ARCHIVE_OK;
+			return (r);
+		}
+		break;
+	case 'r':
+		if (strcmp(key, "rockridge") == 0 ||
+		    strcmp(key, "Rockridge") == 0) {
+			if (value == NULL)
+				iso9660->opt.rr = OPT_RR_DISABLED;
+			else if (strcmp(value, "1") == 0)
+				iso9660->opt.rr = OPT_RR_USEFUL;
+			else if (strcmp(value, "strict") == 0)
+				iso9660->opt.rr = OPT_RR_STRICT;
+			else if (strcmp(value, "useful") == 0)
+				iso9660->opt.rr = OPT_RR_USEFUL;
+			else
+				goto invalid_value;
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'v':
+		if (strcmp(key, "volume-id") == 0) {
+			r = get_str_opt(a, &(iso9660->volume_identifier),
+			    VOLUME_IDENTIFIER_SIZE, key, value);
+			iso9660->opt.volume_id = r == ARCHIVE_OK;
+			return (r);
+		}
+		break;
+	case 'z':
+		if (strcmp(key, "zisofs") == 0) {
+			if (value == NULL)
+				iso9660->opt.zisofs = OPT_ZISOFS_DISABLED;
+			else {
+#ifdef HAVE_ZLIB_H
+				iso9660->opt.zisofs = OPT_ZISOFS_DIRECT;
+#else
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "``zisofs'' "
+				    "is not supported on this platform.");
+				return (ARCHIVE_FATAL);
+#endif
+			}
+			return (ARCHIVE_OK);
+		}
+		break;
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+
+invalid_value:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Invalid value for option ``%s''", key);
+	return (ARCHIVE_FAILED);
+}
+
+static int
+iso9660_write_header(struct archive_write *a, struct archive_entry *entry)
+{
+	struct iso9660 *iso9660;
+	struct isofile *file;
+	struct isoent *isoent;
+	int r, ret = ARCHIVE_OK;
+
+	iso9660 = a->format_data;
+
+	iso9660->cur_file = NULL;
+	iso9660->bytes_remaining = 0;
+	iso9660->need_multi_extent = 0;
+	if (archive_entry_filetype(entry) == AE_IFLNK
+	    && iso9660->opt.rr == OPT_RR_DISABLED) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Ignore symlink file.");
+		iso9660->cur_file = NULL;
+		return (ARCHIVE_WARN);
+	}
+	if (archive_entry_filetype(entry) == AE_IFREG &&
+	    archive_entry_size(entry) >= MULTI_EXTENT_SIZE) {
+		if (iso9660->opt.iso_level < 3) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "Ignore over %lld bytes file. "
+			    "This file too large.",
+			    MULTI_EXTENT_SIZE);
+				iso9660->cur_file = NULL;
+			return (ARCHIVE_WARN);
+		}
+		iso9660->need_multi_extent = 1;
+	}
+
+	file = isofile_new(a, entry);
+	if (file == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate data");
+		return (ARCHIVE_FATAL);
+	}
+	r = isofile_gen_utility_names(a, file);
+	if (r < ARCHIVE_WARN) {
+		isofile_free(file);
+		return (r);
+	}
+	else if (r < ret)
+		ret = r;
+
+	/*
+	 * Ignore a path which looks like the top of directory name
+	 * since we have already made the root directory of an ISO image.
+	 */
+	if (archive_strlen(&(file->parentdir)) == 0 &&
+	    archive_strlen(&(file->basename)) == 0) {
+		isofile_free(file);
+		return (r);
+	}
+
+	isofile_add_entry(iso9660, file);
+	isoent = isoent_new(file);
+	if (isoent == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate data");
+		return (ARCHIVE_FATAL);
+	}
+	if (isoent->file->dircnt > iso9660->dircnt_max)
+		iso9660->dircnt_max = isoent->file->dircnt;
+
+	/* Add the current file into tree */
+	r = isoent_tree(a, &isoent);
+	if (r != ARCHIVE_OK)
+		return (r);
+
+	/* If there is the same file in tree and
+	 * the current file is older than the file in tree.
+	 * So we don't need the current file data anymore. */
+	if (isoent->file != file)
+		return (ARCHIVE_OK);
+
+	/* Non regular files contents are unneeded to be saved to
+	 * temporary files. */
+	if (archive_entry_filetype(file->entry) != AE_IFREG)
+		return (ret);
+
+	/*
+	 * Set the current file to cur_file to read its contents.
+	 */
+	iso9660->cur_file = file;
+
+	if (archive_entry_nlink(file->entry) > 1) {
+		r = isofile_register_hardlink(a, file);
+		if (r != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Prepare to save the contents of the file.
+	 */
+	if (iso9660->temp_fd < 0) {
+		iso9660->temp_fd = __archive_mktemp(NULL);
+		if (iso9660->temp_fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't create temporary file");
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	/* Save an offset of current file in temporary file. */
+	file->content.offset_of_temp = wb_offset(a);
+	file->cur_content = &(file->content);
+	r = zisofs_init(a, file);
+	if (r < ret)
+		ret = r;
+	iso9660->bytes_remaining =  archive_entry_size(file->entry);
+
+	return (ret);
+}
+
+static int
+write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	ssize_t written;
+	const unsigned char *b;
+
+	b = (const unsigned char *)buff;
+	while (s) {
+		written = write(iso9660->temp_fd, b, s);
+		if (written < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't write to temporary file");
+			return (ARCHIVE_FATAL);
+		}
+		s -= written;
+		b += written;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+wb_write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+	const char *xp = buff;
+	size_t xs = s;
+
+	/*
+	 * If a written data size is big enough to use system-call
+	 * and there is no waiting data, this calls write_to_temp() in
+	 * order to reduce a extra memory copy.
+	 */
+	if (wb_remaining(a) == wb_buffmax() && s > (1024 * 16)) {
+		struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+		xs = s % LOGICAL_BLOCK_SIZE;
+		iso9660->wbuff_offset += s - xs;
+		if (write_to_temp(a, buff, s - xs) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		if (xs == 0)
+			return (ARCHIVE_OK);
+		xp += s - xs;
+	}
+
+	while (xs) {
+		size_t size = xs;
+		if (size > wb_remaining(a))
+			size = wb_remaining(a);
+		memcpy(wb_buffptr(a), xp, size);
+		if (wb_consume(a, size) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		xs -= size;
+		xp += size;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+wb_write_padding_to_temp(struct archive_write *a, int64_t csize)
+{
+	size_t ns;
+	int ret;
+
+	ns = (size_t)(csize % LOGICAL_BLOCK_SIZE);
+	if (ns != 0)
+		ret = write_null(a, LOGICAL_BLOCK_SIZE - ns);
+	else
+		ret = ARCHIVE_OK;
+	return (ret);
+}
+
+static ssize_t
+write_iso9660_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	size_t ws;
+
+	if (iso9660->temp_fd < 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Couldn't create temporary file");
+		return (ARCHIVE_FATAL);
+	}
+
+	ws = s;
+	if (iso9660->need_multi_extent &&
+	    (iso9660->cur_file->cur_content->size + ws) >=
+	      (MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE)) {
+		struct content *con;
+		size_t ts;
+
+		ts = (size_t)(MULTI_EXTENT_SIZE - LOGICAL_BLOCK_SIZE -
+		    iso9660->cur_file->cur_content->size);
+
+		if (iso9660->zisofs.detect_magic)
+			zisofs_detect_magic(a, buff, ts);
+
+		if (iso9660->zisofs.making) {
+			if (zisofs_write_to_temp(a, buff, ts) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		} else {
+			if (wb_write_to_temp(a, buff, ts) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			iso9660->cur_file->cur_content->size += ts;
+		}
+
+		/* Write padding. */
+		if (wb_write_padding_to_temp(a,
+		    iso9660->cur_file->cur_content->size) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+
+		/* Compute the logical block number. */
+		iso9660->cur_file->cur_content->blocks = (int)
+		    ((iso9660->cur_file->cur_content->size
+		     + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
+
+		/*
+		 * Make next extent.
+		 */
+		ws -= ts;
+		buff = (const void *)(((const unsigned char *)buff) + ts);
+		/* Make a content for next extent. */
+		con = calloc(1, sizeof(*con));
+		if (con == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate content data");
+			return (ARCHIVE_FATAL);
+		}
+		con->offset_of_temp = wb_offset(a);
+		iso9660->cur_file->cur_content->next = con;
+		iso9660->cur_file->cur_content = con;
+#ifdef HAVE_ZLIB_H
+		iso9660->zisofs.block_offset = 0;
+#endif
+	}
+
+	if (iso9660->zisofs.detect_magic)
+		zisofs_detect_magic(a, buff, ws);
+
+	if (iso9660->zisofs.making) {
+		if (zisofs_write_to_temp(a, buff, ws) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	} else {
+		if (wb_write_to_temp(a, buff, ws) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		iso9660->cur_file->cur_content->size += ws;
+	}
+
+	return (s);
+}
+
+static ssize_t
+iso9660_write_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	ssize_t r;
+
+	if (iso9660->cur_file == NULL)
+		return (0);
+	if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG)
+		return (0);
+	if (s > iso9660->bytes_remaining)
+		s = (size_t)iso9660->bytes_remaining;
+	if (s == 0)
+		return (0);
+
+	r = write_iso9660_data(a, buff, s);
+	if (r > 0)
+		iso9660->bytes_remaining -= r;
+	return (r);
+}
+
+static int
+iso9660_finish_entry(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+
+	if (iso9660->cur_file == NULL)
+		return (ARCHIVE_OK);
+	if (archive_entry_filetype(iso9660->cur_file->entry) != AE_IFREG)
+		return (ARCHIVE_OK);
+	if (iso9660->cur_file->content.size == 0)
+		return (ARCHIVE_OK);
+
+	/* If there are unwritten data, write null data instead. */
+	while (iso9660->bytes_remaining > 0) {
+		size_t s;
+
+		s = (iso9660->bytes_remaining > a->null_length)?
+		    a->null_length: (size_t)iso9660->bytes_remaining;
+		if (write_iso9660_data(a, a->nulls, s) < 0)
+			return (ARCHIVE_FATAL);
+		iso9660->bytes_remaining -= s;
+	}
+
+	if (iso9660->zisofs.making && zisofs_finish_entry(a) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Write padding. */
+	if (wb_write_padding_to_temp(a, iso9660->cur_file->cur_content->size)
+	    != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Compute the logical block number. */
+	iso9660->cur_file->cur_content->blocks = (int)
+	    ((iso9660->cur_file->cur_content->size
+	     + LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
+
+	/* Add the current file to data file list. */
+	isofile_add_data_file(iso9660, iso9660->cur_file);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+iso9660_close(struct archive_write *a)
+{
+	struct iso9660 *iso9660;
+	int ret, blocks;
+
+	iso9660 = a->format_data;
+
+	/*
+	 * Write remaining data out to the temporary file.
+	 */
+	if (wb_remaining(a) > 0) {
+		ret = wb_write_out(a);
+		if (ret < 0)
+			return (ret);
+	}
+
+	/*
+	 * Preparations...
+	 */
+#ifdef DEBUG
+	if (iso9660->birth_time == 0)
+#endif
+		time(&(iso9660->birth_time));
+
+	/*
+	 * Prepare a bootable ISO image.
+	 */
+	if (iso9660->opt.boot) {
+		/* Find out the boot file entry. */
+		ret = isoent_find_out_boot_file(a, iso9660->primary.rootent);
+		if (ret < 0)
+			return (ret);
+		/* Reconvert the boot file from zisofs'ed form to
+		 * plain form. */
+		ret = zisofs_rewind_boot_file(a);
+		if (ret < 0)
+			return (ret);
+		/* Write remaining data out to the temporary file. */
+		if (wb_remaining(a) > 0) {
+			ret = wb_write_out(a);
+			if (ret < 0)
+				return (ret);
+		}
+		/* Create the boot catalog. */
+		ret = isoent_create_boot_catalog(a, iso9660->primary.rootent);
+		if (ret < 0)
+			return (ret);
+	}
+
+	/*
+	 * Prepare joliet extensions.
+	 */
+	if (iso9660->opt.joliet) {
+		/* Make a new tree for joliet. */
+		ret = isoent_clone_tree(a, &(iso9660->joliet.rootent),
+		    iso9660->primary.rootent);
+		if (ret < 0)
+			return (ret);
+		/* Make sure we have UTF-16BE converters.
+		 * if there is no file entry, converters are still
+		 * uninitialized. */
+		if (iso9660->sconv_to_utf16be == NULL) {
+			iso9660->sconv_to_utf16be =
+			    archive_string_conversion_to_charset(
+				&(a->archive), "UTF-16BE", 1);
+			if (iso9660->sconv_to_utf16be == NULL)
+				/* Couldn't allocate memory */
+				return (ARCHIVE_FATAL);
+			iso9660->sconv_from_utf16be =
+			    archive_string_conversion_from_charset(
+				&(a->archive), "UTF-16BE", 1);
+			if (iso9660->sconv_from_utf16be == NULL)
+				/* Couldn't allocate memory */
+				return (ARCHIVE_FATAL);
+		}
+	}
+
+	/*
+	 * Make Path Tables.
+	 */
+	ret = isoent_make_path_table(a);
+	if (ret < 0)
+		return (ret);
+
+	/*
+	 * Calculate a total volume size and setup all locations of
+	 * contents of an iso9660 image.
+	 */
+	blocks = SYSTEM_AREA_BLOCK
+		+ PRIMARY_VOLUME_DESCRIPTOR_BLOCK
+		+ VOLUME_DESCRIPTOR_SET_TERMINATOR_BLOCK
+		+ NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;
+	if (iso9660->opt.boot)
+		blocks += BOOT_RECORD_DESCRIPTOR_BLOCK;
+	if (iso9660->opt.joliet)
+		blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK;
+	if (iso9660->opt.iso_level == 4)
+		blocks += SUPPLEMENTARY_VOLUME_DESCRIPTOR_BLOCK;
+
+	/* Setup the locations of Path Table. */
+	iso9660->primary.location_type_L_path_table = blocks;
+	blocks += iso9660->primary.path_table_block;
+	iso9660->primary.location_type_M_path_table = blocks;
+	blocks += iso9660->primary.path_table_block;
+	if (iso9660->opt.joliet) {
+		iso9660->joliet.location_type_L_path_table = blocks;
+		blocks += iso9660->joliet.path_table_block;
+		iso9660->joliet.location_type_M_path_table = blocks;
+		blocks += iso9660->joliet.path_table_block;
+	}
+
+	/* Setup the locations of directories. */
+	isoent_setup_directory_location(iso9660, blocks,
+	    &(iso9660->primary));
+	blocks += iso9660->primary.total_dir_block;
+	if (iso9660->opt.joliet) {
+		isoent_setup_directory_location(iso9660, blocks,
+		    &(iso9660->joliet));
+		blocks += iso9660->joliet.total_dir_block;
+	}
+
+	if (iso9660->opt.rr) {
+		iso9660->location_rrip_er = blocks;
+		blocks += RRIP_ER_BLOCK;
+	}
+
+	/* Setup the locations of all file contents. */
+ 	isoent_setup_file_location(iso9660, blocks);
+	blocks += iso9660->total_file_block;
+	if (iso9660->opt.boot && iso9660->opt.boot_info_table) {
+		ret = setup_boot_information(a);
+		if (ret < 0)
+			return (ret);
+	}
+
+	/* Now we have a total volume size. */
+	iso9660->volume_space_size = blocks;
+	if (iso9660->opt.pad)
+		iso9660->volume_space_size += PADDING_BLOCK;
+	iso9660->volume_sequence_number = 1;
+
+
+	/*
+	 * Write an ISO 9660 image.
+	 */
+
+	/* Switch to start using wbuff as file buffer. */
+	iso9660->wbuff_remaining = wb_buffmax();
+	iso9660->wbuff_type = WB_TO_STREAM;
+	iso9660->wbuff_offset = 0;
+	iso9660->wbuff_written = 0;
+	iso9660->wbuff_tail = 0;
+
+	/* Write The System Area */
+	ret = write_null(a, SYSTEM_AREA_BLOCK * LOGICAL_BLOCK_SIZE);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Write Primary Volume Descriptor */
+	ret = write_VD(a, &(iso9660->primary));
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	if (iso9660->opt.boot) {
+		/* Write Boot Record Volume Descriptor */
+		ret = write_VD_boot_record(a);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	if (iso9660->opt.iso_level == 4) {
+		/* Write Enhanced Volume Descriptor */
+		iso9660->primary.vdd_type = VDD_ENHANCED;
+		ret = write_VD(a, &(iso9660->primary));
+		iso9660->primary.vdd_type = VDD_PRIMARY;
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	if (iso9660->opt.joliet) {
+		ret = write_VD(a, &(iso9660->joliet));
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	/* Write Volume Descriptor Set Terminator */
+	ret = write_VD_terminator(a);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Write Non-ISO File System Information */
+	ret = write_information_block(a);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Write Type L Path Table */
+	ret = write_path_table(a, 0, &(iso9660->primary));
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Write Type M Path Table */
+	ret = write_path_table(a, 1, &(iso9660->primary));
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	if (iso9660->opt.joliet) {
+		/* Write Type L Path Table */
+		ret = write_path_table(a, 0, &(iso9660->joliet));
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+
+		/* Write Type M Path Table */
+		ret = write_path_table(a, 1, &(iso9660->joliet));
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	/* Write Directory Descriptors */
+	ret = write_directory_descriptors(a, &(iso9660->primary));
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	if (iso9660->opt.joliet) {
+		ret = write_directory_descriptors(a, &(iso9660->joliet));
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	if (iso9660->opt.rr) {
+		/* Write Rockridge ER(Extensions Reference) */
+		ret = write_rr_ER(a);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	/* Write File Descriptors */
+	ret = write_file_descriptors(a);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Write Padding  */
+	if (iso9660->opt.pad) {
+		ret = write_null(a, PADDING_BLOCK * LOGICAL_BLOCK_SIZE);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	if (iso9660->directories_too_deep != NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "%s: Directories too deep.",
+		    archive_entry_pathname(
+			iso9660->directories_too_deep->file->entry));
+		return (ARCHIVE_WARN);
+	}
+
+	/* Write remaining data out. */
+	ret = wb_write_out(a);
+
+	return (ret);
+}
+
+static int
+iso9660_free(struct archive_write *a)
+{
+	struct iso9660 *iso9660;
+	int i, ret;
+
+	iso9660 = a->format_data;
+
+	/* Close the temporary file. */
+	if (iso9660->temp_fd >= 0)
+		close(iso9660->temp_fd);
+
+	/* Free some stuff for zisofs operations. */
+	ret = zisofs_free(a);
+
+	/* Remove directory entries in tree which includes file entries. */
+	isoent_free_all(iso9660->primary.rootent);
+	for (i = 0; i < iso9660->primary.max_depth; i++)
+		free(iso9660->primary.pathtbl[i].sorted);
+	free(iso9660->primary.pathtbl);
+
+	if (iso9660->opt.joliet) {
+		isoent_free_all(iso9660->joliet.rootent);
+		for (i = 0; i < iso9660->joliet.max_depth; i++)
+			free(iso9660->joliet.pathtbl[i].sorted);
+		free(iso9660->joliet.pathtbl);
+	}
+
+	/* Remove isofile entries. */
+	isofile_free_all_entries(iso9660);
+	isofile_free_hardlinks(iso9660);
+
+	archive_string_free(&(iso9660->cur_dirstr));
+	archive_string_free(&(iso9660->volume_identifier));
+	archive_string_free(&(iso9660->publisher_identifier));
+	archive_string_free(&(iso9660->data_preparer_identifier));
+	archive_string_free(&(iso9660->application_identifier));
+	archive_string_free(&(iso9660->copyright_file_identifier));
+	archive_string_free(&(iso9660->abstract_file_identifier));
+	archive_string_free(&(iso9660->bibliographic_file_identifier));
+	archive_string_free(&(iso9660->el_torito.catalog_filename));
+	archive_string_free(&(iso9660->el_torito.boot_filename));
+	archive_string_free(&(iso9660->el_torito.id));
+	archive_string_free(&(iso9660->utf16be));
+	archive_string_free(&(iso9660->mbs));
+
+	free(iso9660);
+	a->format_data = NULL;
+
+	return (ret);
+}
+
+/*
+ * Get the System Identifier
+ */
+static void
+get_system_identitier(char *system_id, size_t size)
+{
+#if defined(HAVE_SYS_UTSNAME_H)
+	struct utsname u;
+
+	uname(&u);
+	strncpy(system_id, u.sysname, size-1);
+	system_id[size-1] = '\0';
+#elif defined(_WIN32) && !defined(__CYGWIN__)
+	strncpy(system_id, "Windows", size-1);
+	system_id[size-1] = '\0';
+#else
+#error no way to get the system identifier on your platform.
+#endif
+}
+
+static void
+set_str(unsigned char *p, const char *s, size_t l, char f, const char *map)
+{
+	unsigned char c;
+
+	if (s == NULL)
+		s = "";
+	while ((c = *s++) != 0 && l > 0) {
+		if (c >= 0x80 || map[c] == 0)
+		 {
+			/* illegal character */
+			if (c >= 'a' && c <= 'z') {
+				/* convert c from a-z to A-Z */
+				c -= 0x20;
+			} else
+				c = 0x5f;
+		}
+		*p++ = c;
+		l--;
+	}
+	/* If l isn't zero, fill p buffer by the character
+	 * which indicated by f. */
+	if (l > 0)
+		memset(p , f, l);
+}
+
+static inline int
+joliet_allowed_char(unsigned char high, unsigned char low)
+{
+	int utf16 = (high << 8) | low;
+
+	if (utf16 <= 0x001F)
+		return (0);
+
+	switch (utf16) {
+	case 0x002A: /* '*' */
+	case 0x002F: /* '/' */
+	case 0x003A: /* ':' */
+	case 0x003B: /* ';' */
+	case 0x003F: /* '?' */
+	case 0x005C: /* '\' */
+		return (0);/* Not allowed. */
+	}
+	return (1);
+}
+
+static int
+set_str_utf16be(struct archive_write *a, unsigned char *p, const char *s,
+    size_t l, uint16_t uf, enum vdc vdc)
+{
+	size_t size, i;
+	int onepad;
+
+	if (s == NULL)
+		s = "";
+	if (l & 0x01) {
+		onepad = 1;
+		l &= ~1;
+	} else
+		onepad = 0;
+	if (vdc == VDC_UCS2) {
+		struct iso9660 *iso9660 = a->format_data;
+		if (archive_strncpy_l(&iso9660->utf16be, s, strlen(s),
+		    iso9660->sconv_to_utf16be) != 0 && errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for UTF-16BE");
+			return (ARCHIVE_FATAL);
+		}
+		size = iso9660->utf16be.length;
+		if (size > l)
+			size = l;
+		memcpy(p, iso9660->utf16be.s, size);
+	} else {
+		const uint16_t *u16 = (const uint16_t *)s;
+
+		size = 0;
+		while (*u16++)
+			size += 2;
+		if (size > l)
+			size = l;
+		memcpy(p, s, size);
+	}
+	for (i = 0; i < size; i += 2, p += 2) {
+		if (!joliet_allowed_char(p[0], p[1]))
+			archive_be16enc(p, 0x005F);/* '_' */
+	}
+	l -= size;
+	while (l > 0) {
+		archive_be16enc(p, uf);
+		p += 2;
+		l -= 2;
+	}
+	if (onepad)
+		*p = 0;
+	return (ARCHIVE_OK);
+}
+
+static const char a_characters_map[0x80] = {
+/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+    1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static const char a1_characters_map[0x80] = {
+/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+    1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 20-2F */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 30-3F */
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static const char d_characters_map[0x80] = {
+/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 60-6F */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static const char d1_characters_map[0x80] = {
+/*  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F          */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 00-0F */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 10-1F */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,/* 20-2F */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,/* 30-3F */
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 40-4F */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,/* 50-5F */
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,/* 60-6F */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,/* 70-7F */
+};
+
+static int
+set_str_a_characters_bp(struct archive_write *a, unsigned char *bp,
+    int from, int to, const char *s, enum vdc vdc)
+{
+	int r;
+
+	switch (vdc) {
+	case VDC_STD:
+		set_str(bp+from, s, to - from + 1, 0x20,
+		    a_characters_map);
+		r = ARCHIVE_OK;
+		break;
+	case VDC_LOWERCASE:
+		set_str(bp+from, s, to - from + 1, 0x20,
+		    a1_characters_map);
+		r = ARCHIVE_OK;
+		break;
+	case VDC_UCS2:
+	case VDC_UCS2_DIRECT:
+		r = set_str_utf16be(a, bp+from, s, to - from + 1,
+		    0x0020, vdc);
+		break;
+	default:
+		r = ARCHIVE_FATAL;
+	}
+	return (r);
+}
+
+static int
+set_str_d_characters_bp(struct archive_write *a, unsigned char *bp,
+    int from, int to, const char *s, enum  vdc vdc)
+{
+	int r;
+
+	switch (vdc) {
+	case VDC_STD:
+		set_str(bp+from, s, to - from + 1, 0x20,
+		    d_characters_map);
+		r = ARCHIVE_OK;
+		break;
+	case VDC_LOWERCASE:
+		set_str(bp+from, s, to - from + 1, 0x20,
+		    d1_characters_map);
+		r = ARCHIVE_OK;
+		break;
+	case VDC_UCS2:
+	case VDC_UCS2_DIRECT:
+		r = set_str_utf16be(a, bp+from, s, to - from + 1,
+		    0x0020, vdc);
+		break;
+	default:
+		r = ARCHIVE_FATAL;
+	}
+	return (r);
+}
+
+static void
+set_VD_bp(unsigned char *bp, enum VD_type type, unsigned char ver)
+{
+
+	/* Volume Descriptor Type */
+	bp[1] = (unsigned char)type;
+	/* Standard Identifier */
+	memcpy(bp + 2, "CD001", 5);
+	/* Volume Descriptor Version */
+	bp[7] = ver;
+}
+
+static inline void
+set_unused_field_bp(unsigned char *bp, int from, int to)
+{
+	memset(bp + from, 0, to - from + 1);
+}
+
+/*
+ * 8-bit unsigned numerical values.
+ * ISO9660 Standard 7.1.1
+ */
+static inline void
+set_num_711(unsigned char *p, unsigned char value)
+{
+	*p = value;
+}
+
+/*
+ * 8-bit signed numerical values.
+ * ISO9660 Standard 7.1.2
+ */
+static inline void
+set_num_712(unsigned char *p, char value)
+{
+	*((char *)p) = value;
+}
+
+/*
+ * Least significant byte first.
+ * ISO9660 Standard 7.2.1
+ */
+static inline void
+set_num_721(unsigned char *p, uint16_t value)
+{
+	archive_le16enc(p, value);
+}
+
+/*
+ * Most significant byte first.
+ * ISO9660 Standard 7.2.2
+ */
+static inline void
+set_num_722(unsigned char *p, uint16_t value)
+{
+	archive_be16enc(p, value);
+}
+
+/*
+ * Both-byte orders.
+ * ISO9660 Standard 7.2.3
+ */
+static void
+set_num_723(unsigned char *p, uint16_t value)
+{
+	archive_le16enc(p, value);
+	archive_be16enc(p+2, value);
+}
+
+/*
+ * Least significant byte first.
+ * ISO9660 Standard 7.3.1
+ */
+static inline void
+set_num_731(unsigned char *p, uint32_t value)
+{
+	archive_le32enc(p, value);
+}
+
+/*
+ * Most significant byte first.
+ * ISO9660 Standard 7.3.2
+ */
+static inline void
+set_num_732(unsigned char *p, uint32_t value)
+{
+	archive_be32enc(p, value);
+}
+
+/*
+ * Both-byte orders.
+ * ISO9660 Standard 7.3.3
+ */
+static inline void
+set_num_733(unsigned char *p, uint32_t value)
+{
+	archive_le32enc(p, value);
+	archive_be32enc(p+4, value);
+}
+
+static void
+set_digit(unsigned char *p, size_t s, int value)
+{
+
+	while (s--) {
+		p[s] = '0' + (value % 10);
+		value /= 10;
+	}
+}
+
+#if defined(HAVE_STRUCT_TM_TM_GMTOFF)
+#define get_gmoffset(tm)	((tm)->tm_gmtoff)
+#elif defined(HAVE_STRUCT_TM___TM_GMTOFF)
+#define get_gmoffset(tm)	((tm)->__tm_gmtoff)
+#else
+static long
+get_gmoffset(struct tm *tm)
+{
+	long offset;
+
+#if defined(HAVE__GET_TIMEZONE)
+	_get_timezone(&offset);
+#elif defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
+	offset = _timezone;
+#else
+	offset = timezone;
+#endif
+	offset *= -1;
+	if (tm->tm_isdst)
+		offset += 3600;
+	return (offset);
+}
+#endif
+
+static void
+get_tmfromtime(struct tm *tm, time_t *t)
+{
+#if HAVE_LOCALTIME_R
+	tzset();
+	localtime_r(t, tm);
+#elif HAVE__LOCALTIME64_S
+	__time64_t tmp_t = (__time64_t) *t; //time_t may be shorter than 64 bits
+	_localtime64_s(tm, &tmp_t);
+#else
+	memcpy(tm, localtime(t), sizeof(*tm));
+#endif
+}
+
+/*
+ * Date and Time Format.
+ * ISO9660 Standard 8.4.26.1
+ */
+static void
+set_date_time(unsigned char *p, time_t t)
+{
+	struct tm tm;
+
+	get_tmfromtime(&tm, &t);
+	set_digit(p, 4, tm.tm_year + 1900);
+	set_digit(p+4, 2, tm.tm_mon + 1);
+	set_digit(p+6, 2, tm.tm_mday);
+	set_digit(p+8, 2, tm.tm_hour);
+	set_digit(p+10, 2, tm.tm_min);
+	set_digit(p+12, 2, tm.tm_sec);
+	set_digit(p+14, 2, 0);
+	set_num_712(p+16, (char)(get_gmoffset(&tm)/(60*15)));
+}
+
+static void
+set_date_time_null(unsigned char *p)
+{
+	memset(p, (int)'0', 16);
+	p[16] = 0;
+}
+
+static void
+set_time_915(unsigned char *p, time_t t)
+{
+	struct tm tm;
+
+	get_tmfromtime(&tm, &t);
+	set_num_711(p+0, tm.tm_year);
+	set_num_711(p+1, tm.tm_mon+1);
+	set_num_711(p+2, tm.tm_mday);
+	set_num_711(p+3, tm.tm_hour);
+	set_num_711(p+4, tm.tm_min);
+	set_num_711(p+5, tm.tm_sec);
+	set_num_712(p+6, (char)(get_gmoffset(&tm)/(60*15)));
+}
+
+
+/*
+ * Write SUSP "CE" System Use Entry.
+ */
+static int
+set_SUSP_CE(unsigned char *p, int location, int offset, int size)
+{
+	unsigned char *bp = p -1;
+	/*  Extend the System Use Area
+	 *   "CE" Format:
+	 *               len  ver
+	 *    +----+----+----+----+-----------+-----------+
+	 *    | 'C'| 'E'| 1C | 01 | LOCATION1 | LOCATION2 |
+	 *    +----+----+----+----+-----------+-----------+
+	 *    0    1    2    3    4          12          20
+	 *    +-----------+
+	 *    | LOCATION3 |
+	 *    +-----------+
+	 *   20          28
+	 *   LOCATION1 : Location of Continuation of System Use Area.
+	 *   LOCATION2 : Offset to Start of Continuation.
+	 *   LOCATION3 : Length of the Continuation.
+	 */
+
+	bp[1] = 'C';
+	bp[2] = 'E';
+	bp[3] = RR_CE_SIZE;	/* length	*/
+	bp[4] = 1;		/* version	*/
+	set_num_733(bp+5, location);
+	set_num_733(bp+13, offset);
+	set_num_733(bp+21, size);
+	return (RR_CE_SIZE);
+}
+
+/*
+ * The functions, which names are beginning with extra_, are used to
+ * control extra records.
+ * The maximum size of a Directory Record is 254. When a filename is
+ * very long, all of RRIP data of a file won't stored to the Directory
+ * Record and so remaining RRIP data store to an extra record instead.
+ */
+static unsigned char *
+extra_open_record(unsigned char *bp, int dr_len, struct isoent *isoent,
+    struct ctl_extr_rec *ctl)
+{
+	ctl->bp = bp;
+	if (bp != NULL)
+		bp += dr_len;
+	ctl->use_extr = 0;
+	ctl->isoent = isoent;
+	ctl->ce_ptr = NULL;
+	ctl->cur_len = ctl->dr_len = dr_len;
+	ctl->limit = DR_LIMIT;
+
+	return (bp);
+}
+
+static void
+extra_close_record(struct ctl_extr_rec *ctl, int ce_size)
+{
+	int padding = 0;
+
+	if (ce_size > 0)
+		extra_tell_used_size(ctl, ce_size);
+	/* Padding. */
+	if (ctl->cur_len & 0x01) {
+		ctl->cur_len++;
+		if (ctl->bp != NULL)
+			ctl->bp[ctl->cur_len] = 0;
+		padding = 1;
+	}
+	if (ctl->use_extr) {
+		if (ctl->ce_ptr != NULL)
+			set_SUSP_CE(ctl->ce_ptr, ctl->extr_loc,
+			    ctl->extr_off, ctl->cur_len - padding);
+	} else
+		ctl->dr_len = ctl->cur_len;
+}
+
+#define extra_space(ctl)	((ctl)->limit - (ctl)->cur_len)
+
+static unsigned char *
+extra_next_record(struct ctl_extr_rec *ctl, int length)
+{
+	int cur_len = ctl->cur_len;/* save cur_len */
+
+	/* Close the current extra record or Directory Record. */
+	extra_close_record(ctl, RR_CE_SIZE);
+
+	/* Get a next extra record. */
+	ctl->use_extr = 1;
+	if (ctl->bp != NULL) {
+		/* Storing data into an extra record. */
+		unsigned char *p;
+
+		/* Save the pointer where a CE extension will be
+		 * stored to. */
+		ctl->ce_ptr = &ctl->bp[cur_len+1];
+		p = extra_get_record(ctl->isoent,
+		    &ctl->limit, &ctl->extr_off, &ctl->extr_loc);
+		ctl->bp = p - 1;/* the base of bp offset is 1. */
+	} else
+		/* Calculating the size of an extra record. */
+		(void)extra_get_record(ctl->isoent,
+		    &ctl->limit, NULL, NULL);
+	ctl->cur_len = 0;
+	/* Check if an extra record is almost full.
+	 * If so, get a next one. */
+	if (extra_space(ctl) < length)
+		(void)extra_next_record(ctl, length);
+
+	return (ctl->bp);
+}
+
+static inline struct extr_rec *
+extra_last_record(struct isoent *isoent)
+{
+	if (isoent->extr_rec_list.first == NULL)
+		return (NULL);
+	return ((struct extr_rec *)(void *)
+		((char *)(isoent->extr_rec_list.last)
+		    - offsetof(struct extr_rec, next)));
+}
+
+static unsigned char *
+extra_get_record(struct isoent *isoent, int *space, int *off, int *loc)
+{
+	struct extr_rec *rec;
+
+	isoent = isoent->parent;
+	if (off != NULL) {
+		/* Storing data into an extra record. */
+		rec = isoent->extr_rec_list.current;
+		if (DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset)
+			rec = rec->next;
+	} else {
+		/* Calculating the size of an extra record. */
+		rec = extra_last_record(isoent);
+		if (rec == NULL ||
+		    DR_SAFETY > LOGICAL_BLOCK_SIZE - rec->offset) {
+			rec = malloc(sizeof(*rec));
+			if (rec == NULL)
+				return (NULL);
+			rec->location = 0;
+			rec->offset = 0;
+			/* Insert `rec` into the tail of isoent->extr_rec_list */
+			rec->next = NULL;
+			/*
+			 * Note: testing isoent->extr_rec_list.last == NULL
+			 * here is really unneeded since it has been already
+			 * initialized at isoent_new function but Clang Static
+			 * Analyzer claims that it is dereference of null
+			 * pointer.
+			 */
+			if (isoent->extr_rec_list.last == NULL)
+				isoent->extr_rec_list.last =
+					&(isoent->extr_rec_list.first);
+			*isoent->extr_rec_list.last = rec;
+			isoent->extr_rec_list.last = &(rec->next);
+		}
+	}
+	*space = LOGICAL_BLOCK_SIZE - rec->offset - DR_SAFETY;
+	if (*space & 0x01)
+		*space -= 1;/* Keep padding space. */
+	if (off != NULL)
+		*off = rec->offset;
+	if (loc != NULL)
+		*loc = rec->location;
+	isoent->extr_rec_list.current = rec;
+
+	return (&rec->buf[rec->offset]);
+}
+
+static void
+extra_tell_used_size(struct ctl_extr_rec *ctl, int size)
+{
+	struct isoent *isoent;
+	struct extr_rec *rec;
+
+	if (ctl->use_extr) {
+		isoent = ctl->isoent->parent;
+		rec = isoent->extr_rec_list.current;
+		if (rec != NULL)
+			rec->offset += size;
+	}
+	ctl->cur_len += size;
+}
+
+static int
+extra_setup_location(struct isoent *isoent, int location)
+{
+	struct extr_rec *rec;
+	int cnt;
+
+	cnt = 0;
+	rec = isoent->extr_rec_list.first;
+	isoent->extr_rec_list.current = rec;
+	while (rec) {
+		cnt++;
+		rec->location = location++;
+		rec->offset = 0;
+		rec = rec->next;
+	}
+	return (cnt);
+}
+
+/*
+ * Create the RRIP entries.
+ */
+static int
+set_directory_record_rr(unsigned char *bp, int dr_len,
+    struct isoent *isoent, struct iso9660 *iso9660, enum dir_rec_type t)
+{
+	/* Flags(BP 5) of the Rockridge "RR" System Use Field */
+	unsigned char rr_flag;
+#define RR_USE_PX	0x01
+#define RR_USE_PN	0x02
+#define RR_USE_SL	0x04
+#define RR_USE_NM	0x08
+#define RR_USE_CL	0x10
+#define RR_USE_PL	0x20
+#define RR_USE_RE	0x40
+#define RR_USE_TF	0x80
+	int length;
+	struct ctl_extr_rec ctl;
+	struct isoent *rr_parent, *pxent;
+	struct isofile *file;
+
+	bp = extra_open_record(bp, dr_len, isoent, &ctl);
+
+	if (t == DIR_REC_PARENT) {
+		rr_parent = isoent->rr_parent;
+		pxent = isoent->parent;
+		if (rr_parent != NULL)
+			isoent = rr_parent;
+		else
+			isoent = isoent->parent;
+	} else {
+		rr_parent = NULL;
+		pxent = isoent;
+	}
+	file = isoent->file;
+
+	if (t != DIR_REC_NORMAL) {
+		rr_flag = RR_USE_PX | RR_USE_TF;
+		if (rr_parent != NULL)
+			rr_flag |= RR_USE_PL;
+	} else {
+		rr_flag = RR_USE_PX | RR_USE_NM | RR_USE_TF;
+		if (archive_entry_filetype(file->entry) == AE_IFLNK)
+			rr_flag |= RR_USE_SL;
+		if (isoent->rr_parent != NULL)
+			rr_flag |= RR_USE_RE;
+		if (isoent->rr_child != NULL)
+			rr_flag |= RR_USE_CL;
+		if (archive_entry_filetype(file->entry) == AE_IFCHR ||
+		    archive_entry_filetype(file->entry) == AE_IFBLK)
+			rr_flag |= RR_USE_PN;
+#ifdef COMPAT_MKISOFS
+		/*
+		 * mkisofs 2.01.01a63 records "RE" extension to
+		 * the entry of "rr_moved" directory.
+		 * I don't understand this behavior.
+		 */
+		if (isoent->virtual &&
+		    isoent->parent == iso9660->primary.rootent &&
+		    strcmp(isoent->file->basename.s, "rr_moved") == 0)
+			rr_flag |= RR_USE_RE;
+#endif
+	}
+
+	/* Write "SP" System Use Entry. */
+	if (t == DIR_REC_SELF && isoent == isoent->parent) {
+		length = 7;
+		if (bp != NULL) {
+			bp[1] = 'S';
+			bp[2] = 'P';
+			bp[3] = length;
+			bp[4] = 1;	/* version	*/
+			bp[5] = 0xBE;  /* Check Byte	*/
+			bp[6] = 0xEF;  /* Check Byte	*/
+			bp[7] = 0;
+			bp += length;
+		}
+		extra_tell_used_size(&ctl, length);
+	}
+
+	/* Write "RR" System Use Entry. */
+	length = 5;
+	if (extra_space(&ctl) < length)
+		bp = extra_next_record(&ctl, length);
+	if (bp != NULL) {
+		bp[1] = 'R';
+		bp[2] = 'R';
+		bp[3] = length;
+		bp[4] = 1;	/* version */
+		bp[5] = rr_flag;
+		bp += length;
+	}
+	extra_tell_used_size(&ctl, length);
+
+	/* Write "NM" System Use Entry. */
+	if (rr_flag & RR_USE_NM) {
+		/*
+		 *   "NM" Format:
+		 *     e.g. a basename is 'foo'
+		 *               len  ver  flg
+		 *    +----+----+----+----+----+----+----+----+
+		 *    | 'N'| 'M'| 08 | 01 | 00 | 'f'| 'o'| 'o'|
+		 *    +----+----+----+----+----+----+----+----+
+		 *    <----------------- len ----------------->
+		 */
+		size_t nmlen = file->basename.length;
+		const char *nm = file->basename.s;
+		size_t nmmax;
+
+		if (extra_space(&ctl) < 6)
+			bp = extra_next_record(&ctl, 6);
+		if (bp != NULL) {
+			bp[1] = 'N';
+			bp[2] = 'M';
+			bp[4] = 1;	    /* version	*/
+		}
+		nmmax = extra_space(&ctl);
+		if (nmmax > 0xff)
+			nmmax = 0xff;
+		while (nmlen + 5 > nmmax) {
+			length = (int)nmmax;
+			if (bp != NULL) {
+				bp[3] = length;
+				bp[5] = 0x01;/* Alternate Name continues
+					       * in next "NM" field */
+				memcpy(bp+6, nm, length - 5);
+				bp += length;
+			}
+			nmlen -= length - 5;
+			nm += length - 5;
+			extra_tell_used_size(&ctl, length);
+			if (extra_space(&ctl) < 6) {
+				bp = extra_next_record(&ctl, 6);
+				nmmax = extra_space(&ctl);
+				if (nmmax > 0xff)
+					nmmax = 0xff;
+			}
+			if (bp != NULL) {
+				bp[1] = 'N';
+				bp[2] = 'M';
+				bp[4] = 1;    /* version */
+			}
+		}
+		length = 5 + (int)nmlen;
+		if (bp != NULL) {
+			bp[3] = length;
+			bp[5] = 0;
+			memcpy(bp+6, nm, nmlen);
+			bp += length;
+		}
+		extra_tell_used_size(&ctl, length);
+	}
+
+	/* Write "PX" System Use Entry. */
+	if (rr_flag & RR_USE_PX) {
+		/*
+		 *   "PX" Format:
+		 *               len  ver
+		 *    +----+----+----+----+-----------+-----------+
+		 *    | 'P'| 'X'| 2C | 01 | FILE MODE |   LINKS   |
+		 *    +----+----+----+----+-----------+-----------+
+		 *    0    1    2    3    4          12          20
+		 *    +-----------+-----------+------------------+
+		 *    |  USER ID  | GROUP ID  |FILE SERIAL NUMBER|
+		 *    +-----------+-----------+------------------+
+		 *   20          28          36                 44
+		 */
+		length = 44;
+		if (extra_space(&ctl) < length)
+			bp = extra_next_record(&ctl, length);
+		if (bp != NULL) {
+			mode_t mode;
+			int64_t uid;
+			int64_t gid;
+
+			mode = archive_entry_mode(file->entry);
+			uid = archive_entry_uid(file->entry);
+			gid = archive_entry_gid(file->entry);
+			if (iso9660->opt.rr == OPT_RR_USEFUL) {
+				/*
+				 * This action is similar to mkisofs -r option
+				 * but our rockridge=useful option does not
+				 * set a zero to uid and gid.
+				 */
+				/* set all read bit ON */
+				mode |= 0444;
+#if !defined(_WIN32) && !defined(__CYGWIN__)
+				if (mode & 0111)
+#endif
+					/* set all exec bit ON */
+					mode |= 0111;
+				/* clear all write bits. */
+				mode &= ~0222;
+				/* clear setuid,setgid,sticky bits. */
+				mode &= ~07000;
+			}
+
+			bp[1] = 'P';
+			bp[2] = 'X';
+			bp[3] = length;
+			bp[4] = 1;	/* version	*/
+			/* file mode */
+			set_num_733(bp+5, mode);
+			/* file links (stat.st_nlink) */
+			set_num_733(bp+13,
+			    archive_entry_nlink(file->entry));
+			set_num_733(bp+21, (uint32_t)uid);
+			set_num_733(bp+29, (uint32_t)gid);
+			/* File Serial Number */
+			if (pxent->dir)
+				set_num_733(bp+37, pxent->dir_location);
+			else if (file->hardlink_target != NULL)
+				set_num_733(bp+37,
+				    file->hardlink_target->cur_content->location);
+			else
+				set_num_733(bp+37,
+				    file->cur_content->location);
+			bp += length;
+		}
+		extra_tell_used_size(&ctl, length);
+	}
+
+	/* Write "SL" System Use Entry. */
+	if (rr_flag & RR_USE_SL) {
+		/*
+		 *   "SL" Format:
+		 *     e.g. a symbolic name is 'foo/bar'
+		 *               len  ver  flg
+		 *    +----+----+----+----+----+------------+
+		 *    | 'S'| 'L'| 0F | 01 | 00 | components |
+		 *    +----+----+----+----+----+-----+------+
+		 *    0    1    2    3    4    5  ...|...  15
+		 *    <----------------- len --------+------>
+		 *    components :                   |
+		 *     cflg clen                     |
+		 *    +----+----+----+----+----+     |
+		 *    | 00 | 03 | 'f'| 'o'| 'o'| <---+
+		 *    +----+----+----+----+----+     |
+		 *    5    6    7    8    9   10     |
+		 *     cflg clen                     |
+		 *    +----+----+----+----+----+     |
+		 *    | 00 | 03 | 'b'| 'a'| 'r'| <---+
+		 *    +----+----+----+----+----+
+		 *   10   11   12   13   14   15
+		 *
+		 *    - cflg : flag of component
+		 *    - clen : length of component
+		 */
+		const char *sl;
+		char sl_last;
+
+		if (extra_space(&ctl) < 7)
+			bp = extra_next_record(&ctl, 7);
+		sl = file->symlink.s;
+		sl_last = '\0';
+		if (bp != NULL) {
+			bp[1] = 'S';
+			bp[2] = 'L';
+			bp[4] = 1;	/* version	*/
+		}
+		for (;;) {
+			unsigned char *nc, *cf,  *cl, cldmy = 0;
+			int sllen, slmax;
+
+			slmax = extra_space(&ctl);
+			if (slmax > 0xff)
+				slmax = 0xff;
+			if (bp != NULL)
+				nc = &bp[6];
+			else
+				nc = NULL;
+			cf = cl = NULL;
+			sllen = 0;
+			while (*sl && sllen + 11 < slmax) {
+				if (sl_last == '\0' && sl[0] == '/') {
+					/*
+					 *     flg  len
+					 *    +----+----+
+					 *    | 08 | 00 | ROOT component.
+					 *    +----+----+ ("/")
+					 *
+				 	 * Root component has to appear
+				 	 * at the first component only.
+					 */
+					if (nc != NULL) {
+						cf = nc++;
+						*cf = 0x08; /* ROOT */
+						*nc++ = 0;
+					}
+					sllen += 2;
+					sl++;
+					sl_last = '/';
+					cl = NULL;
+					continue;
+				}
+				if (((sl_last == '\0' || sl_last == '/') &&
+				      sl[0] == '.' && sl[1] == '.' &&
+				     (sl[2] == '/' || sl[2] == '\0')) ||
+				    (sl[0] == '/' &&
+				      sl[1] == '.' && sl[2] == '.' &&
+				     (sl[3] == '/' || sl[3] == '\0'))) {
+					/*
+					 *     flg  len
+					 *    +----+----+
+					 *    | 04 | 00 | PARENT component.
+					 *    +----+----+ ("..")
+					 */
+					if (nc != NULL) {
+						cf = nc++;
+						*cf = 0x04; /* PARENT */
+						*nc++ = 0;
+					}
+					sllen += 2;
+					if (sl[0] == '/')
+						sl += 3;/* skip "/.." */
+					else
+						sl += 2;/* skip ".." */
+					sl_last = '.';
+					cl = NULL;
+					continue;
+				}
+				if (((sl_last == '\0' || sl_last == '/') &&
+				      sl[0] == '.' &&
+				     (sl[1] == '/' || sl[1] == '\0')) ||
+				    (sl[0] == '/' && sl[1] == '.' &&
+				     (sl[2] == '/' || sl[2] == '\0'))) {
+					/*
+					 *     flg  len
+					 *    +----+----+
+					 *    | 02 | 00 | CURRENT component.
+					 *    +----+----+ (".")
+					 */
+					if (nc != NULL) {
+						cf = nc++;
+						*cf = 0x02; /* CURRENT */
+						*nc++ = 0;
+					}
+					sllen += 2;
+					if (sl[0] == '/')
+						sl += 2;/* skip "/." */
+					else
+						sl ++;  /* skip "." */
+					sl_last = '.';
+					cl = NULL;
+					continue;
+				}
+				if (sl[0] == '/' || cl == NULL) {
+					if (nc != NULL) {
+						cf = nc++;
+						*cf = 0;
+						cl = nc++;
+						*cl = 0;
+					} else
+						cl = &cldmy;
+					sllen += 2;
+					if (sl[0] == '/') {
+						sl_last = *sl++;
+						continue;
+					}
+				}
+				sl_last = *sl++;
+				if (nc != NULL) {
+					*nc++ = sl_last;
+					(*cl) ++;
+				}
+				sllen++;
+			}
+			if (*sl) {
+				length = 5 + sllen;
+				if (bp != NULL) {
+					/*
+					 * Mark flg as CONTINUE component.
+					 */
+					*cf |= 0x01;
+					/*
+					 *               len  ver  flg
+					 *    +----+----+----+----+----+-
+					 *    | 'S'| 'L'| XX | 01 | 01 |
+					 *    +----+----+----+----+----+-
+					 *                           ^
+					 *           continues in next "SL"
+					 */
+					bp[3] = length;
+					bp[5] = 0x01;/* This Symbolic Link
+						      * continues in next
+						      * "SL" field */
+					bp += length;
+				}
+				extra_tell_used_size(&ctl, length);
+				if (extra_space(&ctl) < 11)
+					bp = extra_next_record(&ctl, 11);
+				if (bp != NULL) {
+					/* Next 'SL' */
+					bp[1] = 'S';
+					bp[2] = 'L';
+					bp[4] = 1;    /* version */
+				}
+			} else {
+				length = 5 + sllen;
+				if (bp != NULL) {
+					bp[3] = length;
+					bp[5] = 0;
+					bp += length;
+				}
+				extra_tell_used_size(&ctl, length);
+				break;
+			}
+		}
+	}
+
+	/* Write "TF" System Use Entry. */
+	if (rr_flag & RR_USE_TF) {
+		/*
+		 *   "TF" Format:
+		 *               len  ver
+		 *    +----+----+----+----+-----+-------------+
+		 *    | 'T'| 'F'| XX | 01 |FLAGS| TIME STAMPS |
+		 *    +----+----+----+----+-----+-------------+
+		 *    0    1    2    3    4     5            XX
+		 *    TIME STAMPS : ISO 9660 Standard 9.1.5.
+		 *                  If TF_LONG_FORM FLAGS is set,
+		 *                  use ISO9660 Standard 8.4.26.1.
+		 */
+#define TF_CREATION	0x01	/* Creation time recorded		*/
+#define TF_MODIFY	0x02	/* Modification time recorded		*/
+#define TF_ACCESS	0x04	/* Last Access time recorded		*/
+#define TF_ATTRIBUTES	0x08	/* Last Attribute Change time recorded  */
+#define TF_BACKUP	0x10	/* Last Backup time recorded		*/
+#define TF_EXPIRATION	0x20	/* Expiration time recorded		*/
+#define TF_EFFECTIVE	0x40	/* Effective time recorded		*/
+#define TF_LONG_FORM	0x80	/* ISO 9660 17-byte time format used	*/
+		unsigned char tf_flags;
+
+		length = 5;
+		tf_flags = 0;
+#ifndef COMPAT_MKISOFS
+		if (archive_entry_birthtime_is_set(file->entry) &&
+		    archive_entry_birthtime(file->entry) <=
+		    archive_entry_mtime(file->entry)) {
+			length += 7;
+			tf_flags |= TF_CREATION;
+		}
+#endif
+		if (archive_entry_mtime_is_set(file->entry)) {
+			length += 7;
+			tf_flags |= TF_MODIFY;
+		}
+		if (archive_entry_atime_is_set(file->entry)) {
+			length += 7;
+			tf_flags |= TF_ACCESS;
+		}
+		if (archive_entry_ctime_is_set(file->entry)) {
+			length += 7;
+			tf_flags |= TF_ATTRIBUTES;
+		}
+		if (extra_space(&ctl) < length)
+			bp = extra_next_record(&ctl, length);
+		if (bp != NULL) {
+			bp[1] = 'T';
+			bp[2] = 'F';
+			bp[3] = length;
+			bp[4] = 1;	/* version	*/
+			bp[5] = tf_flags;
+			bp += 5;
+			/* Creation time */
+			if (tf_flags & TF_CREATION) {
+				set_time_915(bp+1,
+				    archive_entry_birthtime(file->entry));
+				bp += 7;
+			}
+			/* Modification time */
+			if (tf_flags & TF_MODIFY) {
+				set_time_915(bp+1,
+				    archive_entry_mtime(file->entry));
+				bp += 7;
+			}
+			/* Last Access time */
+			if (tf_flags & TF_ACCESS) {
+				set_time_915(bp+1,
+				    archive_entry_atime(file->entry));
+				bp += 7;
+			}
+			/* Last Attribute Change time */
+			if (tf_flags & TF_ATTRIBUTES) {
+				set_time_915(bp+1,
+				    archive_entry_ctime(file->entry));
+				bp += 7;
+			}
+		}
+		extra_tell_used_size(&ctl, length);
+	}
+
+	/* Write "RE" System Use Entry. */
+	if (rr_flag & RR_USE_RE) {
+		/*
+		 *   "RE" Format:
+		 *               len  ver
+		 *    +----+----+----+----+
+		 *    | 'R'| 'E'| 04 | 01 |
+		 *    +----+----+----+----+
+		 *    0    1    2    3    4
+		 */
+		length = 4;
+		if (extra_space(&ctl) < length)
+			bp = extra_next_record(&ctl, length);
+		if (bp != NULL) {
+			bp[1] = 'R';
+			bp[2] = 'E';
+			bp[3] = length;
+			bp[4] = 1;	/* version	*/
+			bp += length;
+		}
+		extra_tell_used_size(&ctl, length);
+	}
+
+	/* Write "PL" System Use Entry. */
+	if (rr_flag & RR_USE_PL) {
+		/*
+		 *   "PL" Format:
+		 *               len  ver
+		 *    +----+----+----+----+------------+
+		 *    | 'P'| 'L'| 0C | 01 | *LOCATION  |
+		 *    +----+----+----+----+------------+
+		 *    0    1    2    3    4           12
+		 *    *LOCATION: location of parent directory
+		 */
+		length = 12;
+		if (extra_space(&ctl) < length)
+			bp = extra_next_record(&ctl, length);
+		if (bp != NULL) {
+			bp[1] = 'P';
+			bp[2] = 'L';
+			bp[3] = length;
+			bp[4] = 1;	/* version	*/
+			set_num_733(bp + 5,
+			    rr_parent->dir_location);
+			bp += length;
+		}
+		extra_tell_used_size(&ctl, length);
+	}
+
+	/* Write "CL" System Use Entry. */
+	if (rr_flag & RR_USE_CL) {
+		/*
+		 *   "CL" Format:
+		 *               len  ver
+		 *    +----+----+----+----+------------+
+		 *    | 'C'| 'L'| 0C | 01 | *LOCATION  |
+		 *    +----+----+----+----+------------+
+		 *    0    1    2    3    4           12
+		 *    *LOCATION: location of child directory
+		 */
+		length = 12;
+		if (extra_space(&ctl) < length)
+			bp = extra_next_record(&ctl, length);
+		if (bp != NULL) {
+			bp[1] = 'C';
+			bp[2] = 'L';
+			bp[3] = length;
+			bp[4] = 1;	/* version	*/
+			set_num_733(bp + 5,
+			    isoent->rr_child->dir_location);
+			bp += length;
+		}
+		extra_tell_used_size(&ctl, length);
+	}
+
+	/* Write "PN" System Use Entry. */
+	if (rr_flag & RR_USE_PN) {
+		/*
+		 *   "PN" Format:
+		 *               len  ver
+		 *    +----+----+----+----+------------+------------+
+		 *    | 'P'| 'N'| 14 | 01 | dev_t high | dev_t low  |
+		 *    +----+----+----+----+------------+------------+
+		 *    0    1    2    3    4           12           20
+		 */
+		length = 20;
+		if (extra_space(&ctl) < length)
+			bp = extra_next_record(&ctl, length);
+		if (bp != NULL) {
+			uint64_t dev;
+
+			bp[1] = 'P';
+			bp[2] = 'N';
+			bp[3] = length;
+			bp[4] = 1;	/* version	*/
+			dev = (uint64_t)archive_entry_rdev(file->entry);
+			set_num_733(bp + 5, (uint32_t)(dev >> 32));
+			set_num_733(bp + 13, (uint32_t)(dev & 0xFFFFFFFF));
+			bp += length;
+		}
+		extra_tell_used_size(&ctl, length);
+	}
+
+	/* Write "ZF" System Use Entry. */
+	if (file->zisofs.header_size) {
+		/*
+		 *   "ZF" Format:
+		 *               len  ver
+		 *    +----+----+----+----+----+----+-------------+
+		 *    | 'Z'| 'F'| 10 | 01 | 'p'| 'z'| Header Size |
+		 *    +----+----+----+----+----+----+-------------+
+		 *    0    1    2    3    4    5    6             7
+		 *    +--------------------+-------------------+
+		 *    | Log2 of block Size | Uncompressed Size |
+		 *    +--------------------+-------------------+
+		 *    7                    8                   16
+		 */
+		length = 16;
+		if (extra_space(&ctl) < length)
+			bp = extra_next_record(&ctl, length);
+		if (bp != NULL) {
+			bp[1] = 'Z';
+			bp[2] = 'F';
+			bp[3] = length;
+			bp[4] = 1;	/* version	*/
+			bp[5] = 'p';
+			bp[6] = 'z';
+			bp[7] = file->zisofs.header_size;
+			bp[8] = file->zisofs.log2_bs;
+			set_num_733(bp + 9, file->zisofs.uncompressed_size);
+			bp += length;
+		}
+		extra_tell_used_size(&ctl, length);
+	}
+
+	/* Write "CE" System Use Entry. */
+	if (t == DIR_REC_SELF && isoent == isoent->parent) {
+		length = RR_CE_SIZE;
+		if (bp != NULL)
+			set_SUSP_CE(bp+1, iso9660->location_rrip_er,
+			    0, RRIP_ER_SIZE);
+		extra_tell_used_size(&ctl, length);
+	}
+
+	extra_close_record(&ctl, 0);
+
+	return (ctl.dr_len);
+}
+
+/*
+ * Write data of a Directory Record or calculate writing bytes itself.
+ * If parameter `p' is NULL, calculates the size of writing data, which
+ * a Directory Record needs to write, then it saved and return
+ * the calculated size.
+ * Parameter `n' is a remaining size of buffer. when parameter `p' is
+ * not NULL, check whether that `n' is not less than the saved size.
+ * if that `n' is small, return zero.
+ *
+ * This format of the Directory Record is according to
+ * ISO9660 Standard 9.1
+ */
+static int
+set_directory_record(unsigned char *p, size_t n, struct isoent *isoent,
+    struct iso9660 *iso9660, enum dir_rec_type t,
+    enum vdd_type vdd_type)
+{
+	unsigned char *bp;
+	size_t dr_len;
+	size_t fi_len;
+
+	if (p != NULL) {
+		/*
+		 * Check whether a write buffer size is less than the
+		 * saved size which is needed to write this Directory
+		 * Record.
+		 */
+		switch (t) {
+		case DIR_REC_VD:
+			dr_len = isoent->dr_len.vd; break;
+		case DIR_REC_SELF:
+			dr_len = isoent->dr_len.self; break;
+		case DIR_REC_PARENT:
+			dr_len = isoent->dr_len.parent; break;
+		case DIR_REC_NORMAL:
+		default:
+			dr_len = isoent->dr_len.normal; break;
+		}
+		if (dr_len > n)
+			return (0);/* Needs more buffer size. */
+	}
+
+	if (t == DIR_REC_NORMAL && isoent->identifier != NULL)
+		fi_len = isoent->id_len;
+	else
+		fi_len = 1;
+
+	if (p != NULL) {
+		struct isoent *xisoent;
+		struct isofile *file;
+		unsigned char flag;
+
+		if (t == DIR_REC_PARENT)
+			xisoent = isoent->parent;
+		else
+			xisoent = isoent;
+		file = isoent->file;
+		if (file->hardlink_target != NULL)
+			file = file->hardlink_target;
+		/* Make a file flag. */
+		if (xisoent->dir)
+			flag = FILE_FLAG_DIRECTORY;
+		else {
+			if (file->cur_content->next != NULL)
+				flag = FILE_FLAG_MULTI_EXTENT;
+			else
+				flag = 0;
+		}
+
+		bp = p -1;
+		/* Extended Attribute Record Length */
+		set_num_711(bp+2, 0);
+		/* Location of Extent */
+		if (xisoent->dir)
+			set_num_733(bp+3, xisoent->dir_location);
+		else
+			set_num_733(bp+3, file->cur_content->location);
+		/* Data Length */
+		if (xisoent->dir)
+			set_num_733(bp+11,
+			    xisoent->dir_block * LOGICAL_BLOCK_SIZE);
+		else
+			set_num_733(bp+11, (uint32_t)file->cur_content->size);
+		/* Recording Date and Time */
+		/* NOTE:
+		 *  If a file type is symbolic link, you are seeing this
+		 *  field value is different from a value mkisofs makes.
+		 *  libarchive uses lstat to get this one, but it
+		 *  seems mkisofs uses stat to get.
+		 */
+		set_time_915(bp+19,
+		    archive_entry_mtime(xisoent->file->entry));
+		/* File Flags */
+		bp[26] = flag;
+		/* File Unit Size */
+		set_num_711(bp+27, 0);
+		/* Interleave Gap Size */
+		set_num_711(bp+28, 0);
+		/* Volume Sequence Number */
+		set_num_723(bp+29, iso9660->volume_sequence_number);
+		/* Length of File Identifier */
+		set_num_711(bp+33, (unsigned char)fi_len);
+		/* File Identifier */
+		switch (t) {
+		case DIR_REC_VD:
+		case DIR_REC_SELF:
+			set_num_711(bp+34, 0);
+			break;
+		case DIR_REC_PARENT:
+			set_num_711(bp+34, 1);
+			break;
+		case DIR_REC_NORMAL:
+			if (isoent->identifier != NULL)
+				memcpy(bp+34, isoent->identifier, fi_len);
+			else
+				set_num_711(bp+34, 0);
+			break;
+		}
+	} else
+		bp = NULL;
+	dr_len = 33 + fi_len;
+	/* Padding Field */
+	if (dr_len & 0x01) {
+		dr_len ++;
+		if (p != NULL)
+			bp[dr_len] = 0;
+	}
+
+	/* Volume Descriptor does not record extension. */
+	if (t == DIR_REC_VD) {
+		if (p != NULL)
+			/* Length of Directory Record */
+			set_num_711(p, (unsigned char)dr_len);
+		else
+			isoent->dr_len.vd = (int)dr_len;
+		return ((int)dr_len);
+	}
+
+	/* Rockridge */
+	if (iso9660->opt.rr && vdd_type != VDD_JOLIET)
+		dr_len = set_directory_record_rr(bp, (int)dr_len,
+		    isoent, iso9660, t);
+
+	if (p != NULL)
+		/* Length of Directory Record */
+		set_num_711(p, (unsigned char)dr_len);
+	else {
+		/*
+		 * Save the size which is needed to write this
+		 * Directory Record.
+		 */
+		switch (t) {
+		case DIR_REC_VD:
+			/* This case does not come, but compiler
+			 * complains that DIR_REC_VD not handled
+			 *  in switch ....  */
+			break;
+		case DIR_REC_SELF:
+			isoent->dr_len.self = (int)dr_len; break;
+		case DIR_REC_PARENT:
+			isoent->dr_len.parent = (int)dr_len; break;
+		case DIR_REC_NORMAL:
+			isoent->dr_len.normal = (int)dr_len; break;
+		}
+	}
+
+	return ((int)dr_len);
+}
+
+/*
+ * Calculate the size of a directory record.
+ */
+static inline int
+get_dir_rec_size(struct iso9660 *iso9660, struct isoent *isoent,
+    enum dir_rec_type t, enum vdd_type vdd_type)
+{
+
+	return (set_directory_record(NULL, SIZE_MAX,
+	    isoent, iso9660, t, vdd_type));
+}
+
+/*
+ * Manage to write ISO-image data with wbuff to reduce calling
+ * __archive_write_output() for performance.
+ */
+
+
+static inline unsigned char *
+wb_buffptr(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+
+	return (&(iso9660->wbuff[sizeof(iso9660->wbuff)
+		- iso9660->wbuff_remaining]));
+}
+
+static int
+wb_write_out(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+	size_t wsize, nw;
+	int r;
+
+	wsize = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining;
+	nw = wsize % LOGICAL_BLOCK_SIZE;
+	if (iso9660->wbuff_type == WB_TO_STREAM)
+		r = __archive_write_output(a, iso9660->wbuff, wsize - nw);
+	else
+		r = write_to_temp(a, iso9660->wbuff, wsize - nw);
+	/* Increase the offset. */
+	iso9660->wbuff_offset += wsize - nw;
+	if (iso9660->wbuff_offset > iso9660->wbuff_written)
+		iso9660->wbuff_written = iso9660->wbuff_offset;
+	iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
+	if (nw) {
+		iso9660->wbuff_remaining -= nw;
+		memmove(iso9660->wbuff, iso9660->wbuff + wsize - nw, nw);
+	}
+	return (r);
+}
+
+static int
+wb_consume(struct archive_write *a, size_t size)
+{
+	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+
+	if (size > iso9660->wbuff_remaining ||
+	    iso9660->wbuff_remaining == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal Programming error: iso9660:wb_consume()"
+		    " size=%jd, wbuff_remaining=%jd",
+		    (intmax_t)size, (intmax_t)iso9660->wbuff_remaining);
+		return (ARCHIVE_FATAL);
+	}
+	iso9660->wbuff_remaining -= size;
+	if (iso9660->wbuff_remaining < LOGICAL_BLOCK_SIZE)
+		return (wb_write_out(a));
+	return (ARCHIVE_OK);
+}
+
+#ifdef HAVE_ZLIB_H
+
+static int
+wb_set_offset(struct archive_write *a, int64_t off)
+{
+	struct iso9660 *iso9660 = (struct iso9660 *)a->format_data;
+	int64_t used, ext_bytes;
+
+	if (iso9660->wbuff_type != WB_TO_TEMP) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal Programming error: iso9660:wb_set_offset()");
+		return (ARCHIVE_FATAL);
+	}
+
+	used = sizeof(iso9660->wbuff) - iso9660->wbuff_remaining;
+	if (iso9660->wbuff_offset + used > iso9660->wbuff_tail)
+		iso9660->wbuff_tail = iso9660->wbuff_offset + used;
+	if (iso9660->wbuff_offset < iso9660->wbuff_written) {
+		if (used > 0 &&
+		    write_to_temp(a, iso9660->wbuff, (size_t)used) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		iso9660->wbuff_offset = iso9660->wbuff_written;
+		lseek(iso9660->temp_fd, iso9660->wbuff_offset, SEEK_SET);
+		iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
+		used = 0;
+	}
+	if (off < iso9660->wbuff_offset) {
+		/*
+		 * Write out waiting data.
+		 */
+		if (used > 0) {
+			if (wb_write_out(a) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		}
+		lseek(iso9660->temp_fd, off, SEEK_SET);
+		iso9660->wbuff_offset = off;
+		iso9660->wbuff_remaining = sizeof(iso9660->wbuff);
+	} else if (off <= iso9660->wbuff_tail) {
+		iso9660->wbuff_remaining = (size_t)
+		    (sizeof(iso9660->wbuff) - (off - iso9660->wbuff_offset));
+	} else {
+		ext_bytes = off - iso9660->wbuff_tail;
+		iso9660->wbuff_remaining = (size_t)(sizeof(iso9660->wbuff)
+		   - (iso9660->wbuff_tail - iso9660->wbuff_offset));
+		while (ext_bytes >= (int64_t)iso9660->wbuff_remaining) {
+			if (write_null(a, (size_t)iso9660->wbuff_remaining)
+			    != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			ext_bytes -= iso9660->wbuff_remaining;
+		}
+		if (ext_bytes > 0) {
+			if (write_null(a, (size_t)ext_bytes) != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static int
+write_null(struct archive_write *a, size_t size)
+{
+	size_t remaining;
+	unsigned char *p, *old;
+	int r;
+
+	remaining = wb_remaining(a);
+	p = wb_buffptr(a);
+	if (size <= remaining) {
+		memset(p, 0, size);
+		return (wb_consume(a, size));
+	}
+	memset(p, 0, remaining);
+	r = wb_consume(a, remaining);
+	if (r != ARCHIVE_OK)
+		return (r);
+	size -= remaining;
+	old = p;
+	p = wb_buffptr(a);
+	memset(p, 0, old - p);
+	remaining = wb_remaining(a);
+	while (size) {
+		size_t wsize = size;
+
+		if (wsize > remaining)
+			wsize = remaining;
+		r = wb_consume(a, wsize);
+		if (r != ARCHIVE_OK)
+			return (r);
+		size -= wsize;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Write Volume Descriptor Set Terminator
+ */
+static int
+write_VD_terminator(struct archive_write *a)
+{
+	unsigned char *bp;
+
+	bp = wb_buffptr(a) -1;
+	set_VD_bp(bp, VDT_TERMINATOR, 1);
+	set_unused_field_bp(bp, 8, LOGICAL_BLOCK_SIZE);
+
+	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static int
+set_file_identifier(unsigned char *bp, int from, int to, enum vdc vdc,
+    struct archive_write *a, struct vdd *vdd, struct archive_string *id,
+    const char *label, int leading_under, enum char_type char_type)
+{
+	char identifier[256];
+	struct isoent *isoent;
+	const char *ids;
+	size_t len;
+	int r;
+
+	if (id->length > 0 && leading_under && id->s[0] != '_') {
+		if (char_type == A_CHAR)
+			r = set_str_a_characters_bp(a, bp, from, to, id->s, vdc);
+		else
+			r = set_str_d_characters_bp(a, bp, from, to, id->s, vdc);
+	} else if (id->length > 0) {
+		ids = id->s;
+		if (leading_under)
+			ids++;
+		isoent = isoent_find_entry(vdd->rootent, ids);
+		if (isoent == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Not Found %s `%s'.",
+			    label, ids);
+			return (ARCHIVE_FATAL);
+		}
+		len = isoent->ext_off + isoent->ext_len;
+		if (vdd->vdd_type == VDD_JOLIET) {
+			if (len > sizeof(identifier)-2)
+				len = sizeof(identifier)-2;
+		} else {
+			if (len > sizeof(identifier)-1)
+				len = sizeof(identifier)-1;
+		}
+		memcpy(identifier, isoent->identifier, len);
+		identifier[len] = '\0';
+		if (vdd->vdd_type == VDD_JOLIET) {
+			identifier[len+1] = 0;
+			vdc = VDC_UCS2_DIRECT;
+		}
+		if (char_type == A_CHAR)
+			r = set_str_a_characters_bp(a, bp, from, to,
+			    identifier, vdc);
+		else
+			r = set_str_d_characters_bp(a, bp, from, to,
+			    identifier, vdc);
+	} else {
+		if (char_type == A_CHAR)
+			r = set_str_a_characters_bp(a, bp, from, to, NULL, vdc);
+		else
+			r = set_str_d_characters_bp(a, bp, from, to, NULL, vdc);
+	}
+	return (r);
+}
+
+/*
+ * Write Primary/Supplementary Volume Descriptor
+ */
+static int
+write_VD(struct archive_write *a, struct vdd *vdd)
+{
+	struct iso9660 *iso9660;
+	unsigned char *bp;
+	uint16_t volume_set_size = 1;
+	char identifier[256];
+	enum VD_type vdt;
+	enum vdc vdc;
+	unsigned char vd_ver, fst_ver;
+	int r;
+
+	iso9660 = a->format_data;
+	switch (vdd->vdd_type) {
+	case VDD_JOLIET:
+		vdt = VDT_SUPPLEMENTARY;
+		vd_ver = fst_ver = 1;
+		vdc = VDC_UCS2;
+		break;
+	case VDD_ENHANCED:
+		vdt = VDT_SUPPLEMENTARY;
+		vd_ver = fst_ver = 2;
+		vdc = VDC_LOWERCASE;
+		break;
+	case VDD_PRIMARY:
+	default:
+		vdt = VDT_PRIMARY;
+		vd_ver = fst_ver = 1;
+#ifdef COMPAT_MKISOFS
+		vdc = VDC_LOWERCASE;
+#else
+		vdc = VDC_STD;
+#endif
+		break;
+	}
+
+	bp = wb_buffptr(a) -1;
+	/* Volume Descriptor Type */
+	set_VD_bp(bp, vdt, vd_ver);
+	/* Unused Field */
+	set_unused_field_bp(bp, 8, 8);
+	/* System Identifier */
+	get_system_identitier(identifier, sizeof(identifier));
+	r = set_str_a_characters_bp(a, bp, 9, 40, identifier, vdc);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Volume Identifier */
+	r = set_str_d_characters_bp(a, bp, 41, 72,
+	    iso9660->volume_identifier.s, vdc);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Unused Field */
+	set_unused_field_bp(bp, 73, 80);
+	/* Volume Space Size */
+	set_num_733(bp+81, iso9660->volume_space_size);
+	if (vdd->vdd_type == VDD_JOLIET) {
+		/* Escape Sequences */
+		bp[89] = 0x25;/* UCS-2 Level 3 */
+		bp[90] = 0x2F;
+		bp[91] = 0x45;
+		memset(bp + 92, 0, 120 - 92 + 1);
+	} else {
+		/* Unused Field */
+		set_unused_field_bp(bp, 89, 120);
+	}
+	/* Volume Set Size */
+	set_num_723(bp+121, volume_set_size);
+	/* Volume Sequence Number */
+	set_num_723(bp+125, iso9660->volume_sequence_number);
+	/* Logical Block Size */
+	set_num_723(bp+129, LOGICAL_BLOCK_SIZE);
+	/* Path Table Size */
+	set_num_733(bp+133, vdd->path_table_size);
+	/* Location of Occurrence of Type L Path Table */
+	set_num_731(bp+141, vdd->location_type_L_path_table);
+	/* Location of Optional Occurrence of Type L Path Table */
+	set_num_731(bp+145, 0);
+	/* Location of Occurrence of Type M Path Table */
+	set_num_732(bp+149, vdd->location_type_M_path_table);
+	/* Location of Optional Occurrence of Type M Path Table */
+	set_num_732(bp+153, 0);
+	/* Directory Record for Root Directory(BP 157 to 190) */
+	set_directory_record(bp+157, 190-157+1, vdd->rootent,
+	    iso9660, DIR_REC_VD, vdd->vdd_type);
+	/* Volume Set Identifier */
+	r = set_str_d_characters_bp(a, bp, 191, 318, "", vdc);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Publisher Identifier */
+	r = set_file_identifier(bp, 319, 446, vdc, a, vdd,
+	    &(iso9660->publisher_identifier),
+	    "Publisher File", 1, A_CHAR);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Data Preparer Identifier */
+	r = set_file_identifier(bp, 447, 574, vdc, a, vdd,
+	    &(iso9660->data_preparer_identifier),
+	    "Data Preparer File", 1, A_CHAR);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Application Identifier */
+	r = set_file_identifier(bp, 575, 702, vdc, a, vdd,
+	    &(iso9660->application_identifier),
+	    "Application File", 1, A_CHAR);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Copyright File Identifier */
+	r = set_file_identifier(bp, 703, 739, vdc, a, vdd,
+	    &(iso9660->copyright_file_identifier),
+	    "Copyright File", 0, D_CHAR);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Abstract File Identifier */
+	r = set_file_identifier(bp, 740, 776, vdc, a, vdd,
+	    &(iso9660->abstract_file_identifier),
+	    "Abstract File", 0, D_CHAR);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Bibliographic File Identifier */
+	r = set_file_identifier(bp, 777, 813, vdc, a, vdd,
+	    &(iso9660->bibliographic_file_identifier),
+	    "Bibliongraphic File", 0, D_CHAR);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* Volume Creation Date and Time */
+	set_date_time(bp+814, iso9660->birth_time);
+	/* Volume Modification Date and Time */
+	set_date_time(bp+831, iso9660->birth_time);
+	/* Volume Expiration Date and Time(obsolete) */
+	set_date_time_null(bp+848);
+	/* Volume Effective Date and Time */
+	set_date_time(bp+865, iso9660->birth_time);
+	/* File Structure Version */
+	bp[882] = fst_ver;
+	/* Reserved */
+	bp[883] = 0;
+	/* Application Use */
+	memset(bp + 884, 0x20, 1395 - 884 + 1);
+	/* Reserved */
+	set_unused_field_bp(bp, 1396, LOGICAL_BLOCK_SIZE);
+
+	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+/*
+ * Write Boot Record Volume Descriptor
+ */
+static int
+write_VD_boot_record(struct archive_write *a)
+{
+	struct iso9660 *iso9660;
+	unsigned char *bp;
+
+	iso9660 = a->format_data;
+	bp = wb_buffptr(a) -1;
+	/* Volume Descriptor Type */
+	set_VD_bp(bp, VDT_BOOT_RECORD, 1);
+	/* Boot System Identifier */
+	memcpy(bp+8, "EL TORITO SPECIFICATION", 23);
+	set_unused_field_bp(bp, 8+23, 39);
+	/* Unused */
+	set_unused_field_bp(bp, 40, 71);
+	/* Absolute pointer to first sector of Boot Catalog */
+	set_num_731(bp+72,
+	    iso9660->el_torito.catalog->file->content.location);
+	/* Unused */
+	set_unused_field_bp(bp, 76, LOGICAL_BLOCK_SIZE);
+
+	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+enum keytype {
+	KEY_FLG,
+	KEY_STR,
+	KEY_INT,
+	KEY_HEX
+};
+static void
+set_option_info(struct archive_string *info, int *opt, const char *key,
+    enum keytype type,  ...)
+{
+	va_list ap;
+	char prefix;
+	const char *s;
+	int d;
+
+	prefix = (*opt==0)? ' ':',';
+	va_start(ap, type);
+	switch (type) {
+	case KEY_FLG:
+		d = va_arg(ap, int);
+		archive_string_sprintf(info, "%c%s%s",
+		    prefix, (d == 0)?"!":"", key);
+		break;
+	case KEY_STR:
+		s = va_arg(ap, const char *);
+		archive_string_sprintf(info, "%c%s=%s",
+		    prefix, key, s);
+		break;
+	case KEY_INT:
+		d = va_arg(ap, int);
+		archive_string_sprintf(info, "%c%s=%d",
+		    prefix, key, d);
+		break;
+	case KEY_HEX:
+		d = va_arg(ap, int);
+		archive_string_sprintf(info, "%c%s=%x",
+		    prefix, key, d);
+		break;
+	}
+	va_end(ap);
+
+	*opt = 1;
+}
+
+/*
+ * Make Non-ISO File System Information
+ */
+static int
+write_information_block(struct archive_write *a)
+{
+	struct iso9660 *iso9660;
+	char buf[128];
+	const char *v;
+	int opt, r;
+	struct archive_string info;
+	size_t info_size = LOGICAL_BLOCK_SIZE *
+			       NON_ISO_FILE_SYSTEM_INFORMATION_BLOCK;
+
+	iso9660 = (struct iso9660 *)a->format_data;
+	if (info_size > wb_remaining(a)) {
+		r = wb_write_out(a);
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+	archive_string_init(&info);
+	if (archive_string_ensure(&info, info_size) == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	memset(info.s, 0, info_size);
+	opt = 0;
+#if defined(HAVE__CTIME64_S)
+	{
+		__time64_t iso9660_birth_time_tmp = (__time64_t) iso9660->birth_time; //time_t may be shorter than 64 bits
+		_ctime64_s(buf, sizeof(buf), &(iso9660_birth_time_tmp));
+	}
+#elif defined(HAVE_CTIME_R)
+	ctime_r(&(iso9660->birth_time), buf);
+#else
+	strncpy(buf, ctime(&(iso9660->birth_time)), sizeof(buf)-1);
+	buf[sizeof(buf)-1] = '\0';
+#endif
+	archive_string_sprintf(&info,
+	    "INFO %s%s", buf, archive_version_string());
+	if (iso9660->opt.abstract_file != OPT_ABSTRACT_FILE_DEFAULT)
+		set_option_info(&info, &opt, "abstract-file",
+		    KEY_STR, iso9660->abstract_file_identifier.s);
+	if (iso9660->opt.application_id != OPT_APPLICATION_ID_DEFAULT)
+		set_option_info(&info, &opt, "application-id",
+		    KEY_STR, iso9660->application_identifier.s);
+	if (iso9660->opt.allow_vernum != OPT_ALLOW_VERNUM_DEFAULT)
+		set_option_info(&info, &opt, "allow-vernum",
+		    KEY_FLG, iso9660->opt.allow_vernum);
+	if (iso9660->opt.biblio_file != OPT_BIBLIO_FILE_DEFAULT)
+		set_option_info(&info, &opt, "biblio-file",
+		    KEY_STR, iso9660->bibliographic_file_identifier.s);
+	if (iso9660->opt.boot != OPT_BOOT_DEFAULT)
+		set_option_info(&info, &opt, "boot",
+		    KEY_STR, iso9660->el_torito.boot_filename.s);
+	if (iso9660->opt.boot_catalog != OPT_BOOT_CATALOG_DEFAULT)
+		set_option_info(&info, &opt, "boot-catalog",
+		    KEY_STR, iso9660->el_torito.catalog_filename.s);
+	if (iso9660->opt.boot_info_table != OPT_BOOT_INFO_TABLE_DEFAULT)
+		set_option_info(&info, &opt, "boot-info-table",
+		    KEY_FLG, iso9660->opt.boot_info_table);
+	if (iso9660->opt.boot_load_seg != OPT_BOOT_LOAD_SEG_DEFAULT)
+		set_option_info(&info, &opt, "boot-load-seg",
+		    KEY_HEX, iso9660->el_torito.boot_load_seg);
+	if (iso9660->opt.boot_load_size != OPT_BOOT_LOAD_SIZE_DEFAULT)
+		set_option_info(&info, &opt, "boot-load-size",
+		    KEY_INT, iso9660->el_torito.boot_load_size);
+	if (iso9660->opt.boot_type != OPT_BOOT_TYPE_DEFAULT) {
+		v = "no-emulation";
+		if (iso9660->opt.boot_type == OPT_BOOT_TYPE_FD)
+			v = "fd";
+		if (iso9660->opt.boot_type == OPT_BOOT_TYPE_HARD_DISK)
+			v = "hard-disk";
+		set_option_info(&info, &opt, "boot-type",
+		    KEY_STR, v);
+	}
+#ifdef HAVE_ZLIB_H
+	if (iso9660->opt.compression_level != OPT_COMPRESSION_LEVEL_DEFAULT)
+		set_option_info(&info, &opt, "compression-level",
+		    KEY_INT, iso9660->zisofs.compression_level);
+#endif
+	if (iso9660->opt.copyright_file != OPT_COPYRIGHT_FILE_DEFAULT)
+		set_option_info(&info, &opt, "copyright-file",
+		    KEY_STR, iso9660->copyright_file_identifier.s);
+	if (iso9660->opt.iso_level != OPT_ISO_LEVEL_DEFAULT)
+		set_option_info(&info, &opt, "iso-level",
+		    KEY_INT, iso9660->opt.iso_level);
+	if (iso9660->opt.joliet != OPT_JOLIET_DEFAULT) {
+		if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)
+			set_option_info(&info, &opt, "joliet",
+			    KEY_STR, "long");
+		else
+			set_option_info(&info, &opt, "joliet",
+			    KEY_FLG, iso9660->opt.joliet);
+	}
+	if (iso9660->opt.limit_depth != OPT_LIMIT_DEPTH_DEFAULT)
+		set_option_info(&info, &opt, "limit-depth",
+		    KEY_FLG, iso9660->opt.limit_depth);
+	if (iso9660->opt.limit_dirs != OPT_LIMIT_DIRS_DEFAULT)
+		set_option_info(&info, &opt, "limit-dirs",
+		    KEY_FLG, iso9660->opt.limit_dirs);
+	if (iso9660->opt.pad != OPT_PAD_DEFAULT)
+		set_option_info(&info, &opt, "pad",
+		    KEY_FLG, iso9660->opt.pad);
+	if (iso9660->opt.publisher != OPT_PUBLISHER_DEFAULT)
+		set_option_info(&info, &opt, "publisher",
+		    KEY_STR, iso9660->publisher_identifier.s);
+	if (iso9660->opt.rr != OPT_RR_DEFAULT) {
+		if (iso9660->opt.rr == OPT_RR_DISABLED)
+			set_option_info(&info, &opt, "rockridge",
+			    KEY_FLG, iso9660->opt.rr);
+		else if (iso9660->opt.rr == OPT_RR_STRICT)
+			set_option_info(&info, &opt, "rockridge",
+			    KEY_STR, "strict");
+		else if (iso9660->opt.rr == OPT_RR_USEFUL)
+			set_option_info(&info, &opt, "rockridge",
+			    KEY_STR, "useful");
+	}
+	if (iso9660->opt.volume_id != OPT_VOLUME_ID_DEFAULT)
+		set_option_info(&info, &opt, "volume-id",
+		    KEY_STR, iso9660->volume_identifier.s);
+	if (iso9660->opt.zisofs != OPT_ZISOFS_DEFAULT)
+		set_option_info(&info, &opt, "zisofs",
+		    KEY_FLG, iso9660->opt.zisofs);
+
+	memcpy(wb_buffptr(a), info.s, info_size);
+	archive_string_free(&info);
+	return (wb_consume(a, info_size));
+}
+
+static int
+write_rr_ER(struct archive_write *a)
+{
+	unsigned char *p;
+
+	p = wb_buffptr(a);
+
+	memset(p, 0, LOGICAL_BLOCK_SIZE);
+	p[0] = 'E';
+	p[1] = 'R';
+	p[3] = 0x01;
+	p[2] = RRIP_ER_SIZE;
+	p[4] = RRIP_ER_ID_SIZE;
+	p[5] = RRIP_ER_DSC_SIZE;
+	p[6] = RRIP_ER_SRC_SIZE;
+	p[7] = 0x01;
+	memcpy(&p[8], rrip_identifier, p[4]);
+	memcpy(&p[8+p[4]], rrip_descriptor, p[5]);
+	memcpy(&p[8+p[4]+p[5]], rrip_source, p[6]);
+
+	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static void
+calculate_path_table_size(struct vdd *vdd)
+{
+	int depth, size;
+	struct path_table *pt;
+
+	pt = vdd->pathtbl;
+	size = 0;
+	for (depth = 0; depth < vdd->max_depth; depth++) {
+		struct isoent **ptbl;
+		int i, cnt;
+
+		if ((cnt = pt[depth].cnt) == 0)
+			break;
+
+		ptbl = pt[depth].sorted;
+		for (i = 0; i < cnt; i++) {
+			int len;
+
+			if (ptbl[i]->identifier == NULL)
+				len = 1; /* root directory */
+			else
+				len = ptbl[i]->id_len;
+			if (len & 0x01)
+				len++; /* Padding Field */
+			size += 8 + len;
+		}
+	}
+	vdd->path_table_size = size;
+	vdd->path_table_block =
+	    ((size + PATH_TABLE_BLOCK_SIZE -1) /
+	    PATH_TABLE_BLOCK_SIZE) *
+	    (PATH_TABLE_BLOCK_SIZE / LOGICAL_BLOCK_SIZE);
+}
+
+static int
+_write_path_table(struct archive_write *a, int type_m, int depth,
+    struct vdd *vdd)
+{
+	unsigned char *bp, *wb;
+	struct isoent **ptbl;
+	size_t wbremaining;
+	int i, r, wsize;
+
+	if (vdd->pathtbl[depth].cnt == 0)
+		return (0);
+
+	wsize = 0;
+	wb = wb_buffptr(a);
+	wbremaining = wb_remaining(a);
+	bp = wb - 1;
+	ptbl = vdd->pathtbl[depth].sorted;
+	for (i = 0; i < vdd->pathtbl[depth].cnt; i++) {
+		struct isoent *np;
+		size_t len;
+
+		np = ptbl[i];
+		if (np->identifier == NULL)
+			len = 1; /* root directory */
+		else
+			len = np->id_len;
+		if (wbremaining - ((bp+1) - wb) < (len + 1 + 8)) {
+			r = wb_consume(a, (bp+1) - wb);
+			if (r < 0)
+				return (r);
+			wb = wb_buffptr(a);
+			wbremaining = wb_remaining(a);
+			bp = wb -1;
+		}
+		/* Length of Directory Identifier */
+		set_num_711(bp+1, (unsigned char)len);
+		/* Extended Attribute Record Length */
+		set_num_711(bp+2, 0);
+		/* Location of Extent */
+		if (type_m)
+			set_num_732(bp+3, np->dir_location);
+		else
+			set_num_731(bp+3, np->dir_location);
+		/* Parent Directory Number */
+		if (type_m)
+			set_num_722(bp+7, np->parent->dir_number);
+		else
+			set_num_721(bp+7, np->parent->dir_number);
+		/* Directory Identifier */
+		if (np->identifier == NULL)
+			bp[9] = 0;
+		else
+			memcpy(&bp[9], np->identifier, len);
+		if (len & 0x01) {
+			/* Padding Field */
+			bp[9+len] = 0;
+			len++;
+		}
+		wsize += 8 + (int)len;
+		bp += 8 + len;
+	}
+	if ((bp + 1) > wb) {
+		r = wb_consume(a, (bp+1)-wb);
+		if (r < 0)
+			return (r);
+	}
+	return (wsize);
+}
+
+static int
+write_path_table(struct archive_write *a, int type_m, struct vdd *vdd)
+{
+	int depth, r;
+	size_t path_table_size;
+
+	r = ARCHIVE_OK;
+	path_table_size = 0;
+	for (depth = 0; depth < vdd->max_depth; depth++) {
+		r = _write_path_table(a, type_m, depth, vdd);
+		if (r < 0)
+			return (r);
+		path_table_size += r;
+	}
+
+	/* Write padding data. */
+	path_table_size = path_table_size % PATH_TABLE_BLOCK_SIZE;
+	if (path_table_size > 0)
+		r = write_null(a, PATH_TABLE_BLOCK_SIZE - path_table_size);
+	return (r);
+}
+
+static int
+calculate_directory_descriptors(struct iso9660 *iso9660, struct vdd *vdd,
+    struct isoent *isoent, int depth)
+{
+	struct isoent **enttbl;
+	int bs, block, i;
+
+	block = 1;
+	bs = get_dir_rec_size(iso9660, isoent, DIR_REC_SELF, vdd->vdd_type);
+	bs += get_dir_rec_size(iso9660, isoent, DIR_REC_PARENT, vdd->vdd_type);
+
+	if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET &&
+	    !iso9660->opt.rr && depth + 1 >= vdd->max_depth))
+		return (block);
+
+	enttbl = isoent->children_sorted;
+	for (i = 0; i < isoent->children.cnt; i++) {
+		struct isoent *np = enttbl[i];
+		struct isofile *file;
+
+		file = np->file;
+		if (file->hardlink_target != NULL)
+			file = file->hardlink_target;
+		file->cur_content = &(file->content);
+		do {
+			int dr_l;
+
+			dr_l = get_dir_rec_size(iso9660, np, DIR_REC_NORMAL,
+			    vdd->vdd_type);
+			if ((bs + dr_l) > LOGICAL_BLOCK_SIZE) {
+				block ++;
+				bs = dr_l;
+			} else
+				bs += dr_l;
+			file->cur_content = file->cur_content->next;
+		} while (file->cur_content != NULL);
+	}
+	return (block);
+}
+
+static int
+_write_directory_descriptors(struct archive_write *a, struct vdd *vdd,
+    struct isoent *isoent, int depth)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct isoent **enttbl;
+	unsigned char *p, *wb;
+	int i, r;
+	int dr_l;
+
+	p = wb = wb_buffptr(a);
+#define WD_REMAINING	(LOGICAL_BLOCK_SIZE - (p - wb))
+	p += set_directory_record(p, WD_REMAINING, isoent,
+	    iso9660, DIR_REC_SELF, vdd->vdd_type);
+	p += set_directory_record(p, WD_REMAINING, isoent,
+	    iso9660, DIR_REC_PARENT, vdd->vdd_type);
+
+	if (isoent->children.cnt <= 0 || (vdd->vdd_type != VDD_JOLIET &&
+	    !iso9660->opt.rr && depth + 1 >= vdd->max_depth)) {
+		memset(p, 0, WD_REMAINING);
+		return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+	}
+
+	enttbl = isoent->children_sorted;
+	for (i = 0; i < isoent->children.cnt; i++) {
+		struct isoent *np = enttbl[i];
+		struct isofile *file = np->file;
+
+		if (file->hardlink_target != NULL)
+			file = file->hardlink_target;
+		file->cur_content = &(file->content);
+		do {
+			dr_l = set_directory_record(p, WD_REMAINING,
+			    np, iso9660, DIR_REC_NORMAL,
+			    vdd->vdd_type);
+			if (dr_l == 0) {
+				memset(p, 0, WD_REMAINING);
+				r = wb_consume(a, LOGICAL_BLOCK_SIZE);
+				if (r < 0)
+					return (r);
+				p = wb = wb_buffptr(a);
+				dr_l = set_directory_record(p,
+				    WD_REMAINING, np, iso9660,
+				    DIR_REC_NORMAL, vdd->vdd_type);
+			}
+			p += dr_l;
+			file->cur_content = file->cur_content->next;
+		} while (file->cur_content != NULL);
+	}
+	memset(p, 0, WD_REMAINING);
+	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static int
+write_directory_descriptors(struct archive_write *a, struct vdd *vdd)
+{
+	struct isoent *np;
+	int depth, r;
+
+	depth = 0;
+	np = vdd->rootent;
+	do {
+		struct extr_rec *extr;
+
+		r = _write_directory_descriptors(a, vdd, np, depth);
+		if (r < 0)
+			return (r);
+		if (vdd->vdd_type != VDD_JOLIET) {
+			/*
+			 * This extract record is used by SUSP,RRIP.
+			 * Not for joliet.
+			 */
+			for (extr = np->extr_rec_list.first;
+			    extr != NULL;
+			    extr = extr->next) {
+				unsigned char *wb;
+
+				wb = wb_buffptr(a);
+				memcpy(wb, extr->buf, extr->offset);
+				memset(wb + extr->offset, 0,
+				    LOGICAL_BLOCK_SIZE - extr->offset);
+				r = wb_consume(a, LOGICAL_BLOCK_SIZE);
+				if (r < 0)
+					return (r);
+			}
+		}
+
+		if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
+			/* Enter to sub directories. */
+			np = np->subdirs.first;
+			depth++;
+			continue;
+		}
+		while (np != np->parent) {
+			if (np->drnext == NULL) {
+				/* Return to the parent directory. */
+				np = np->parent;
+				depth--;
+			} else {
+				np = np->drnext;
+				break;
+			}
+		}
+	} while (np != np->parent);
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Read file contents from the temporary file, and write it.
+ */
+static int
+write_file_contents(struct archive_write *a, int64_t offset, int64_t size)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	int r;
+
+	lseek(iso9660->temp_fd, offset, SEEK_SET);
+
+	while (size) {
+		size_t rsize;
+		ssize_t rs;
+		unsigned char *wb;
+
+		wb = wb_buffptr(a);
+		rsize = wb_remaining(a);
+		if (rsize > (size_t)size)
+			rsize = (size_t)size;
+		rs = read(iso9660->temp_fd, wb, rsize);
+		if (rs <= 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't read temporary file(%jd)", (intmax_t)rs);
+			return (ARCHIVE_FATAL);
+		}
+		size -= rs;
+		r = wb_consume(a, rs);
+		if (r < 0)
+			return (r);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+write_file_descriptors(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct isofile *file;
+	int64_t blocks, offset;
+	int r;
+
+	blocks = 0;
+	offset = 0;
+
+	/* Make the boot catalog contents, and write it. */
+	if (iso9660->el_torito.catalog != NULL) {
+		r = make_boot_catalog(a);
+		if (r < 0)
+			return (r);
+	}
+
+	/* Write the boot file contents. */
+	if (iso9660->el_torito.boot != NULL) {
+		file = iso9660->el_torito.boot->file;
+		blocks = file->content.blocks;
+		offset = file->content.offset_of_temp;
+		if (offset != 0) {
+			r = write_file_contents(a, offset,
+			    blocks << LOGICAL_BLOCK_BITS);
+			if (r < 0)
+				return (r);
+			blocks = 0;
+			offset = 0;
+		}
+	}
+
+	/* Write out all file contents. */
+	for (file = iso9660->data_file_list.first;
+	    file != NULL; file = file->datanext) {
+
+		if (!file->write_content)
+			continue;
+
+		if ((offset + (blocks << LOGICAL_BLOCK_BITS)) <
+		     file->content.offset_of_temp) {
+			if (blocks > 0) {
+				r = write_file_contents(a, offset,
+				    blocks << LOGICAL_BLOCK_BITS);
+				if (r < 0)
+					return (r);
+			}
+			blocks = 0;
+			offset = file->content.offset_of_temp;
+		}
+
+		file->cur_content = &(file->content);
+		do {
+			blocks += file->cur_content->blocks;
+			/* Next fragment */
+			file->cur_content = file->cur_content->next;
+		} while (file->cur_content != NULL);
+	}
+
+	/* Flush out remaining blocks. */
+	if (blocks > 0) {
+		r = write_file_contents(a, offset,
+		    blocks << LOGICAL_BLOCK_BITS);
+		if (r < 0)
+			return (r);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static void
+isofile_init_entry_list(struct iso9660 *iso9660)
+{
+	iso9660->all_file_list.first = NULL;
+	iso9660->all_file_list.last = &(iso9660->all_file_list.first);
+}
+
+static void
+isofile_add_entry(struct iso9660 *iso9660, struct isofile *file)
+{
+	file->allnext = NULL;
+	*iso9660->all_file_list.last = file;
+	iso9660->all_file_list.last = &(file->allnext);
+}
+
+static void
+isofile_free_all_entries(struct iso9660 *iso9660)
+{
+	struct isofile *file, *file_next;
+
+	file = iso9660->all_file_list.first;
+	while (file != NULL) {
+		file_next = file->allnext;
+		isofile_free(file);
+		file = file_next;
+	}
+}
+
+static void
+isofile_init_entry_data_file_list(struct iso9660 *iso9660)
+{
+	iso9660->data_file_list.first = NULL;
+	iso9660->data_file_list.last = &(iso9660->data_file_list.first);
+}
+
+static void
+isofile_add_data_file(struct iso9660 *iso9660, struct isofile *file)
+{
+	file->datanext = NULL;
+	*iso9660->data_file_list.last = file;
+	iso9660->data_file_list.last = &(file->datanext);
+}
+
+
+static struct isofile *
+isofile_new(struct archive_write *a, struct archive_entry *entry)
+{
+	struct isofile *file;
+
+	file = calloc(1, sizeof(*file));
+	if (file == NULL)
+		return (NULL);
+
+	if (entry != NULL)
+		file->entry = archive_entry_clone(entry);
+	else
+		file->entry = archive_entry_new2(&a->archive);
+	if (file->entry == NULL) {
+		free(file);
+		return (NULL);
+	}
+	archive_string_init(&(file->parentdir));
+	archive_string_init(&(file->basename));
+	archive_string_init(&(file->basename_utf16));
+	archive_string_init(&(file->symlink));
+	file->cur_content = &(file->content);
+
+	return (file);
+}
+
+static void
+isofile_free(struct isofile *file)
+{
+	struct content *con, *tmp;
+
+	con = file->content.next;
+	while (con != NULL) {
+		tmp = con;
+		con = con->next;
+		free(tmp);
+	}
+	archive_entry_free(file->entry);
+	archive_string_free(&(file->parentdir));
+	archive_string_free(&(file->basename));
+	archive_string_free(&(file->basename_utf16));
+	archive_string_free(&(file->symlink));
+	free(file);
+}
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+static int
+cleanup_backslash_1(char *p)
+{
+	int mb, dos;
+
+	mb = dos = 0;
+	while (*p) {
+		if (*(unsigned char *)p > 127)
+			mb = 1;
+		if (*p == '\\') {
+			/* If we have not met any multi-byte characters,
+			 * we can replace '\' with '/'. */
+			if (!mb)
+				*p = '/';
+			dos = 1;
+		}
+		p++;
+	}
+	if (!mb || !dos)
+		return (0);
+	return (-1);
+}
+
+static void
+cleanup_backslash_2(wchar_t *p)
+{
+
+	/* Convert a path-separator from '\' to  '/' */
+	while (*p != L'\0') {
+		if (*p == L'\\')
+			*p = L'/';
+		p++;
+	}
+}
+#endif
+
+/*
+ * Generate a parent directory name and a base name from a pathname.
+ */
+static int
+isofile_gen_utility_names(struct archive_write *a, struct isofile *file)
+{
+	struct iso9660 *iso9660;
+	const char *pathname;
+	char *p, *dirname, *slash;
+	size_t len;
+	int ret = ARCHIVE_OK;
+
+	iso9660 = a->format_data;
+
+	archive_string_empty(&(file->parentdir));
+	archive_string_empty(&(file->basename));
+	archive_string_empty(&(file->basename_utf16));
+	archive_string_empty(&(file->symlink));
+
+	pathname =  archive_entry_pathname(file->entry);
+	if (pathname == NULL || pathname[0] == '\0') {/* virtual root */
+		file->dircnt = 0;
+		return (ret);
+	}
+
+	/*
+	 * Make a UTF-16BE basename if Joliet extension enabled.
+	 */
+	if (iso9660->opt.joliet) {
+		const char *u16, *ulast;
+		size_t u16len, ulen_last;
+
+		if (iso9660->sconv_to_utf16be == NULL) {
+			iso9660->sconv_to_utf16be =
+			    archive_string_conversion_to_charset(
+				&(a->archive), "UTF-16BE", 1);
+			if (iso9660->sconv_to_utf16be == NULL)
+				/* Couldn't allocate memory */
+				return (ARCHIVE_FATAL);
+			iso9660->sconv_from_utf16be =
+			    archive_string_conversion_from_charset(
+				&(a->archive), "UTF-16BE", 1);
+			if (iso9660->sconv_from_utf16be == NULL)
+				/* Couldn't allocate memory */
+				return (ARCHIVE_FATAL);
+		}
+
+		/*
+		 * Convert a filename to UTF-16BE.
+		 */
+		if (0 > archive_entry_pathname_l(file->entry, &u16, &u16len,
+		    iso9660->sconv_to_utf16be)) {
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for UTF-16BE");
+				return (ARCHIVE_FATAL);
+			}
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "A filename cannot be converted to UTF-16BE;"
+			    "You should disable making Joliet extension");
+			ret = ARCHIVE_WARN;
+		}
+
+		/*
+		 * Make sure a path separator is not in the last;
+		 * Remove trailing '/'.
+		 */
+		while (u16len >= 2) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+			if (u16[u16len-2] == 0 &&
+			    (u16[u16len-1] == '/' || u16[u16len-1] == '\\'))
+#else
+			if (u16[u16len-2] == 0 && u16[u16len-1] == '/')
+#endif
+			{
+				u16len -= 2;
+			} else
+				break;
+		}
+
+		/*
+		 * Find a basename in UTF-16BE.
+		 */
+		ulast = u16;
+		u16len >>= 1;
+		ulen_last = u16len;
+		while (u16len > 0) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+			if (u16[0] == 0 && (u16[1] == '/' || u16[1] == '\\'))
+#else
+			if (u16[0] == 0 && u16[1] == '/')
+#endif
+			{
+				ulast = u16 + 2;
+				ulen_last = u16len -1;
+			}
+			u16 += 2;
+			u16len --;
+		}
+		ulen_last <<= 1;
+		if (archive_string_ensure(&(file->basename_utf16),
+		    ulen_last) == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for UTF-16BE");
+			return (ARCHIVE_FATAL);
+		}
+
+		/*
+		 * Set UTF-16BE basename.
+		 */
+		memcpy(file->basename_utf16.s, ulast, ulen_last);
+		file->basename_utf16.length = ulen_last;
+	}
+
+	archive_strcpy(&(file->parentdir), pathname);
+#if defined(_WIN32) || defined(__CYGWIN__)
+	/*
+	 * Convert a path-separator from '\' to  '/'
+	 */
+	if (cleanup_backslash_1(file->parentdir.s) != 0) {
+		const wchar_t *wp = archive_entry_pathname_w(file->entry);
+		struct archive_wstring ws;
+
+		if (wp != NULL) {
+			int r;
+			archive_string_init(&ws);
+			archive_wstrcpy(&ws, wp);
+			cleanup_backslash_2(ws.s);
+			archive_string_empty(&(file->parentdir));
+			r = archive_string_append_from_wcs(&(file->parentdir),
+			    ws.s, ws.length);
+			archive_wstring_free(&ws);
+			if (r < 0 && errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory");
+				return (ARCHIVE_FATAL);
+			}
+		}
+	}
+#endif
+
+	len = file->parentdir.length;
+	p = dirname = file->parentdir.s;
+
+	/*
+	 * Remove leading '/', '../' and './' elements
+	 */
+	while (*p) {
+		if (p[0] == '/') {
+			p++;
+			len--;
+		} else if (p[0] != '.')
+			break;
+		else if (p[1] == '.' && p[2] == '/') {
+			p += 3;
+			len -= 3;
+		} else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {
+			p += 2;
+			len -= 2;
+		} else if (p[1] == '\0') {
+			p++;
+			len--;
+		} else
+			break;
+	}
+	if (p != dirname) {
+		memmove(dirname, p, len+1);
+		p = dirname;
+	}
+	/*
+	 * Remove "/","/." and "/.." elements from tail.
+	 */
+	while (len > 0) {
+		size_t ll = len;
+
+		if (len > 0 && p[len-1] == '/') {
+			p[len-1] = '\0';
+			len--;
+		}
+		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
+			p[len-2] = '\0';
+			len -= 2;
+		}
+		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
+		    p[len-1] == '.') {
+			p[len-3] = '\0';
+			len -= 3;
+		}
+		if (ll == len)
+			break;
+	}
+	while (*p) {
+		if (p[0] == '/') {
+			if (p[1] == '/')
+				/* Convert '//' --> '/' */
+				memmove(p, p+1, strlen(p+1) + 1);
+			else if (p[1] == '.' && p[2] == '/')
+				/* Convert '/./' --> '/' */
+				memmove(p, p+2, strlen(p+2) + 1);
+			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
+				/* Convert 'dir/dir1/../dir2/'
+				 *     --> 'dir/dir2/'
+				 */
+				char *rp = p -1;
+				while (rp >= dirname) {
+					if (*rp == '/')
+						break;
+					--rp;
+				}
+				if (rp > dirname) {
+					strcpy(rp, p+3);
+					p = rp;
+				} else {
+					strcpy(dirname, p+4);
+					p = dirname;
+				}
+			} else
+				p++;
+		} else
+			p++;
+	}
+	p = dirname;
+	len = strlen(p);
+
+	if (archive_entry_filetype(file->entry) == AE_IFLNK) {
+		/* Convert symlink name too. */
+		pathname = archive_entry_symlink(file->entry);
+		archive_strcpy(&(file->symlink),  pathname);
+#if defined(_WIN32) || defined(__CYGWIN__)
+		/*
+		 * Convert a path-separator from '\' to  '/'
+		 */
+		if (archive_strlen(&(file->symlink)) > 0 &&
+		    cleanup_backslash_1(file->symlink.s) != 0) {
+			const wchar_t *wp =
+			    archive_entry_symlink_w(file->entry);
+			struct archive_wstring ws;
+
+			if (wp != NULL) {
+				int r;
+				archive_string_init(&ws);
+				archive_wstrcpy(&ws, wp);
+				cleanup_backslash_2(ws.s);
+				archive_string_empty(&(file->symlink));
+				r = archive_string_append_from_wcs(
+				    &(file->symlink),
+				    ws.s, ws.length);
+				archive_wstring_free(&ws);
+				if (r < 0 && errno == ENOMEM) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "Can't allocate memory");
+					return (ARCHIVE_FATAL);
+				}
+			}
+		}
+#endif
+	}
+	/*
+	 * - Count up directory elements.
+	 * - Find out the position which points the last position of
+	 *   path separator('/').
+	 */
+	slash = NULL;
+	file->dircnt = 0;
+	for (; *p != '\0'; p++)
+		if (*p == '/') {
+			slash = p;
+			file->dircnt++;
+		}
+	if (slash == NULL) {
+		/* The pathname doesn't have a parent directory. */
+		file->parentdir.length = len;
+		archive_string_copy(&(file->basename), &(file->parentdir));
+		archive_string_empty(&(file->parentdir));
+		*file->parentdir.s = '\0';
+		return (ret);
+	}
+
+	/* Make a basename from dirname and slash */
+	*slash  = '\0';
+	file->parentdir.length = slash - dirname;
+	archive_strcpy(&(file->basename),  slash + 1);
+	if (archive_entry_filetype(file->entry) == AE_IFDIR)
+		file->dircnt ++;
+	return (ret);
+}
+
+/*
+ * Register a entry to get a hardlink target.
+ */
+static int
+isofile_register_hardlink(struct archive_write *a, struct isofile *file)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct hardlink *hl;
+	const char *pathname;
+
+	archive_entry_set_nlink(file->entry, 1);
+	pathname = archive_entry_hardlink(file->entry);
+	if (pathname == NULL) {
+		/* This `file` is a hardlink target. */
+		hl = malloc(sizeof(*hl));
+		if (hl == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+			return (ARCHIVE_FATAL);
+		}
+		hl->nlink = 1;
+		/* A hardlink target must be the first position. */
+		file->hlnext = NULL;
+		hl->file_list.first = file;
+		hl->file_list.last = &(file->hlnext);
+		__archive_rb_tree_insert_node(&(iso9660->hardlink_rbtree),
+		    (struct archive_rb_node *)hl);
+	} else {
+		hl = (struct hardlink *)__archive_rb_tree_find_node(
+		    &(iso9660->hardlink_rbtree), pathname);
+		if (hl != NULL) {
+			/* Insert `file` entry into the tail. */
+			file->hlnext = NULL;
+			*hl->file_list.last = file;
+			hl->file_list.last = &(file->hlnext);
+			hl->nlink++;
+		}
+		archive_entry_unset_size(file->entry);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Hardlinked files have to have the same location of extent.
+ * We have to find out hardlink target entries for the entries
+ * which have a hardlink target name.
+ */
+static void
+isofile_connect_hardlink_files(struct iso9660 *iso9660)
+{
+	struct archive_rb_node *n;
+	struct hardlink *hl;
+	struct isofile *target, *nf;
+
+	ARCHIVE_RB_TREE_FOREACH(n, &(iso9660->hardlink_rbtree)) {
+		hl = (struct hardlink *)n;
+
+		/* The first entry must be a hardlink target. */
+		target = hl->file_list.first;
+		archive_entry_set_nlink(target->entry, hl->nlink);
+		/* Set a hardlink target to reference entries. */
+		for (nf = target->hlnext;
+		    nf != NULL; nf = nf->hlnext) {
+			nf->hardlink_target = target;
+			archive_entry_set_nlink(nf->entry, hl->nlink);
+		}
+	}
+}
+
+static int
+isofile_hd_cmp_node(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	const struct hardlink *h1 = (const struct hardlink *)n1;
+	const struct hardlink *h2 = (const struct hardlink *)n2;
+
+	return (strcmp(archive_entry_pathname(h1->file_list.first->entry),
+		       archive_entry_pathname(h2->file_list.first->entry)));
+}
+
+static int
+isofile_hd_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+	const struct hardlink *h = (const struct hardlink *)n;
+
+	return (strcmp(archive_entry_pathname(h->file_list.first->entry),
+		       (const char *)key));
+}
+
+static void
+isofile_init_hardlinks(struct iso9660 *iso9660)
+{
+	static const struct archive_rb_tree_ops rb_ops = {
+		isofile_hd_cmp_node, isofile_hd_cmp_key,
+	};
+
+	__archive_rb_tree_init(&(iso9660->hardlink_rbtree), &rb_ops);
+}
+
+static void
+isofile_free_hardlinks(struct iso9660 *iso9660)
+{
+	struct archive_rb_node *n, *tmp;
+
+	ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(iso9660->hardlink_rbtree), tmp) {
+		__archive_rb_tree_remove_node(&(iso9660->hardlink_rbtree), n);
+		free(n);
+	}
+}
+
+static struct isoent *
+isoent_new(struct isofile *file)
+{
+	struct isoent *isoent;
+	static const struct archive_rb_tree_ops rb_ops = {
+		isoent_cmp_node, isoent_cmp_key,
+	};
+
+	isoent = calloc(1, sizeof(*isoent));
+	if (isoent == NULL)
+		return (NULL);
+	isoent->file = file;
+	isoent->children.first = NULL;
+	isoent->children.last = &(isoent->children.first);
+	__archive_rb_tree_init(&(isoent->rbtree), &rb_ops);
+	isoent->subdirs.first = NULL;
+	isoent->subdirs.last = &(isoent->subdirs.first);
+	isoent->extr_rec_list.first = NULL;
+	isoent->extr_rec_list.last = &(isoent->extr_rec_list.first);
+	isoent->extr_rec_list.current = NULL;
+	if (archive_entry_filetype(file->entry) == AE_IFDIR)
+		isoent->dir = 1;
+
+	return (isoent);
+}
+
+static inline struct isoent *
+isoent_clone(struct isoent *src)
+{
+	return (isoent_new(src->file));
+}
+
+static void
+_isoent_free(struct isoent *isoent)
+{
+	struct extr_rec *er, *er_next;
+
+	free(isoent->children_sorted);
+	free(isoent->identifier);
+	er = isoent->extr_rec_list.first;
+	while (er != NULL) {
+		er_next = er->next;
+		free(er);
+		er = er_next;
+	}
+	free(isoent);
+}
+
+static void
+isoent_free_all(struct isoent *isoent)
+{
+	struct isoent *np, *np_temp;
+
+	if (isoent == NULL)
+		return;
+	np = isoent;
+	for (;;) {
+		if (np->dir) {
+			if (np->children.first != NULL) {
+				/* Enter to sub directories. */
+				np = np->children.first;
+				continue;
+			}
+		}
+		for (;;) {
+			np_temp = np;
+			if (np->chnext == NULL) {
+				/* Return to the parent directory. */
+				np = np->parent;
+				_isoent_free(np_temp);
+				if (np == np_temp)
+					return;
+			} else {
+				np = np->chnext;
+				_isoent_free(np_temp);
+				break;
+			}
+		}
+	}
+}
+
+static struct isoent *
+isoent_create_virtual_dir(struct archive_write *a, struct iso9660 *iso9660, const char *pathname)
+{
+	struct isofile *file;
+	struct isoent *isoent;
+
+	file = isofile_new(a, NULL);
+	if (file == NULL)
+		return (NULL);
+	archive_entry_set_pathname(file->entry, pathname);
+	archive_entry_unset_mtime(file->entry);
+	archive_entry_unset_atime(file->entry);
+	archive_entry_unset_ctime(file->entry);
+	archive_entry_set_uid(file->entry, getuid());
+	archive_entry_set_gid(file->entry, getgid());
+	archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);
+	archive_entry_set_nlink(file->entry, 2);
+	if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) {
+		isofile_free(file);
+		return (NULL);
+	}
+	isofile_add_entry(iso9660, file);
+
+	isoent = isoent_new(file);
+	if (isoent == NULL)
+		return (NULL);
+	isoent->dir = 1;
+	isoent->virtual = 1;
+
+	return (isoent);
+}
+
+static int
+isoent_cmp_node(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	const struct isoent *e1 = (const struct isoent *)n1;
+	const struct isoent *e2 = (const struct isoent *)n2;
+
+	return (strcmp(e1->file->basename.s, e2->file->basename.s));
+}
+
+static int
+isoent_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+	const struct isoent *e = (const struct isoent *)n;
+
+	return (strcmp(e->file->basename.s, (const char *)key));
+}
+
+static int
+isoent_add_child_head(struct isoent *parent, struct isoent *child)
+{
+
+	if (!__archive_rb_tree_insert_node(
+	    &(parent->rbtree), (struct archive_rb_node *)child))
+		return (0);
+	if ((child->chnext = parent->children.first) == NULL)
+		parent->children.last = &(child->chnext);
+	parent->children.first = child;
+	parent->children.cnt++;
+	child->parent = parent;
+
+	/* Add a child to a sub-directory chain */
+	if (child->dir) {
+		if ((child->drnext = parent->subdirs.first) == NULL)
+			parent->subdirs.last = &(child->drnext);
+		parent->subdirs.first = child;
+		parent->subdirs.cnt++;
+		child->parent = parent;
+	} else
+		child->drnext = NULL;
+	return (1);
+}
+
+static int
+isoent_add_child_tail(struct isoent *parent, struct isoent *child)
+{
+
+	if (!__archive_rb_tree_insert_node(
+	    &(parent->rbtree), (struct archive_rb_node *)child))
+		return (0);
+	child->chnext = NULL;
+	*parent->children.last = child;
+	parent->children.last = &(child->chnext);
+	parent->children.cnt++;
+	child->parent = parent;
+
+	/* Add a child to a sub-directory chain */
+	child->drnext = NULL;
+	if (child->dir) {
+		*parent->subdirs.last = child;
+		parent->subdirs.last = &(child->drnext);
+		parent->subdirs.cnt++;
+		child->parent = parent;
+	}
+	return (1);
+}
+
+static void
+isoent_remove_child(struct isoent *parent, struct isoent *child)
+{
+	struct isoent *ent;
+
+	/* Remove a child entry from children chain. */
+	ent = parent->children.first;
+	while (ent->chnext != child)
+		ent = ent->chnext;
+	if ((ent->chnext = ent->chnext->chnext) == NULL)
+		parent->children.last = &(ent->chnext);
+	parent->children.cnt--;
+
+	if (child->dir) {
+		/* Remove a child entry from sub-directory chain. */
+		ent = parent->subdirs.first;
+		while (ent->drnext != child)
+			ent = ent->drnext;
+		if ((ent->drnext = ent->drnext->drnext) == NULL)
+			parent->subdirs.last = &(ent->drnext);
+		parent->subdirs.cnt--;
+	}
+
+	__archive_rb_tree_remove_node(&(parent->rbtree),
+	    (struct archive_rb_node *)child);
+}
+
+static int
+isoent_clone_tree(struct archive_write *a, struct isoent **nroot,
+    struct isoent *root)
+{
+	struct isoent *np, *xroot, *newent;
+
+	np = root;
+	xroot = NULL;
+	do {
+		newent = isoent_clone(np);
+		if (newent == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+			return (ARCHIVE_FATAL);
+		}
+		if (xroot == NULL) {
+			*nroot = xroot = newent;
+			newent->parent = xroot;
+		} else
+			isoent_add_child_tail(xroot, newent);
+		if (np->dir && np->children.first != NULL) {
+			/* Enter to sub directories. */
+			np = np->children.first;
+			xroot = newent;
+			continue;
+		}
+		while (np != np->parent) {
+			if (np->chnext == NULL) {
+				/* Return to the parent directory. */
+				np = np->parent;
+				xroot = xroot->parent;
+			} else {
+				np = np->chnext;
+				break;
+			}
+		}
+	} while (np != np->parent);
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Setup directory locations.
+ */
+static void
+isoent_setup_directory_location(struct iso9660 *iso9660, int location,
+    struct vdd *vdd)
+{
+	struct isoent *np;
+	int depth;
+
+	vdd->total_dir_block = 0;
+	depth = 0;
+	np = vdd->rootent;
+	do {
+		int block;
+
+		np->dir_block = calculate_directory_descriptors(
+		    iso9660, vdd, np, depth);
+		vdd->total_dir_block += np->dir_block;
+		np->dir_location = location;
+		location += np->dir_block;
+		block = extra_setup_location(np, location);
+		vdd->total_dir_block += block;
+		location += block;
+
+		if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
+			/* Enter to sub directories. */
+			np = np->subdirs.first;
+			depth++;
+			continue;
+		}
+		while (np != np->parent) {
+			if (np->drnext == NULL) {
+				/* Return to the parent directory. */
+				np = np->parent;
+				depth--;
+			} else {
+				np = np->drnext;
+				break;
+			}
+		}
+	} while (np != np->parent);
+}
+
+static void
+_isoent_file_location(struct iso9660 *iso9660, struct isoent *isoent,
+    int *symlocation)
+{
+	struct isoent **children;
+	int n;
+
+	if (isoent->children.cnt == 0)
+		return;
+
+	children = isoent->children_sorted;
+	for (n = 0; n < isoent->children.cnt; n++) {
+		struct isoent *np;
+		struct isofile *file;
+
+		np = children[n];
+		if (np->dir)
+			continue;
+		if (np == iso9660->el_torito.boot)
+			continue;
+		file = np->file;
+		if (file->boot || file->hardlink_target != NULL)
+			continue;
+		if (archive_entry_filetype(file->entry) == AE_IFLNK ||
+		    file->content.size == 0) {
+			/*
+			 * Do not point a valid location.
+			 * Make sure entry is not hardlink file.
+			 */
+			file->content.location = (*symlocation)--;
+			continue;
+		}
+
+		file->write_content = 1;
+	}
+}
+
+/*
+ * Setup file locations.
+ */
+static void
+isoent_setup_file_location(struct iso9660 *iso9660, int location)
+{
+	struct isoent *isoent;
+	struct isoent *np;
+	struct isofile *file;
+	size_t size;
+	int block;
+	int depth;
+	int joliet;
+	int symlocation;
+	int total_block;
+
+	iso9660->total_file_block = 0;
+	if ((isoent = iso9660->el_torito.catalog) != NULL) {
+		isoent->file->content.location = location;
+		block = (int)((archive_entry_size(isoent->file->entry) +
+		    LOGICAL_BLOCK_SIZE -1) >> LOGICAL_BLOCK_BITS);
+		location += block;
+		iso9660->total_file_block += block;
+	}
+	if ((isoent = iso9660->el_torito.boot) != NULL) {
+		isoent->file->content.location = location;
+		size = fd_boot_image_size(iso9660->el_torito.media_type);
+		if (size == 0)
+			size = (size_t)archive_entry_size(isoent->file->entry);
+		block = ((int)size + LOGICAL_BLOCK_SIZE -1)
+		    >> LOGICAL_BLOCK_BITS;
+		location += block;
+		iso9660->total_file_block += block;
+		isoent->file->content.blocks = block;
+	}
+
+	depth = 0;
+	symlocation = -16;
+	if (!iso9660->opt.rr && iso9660->opt.joliet) {
+		joliet = 1;
+		np = iso9660->joliet.rootent;
+	} else {
+		joliet = 0;
+		np = iso9660->primary.rootent;
+	}
+	do {
+		_isoent_file_location(iso9660, np, &symlocation);
+
+		if (np->subdirs.first != NULL &&
+		    (joliet ||
+		    ((iso9660->opt.rr == OPT_RR_DISABLED &&
+		      depth + 2 < iso9660->primary.max_depth) ||
+		     (iso9660->opt.rr &&
+		      depth + 1 < iso9660->primary.max_depth)))) {
+			/* Enter to sub directories. */
+			np = np->subdirs.first;
+			depth++;
+			continue;
+		}
+		while (np != np->parent) {
+			if (np->drnext == NULL) {
+				/* Return to the parent directory. */
+				np = np->parent;
+				depth--;
+			} else {
+				np = np->drnext;
+				break;
+			}
+		}
+	} while (np != np->parent);
+
+	total_block = 0;
+	for (file = iso9660->data_file_list.first;
+	    file != NULL; file = file->datanext) {
+
+		if (!file->write_content)
+			continue;
+
+		file->cur_content = &(file->content);
+		do {
+			file->cur_content->location = location;
+			location += file->cur_content->blocks;
+			total_block += file->cur_content->blocks;
+			/* Next fragment */
+			file->cur_content = file->cur_content->next;
+		} while (file->cur_content != NULL);
+	}
+	iso9660->total_file_block += total_block;
+}
+
+static int
+get_path_component(char *name, size_t n, const char *fn)
+{
+	char *p;
+	size_t l;
+
+	p = strchr(fn, '/');
+	if (p == NULL) {
+		if ((l = strlen(fn)) == 0)
+			return (0);
+	} else
+		l = p - fn;
+	if (l > n -1)
+		return (-1);
+	memcpy(name, fn, l);
+	name[l] = '\0';
+
+	return ((int)l);
+}
+
+/*
+ * Add a new entry into the tree.
+ */
+static int
+isoent_tree(struct archive_write *a, struct isoent **isoentpp)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+	char name[NAME_MAX+1];
+#else
+	char name[256];
+#endif
+	struct iso9660 *iso9660 = a->format_data;
+	struct isoent *dent, *isoent, *np;
+	struct isofile *f1, *f2;
+	const char *fn, *p;
+	int l;
+
+	isoent = *isoentpp;
+	dent = iso9660->primary.rootent;
+	if (isoent->file->parentdir.length > 0)
+		fn = p = isoent->file->parentdir.s;
+	else
+		fn = p = "";
+
+	/*
+	 * If the path of the parent directory of `isoent' entry is
+	 * the same as the path of `cur_dirent', add isoent to
+	 * `cur_dirent'.
+	 */
+	if (archive_strlen(&(iso9660->cur_dirstr))
+	      == archive_strlen(&(isoent->file->parentdir)) &&
+	    strcmp(iso9660->cur_dirstr.s, fn) == 0) {
+		if (!isoent_add_child_tail(iso9660->cur_dirent, isoent)) {
+			np = (struct isoent *)__archive_rb_tree_find_node(
+			    &(iso9660->cur_dirent->rbtree),
+			    isoent->file->basename.s);
+			goto same_entry;
+		}
+		return (ARCHIVE_OK);
+	}
+
+	for (;;) {
+		l = get_path_component(name, sizeof(name), fn);
+		if (l == 0) {
+			np = NULL;
+			break;
+		}
+		if (l < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "A name buffer is too small");
+			_isoent_free(isoent);
+			return (ARCHIVE_FATAL);
+		}
+
+		np = isoent_find_child(dent, name);
+		if (np == NULL || fn[0] == '\0')
+			break;
+
+		/* Find next subdirectory. */
+		if (!np->dir) {
+			/* NOT Directory! */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "`%s' is not directory, we cannot insert `%s' ",
+			    archive_entry_pathname(np->file->entry),
+			    archive_entry_pathname(isoent->file->entry));
+			_isoent_free(isoent);
+			*isoentpp = NULL;
+			return (ARCHIVE_FAILED);
+		}
+		fn += l;
+		if (fn[0] == '/')
+			fn++;
+		dent = np;
+	}
+	if (np == NULL) {
+		/*
+		 * Create virtual parent directories.
+		 */
+		while (fn[0] != '\0') {
+			struct isoent *vp;
+			struct archive_string as;
+
+			archive_string_init(&as);
+			archive_strncat(&as, p, fn - p + l);
+			if (as.s[as.length-1] == '/') {
+				as.s[as.length-1] = '\0';
+				as.length--;
+			}
+			vp = isoent_create_virtual_dir(a, iso9660, as.s);
+			if (vp == NULL) {
+				archive_string_free(&as);
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory");
+				_isoent_free(isoent);
+				*isoentpp = NULL;
+				return (ARCHIVE_FATAL);
+			}
+			archive_string_free(&as);
+
+			if (vp->file->dircnt > iso9660->dircnt_max)
+				iso9660->dircnt_max = vp->file->dircnt;
+			isoent_add_child_tail(dent, vp);
+			np = vp;
+
+			fn += l;
+			if (fn[0] == '/')
+				fn++;
+			l = get_path_component(name, sizeof(name), fn);
+			if (l < 0) {
+				archive_string_free(&as);
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "A name buffer is too small");
+				_isoent_free(isoent);
+				*isoentpp = NULL;
+				return (ARCHIVE_FATAL);
+			}
+			dent = np;
+		}
+
+		/* Found out the parent directory where isoent can be
+		 * inserted. */
+		iso9660->cur_dirent = dent;
+		archive_string_empty(&(iso9660->cur_dirstr));
+		archive_string_ensure(&(iso9660->cur_dirstr),
+		    archive_strlen(&(dent->file->parentdir)) +
+		    archive_strlen(&(dent->file->basename)) + 2);
+		if (archive_strlen(&(dent->file->parentdir)) +
+		    archive_strlen(&(dent->file->basename)) == 0)
+			iso9660->cur_dirstr.s[0] = 0;
+		else {
+			if (archive_strlen(&(dent->file->parentdir)) > 0) {
+				archive_string_copy(&(iso9660->cur_dirstr),
+				    &(dent->file->parentdir));
+				archive_strappend_char(&(iso9660->cur_dirstr), '/');
+			}
+			archive_string_concat(&(iso9660->cur_dirstr),
+			    &(dent->file->basename));
+		}
+
+		if (!isoent_add_child_tail(dent, isoent)) {
+			np = (struct isoent *)__archive_rb_tree_find_node(
+			    &(dent->rbtree), isoent->file->basename.s);
+			goto same_entry;
+		}
+		return (ARCHIVE_OK);
+	}
+
+same_entry:
+	/*
+	 * We have already has the entry the filename of which is
+	 * the same.
+	 */
+	f1 = np->file;
+	f2 = isoent->file;
+
+	/* If the file type of entries is different,
+	 * we cannot handle it. */
+	if (archive_entry_filetype(f1->entry) !=
+	    archive_entry_filetype(f2->entry)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Found duplicate entries `%s' and its file type is "
+		    "different",
+		    archive_entry_pathname(f1->entry));
+		_isoent_free(isoent);
+		*isoentpp = NULL;
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Swap file entries. */
+	np->file = f2;
+	isoent->file = f1;
+	np->virtual = 0;
+
+	_isoent_free(isoent);
+	*isoentpp = np;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Find a entry from `isoent'
+ */
+static struct isoent *
+isoent_find_child(struct isoent *isoent, const char *child_name)
+{
+	struct isoent *np;
+
+	np = (struct isoent *)__archive_rb_tree_find_node(
+	    &(isoent->rbtree), child_name);
+	return (np);
+}
+
+/*
+ * Find a entry full-path of which is specified by `fn' parameter,
+ * in the tree.
+ */
+static struct isoent *
+isoent_find_entry(struct isoent *rootent, const char *fn)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+	char name[NAME_MAX+1];
+#else
+	char name[256];
+#endif
+	struct isoent *isoent, *np;
+	int l;
+
+	isoent = rootent;
+	np = NULL;
+	for (;;) {
+		l = get_path_component(name, sizeof(name), fn);
+		if (l == 0)
+			break;
+		fn += l;
+		if (fn[0] == '/')
+			fn++;
+
+		np = isoent_find_child(isoent, name);
+		if (np == NULL)
+			break;
+		if (fn[0] == '\0')
+			break;/* We found out the entry */
+
+		/* Try sub directory. */
+		isoent = np;
+		np = NULL;
+		if (!isoent->dir)
+			break;/* Not directory */
+	}
+
+	return (np);
+}
+
+/*
+ * Following idr_* functions are used for resolving duplicated filenames
+ * and unreceivable filenames to generate ISO9660/Joliet Identifiers.
+ */
+
+static void
+idr_relaxed_filenames(char *map)
+{
+	int i;
+
+	for (i = 0x21; i <= 0x2F; i++)
+		map[i] = 1;
+	for (i = 0x3A; i <= 0x41; i++)
+		map[i] = 1;
+	for (i = 0x5B; i <= 0x5E; i++)
+		map[i] = 1;
+	map[0x60] = 1;
+	for (i = 0x7B; i <= 0x7E; i++)
+		map[i] = 1;
+}
+
+static void
+idr_init(struct iso9660 *iso9660, struct vdd *vdd, struct idr *idr)
+{
+
+	idr->idrent_pool = NULL;
+	idr->pool_size = 0;
+	if (vdd->vdd_type != VDD_JOLIET) {
+		if (iso9660->opt.iso_level <= 3) {
+			memcpy(idr->char_map, d_characters_map,
+			    sizeof(idr->char_map));
+		} else {
+			memcpy(idr->char_map, d1_characters_map,
+			    sizeof(idr->char_map));
+			idr_relaxed_filenames(idr->char_map);
+		}
+	}
+}
+
+static void
+idr_cleanup(struct idr *idr)
+{
+	free(idr->idrent_pool);
+}
+
+static int
+idr_ensure_poolsize(struct archive_write *a, struct idr *idr,
+    int cnt)
+{
+
+	if (idr->pool_size < cnt) {
+		void *p;
+		const int bk = (1 << 7) - 1;
+		int psize;
+
+		psize = (cnt + bk) & ~bk;
+		p = realloc(idr->idrent_pool, sizeof(struct idrent) * psize);
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+			return (ARCHIVE_FATAL);
+		}
+		idr->idrent_pool = (struct idrent *)p;
+		idr->pool_size = psize;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+idr_start(struct archive_write *a, struct idr *idr, int cnt, int ffmax,
+    int num_size, int null_size, const struct archive_rb_tree_ops *rbt_ops)
+{
+	int r;
+
+	(void)ffmax; /* UNUSED */
+
+	r = idr_ensure_poolsize(a, idr, cnt);
+	if (r != ARCHIVE_OK)
+		return (r);
+	__archive_rb_tree_init(&(idr->rbtree), rbt_ops);
+	idr->wait_list.first = NULL;
+	idr->wait_list.last = &(idr->wait_list.first);
+	idr->pool_idx = 0;
+	idr->num_size = num_size;
+	idr->null_size = null_size;
+	return (ARCHIVE_OK);
+}
+
+static void
+idr_register(struct idr *idr, struct isoent *isoent, int weight, int noff)
+{
+	struct idrent *idrent, *n;
+
+	idrent = &(idr->idrent_pool[idr->pool_idx++]);
+	idrent->wnext = idrent->avail = NULL;
+	idrent->isoent = isoent;
+	idrent->weight = weight;
+	idrent->noff = noff;
+	idrent->rename_num = 0;
+
+	if (!__archive_rb_tree_insert_node(&(idr->rbtree), &(idrent->rbnode))) {
+		n = (struct idrent *)__archive_rb_tree_find_node(
+		    &(idr->rbtree), idrent->isoent);
+		if (n != NULL) {
+			/* this `idrent' needs to rename. */
+			idrent->avail = n;
+			*idr->wait_list.last = idrent;
+			idr->wait_list.last = &(idrent->wnext);
+		}
+	}
+}
+
+static void
+idr_extend_identifier(struct idrent *wnp, int numsize, int nullsize)
+{
+	unsigned char *p;
+	int wnp_ext_off;
+
+	wnp_ext_off = wnp->isoent->ext_off;
+	if (wnp->noff + numsize != wnp_ext_off) {
+		p = (unsigned char *)wnp->isoent->identifier;
+		/* Extend the filename; foo.c --> foo___.c */
+		memmove(p + wnp->noff + numsize, p + wnp_ext_off,
+		    wnp->isoent->ext_len + nullsize);
+		wnp->isoent->ext_off = wnp_ext_off = wnp->noff + numsize;
+		wnp->isoent->id_len = wnp_ext_off + wnp->isoent->ext_len;
+	}
+}
+
+static void
+idr_resolve(struct idr *idr, void (*fsetnum)(unsigned char *p, int num))
+{
+	struct idrent *n;
+	unsigned char *p;
+
+	for (n = idr->wait_list.first; n != NULL; n = n->wnext) {
+		idr_extend_identifier(n, idr->num_size, idr->null_size);
+		p = (unsigned char *)n->isoent->identifier + n->noff;
+		do {
+			fsetnum(p, n->avail->rename_num++);
+		} while (!__archive_rb_tree_insert_node(
+		    &(idr->rbtree), &(n->rbnode)));
+	}
+}
+
+static void
+idr_set_num(unsigned char *p, int num)
+{
+	static const char xdig[] = {
+		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+		'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+		'U', 'V', 'W', 'X', 'Y', 'Z'
+	};
+
+	num %= sizeof(xdig) * sizeof(xdig) * sizeof(xdig);
+	p[0] = xdig[(num / (sizeof(xdig) * sizeof(xdig)))];
+	num %= sizeof(xdig) * sizeof(xdig);
+	p[1] = xdig[ (num / sizeof(xdig))];
+	num %= sizeof(xdig);
+	p[2] = xdig[num];
+}
+
+static void
+idr_set_num_beutf16(unsigned char *p, int num)
+{
+	static const uint16_t xdig[] = {
+		0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035,
+		0x0036, 0x0037, 0x0038, 0x0039,
+		0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046,
+		0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C,
+		0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052,
+		0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
+		0x0059, 0x005A
+	};
+#define XDIG_CNT	(sizeof(xdig)/sizeof(xdig[0]))
+
+	num %= XDIG_CNT * XDIG_CNT * XDIG_CNT;
+	archive_be16enc(p, xdig[(num / (XDIG_CNT * XDIG_CNT))]);
+	num %= XDIG_CNT * XDIG_CNT;
+	archive_be16enc(p+2, xdig[ (num / XDIG_CNT)]);
+	num %= XDIG_CNT;
+	archive_be16enc(p+4, xdig[num]);
+}
+
+/*
+ * Generate ISO9660 Identifier.
+ */
+static int
+isoent_gen_iso9660_identifier(struct archive_write *a, struct isoent *isoent,
+    struct idr *idr)
+{
+	struct iso9660 *iso9660;
+	struct isoent *np;
+	char *p;
+	int l, r;
+	const char *char_map;
+	char allow_ldots, allow_multidot, allow_period, allow_vernum;
+	int fnmax, ffmax, dnmax;
+	static const struct archive_rb_tree_ops rb_ops = {
+		isoent_cmp_node_iso9660, isoent_cmp_key_iso9660
+	};
+
+	if (isoent->children.cnt == 0)
+		return (0);
+
+	iso9660 = a->format_data;
+	char_map = idr->char_map;
+	if (iso9660->opt.iso_level <= 3) {
+		allow_ldots = 0;
+		allow_multidot = 0;
+		allow_period = 1;
+		allow_vernum = iso9660->opt.allow_vernum;
+		if (iso9660->opt.iso_level == 1) {
+			fnmax = 8;
+			ffmax = 12;/* fnmax + '.' + 3 */
+			dnmax = 8;
+		} else {
+			fnmax = 30;
+			ffmax = 31;
+			dnmax = 31;
+		}
+	} else {
+		allow_ldots = allow_multidot = 1;
+		allow_period = allow_vernum = 0;
+		if (iso9660->opt.rr)
+			/*
+			 * MDR : The maximum size of Directory Record(254).
+			 * DRL : A Directory Record Length(33).
+			 * CE  : A size of SUSP CE System Use Entry(28).
+			 * MDR - DRL - CE = 254 - 33 - 28 = 193.
+			 */
+			fnmax = ffmax = dnmax = 193;
+		else
+			/*
+			 * XA  : CD-ROM XA System Use Extension
+			 *       Information(14).
+			 * MDR - DRL - XA = 254 - 33 -14 = 207.
+			 */
+			fnmax = ffmax = dnmax = 207;
+	}
+
+	r = idr_start(a, idr, isoent->children.cnt, ffmax, 3, 1, &rb_ops);
+	if (r < 0)
+		return (r);
+
+	for (np = isoent->children.first; np != NULL; np = np->chnext) {
+		char *dot, *xdot;
+		int ext_off, noff, weight;
+
+		l = (int)np->file->basename.length;
+		p = malloc(l+31+2+1);
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+			return (ARCHIVE_FATAL);
+		}
+		memcpy(p, np->file->basename.s, l);
+		p[l] = '\0';
+		np->identifier = p;
+
+		dot = xdot = NULL;
+		if (!allow_ldots) {
+			/*
+			 * If there is a '.' character at the first byte,
+			 * it has to be replaced by '_' character.
+			 */
+			if (*p == '.')
+				*p++ = '_';
+		}
+		for (;*p; p++) {
+			if (*p & 0x80) {
+				*p = '_';
+				continue;
+			}
+			if (char_map[(unsigned char)*p]) {
+				/* if iso-level is '4', a character '.' is
+				 * allowed by char_map. */
+				if (*p == '.') {
+					xdot = dot;
+					dot = p;
+				}
+				continue;
+			}
+			if (*p >= 'a' && *p <= 'z') {
+				*p -= 'a' - 'A';
+				continue;
+			}
+			if (*p == '.') {
+				xdot = dot;
+				dot = p;
+				if (allow_multidot)
+					continue;
+			}
+			*p = '_';
+		}
+		p = np->identifier;
+		weight = -1;
+		if (dot == NULL) {
+			int nammax;
+
+			if (np->dir)
+				nammax = dnmax;
+			else
+				nammax = fnmax;
+
+			if (l > nammax) {
+				p[nammax] = '\0';
+				weight = nammax;
+				ext_off = nammax;
+			} else
+				ext_off = l;
+		} else {
+			*dot = '.';
+			ext_off = (int)(dot - p);
+
+			if (iso9660->opt.iso_level == 1) {
+				if (dot - p <= 8) {
+					if (strlen(dot) > 4) {
+						/* A length of a file extension
+						 * must be less than 4 */
+						dot[4] = '\0';
+						weight = 0;
+					}
+				} else {
+					p[8] = dot[0];
+					p[9] = dot[1];
+					p[10] = dot[2];
+					p[11] = dot[3];
+					p[12] = '\0';
+					weight = 8;
+					ext_off = 8;
+				}
+			} else if (np->dir) {
+				if (l > dnmax) {
+					p[dnmax] = '\0';
+					weight = dnmax;
+					if (ext_off > dnmax)
+						ext_off = dnmax;
+				}
+			} else if (l > ffmax) {
+				int extlen = (int)strlen(dot);
+				int xdoff;
+
+				if (xdot != NULL)
+					xdoff = (int)(xdot - p);
+				else
+					xdoff = 0;
+
+				if (extlen > 1 && xdoff < fnmax-1) {
+					int off;
+
+					if (extlen > ffmax)
+						extlen = ffmax;
+					off = ffmax - extlen;
+					if (off == 0) {
+						/* A dot('.')  character
+						 * doesn't place to the first
+						 * byte of identifier. */
+						off ++;
+						extlen --;
+					}
+					memmove(p+off, dot, extlen);
+					p[ffmax] = '\0';
+					ext_off = off;
+					weight = off;
+#ifdef COMPAT_MKISOFS
+				} else if (xdoff >= fnmax-1) {
+					/* Simulate a bug(?) of mkisofs. */
+					p[fnmax-1] = '\0';
+					ext_off = fnmax-1;
+					weight = fnmax-1;
+#endif
+				} else {
+					p[fnmax] = '\0';
+					ext_off = fnmax;
+					weight = fnmax;
+				}
+			}
+		}
+		/* Save an offset of a file name extension to sort files. */
+		np->ext_off = ext_off;
+		np->ext_len = (int)strlen(&p[ext_off]);
+		np->id_len = l = ext_off + np->ext_len;
+
+		/* Make an offset of the number which is used to be set
+		 * hexadecimal number to avoid duplicate identifier. */
+		if (iso9660->opt.iso_level == 1) {
+			if (ext_off >= 5)
+				noff = 5;
+			else
+				noff = ext_off;
+		} else {
+			if (l == ffmax)
+				noff = ext_off - 3;
+			else if (l == ffmax-1)
+				noff = ext_off - 2;
+			else if (l == ffmax-2)
+				noff = ext_off - 1;
+			else
+				noff = ext_off;
+		}
+		/* Register entry to the identifier resolver. */
+		idr_register(idr, np, weight, noff);
+	}
+
+	/* Resolve duplicate identifier. */
+	idr_resolve(idr, idr_set_num);
+
+	/* Add a period and a version number to identifiers. */
+	for (np = isoent->children.first; np != NULL; np = np->chnext) {
+		if (!np->dir && np->rr_child == NULL) {
+			p = np->identifier + np->ext_off + np->ext_len;
+			if (np->ext_len == 0 && allow_period) {
+				*p++ = '.';
+				np->ext_len = 1;
+			}
+			if (np->ext_len == 1 && !allow_period) {
+				*--p = '\0';
+				np->ext_len = 0;
+			}
+			np->id_len = np->ext_off + np->ext_len;
+			if (allow_vernum) {
+				*p++ = ';';
+				*p++ = '1';
+				np->id_len += 2;
+			}
+			*p = '\0';
+		} else
+			np->id_len = np->ext_off + np->ext_len;
+		np->mb_len = np->id_len;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Generate Joliet Identifier.
+ */
+static int
+isoent_gen_joliet_identifier(struct archive_write *a, struct isoent *isoent,
+    struct idr *idr)
+{
+	struct iso9660 *iso9660;
+	struct isoent *np;
+	unsigned char *p;
+	size_t l;
+	int r;
+	size_t ffmax, parent_len;
+	static const struct archive_rb_tree_ops rb_ops = {
+		isoent_cmp_node_joliet, isoent_cmp_key_joliet
+	};
+
+	if (isoent->children.cnt == 0)
+		return (0);
+
+	iso9660 = a->format_data;
+	if (iso9660->opt.joliet == OPT_JOLIET_LONGNAME)
+		ffmax = 206;
+	else
+		ffmax = 128;
+
+	r = idr_start(a, idr, isoent->children.cnt, (int)ffmax, 6, 2, &rb_ops);
+	if (r < 0)
+		return (r);
+
+	parent_len = 1;
+	for (np = isoent; np->parent != np; np = np->parent)
+		parent_len += np->mb_len + 1;
+
+	for (np = isoent->children.first; np != NULL; np = np->chnext) {
+		unsigned char *dot;
+		int ext_off, noff, weight;
+		size_t lt;
+
+		if ((l = np->file->basename_utf16.length) > ffmax)
+			l = ffmax;
+
+		p = malloc((l+1)*2);
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+			return (ARCHIVE_FATAL);
+		}
+		memcpy(p, np->file->basename_utf16.s, l);
+		p[l] = 0;
+		p[l+1] = 0;
+
+		np->identifier = (char *)p;
+		lt = l;
+		dot = p + l;
+		weight = 0;
+		while (lt > 0) {
+			if (!joliet_allowed_char(p[0], p[1]))
+				archive_be16enc(p, 0x005F); /* '_' */
+			else if (p[0] == 0 && p[1] == 0x2E) /* '.' */
+				dot = p;
+			p += 2;
+			lt -= 2;
+		}
+		ext_off = (int)(dot - (unsigned char *)np->identifier);
+		np->ext_off = ext_off;
+		np->ext_len = (int)l - ext_off;
+		np->id_len = (int)l;
+
+		/*
+		 * Get a length of MBS of a full-pathname.
+		 */
+		if (np->file->basename_utf16.length > ffmax) {
+			if (archive_strncpy_l(&iso9660->mbs,
+			    (const char *)np->identifier, l,
+				iso9660->sconv_from_utf16be) != 0 &&
+			    errno == ENOMEM) {
+				archive_set_error(&a->archive, errno,
+				    "No memory");
+				return (ARCHIVE_FATAL);
+			}
+			np->mb_len = (int)iso9660->mbs.length;
+			if (np->mb_len != (int)np->file->basename.length)
+				weight = np->mb_len;
+		} else
+			np->mb_len = (int)np->file->basename.length;
+
+		/* If a length of full-pathname is longer than 240 bytes,
+		 * it violates Joliet extensions regulation. */
+		if (parent_len > 240
+		    || np->mb_len > 240
+		    || parent_len + np->mb_len > 240) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "The regulation of Joliet extensions;"
+			    " A length of a full-pathname of `%s' is "
+			    "longer than 240 bytes, (p=%d, b=%d)",
+			    archive_entry_pathname(np->file->entry),
+			    (int)parent_len, (int)np->mb_len);
+			return (ARCHIVE_FATAL);
+		}
+
+		/* Make an offset of the number which is used to be set
+		 * hexadecimal number to avoid duplicate identifier. */
+		if (l == ffmax)
+			noff = ext_off - 6;
+		else if (l == ffmax-2)
+			noff = ext_off - 4;
+		else if (l == ffmax-4)
+			noff = ext_off - 2;
+		else
+			noff = ext_off;
+		/* Register entry to the identifier resolver. */
+		idr_register(idr, np, weight, noff);
+	}
+
+	/* Resolve duplicate identifier with Joliet Volume. */
+	idr_resolve(idr, idr_set_num_beutf16);
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * This comparing rule is according to ISO9660 Standard 9.3
+ */
+static int
+isoent_cmp_iso9660_identifier(const struct isoent *p1, const struct isoent *p2)
+{
+	const char *s1, *s2;
+	int cmp;
+	int l;
+
+	s1 = p1->identifier;
+	s2 = p2->identifier;
+
+	/* Compare File Name */
+	l = p1->ext_off;
+	if (l > p2->ext_off)
+		l = p2->ext_off;
+	cmp = memcmp(s1, s2, l);
+	if (cmp != 0)
+		return (cmp);
+	if (p1->ext_off < p2->ext_off) {
+		s2 += l;
+		l = p2->ext_off - p1->ext_off;
+		while (l--)
+			if (0x20 != *s2++)
+				return (0x20
+				    - *(const unsigned char *)(s2 - 1));
+	} else if (p1->ext_off > p2->ext_off) {
+		s1 += l;
+		l = p1->ext_off - p2->ext_off;
+		while (l--)
+			if (0x20 != *s1++)
+				return (*(const unsigned char *)(s1 - 1)
+				    - 0x20);
+	}
+	/* Compare File Name Extension */
+	if (p1->ext_len == 0 && p2->ext_len == 0)
+		return (0);
+	if (p1->ext_len == 1 && p2->ext_len == 1)
+		return (0);
+	if (p1->ext_len <= 1)
+		return (-1);
+	if (p2->ext_len <= 1)
+		return (1);
+	l = p1->ext_len;
+	if (l > p2->ext_len)
+		l = p2->ext_len;
+	s1 = p1->identifier + p1->ext_off;
+	s2 = p2->identifier + p2->ext_off;
+	if (l > 1) {
+		cmp = memcmp(s1, s2, l);
+		if (cmp != 0)
+			return (cmp);
+	}
+	if (p1->ext_len < p2->ext_len) {
+		s2 += l;
+		l = p2->ext_len - p1->ext_len;
+		while (l--)
+			if (0x20 != *s2++)
+				return (0x20
+				    - *(const unsigned char *)(s2 - 1));
+	} else if (p1->ext_len > p2->ext_len) {
+		s1 += l;
+		l = p1->ext_len - p2->ext_len;
+		while (l--)
+			if (0x20 != *s1++)
+				return (*(const unsigned char *)(s1 - 1)
+				    - 0x20);
+	}
+	/* Compare File Version Number */
+	/* No operation. The File Version Number is always one. */
+
+	return (cmp);
+}
+
+static int
+isoent_cmp_node_iso9660(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	const struct idrent *e1 = (const struct idrent *)n1;
+	const struct idrent *e2 = (const struct idrent *)n2;
+
+	return (isoent_cmp_iso9660_identifier(e2->isoent, e1->isoent));
+}
+
+static int
+isoent_cmp_key_iso9660(const struct archive_rb_node *node, const void *key)
+{
+	const struct isoent *isoent = (const struct isoent *)key;
+	const struct idrent *idrent = (const struct idrent *)node;
+
+	return (isoent_cmp_iso9660_identifier(isoent, idrent->isoent));
+}
+
+static int
+isoent_cmp_joliet_identifier(const struct isoent *p1, const struct isoent *p2)
+{
+	const unsigned char *s1, *s2;
+	int cmp;
+	int l;
+
+	s1 = (const unsigned char *)p1->identifier;
+	s2 = (const unsigned char *)p2->identifier;
+
+	/* Compare File Name */
+	l = p1->ext_off;
+	if (l > p2->ext_off)
+		l = p2->ext_off;
+	cmp = memcmp(s1, s2, l);
+	if (cmp != 0)
+		return (cmp);
+	if (p1->ext_off < p2->ext_off) {
+		s2 += l;
+		l = p2->ext_off - p1->ext_off;
+		while (l--)
+			if (0 != *s2++)
+				return (- *(const unsigned char *)(s2 - 1));
+	} else if (p1->ext_off > p2->ext_off) {
+		s1 += l;
+		l = p1->ext_off - p2->ext_off;
+		while (l--)
+			if (0 != *s1++)
+				return (*(const unsigned char *)(s1 - 1));
+	}
+	/* Compare File Name Extension */
+	if (p1->ext_len == 0 && p2->ext_len == 0)
+		return (0);
+	if (p1->ext_len == 2 && p2->ext_len == 2)
+		return (0);
+	if (p1->ext_len <= 2)
+		return (-1);
+	if (p2->ext_len <= 2)
+		return (1);
+	l = p1->ext_len;
+	if (l > p2->ext_len)
+		l = p2->ext_len;
+	s1 = (unsigned char *)(p1->identifier + p1->ext_off);
+	s2 = (unsigned char *)(p2->identifier + p2->ext_off);
+	if (l > 1) {
+		cmp = memcmp(s1, s2, l);
+		if (cmp != 0)
+			return (cmp);
+	}
+	if (p1->ext_len < p2->ext_len) {
+		s2 += l;
+		l = p2->ext_len - p1->ext_len;
+		while (l--)
+			if (0 != *s2++)
+				return (- *(const unsigned char *)(s2 - 1));
+	} else if (p1->ext_len > p2->ext_len) {
+		s1 += l;
+		l = p1->ext_len - p2->ext_len;
+		while (l--)
+			if (0 != *s1++)
+				return (*(const unsigned char *)(s1 - 1));
+	}
+	/* Compare File Version Number */
+	/* No operation. The File Version Number is always one. */
+
+	return (cmp);
+}
+
+static int
+isoent_cmp_node_joliet(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	const struct idrent *e1 = (const struct idrent *)n1;
+	const struct idrent *e2 = (const struct idrent *)n2;
+
+	return (isoent_cmp_joliet_identifier(e2->isoent, e1->isoent));
+}
+
+static int
+isoent_cmp_key_joliet(const struct archive_rb_node *node, const void *key)
+{
+	const struct isoent *isoent = (const struct isoent *)key;
+	const struct idrent *idrent = (const struct idrent *)node;
+
+	return (isoent_cmp_joliet_identifier(isoent, idrent->isoent));
+}
+
+static int
+isoent_make_sorted_files(struct archive_write *a, struct isoent *isoent,
+    struct idr *idr)
+{
+	struct archive_rb_node *rn;
+	struct isoent **children;
+
+	children = malloc(isoent->children.cnt * sizeof(struct isoent *));
+	if (children == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	isoent->children_sorted = children;
+
+	ARCHIVE_RB_TREE_FOREACH(rn, &(idr->rbtree)) {
+		struct idrent *idrent = (struct idrent *)rn;
+		*children ++ = idrent->isoent;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * - Generate ISO9660 and Joliet identifiers from basenames.
+ * - Sort files by each directory.
+ */
+static int
+isoent_traverse_tree(struct archive_write *a, struct vdd* vdd)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct isoent *np;
+	struct idr idr;
+	int depth;
+	int r;
+	int (*genid)(struct archive_write *, struct isoent *, struct idr *);
+
+	idr_init(iso9660, vdd, &idr);
+	np = vdd->rootent;
+	depth = 0;
+	if (vdd->vdd_type == VDD_JOLIET)
+		genid = isoent_gen_joliet_identifier;
+	else
+		genid = isoent_gen_iso9660_identifier;
+	do {
+		if (np->virtual &&
+		    !archive_entry_mtime_is_set(np->file->entry)) {
+			/* Set properly times to virtual directory */
+			archive_entry_set_mtime(np->file->entry,
+			    iso9660->birth_time, 0);
+			archive_entry_set_atime(np->file->entry,
+			    iso9660->birth_time, 0);
+			archive_entry_set_ctime(np->file->entry,
+			    iso9660->birth_time, 0);
+		}
+		if (np->children.first != NULL) {
+			if (vdd->vdd_type != VDD_JOLIET &&
+			    !iso9660->opt.rr && depth + 1 >= vdd->max_depth) {
+				if (np->children.cnt > 0)
+					iso9660->directories_too_deep = np;
+			} else {
+				/* Generate Identifier */
+				r = genid(a, np, &idr);
+				if (r < 0)
+					goto exit_traverse_tree;
+				r = isoent_make_sorted_files(a, np, &idr);
+				if (r < 0)
+					goto exit_traverse_tree;
+
+				if (np->subdirs.first != NULL &&
+				    depth + 1 < vdd->max_depth) {
+					/* Enter to sub directories. */
+					np = np->subdirs.first;
+					depth++;
+					continue;
+				}
+			}
+		}
+		while (np != np->parent) {
+			if (np->drnext == NULL) {
+				/* Return to the parent directory. */
+				np = np->parent;
+				depth--;
+			} else {
+				np = np->drnext;
+				break;
+			}
+		}
+	} while (np != np->parent);
+
+	r = ARCHIVE_OK;
+exit_traverse_tree:
+	idr_cleanup(&idr);
+
+	return (r);
+}
+
+/*
+ * Collect directory entries into path_table by a directory depth.
+ */
+static int
+isoent_collect_dirs(struct vdd *vdd, struct isoent *rootent, int depth)
+{
+	struct isoent *np;
+
+	if (rootent == NULL)
+		rootent = vdd->rootent;
+	np = rootent;
+	do {
+		/* Register current directory to pathtable. */
+		path_table_add_entry(&(vdd->pathtbl[depth]), np);
+
+		if (np->subdirs.first != NULL && depth + 1 < vdd->max_depth) {
+			/* Enter to sub directories. */
+			np = np->subdirs.first;
+			depth++;
+			continue;
+		}
+		while (np != rootent) {
+			if (np->drnext == NULL) {
+				/* Return to the parent directory. */
+				np = np->parent;
+				depth--;
+			} else {
+				np = np->drnext;
+				break;
+			}
+		}
+	} while (np != rootent);
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * The entry whose number of levels in a directory hierarchy is
+ * large than eight relocate to rr_move directory.
+ */
+static int
+isoent_rr_move_dir(struct archive_write *a, struct isoent **rr_moved,
+    struct isoent *curent, struct isoent **newent)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct isoent *rrmoved, *mvent, *np;
+
+	if ((rrmoved = *rr_moved) == NULL) {
+		struct isoent *rootent = iso9660->primary.rootent;
+		/* There isn't rr_move entry.
+		 * Create rr_move entry and insert it into the root entry.
+		 */
+		rrmoved = isoent_create_virtual_dir(a, iso9660, "rr_moved");
+		if (rrmoved == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+			return (ARCHIVE_FATAL);
+		}
+		/* Add "rr_moved" entry to the root entry. */
+		isoent_add_child_head(rootent, rrmoved);
+		archive_entry_set_nlink(rootent->file->entry,
+		    archive_entry_nlink(rootent->file->entry) + 1);
+		/* Register "rr_moved" entry to second level pathtable. */
+		path_table_add_entry(&(iso9660->primary.pathtbl[1]), rrmoved);
+		/* Save rr_moved. */
+		*rr_moved = rrmoved;
+	}
+	/*
+	 * Make a clone of curent which is going to be relocated
+	 * to rr_moved.
+	 */
+	mvent = isoent_clone(curent);
+	if (mvent == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	/* linking..  and use for creating "CL", "PL" and "RE" */
+	mvent->rr_parent = curent->parent;
+	curent->rr_child = mvent;
+	/*
+	 * Move subdirectories from the curent to mvent
+	 */
+	if (curent->children.first != NULL) {
+		*mvent->children.last = curent->children.first;
+		mvent->children.last = curent->children.last;
+	}
+	for (np = mvent->children.first; np != NULL; np = np->chnext)
+		np->parent = mvent;
+	mvent->children.cnt = curent->children.cnt;
+	curent->children.cnt = 0;
+	curent->children.first = NULL;
+	curent->children.last = &curent->children.first;
+
+	if (curent->subdirs.first != NULL) {
+		*mvent->subdirs.last = curent->subdirs.first;
+		mvent->subdirs.last = curent->subdirs.last;
+	}
+	mvent->subdirs.cnt = curent->subdirs.cnt;
+	curent->subdirs.cnt = 0;
+	curent->subdirs.first = NULL;
+	curent->subdirs.last = &curent->subdirs.first;
+
+	/*
+	 * The mvent becomes a child of the rr_moved entry.
+	 */
+	isoent_add_child_tail(rrmoved, mvent);
+	archive_entry_set_nlink(rrmoved->file->entry,
+	    archive_entry_nlink(rrmoved->file->entry) + 1);
+	/*
+	 * This entry which relocated to the rr_moved directory
+	 * has to set the flag as a file.
+	 * See also RRIP 4.1.5.1 Description of the "CL" System Use Entry.
+	 */
+	curent->dir = 0;
+
+	*newent = mvent;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+isoent_rr_move(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct path_table *pt;
+	struct isoent *rootent, *rr_moved;
+	struct isoent *np, *last;
+	int r;
+
+	pt = &(iso9660->primary.pathtbl[MAX_DEPTH-1]);
+	/* There aren't level 8 directories reaching a deeper level. */
+	if (pt->cnt == 0)
+		return (ARCHIVE_OK);
+
+	rootent = iso9660->primary.rootent;
+	/* If "rr_moved" directory is already existing,
+	 * we have to use it. */
+	rr_moved = isoent_find_child(rootent, "rr_moved");
+	if (rr_moved != NULL &&
+	    rr_moved != rootent->children.first) {
+		/*
+		 * It's necessary that rr_move is the first entry
+		 * of the root.
+		 */
+		/* Remove "rr_moved" entry from children chain. */
+		isoent_remove_child(rootent, rr_moved);
+
+		/* Add "rr_moved" entry into the head of children chain. */
+		isoent_add_child_head(rootent, rr_moved);
+	}
+
+	/*
+	 * Check level 8 path_table.
+	 * If find out sub directory entries, that entries move to rr_move.
+	 */
+	np = pt->first;
+	while (np != NULL) {
+		last = path_table_last_entry(pt);
+		for (; np != NULL; np = np->ptnext) {
+			struct isoent *mvent;
+			struct isoent *newent;
+
+			if (!np->dir)
+				continue;
+			for (mvent = np->subdirs.first;
+			    mvent != NULL; mvent = mvent->drnext) {
+				r = isoent_rr_move_dir(a, &rr_moved,
+				    mvent, &newent);
+				if (r < 0)
+					return (r);
+				isoent_collect_dirs(&(iso9660->primary),
+				    newent, 2);
+			}
+		}
+		/* If new entries are added to level 8 path_talbe,
+		 * its sub directory entries move to rr_move too.
+		 */
+		np = last->ptnext;
+	}
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * This comparing rule is according to ISO9660 Standard 6.9.1
+ */
+static int
+_compare_path_table(const void *v1, const void *v2)
+{
+	const struct isoent *p1, *p2;
+	const char *s1, *s2;
+	int cmp, l;
+
+	p1 = *((const struct isoent **)(uintptr_t)v1);
+	p2 = *((const struct isoent **)(uintptr_t)v2);
+
+	/* Compare parent directory number */
+	cmp = p1->parent->dir_number - p2->parent->dir_number;
+	if (cmp != 0)
+		return (cmp);
+
+	/* Compare identifier */
+	s1 = p1->identifier;
+	s2 = p2->identifier;
+	l = p1->ext_off;
+	if (l > p2->ext_off)
+		l = p2->ext_off;
+	cmp = strncmp(s1, s2, l);
+	if (cmp != 0)
+		return (cmp);
+	if (p1->ext_off < p2->ext_off) {
+		s2 += l;
+		l = p2->ext_off - p1->ext_off;
+		while (l--)
+			if (0x20 != *s2++)
+				return (0x20
+				    - *(const unsigned char *)(s2 - 1));
+	} else if (p1->ext_off > p2->ext_off) {
+		s1 += l;
+		l = p1->ext_off - p2->ext_off;
+		while (l--)
+			if (0x20 != *s1++)
+				return (*(const unsigned char *)(s1 - 1)
+				    - 0x20);
+	}
+	return (0);
+}
+
+static int
+_compare_path_table_joliet(const void *v1, const void *v2)
+{
+	const struct isoent *p1, *p2;
+	const unsigned char *s1, *s2;
+	int cmp, l;
+
+	p1 = *((const struct isoent **)(uintptr_t)v1);
+	p2 = *((const struct isoent **)(uintptr_t)v2);
+
+	/* Compare parent directory number */
+	cmp = p1->parent->dir_number - p2->parent->dir_number;
+	if (cmp != 0)
+		return (cmp);
+
+	/* Compare identifier */
+	s1 = (const unsigned char *)p1->identifier;
+	s2 = (const unsigned char *)p2->identifier;
+	l = p1->ext_off;
+	if (l > p2->ext_off)
+		l = p2->ext_off;
+	cmp = memcmp(s1, s2, l);
+	if (cmp != 0)
+		return (cmp);
+	if (p1->ext_off < p2->ext_off) {
+		s2 += l;
+		l = p2->ext_off - p1->ext_off;
+		while (l--)
+			if (0 != *s2++)
+				return (- *(const unsigned char *)(s2 - 1));
+	} else if (p1->ext_off > p2->ext_off) {
+		s1 += l;
+		l = p1->ext_off - p2->ext_off;
+		while (l--)
+			if (0 != *s1++)
+				return (*(const unsigned char *)(s1 - 1));
+	}
+	return (0);
+}
+
+static inline void
+path_table_add_entry(struct path_table *pathtbl, struct isoent *ent)
+{
+	ent->ptnext = NULL;
+	*pathtbl->last = ent;
+	pathtbl->last = &(ent->ptnext);
+	pathtbl->cnt ++;
+}
+
+static inline struct isoent *
+path_table_last_entry(struct path_table *pathtbl)
+{
+	if (pathtbl->first == NULL)
+		return (NULL);
+	return (((struct isoent *)(void *)
+		((char *)(pathtbl->last) - offsetof(struct isoent, ptnext))));
+}
+
+/*
+ * Sort directory entries in path_table
+ * and assign directory number to each entries.
+ */
+static int
+isoent_make_path_table_2(struct archive_write *a, struct vdd *vdd,
+    int depth, int *dir_number)
+{
+	struct isoent *np;
+	struct isoent **enttbl;
+	struct path_table *pt;
+	int i;
+
+	pt = &vdd->pathtbl[depth];
+	if (pt->cnt == 0) {
+		pt->sorted = NULL;
+		return (ARCHIVE_OK);
+	}
+	enttbl = malloc(pt->cnt * sizeof(struct isoent *));
+	if (enttbl == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	pt->sorted = enttbl;
+	for (np = pt->first; np != NULL; np = np->ptnext)
+		*enttbl ++ = np;
+	enttbl = pt->sorted;
+
+	switch (vdd->vdd_type) {
+	case VDD_PRIMARY:
+	case VDD_ENHANCED:
+#ifdef __COMPAR_FN_T
+		qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+		    (__compar_fn_t)_compare_path_table);
+#else
+		qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+		    _compare_path_table);
+#endif
+		break;
+	case VDD_JOLIET:
+#ifdef __COMPAR_FN_T
+		qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+		    (__compar_fn_t)_compare_path_table_joliet);
+#else
+		qsort(enttbl, pt->cnt, sizeof(struct isoent *),
+		    _compare_path_table_joliet);
+#endif
+		break;
+	}
+	for (i = 0; i < pt->cnt; i++)
+		enttbl[i]->dir_number = (*dir_number)++;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+isoent_alloc_path_table(struct archive_write *a, struct vdd *vdd,
+    int max_depth)
+{
+	int i;
+
+	vdd->max_depth = max_depth;
+	vdd->pathtbl = malloc(sizeof(*vdd->pathtbl) * vdd->max_depth);
+	if (vdd->pathtbl == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	for (i = 0; i < vdd->max_depth; i++) {
+		vdd->pathtbl[i].first = NULL;
+		vdd->pathtbl[i].last = &(vdd->pathtbl[i].first);
+		vdd->pathtbl[i].sorted = NULL;
+		vdd->pathtbl[i].cnt = 0;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Make Path Tables
+ */
+static int
+isoent_make_path_table(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	int depth, r;
+	int dir_number;
+
+	/*
+	 * Init Path Table.
+	 */
+	if (iso9660->dircnt_max >= MAX_DEPTH &&
+	    (!iso9660->opt.limit_depth || iso9660->opt.iso_level == 4))
+		r = isoent_alloc_path_table(a, &(iso9660->primary),
+		    iso9660->dircnt_max + 1);
+	else
+		/* The number of levels in the hierarchy cannot exceed
+		 * eight. */
+		r = isoent_alloc_path_table(a, &(iso9660->primary),
+		    MAX_DEPTH);
+	if (r < 0)
+		return (r);
+	if (iso9660->opt.joliet) {
+		r = isoent_alloc_path_table(a, &(iso9660->joliet),
+		    iso9660->dircnt_max + 1);
+		if (r < 0)
+			return (r);
+	}
+
+	/* Step 0.
+	 * - Collect directories for primary and joliet.
+	 */
+	isoent_collect_dirs(&(iso9660->primary), NULL, 0);
+	if (iso9660->opt.joliet)
+		isoent_collect_dirs(&(iso9660->joliet), NULL, 0);
+	/*
+	 * Rockridge; move deeper depth directories to rr_moved.
+	 */
+	if (iso9660->opt.rr) {
+		r = isoent_rr_move(a);
+		if (r < 0)
+			return (r);
+	}
+
+ 	/* Update nlink. */
+	isofile_connect_hardlink_files(iso9660);
+
+	/* Step 1.
+	 * - Renew a value of the depth of that directories.
+	 * - Resolve hardlinks.
+ 	 * - Convert pathnames to ISO9660 name or UCS2(joliet).
+	 * - Sort files by each directory.
+	 */
+	r = isoent_traverse_tree(a, &(iso9660->primary));
+	if (r < 0)
+		return (r);
+	if (iso9660->opt.joliet) {
+		r = isoent_traverse_tree(a, &(iso9660->joliet));
+		if (r < 0)
+			return (r);
+	}
+
+	/* Step 2.
+	 * - Sort directories.
+	 * - Assign all directory number.
+	 */
+	dir_number = 1;
+	for (depth = 0; depth < iso9660->primary.max_depth; depth++) {
+		r = isoent_make_path_table_2(a, &(iso9660->primary),
+		    depth, &dir_number);
+		if (r < 0)
+			return (r);
+	}
+	if (iso9660->opt.joliet) {
+		dir_number = 1;
+		for (depth = 0; depth < iso9660->joliet.max_depth; depth++) {
+			r = isoent_make_path_table_2(a, &(iso9660->joliet),
+			    depth, &dir_number);
+			if (r < 0)
+				return (r);
+		}
+	}
+	if (iso9660->opt.limit_dirs && dir_number > 0xffff) {
+		/*
+		 * Maximum number of directories is 65535(0xffff)
+		 * doe to size(16bit) of Parent Directory Number of
+		 * the Path Table.
+		 * See also ISO9660 Standard 9.4.
+		 */
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Too many directories(%d) over 65535.", dir_number);
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Get the size of the Path Table. */
+	calculate_path_table_size(&(iso9660->primary));
+	if (iso9660->opt.joliet)
+		calculate_path_table_size(&(iso9660->joliet));
+
+	return (ARCHIVE_OK);
+}
+
+static int
+isoent_find_out_boot_file(struct archive_write *a, struct isoent *rootent)
+{
+	struct iso9660 *iso9660 = a->format_data;
+
+	/* Find a isoent of the boot file. */
+	iso9660->el_torito.boot = isoent_find_entry(rootent,
+	    iso9660->el_torito.boot_filename.s);
+	if (iso9660->el_torito.boot == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Can't find the boot image file ``%s''",
+		    iso9660->el_torito.boot_filename.s);
+		return (ARCHIVE_FATAL);
+	}
+	iso9660->el_torito.boot->file->boot = BOOT_IMAGE;
+	return (ARCHIVE_OK);
+}
+
+static int
+isoent_create_boot_catalog(struct archive_write *a, struct isoent *rootent)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct isofile *file;
+	struct isoent *isoent;
+	struct archive_entry *entry;
+
+	(void)rootent; /* UNUSED */
+	/*
+	 * Create the entry which is the "boot.catalog" file.
+	 */
+	file = isofile_new(a, NULL);
+	if (file == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	archive_entry_set_pathname(file->entry,
+	    iso9660->el_torito.catalog_filename.s);
+	archive_entry_set_size(file->entry, LOGICAL_BLOCK_SIZE);
+	archive_entry_set_mtime(file->entry, iso9660->birth_time, 0);
+	archive_entry_set_atime(file->entry, iso9660->birth_time, 0);
+	archive_entry_set_ctime(file->entry, iso9660->birth_time, 0);
+	archive_entry_set_uid(file->entry, getuid());
+	archive_entry_set_gid(file->entry, getgid());
+	archive_entry_set_mode(file->entry, AE_IFREG | 0444);
+	archive_entry_set_nlink(file->entry, 1);
+
+	if (isofile_gen_utility_names(a, file) < ARCHIVE_WARN) {
+		isofile_free(file);
+		return (ARCHIVE_FATAL);
+	}
+	file->boot = BOOT_CATALOG;
+	file->content.size = LOGICAL_BLOCK_SIZE;
+	isofile_add_entry(iso9660, file);
+
+	isoent = isoent_new(file);
+	if (isoent == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	isoent->virtual = 1;
+
+	/* Add the "boot.catalog" entry into tree */
+	if (isoent_tree(a, &isoent) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	iso9660->el_torito.catalog = isoent;
+	/*
+	 * Get a boot media type.
+	 */
+	switch (iso9660->opt.boot_type) {
+	default:
+	case OPT_BOOT_TYPE_AUTO:
+		/* Try detecting a media type of the boot image. */
+		entry = iso9660->el_torito.boot->file->entry;
+		if (archive_entry_size(entry) == FD_1_2M_SIZE)
+			iso9660->el_torito.media_type =
+			    BOOT_MEDIA_1_2M_DISKETTE;
+		else if (archive_entry_size(entry) == FD_1_44M_SIZE)
+			iso9660->el_torito.media_type =
+			    BOOT_MEDIA_1_44M_DISKETTE;
+		else if (archive_entry_size(entry) == FD_2_88M_SIZE)
+			iso9660->el_torito.media_type =
+			    BOOT_MEDIA_2_88M_DISKETTE;
+		else
+			/* We cannot decide whether the boot image is
+			 * hard-disk. */
+			iso9660->el_torito.media_type =
+			    BOOT_MEDIA_NO_EMULATION;
+		break;
+	case OPT_BOOT_TYPE_NO_EMU:
+		iso9660->el_torito.media_type = BOOT_MEDIA_NO_EMULATION;
+		break;
+	case OPT_BOOT_TYPE_HARD_DISK:
+		iso9660->el_torito.media_type = BOOT_MEDIA_HARD_DISK;
+		break;
+	case OPT_BOOT_TYPE_FD:
+		entry = iso9660->el_torito.boot->file->entry;
+		if (archive_entry_size(entry) <= FD_1_2M_SIZE)
+			iso9660->el_torito.media_type =
+			    BOOT_MEDIA_1_2M_DISKETTE;
+		else if (archive_entry_size(entry) <= FD_1_44M_SIZE)
+			iso9660->el_torito.media_type =
+			    BOOT_MEDIA_1_44M_DISKETTE;
+		else if (archive_entry_size(entry) <= FD_2_88M_SIZE)
+			iso9660->el_torito.media_type =
+			    BOOT_MEDIA_2_88M_DISKETTE;
+		else {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Boot image file(``%s'') size is too big "
+			    "for fd type.",
+			    iso9660->el_torito.boot_filename.s);
+			return (ARCHIVE_FATAL);
+		}
+		break;
+	}
+
+	/*
+	 * Get a system type.
+	 * TODO: `El Torito' specification says "A copy of byte 5 from the
+	 *       Partition Table found in the boot image".
+	 */
+	iso9660->el_torito.system_type = 0;
+
+	/*
+	 * Get an ID.
+	 */
+	if (iso9660->opt.publisher)
+		archive_string_copy(&(iso9660->el_torito.id),
+		    &(iso9660->publisher_identifier));
+
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * If a media type is floppy, return its image size.
+ * otherwise return 0.
+ */
+static size_t
+fd_boot_image_size(int media_type)
+{
+	switch (media_type) {
+	case BOOT_MEDIA_1_2M_DISKETTE:
+		return (FD_1_2M_SIZE);
+	case BOOT_MEDIA_1_44M_DISKETTE:
+		return (FD_1_44M_SIZE);
+	case BOOT_MEDIA_2_88M_DISKETTE:
+		return (FD_2_88M_SIZE);
+	default:
+		return (0);
+	}
+}
+
+/*
+ * Make a boot catalog image data.
+ */
+static int
+make_boot_catalog(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	unsigned char *block;
+	unsigned char *p;
+	uint16_t sum, *wp;
+
+	block = wb_buffptr(a);
+	memset(block, 0, LOGICAL_BLOCK_SIZE);
+	p = block;
+	/*
+	 * Validation Entry
+	 */
+	/* Header ID */
+	p[0] = 1;
+	/* Platform ID */
+	p[1] = iso9660->el_torito.platform_id;
+	/* Reserved */
+	p[2] = p[3] = 0;
+	/* ID */
+	if (archive_strlen(&(iso9660->el_torito.id)) > 0)
+		strncpy((char *)p+4, iso9660->el_torito.id.s, 23);
+	p[27] = 0;
+	/* Checksum */
+	p[28] = p[29] = 0;
+	/* Key */
+	p[30] = 0x55;
+	p[31] = 0xAA;
+
+	sum = 0;
+	wp = (uint16_t *)block;
+	while (wp < (uint16_t *)&block[32])
+		sum += archive_le16dec(wp++);
+	set_num_721(&block[28], (~sum) + 1);
+
+	/*
+	 * Initial/Default Entry
+	 */
+	p = &block[32];
+	/* Boot Indicator */
+	p[0] = 0x88;
+	/* Boot media type */
+	p[1] = iso9660->el_torito.media_type;
+	/* Load Segment */
+	if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION)
+		set_num_721(&p[2], iso9660->el_torito.boot_load_seg);
+	else
+		set_num_721(&p[2], 0);
+	/* System Type */
+	p[4] = iso9660->el_torito.system_type;
+	/* Unused */
+	p[5] = 0;
+	/* Sector Count */
+	if (iso9660->el_torito.media_type == BOOT_MEDIA_NO_EMULATION)
+		set_num_721(&p[6], iso9660->el_torito.boot_load_size);
+	else
+		set_num_721(&p[6], 1);
+	/* Load RBA */
+	set_num_731(&p[8],
+	    iso9660->el_torito.boot->file->content.location);
+	/* Unused */
+	memset(&p[12], 0, 20);
+
+	return (wb_consume(a, LOGICAL_BLOCK_SIZE));
+}
+
+static int
+setup_boot_information(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct isoent *np;
+	int64_t size;
+	uint32_t sum;
+	unsigned char buff[4096];
+
+	np = iso9660->el_torito.boot;
+	lseek(iso9660->temp_fd,
+	    np->file->content.offset_of_temp + 64, SEEK_SET);
+	size = archive_entry_size(np->file->entry) - 64;
+	if (size <= 0) {
+		archive_set_error(&a->archive, errno,
+		    "Boot file(%jd) is too small", (intmax_t)size + 64);
+		return (ARCHIVE_FATAL);
+	}
+	sum = 0;
+	while (size > 0) {
+		size_t rsize;
+		ssize_t i, rs;
+
+		if (size > (int64_t)sizeof(buff))
+			rsize = sizeof(buff);
+		else
+			rsize = (size_t)size;
+
+		rs = read(iso9660->temp_fd, buff, rsize);
+		if (rs <= 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't read temporary file(%jd)",
+			    (intmax_t)rs);
+			return (ARCHIVE_FATAL);
+		}
+		for (i = 0; i < rs; i += 4)
+			sum += archive_le32dec(buff + i);
+		size -= rs;
+	}
+	/* Set the location of Primary Volume Descriptor. */
+	set_num_731(buff, SYSTEM_AREA_BLOCK);
+	/* Set the location of the boot file. */
+	set_num_731(buff+4, np->file->content.location);
+	/* Set the size of the boot file. */
+	size = fd_boot_image_size(iso9660->el_torito.media_type);
+	if (size == 0)
+		size = archive_entry_size(np->file->entry);
+	set_num_731(buff+8, (uint32_t)size);
+	/* Set the sum of the boot file. */
+	set_num_731(buff+12, sum);
+	/* Clear reserved bytes. */
+	memset(buff+16, 0, 40);
+
+	/* Overwrite the boot file. */
+	lseek(iso9660->temp_fd,
+	    np->file->content.offset_of_temp + 8, SEEK_SET);
+	return (write_to_temp(a, buff, 56));
+}
+
+#ifdef HAVE_ZLIB_H
+
+static int
+zisofs_init_zstream(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	int r;
+
+	iso9660->zisofs.stream.next_in = NULL;
+	iso9660->zisofs.stream.avail_in = 0;
+	iso9660->zisofs.stream.total_in = 0;
+	iso9660->zisofs.stream.total_out = 0;
+	if (iso9660->zisofs.stream_valid)
+		r = deflateReset(&(iso9660->zisofs.stream));
+	else {
+		r = deflateInit(&(iso9660->zisofs.stream),
+		    iso9660->zisofs.compression_level);
+		iso9660->zisofs.stream_valid = 1;
+	}
+	switch (r) {
+	case Z_OK:
+		break;
+	default:
+	case Z_STREAM_ERROR:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing "
+		    "compression library: invalid setup parameter");
+		return (ARCHIVE_FATAL);
+	case Z_MEM_ERROR:
+		archive_set_error(&a->archive, ENOMEM,
+		    "Internal error initializing "
+		    "compression library");
+		return (ARCHIVE_FATAL);
+	case Z_VERSION_ERROR:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing "
+		    "compression library: invalid library version");
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H */
+
+static int
+zisofs_init(struct archive_write *a,  struct isofile *file)
+{
+	struct iso9660 *iso9660 = a->format_data;
+#ifdef HAVE_ZLIB_H
+	uint64_t tsize;
+	size_t _ceil, bpsize;
+	int r;
+#endif
+
+	iso9660->zisofs.detect_magic = 0;
+	iso9660->zisofs.making = 0;
+
+	if (!iso9660->opt.rr || !iso9660->opt.zisofs)
+		return (ARCHIVE_OK);
+
+	if (archive_entry_size(file->entry) >= 24 &&
+	    archive_entry_size(file->entry) < MULTI_EXTENT_SIZE) {
+		/* Acceptable file size for zisofs. */
+		iso9660->zisofs.detect_magic = 1;
+		iso9660->zisofs.magic_cnt = 0;
+	}
+	if (!iso9660->zisofs.detect_magic)
+		return (ARCHIVE_OK);
+
+#ifdef HAVE_ZLIB_H
+	/* The number of Logical Blocks which uncompressed data
+	 * will use in iso-image file is the same as the number of
+	 * Logical Blocks which zisofs(compressed) data will use
+	 * in ISO-image file. It won't reduce iso-image file size. */
+	if (archive_entry_size(file->entry) <= LOGICAL_BLOCK_SIZE)
+		return (ARCHIVE_OK);
+
+	/* Initialize compression library */
+	r = zisofs_init_zstream(a);
+	if (r != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Mark file->zisofs to create RRIP 'ZF' Use Entry. */
+	file->zisofs.header_size = ZF_HEADER_SIZE >> 2;
+	file->zisofs.log2_bs = ZF_LOG2_BS;
+	file->zisofs.uncompressed_size =
+		(uint32_t)archive_entry_size(file->entry);
+
+	/* Calculate a size of Block Pointers of zisofs. */
+	_ceil = (file->zisofs.uncompressed_size + ZF_BLOCK_SIZE -1)
+		>> file->zisofs.log2_bs;
+	iso9660->zisofs.block_pointers_cnt = (int)_ceil + 1;
+	iso9660->zisofs.block_pointers_idx = 0;
+
+	/* Ensure a buffer size used for Block Pointers */
+	bpsize = iso9660->zisofs.block_pointers_cnt *
+	    sizeof(iso9660->zisofs.block_pointers[0]);
+	if (iso9660->zisofs.block_pointers_allocated < bpsize) {
+		free(iso9660->zisofs.block_pointers);
+		iso9660->zisofs.block_pointers = malloc(bpsize);
+		if (iso9660->zisofs.block_pointers == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate data");
+			return (ARCHIVE_FATAL);
+		}
+		iso9660->zisofs.block_pointers_allocated = bpsize;
+	}
+
+	/*
+	 * Skip zisofs header and Block Pointers, which we will write
+	 * after all compressed data of a file written to the temporary
+	 * file.
+	 */
+	tsize = ZF_HEADER_SIZE + bpsize;
+	if (write_null(a, (size_t)tsize) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/*
+	 * Initialize some variables to make zisofs.
+	 */
+	archive_le32enc(&(iso9660->zisofs.block_pointers[0]),
+		(uint32_t)tsize);
+	iso9660->zisofs.remaining = file->zisofs.uncompressed_size;
+	iso9660->zisofs.making = 1;
+	iso9660->zisofs.allzero = 1;
+	iso9660->zisofs.block_offset = tsize;
+	iso9660->zisofs.total_size = tsize;
+	iso9660->cur_file->cur_content->size = tsize;
+#endif
+
+	return (ARCHIVE_OK);
+}
+
+static void
+zisofs_detect_magic(struct archive_write *a, const void *buff, size_t s)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct isofile *file = iso9660->cur_file;
+	const unsigned char *p, *endp;
+	const unsigned char *magic_buff;
+	uint32_t uncompressed_size;
+	unsigned char header_size;
+	unsigned char log2_bs;
+	size_t _ceil, doff;
+	uint32_t bst, bed;
+	int magic_max;
+	int64_t entry_size;
+
+	entry_size = archive_entry_size(file->entry);
+	if ((int64_t)sizeof(iso9660->zisofs.magic_buffer) > entry_size)
+		magic_max = (int)entry_size;
+	else
+		magic_max = sizeof(iso9660->zisofs.magic_buffer);
+
+	if (iso9660->zisofs.magic_cnt == 0 && s >= (size_t)magic_max)
+		/* It's unnecessary we copy buffer. */
+		magic_buff = buff;
+	else {
+		if (iso9660->zisofs.magic_cnt < magic_max) {
+			size_t l;
+
+			l = sizeof(iso9660->zisofs.magic_buffer)
+			    - iso9660->zisofs.magic_cnt;
+			if (l > s)
+				l = s;
+			memcpy(iso9660->zisofs.magic_buffer
+			    + iso9660->zisofs.magic_cnt, buff, l);
+			iso9660->zisofs.magic_cnt += (int)l;
+			if (iso9660->zisofs.magic_cnt < magic_max)
+				return;
+		}
+		magic_buff = iso9660->zisofs.magic_buffer;
+	}
+	iso9660->zisofs.detect_magic = 0;
+	p = magic_buff;
+
+	/* Check the magic code of zisofs. */
+	if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0)
+		/* This is not zisofs file which made by mkzftree. */
+		return;
+	p += sizeof(zisofs_magic);
+
+	/* Read a zisofs header. */
+	uncompressed_size = archive_le32dec(p);
+	header_size = p[4];
+	log2_bs = p[5];
+	if (uncompressed_size < 24 || header_size != 4 ||
+	    log2_bs > 30 || log2_bs < 7)
+		return;/* Invalid or not supported header. */
+
+	/* Calculate a size of Block Pointers of zisofs. */
+	_ceil = (uncompressed_size +
+	        (ARCHIVE_LITERAL_LL(1) << log2_bs) -1) >> log2_bs;
+	doff = (_ceil + 1) * 4 + 16;
+	if (entry_size < (int64_t)doff)
+		return;/* Invalid data. */
+
+	/* Check every Block Pointer has valid value. */
+	p = magic_buff + 16;
+	endp = magic_buff + magic_max;
+	while (_ceil && p + 8 <= endp) {
+		bst = archive_le32dec(p);
+		if (bst != doff)
+			return;/* Invalid data. */
+		p += 4;
+		bed = archive_le32dec(p);
+		if (bed < bst || bed > entry_size)
+			return;/* Invalid data. */
+		doff += bed - bst;
+		_ceil--;
+	}
+
+	file->zisofs.uncompressed_size = uncompressed_size;
+	file->zisofs.header_size = header_size;
+	file->zisofs.log2_bs = log2_bs;
+
+	/* Disable making a zisofs image. */
+	iso9660->zisofs.making = 0;
+}
+
+#ifdef HAVE_ZLIB_H
+
+/*
+ * Compress data and write it to a temporary file.
+ */
+static int
+zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct isofile *file = iso9660->cur_file;
+	const unsigned char *b;
+	z_stream *zstrm;
+	size_t avail, csize;
+	int flush, r;
+
+	zstrm = &(iso9660->zisofs.stream);
+	zstrm->next_out = wb_buffptr(a);
+	zstrm->avail_out = (uInt)wb_remaining(a);
+	b = (const unsigned char *)buff;
+	do {
+		avail = ZF_BLOCK_SIZE - zstrm->total_in;
+		if (s < avail) {
+			avail = s;
+			flush = Z_NO_FLUSH;
+		} else
+			flush = Z_FINISH;
+		iso9660->zisofs.remaining -= avail;
+		if (iso9660->zisofs.remaining <= 0)
+			flush = Z_FINISH;
+
+		zstrm->next_in = (Bytef *)(uintptr_t)(const void *)b;
+		zstrm->avail_in = (uInt)avail;
+
+		/*
+		 * Check if current data block are all zero.
+		 */
+		if (iso9660->zisofs.allzero) {
+			const unsigned char *nonzero = b;
+			const unsigned char *nonzeroend = b + avail;
+
+			while (nonzero < nonzeroend)
+				if (*nonzero++) {
+					iso9660->zisofs.allzero = 0;
+					break;
+				}
+		}
+		b += avail;
+		s -= avail;
+
+		/*
+		 * If current data block are all zero, we do not use
+		 * compressed data.
+		 */
+		if (flush == Z_FINISH && iso9660->zisofs.allzero &&
+		    avail + zstrm->total_in == ZF_BLOCK_SIZE) {
+			if (iso9660->zisofs.block_offset !=
+			    file->cur_content->size) {
+				int64_t diff;
+
+				r = wb_set_offset(a,
+				    file->cur_content->offset_of_temp +
+				        iso9660->zisofs.block_offset);
+				if (r != ARCHIVE_OK)
+					return (r);
+				diff = file->cur_content->size -
+				    iso9660->zisofs.block_offset;
+				file->cur_content->size -= diff;
+				iso9660->zisofs.total_size -= diff;
+			}
+			zstrm->avail_in = 0;
+		}
+
+		/*
+		 * Compress file data.
+		 */
+		while (zstrm->avail_in > 0) {
+			csize = zstrm->total_out;
+			r = deflate(zstrm, flush);
+			switch (r) {
+			case Z_OK:
+			case Z_STREAM_END:
+				csize = zstrm->total_out - csize;
+				if (wb_consume(a, csize) != ARCHIVE_OK)
+					return (ARCHIVE_FATAL);
+				iso9660->zisofs.total_size += csize;
+				iso9660->cur_file->cur_content->size += csize;
+				zstrm->next_out = wb_buffptr(a);
+				zstrm->avail_out = (uInt)wb_remaining(a);
+				break;
+			default:
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Compression failed:"
+				    " deflate() call returned status %d",
+				    r);
+				return (ARCHIVE_FATAL);
+			}
+		}
+
+		if (flush == Z_FINISH) {
+			/*
+			 * Save the information of one zisofs block.
+			 */
+			iso9660->zisofs.block_pointers_idx ++;
+			archive_le32enc(&(iso9660->zisofs.block_pointers[
+			    iso9660->zisofs.block_pointers_idx]),
+				(uint32_t)iso9660->zisofs.total_size);
+			r = zisofs_init_zstream(a);
+			if (r != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			iso9660->zisofs.allzero = 1;
+			iso9660->zisofs.block_offset = file->cur_content->size;
+		}
+	} while (s);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+zisofs_finish_entry(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct isofile *file = iso9660->cur_file;
+	unsigned char buff[16];
+	size_t s;
+	int64_t tail;
+
+	/* Direct temp file stream to zisofs temp file stream. */
+	archive_entry_set_size(file->entry, iso9660->zisofs.total_size);
+
+	/*
+	 * Save a file pointer which points the end of current zisofs data.
+	 */
+	tail = wb_offset(a);
+
+	/*
+	 * Make a header.
+	 *
+	 * +-----------------+----------------+-----------------+
+	 * | Header 16 bytes | Block Pointers | Compressed data |
+	 * +-----------------+----------------+-----------------+
+	 * 0                16               +X
+	 * Block Pointers :
+	 *   4 * (((Uncompressed file size + block_size -1) / block_size) + 1)
+	 *
+	 * Write zisofs header.
+	 *    Magic number
+	 * +----+----+----+----+----+----+----+----+
+	 * | 37 | E4 | 53 | 96 | C9 | DB | D6 | 07 |
+	 * +----+----+----+----+----+----+----+----+
+	 * 0    1    2    3    4    5    6    7    8
+	 *
+	 * +------------------------+------------------+
+	 * | Uncompressed file size | header_size >> 2 |
+	 * +------------------------+------------------+
+	 * 8                       12                 13
+	 *
+	 * +-----------------+----------------+
+	 * | log2 block_size | Reserved(0000) |
+	 * +-----------------+----------------+
+	 * 13               14               16
+	 */
+	memcpy(buff, zisofs_magic, 8);
+	set_num_731(buff+8, file->zisofs.uncompressed_size);
+	buff[12] = file->zisofs.header_size;
+	buff[13] = file->zisofs.log2_bs;
+	buff[14] = buff[15] = 0;/* Reserved */
+
+	/* Move to the right position to write the header. */
+	wb_set_offset(a, file->content.offset_of_temp);
+
+	/* Write the header. */
+	if (wb_write_to_temp(a, buff, 16) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/*
+	 * Write zisofs Block Pointers.
+	 */
+	s = iso9660->zisofs.block_pointers_cnt *
+	    sizeof(iso9660->zisofs.block_pointers[0]);
+	if (wb_write_to_temp(a, iso9660->zisofs.block_pointers, s)
+	    != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Set a file pointer back to the end of the temporary file. */
+	wb_set_offset(a, tail);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+zisofs_free(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	int ret = ARCHIVE_OK;
+
+	free(iso9660->zisofs.block_pointers);
+	if (iso9660->zisofs.stream_valid &&
+	    deflateEnd(&(iso9660->zisofs.stream)) != Z_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to clean up compressor");
+		ret = ARCHIVE_FATAL;
+	}
+	iso9660->zisofs.block_pointers = NULL;
+	iso9660->zisofs.stream_valid = 0;
+	return (ret);
+}
+
+struct zisofs_extract {
+	int		 pz_log2_bs; /* Log2 of block size */
+	uint64_t	 pz_uncompressed_size;
+	size_t		 uncompressed_buffer_size;
+
+	signed int	 initialized:1;
+	signed int	 header_passed:1;
+
+	uint32_t	 pz_offset;
+	unsigned char	*block_pointers;
+	size_t		 block_pointers_size;
+	size_t		 block_pointers_avail;
+	size_t		 block_off;
+	uint32_t	 block_avail;
+
+	z_stream	 stream;
+	int		 stream_valid;
+};
+
+static ssize_t
+zisofs_extract_init(struct archive_write *a, struct zisofs_extract *zisofs,
+    const unsigned char *p, size_t bytes)
+{
+	size_t avail = bytes;
+	size_t _ceil, xsize;
+
+	/* Allocate block pointers buffer. */
+	_ceil = (size_t)((zisofs->pz_uncompressed_size +
+		(((int64_t)1) << zisofs->pz_log2_bs) - 1)
+		>> zisofs->pz_log2_bs);
+	xsize = (_ceil + 1) * 4;
+	if (zisofs->block_pointers == NULL) {
+		size_t alloc = ((xsize >> 10) + 1) << 10;
+		zisofs->block_pointers = malloc(alloc);
+		if (zisofs->block_pointers == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "No memory for zisofs decompression");
+			return (ARCHIVE_FATAL);
+		}
+	}
+	zisofs->block_pointers_size = xsize;
+
+	/* Allocate uncompressed data buffer. */
+	zisofs->uncompressed_buffer_size = (size_t)1UL << zisofs->pz_log2_bs;
+
+	/*
+	 * Read the file header, and check the magic code of zisofs.
+	 */
+	if (!zisofs->header_passed) {
+		int err = 0;
+		if (avail < 16) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Illegal zisofs file body");
+			return (ARCHIVE_FATAL);
+		}
+
+		if (memcmp(p, zisofs_magic, sizeof(zisofs_magic)) != 0)
+			err = 1;
+		else if (archive_le32dec(p + 8) != zisofs->pz_uncompressed_size)
+			err = 1;
+		else if (p[12] != 4 || p[13] != zisofs->pz_log2_bs)
+			err = 1;
+		if (err) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Illegal zisofs file body");
+			return (ARCHIVE_FATAL);
+		}
+		avail -= 16;
+		p += 16;
+		zisofs->header_passed = 1;
+	}
+
+	/*
+	 * Read block pointers.
+	 */
+	if (zisofs->header_passed &&
+	    zisofs->block_pointers_avail < zisofs->block_pointers_size) {
+		xsize = zisofs->block_pointers_size
+		    - zisofs->block_pointers_avail;
+		if (avail < xsize)
+			xsize = avail;
+		memcpy(zisofs->block_pointers
+		    + zisofs->block_pointers_avail, p, xsize);
+		zisofs->block_pointers_avail += xsize;
+		avail -= xsize;
+	    	if (zisofs->block_pointers_avail
+		    == zisofs->block_pointers_size) {
+			/* We've got all block pointers and initialize
+			 * related variables.	*/
+			zisofs->block_off = 0;
+			zisofs->block_avail = 0;
+			/* Complete a initialization */
+			zisofs->initialized = 1;
+		}
+	}
+	return ((ssize_t)avail);
+}
+
+static ssize_t
+zisofs_extract(struct archive_write *a, struct zisofs_extract *zisofs,
+    const unsigned char *p, size_t bytes)
+{
+	size_t avail;
+	int r;
+
+	if (!zisofs->initialized) {
+		ssize_t rs = zisofs_extract_init(a, zisofs, p, bytes);
+		if (rs < 0)
+			return (rs);
+		if (!zisofs->initialized) {
+			/* We need more data. */
+			zisofs->pz_offset += (uint32_t)bytes;
+			return (bytes);
+		}
+		avail = rs;
+		p += bytes - avail;
+	} else
+		avail = bytes;
+
+	/*
+	 * Get block offsets from block pointers.
+	 */
+	if (zisofs->block_avail == 0) {
+		uint32_t bst, bed;
+
+		if (zisofs->block_off + 4 >= zisofs->block_pointers_size) {
+			/* There isn't a pair of offsets. */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Illegal zisofs block pointers");
+			return (ARCHIVE_FATAL);
+		}
+		bst = archive_le32dec(
+		    zisofs->block_pointers + zisofs->block_off);
+		if (bst != zisofs->pz_offset + (bytes - avail)) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Illegal zisofs block pointers(cannot seek)");
+			return (ARCHIVE_FATAL);
+		}
+		bed = archive_le32dec(
+		    zisofs->block_pointers + zisofs->block_off + 4);
+		if (bed < bst) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Illegal zisofs block pointers");
+			return (ARCHIVE_FATAL);
+		}
+		zisofs->block_avail = bed - bst;
+		zisofs->block_off += 4;
+
+		/* Initialize compression library for new block. */
+		if (zisofs->stream_valid)
+			r = inflateReset(&zisofs->stream);
+		else
+			r = inflateInit(&zisofs->stream);
+		if (r != Z_OK) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Can't initialize zisofs decompression.");
+			return (ARCHIVE_FATAL);
+		}
+		zisofs->stream_valid = 1;
+		zisofs->stream.total_in = 0;
+		zisofs->stream.total_out = 0;
+	}
+
+	/*
+	 * Make uncompressed data.
+	 */
+	if (zisofs->block_avail == 0) {
+		/*
+		 * It's basically 32K bytes NUL data.
+		 */
+		unsigned char *wb;
+		size_t size, wsize;
+
+		size = zisofs->uncompressed_buffer_size;
+		while (size) {
+			wb = wb_buffptr(a);
+			if (size > wb_remaining(a))
+				wsize = wb_remaining(a);
+			else
+				wsize = size;
+			memset(wb, 0, wsize);
+			r = wb_consume(a, wsize);
+			if (r < 0)
+				return (r);
+			size -= wsize;
+		}
+	} else {
+		zisofs->stream.next_in = (Bytef *)(uintptr_t)(const void *)p;
+		if (avail > zisofs->block_avail)
+			zisofs->stream.avail_in = zisofs->block_avail;
+		else
+			zisofs->stream.avail_in = (uInt)avail;
+		zisofs->stream.next_out = wb_buffptr(a);
+		zisofs->stream.avail_out = (uInt)wb_remaining(a);
+
+		r = inflate(&zisofs->stream, 0);
+		switch (r) {
+		case Z_OK: /* Decompressor made some progress.*/
+		case Z_STREAM_END: /* Found end of stream. */
+			break;
+		default:
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "zisofs decompression failed (%d)", r);
+			return (ARCHIVE_FATAL);
+		}
+		avail -= zisofs->stream.next_in - p;
+		zisofs->block_avail -= (uint32_t)(zisofs->stream.next_in - p);
+		r = wb_consume(a, wb_remaining(a) - zisofs->stream.avail_out);
+		if (r < 0)
+			return (r);
+	}
+	zisofs->pz_offset += (uint32_t)bytes;
+	return (bytes - avail);
+}
+
+static int
+zisofs_rewind_boot_file(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+	struct isofile *file;
+	unsigned char *rbuff;
+	ssize_t r;
+	size_t remaining, rbuff_size;
+	struct zisofs_extract zext;
+	int64_t read_offset, write_offset, new_offset;
+	int fd, ret = ARCHIVE_OK;
+
+	file = iso9660->el_torito.boot->file;
+	/*
+	 * There is nothing to do if this boot file does not have
+	 * zisofs header.
+	 */
+	if (file->zisofs.header_size == 0)
+		return (ARCHIVE_OK);
+
+	/*
+	 * Uncompress the zisofs'ed file contents.
+	 */
+	memset(&zext, 0, sizeof(zext));
+	zext.pz_uncompressed_size = file->zisofs.uncompressed_size;
+	zext.pz_log2_bs = file->zisofs.log2_bs;
+
+	fd = iso9660->temp_fd;
+	new_offset = wb_offset(a);
+	read_offset = file->content.offset_of_temp;
+	remaining = (size_t)file->content.size;
+	if (remaining > 1024 * 32)
+		rbuff_size = 1024 * 32;
+	else
+		rbuff_size = remaining;
+
+	rbuff = malloc(rbuff_size);
+	if (rbuff == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	while (remaining) {
+		size_t rsize;
+		ssize_t rs;
+
+		/* Get the current file pointer. */
+		write_offset = lseek(fd, 0, SEEK_CUR);
+
+		/* Change the file pointer to read. */
+		lseek(fd, read_offset, SEEK_SET);
+
+		rsize = rbuff_size;
+		if (rsize > remaining)
+			rsize = remaining;
+		rs = read(iso9660->temp_fd, rbuff, rsize);
+		if (rs <= 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't read temporary file(%jd)", (intmax_t)rs);
+			ret = ARCHIVE_FATAL;
+			break;
+		}
+		remaining -= rs;
+		read_offset += rs;
+
+		/* Put the file pointer back to write. */
+		lseek(fd, write_offset, SEEK_SET);
+
+		r = zisofs_extract(a, &zext, rbuff, rs);
+		if (r < 0) {
+			ret = (int)r;
+			break;
+		}
+	}
+
+	if (ret == ARCHIVE_OK) {
+		/*
+		 * Change the boot file content from zisofs'ed data
+		 * to plain data.
+		 */
+		file->content.offset_of_temp = new_offset;
+		file->content.size = file->zisofs.uncompressed_size;
+		archive_entry_set_size(file->entry, file->content.size);
+		/* Set to be no zisofs. */
+		file->zisofs.header_size = 0;
+		file->zisofs.log2_bs = 0;
+		file->zisofs.uncompressed_size = 0;
+		r = wb_write_padding_to_temp(a, file->content.size);
+		if (r < 0)
+			ret = ARCHIVE_FATAL;
+	}
+
+	/*
+	 * Free the resource we used in this function only.
+	 */
+	free(rbuff);
+	free(zext.block_pointers);
+	if (zext.stream_valid && inflateEnd(&(zext.stream)) != Z_OK) {
+        	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to clean up compressor");
+		ret = ARCHIVE_FATAL;
+	}
+
+	return (ret);
+}
+
+#else
+
+static int
+zisofs_write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+	(void)buff; /* UNUSED */
+	(void)s; /* UNUSED */
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Programming error");
+	return (ARCHIVE_FATAL);
+}
+
+static int
+zisofs_rewind_boot_file(struct archive_write *a)
+{
+	struct iso9660 *iso9660 = a->format_data;
+
+	if (iso9660->el_torito.boot->file->zisofs.header_size != 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "We cannot extract the zisofs imaged boot file;"
+		    " this may not boot in being zisofs imaged");
+		return (ARCHIVE_FAILED);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+zisofs_finish_entry(struct archive_write *a)
+{
+	(void)a; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+static int
+zisofs_free(struct archive_write *a)
+{
+	(void)a; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+#endif /* HAVE_ZLIB_H */
+
diff --git a/c/archive_write_set_format_mtree.c b/c/archive_write_set_format_mtree.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_mtree.c
@@ -0,0 +1,2228 @@
+/*-
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_write_set_format_mtree.c 201171 2009-12-29 06:39:07Z kientzle $");
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+#define INDENTNAMELEN	15
+#define MAXLINELEN	80
+#define SET_KEYS	\
+	(F_FLAGS | F_GID | F_GNAME | F_MODE | F_TYPE | F_UID | F_UNAME)
+
+struct attr_counter {
+	struct attr_counter *prev;
+	struct attr_counter *next;
+	struct mtree_entry *m_entry;
+	int count;
+};
+
+struct att_counter_set {
+	struct attr_counter *uid_list;
+	struct attr_counter *gid_list;
+	struct attr_counter *mode_list;
+	struct attr_counter *flags_list;
+};
+
+struct mtree_chain {
+	struct mtree_entry *first;
+	struct mtree_entry **last;
+};
+
+/*
+ * The Data only for a directory file.
+ */
+struct dir_info {
+	struct archive_rb_tree rbtree;
+	struct mtree_chain children;
+	struct mtree_entry *chnext;
+	int virtual;
+};
+
+/*
+ * The Data only for a regular file.
+ */
+struct reg_info {
+	int compute_sum;
+	uint32_t crc;
+#ifdef ARCHIVE_HAS_MD5
+	unsigned char buf_md5[16];
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	unsigned char buf_rmd160[20];
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	unsigned char buf_sha1[20];
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	unsigned char buf_sha256[32];
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	unsigned char buf_sha384[48];
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	unsigned char buf_sha512[64];
+#endif
+};
+
+struct mtree_entry {
+	struct archive_rb_node rbnode;
+	struct mtree_entry *next;
+	struct mtree_entry *parent;
+	struct dir_info *dir_info;
+	struct reg_info *reg_info;
+
+	struct archive_string parentdir;
+	struct archive_string basename;
+	struct archive_string pathname;
+	struct archive_string symlink;
+	struct archive_string uname;
+	struct archive_string gname;
+	struct archive_string fflags_text;
+	unsigned int nlink;
+	mode_t filetype;
+	mode_t mode;
+	int64_t size;
+	int64_t uid;
+	int64_t gid;
+	time_t mtime;
+	long mtime_nsec;
+	unsigned long fflags_set;
+	unsigned long fflags_clear;
+	dev_t rdevmajor;
+	dev_t rdevminor;
+	dev_t devmajor;
+	dev_t devminor;
+	int64_t ino;
+};
+
+struct mtree_writer {
+	struct mtree_entry *mtree_entry;
+	struct mtree_entry *root;
+	struct mtree_entry *cur_dirent;
+	struct archive_string cur_dirstr;
+	struct mtree_chain file_list;
+
+	struct archive_string ebuf;
+	struct archive_string buf;
+	int first;
+	uint64_t entry_bytes_remaining;
+
+	/*
+	 * Set global value.
+	 */
+	struct {
+		int		processing;
+		mode_t		type;
+		int		keys;
+		int64_t		uid;
+		int64_t		gid;
+		mode_t		mode;
+		unsigned long	fflags_set;
+		unsigned long	fflags_clear;
+	} set;
+	struct att_counter_set	acs;
+	int classic;
+	int depth;
+
+	/* check sum */
+	int compute_sum;
+	uint32_t crc;
+	uint64_t crc_len;
+#ifdef ARCHIVE_HAS_MD5
+	archive_md5_ctx md5ctx;
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	archive_rmd160_ctx rmd160ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	archive_sha1_ctx sha1ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	archive_sha256_ctx sha256ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	archive_sha384_ctx sha384ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	archive_sha512_ctx sha512ctx;
+#endif
+	/* Keyword options */
+	int keys;
+#define	F_CKSUM		0x00000001		/* checksum */
+#define	F_DEV		0x00000002		/* device type */
+#define	F_DONE		0x00000004		/* directory done */
+#define	F_FLAGS		0x00000008		/* file flags */
+#define	F_GID		0x00000010		/* gid */
+#define	F_GNAME		0x00000020		/* group name */
+#define	F_IGN		0x00000040		/* ignore */
+#define	F_MAGIC		0x00000080		/* name has magic chars */
+#define	F_MD5		0x00000100		/* MD5 digest */
+#define	F_MODE		0x00000200		/* mode */
+#define	F_NLINK		0x00000400		/* number of links */
+#define	F_NOCHANGE 	0x00000800		/* If owner/mode "wrong", do
+						 * not change */
+#define	F_OPT		0x00001000		/* existence optional */
+#define	F_RMD160 	0x00002000		/* RIPEMD160 digest */
+#define	F_SHA1		0x00004000		/* SHA-1 digest */
+#define	F_SIZE		0x00008000		/* size */
+#define	F_SLINK		0x00010000		/* symbolic link */
+#define	F_TAGS		0x00020000		/* tags */
+#define	F_TIME		0x00040000		/* modification time */
+#define	F_TYPE		0x00080000		/* file type */
+#define	F_UID		0x00100000		/* uid */
+#define	F_UNAME		0x00200000		/* user name */
+#define	F_VISIT		0x00400000		/* file visited */
+#define	F_SHA256	0x00800000		/* SHA-256 digest */
+#define	F_SHA384	0x01000000		/* SHA-384 digest */
+#define	F_SHA512	0x02000000		/* SHA-512 digest */
+#define	F_INO		0x04000000		/* inode number */
+#define	F_RESDEV	0x08000000		/* device ID on which the
+						 * entry resides */
+
+	/* Options */
+	int dironly;		/* If it is set, ignore all files except
+				 * directory files, like mtree(8) -d option. */
+	int indent;		/* If it is set, indent output data. */
+	int output_global_set;	/* If it is set, use /set keyword to set
+				 * global values. When generating mtree
+				 * classic format, it is set by default. */
+};
+
+#define DEFAULT_KEYS	(F_DEV | F_FLAGS | F_GID | F_GNAME | F_SLINK | F_MODE\
+			 | F_NLINK | F_SIZE | F_TIME | F_TYPE | F_UID\
+			 | F_UNAME)
+#define attr_counter_set_reset	attr_counter_set_free
+
+static void attr_counter_free(struct attr_counter **);
+static int attr_counter_inc(struct attr_counter **, struct attr_counter *,
+	struct attr_counter *, struct mtree_entry *);
+static struct attr_counter * attr_counter_new(struct mtree_entry *,
+	struct attr_counter *);
+static int attr_counter_set_collect(struct mtree_writer *,
+	struct mtree_entry *);
+static void attr_counter_set_free(struct mtree_writer *);
+static int get_global_set_keys(struct mtree_writer *, struct mtree_entry *);
+static int mtree_entry_add_child_tail(struct mtree_entry *,
+	struct mtree_entry *);
+static int mtree_entry_create_virtual_dir(struct archive_write *, const char *,
+	struct mtree_entry **);
+static int mtree_entry_cmp_node(const struct archive_rb_node *,
+	const struct archive_rb_node *);
+static int mtree_entry_cmp_key(const struct archive_rb_node *, const void *);
+static int mtree_entry_exchange_same_entry(struct archive_write *,
+    struct mtree_entry *, struct mtree_entry *);
+static void mtree_entry_free(struct mtree_entry *);
+static int mtree_entry_new(struct archive_write *, struct archive_entry *,
+	struct mtree_entry **);
+static void mtree_entry_register_free(struct mtree_writer *);
+static void mtree_entry_register_init(struct mtree_writer *);
+static int mtree_entry_setup_filenames(struct archive_write *,
+	struct mtree_entry *, struct archive_entry *);
+static int mtree_entry_tree_add(struct archive_write *, struct mtree_entry **);
+static void sum_init(struct mtree_writer *);
+static void sum_update(struct mtree_writer *, const void *, size_t);
+static void sum_final(struct mtree_writer *, struct reg_info *);
+static void sum_write(struct archive_string *, struct reg_info *);
+static int write_mtree_entry(struct archive_write *, struct mtree_entry *);
+static int write_dot_dot_entry(struct archive_write *, struct mtree_entry *);
+
+#define	COMPUTE_CRC(var, ch)	(var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
+static const uint32_t crctab[] = {
+	0x0,
+	0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+	0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+	0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+	0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+	0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
+	0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+	0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+	0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
+	0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+	0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+	0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+	0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+	0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+	0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+	0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
+	0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+	0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+	0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
+	0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+	0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+	0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+	0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+	0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+	0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+	0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+	0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
+	0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+	0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+	0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
+	0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+	0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+	0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+	0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+	0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+	0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+	0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
+	0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+	0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+	0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
+	0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+	0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+	0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+	0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+	0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+	0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+static const unsigned char safe_char[256] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */
+	/* !"$%&'()*+,-./  EXCLUSION:0x20( ) 0x23(#) */
+	0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */
+	/* 0123456789:;<>?  EXCLUSION:0x3d(=) */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, /* 30 - 3F */
+	/* @ABCDEFGHIJKLMNO */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */
+	/* PQRSTUVWXYZ[]^_ EXCLUSION:0x5c(\)  */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 50 - 5F */
+	/* `abcdefghijklmno */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */
+	/* pqrstuvwxyz{|}~ */
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
+};
+
+static void
+mtree_quote(struct archive_string *s, const char *str)
+{
+	const char *start;
+	char buf[4];
+	unsigned char c;
+
+	for (start = str; *str != '\0'; ++str) {
+		if (safe_char[*(const unsigned char *)str])
+			continue;
+		if (start != str)
+			archive_strncat(s, start, str - start);
+		c = (unsigned char)*str;
+		buf[0] = '\\';
+		buf[1] = (c / 64) + '0';
+		buf[2] = (c / 8 % 8) + '0';
+		buf[3] = (c % 8) + '0';
+		archive_strncat(s, buf, 4);
+		start = str + 1;
+	}
+
+	if (start != str)
+		archive_strncat(s, start, str - start);
+}
+
+/*
+ * Indent a line as the mtree utility does so it is readable for people.
+ */
+static void
+mtree_indent(struct mtree_writer *mtree)
+{
+	int i, fn, nd, pd;
+	const char *r, *s, *x;
+
+	if (mtree->classic) {
+		if (mtree->indent) {
+			nd = 0;
+			pd = mtree->depth * 4;
+		} else {
+			nd = mtree->depth?4:0;
+			pd = 0;
+		}
+	} else
+		nd = pd = 0;
+	fn = 1;
+	s = r = mtree->ebuf.s;
+	x = NULL;
+	while (*r == ' ')
+		r++;
+	while ((r = strchr(r, ' ')) != NULL) {
+		if (fn) {
+			fn = 0;
+			for (i = 0; i < nd + pd; i++)
+				archive_strappend_char(&mtree->buf, ' ');
+			archive_strncat(&mtree->buf, s, r - s);
+			if (nd + (r -s) > INDENTNAMELEN) {
+				archive_strncat(&mtree->buf, " \\\n", 3);
+				for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
+					archive_strappend_char(&mtree->buf, ' ');
+			} else {
+				for (i = (int)(r -s + nd);
+				    i < (INDENTNAMELEN + 1); i++)
+					archive_strappend_char(&mtree->buf, ' ');
+			}
+			s = ++r;
+			x = NULL;
+			continue;
+		}
+		if (pd + (r - s) <= MAXLINELEN - 3 - INDENTNAMELEN)
+			x = r++;
+		else {
+			if (x == NULL)
+				x = r;
+			archive_strncat(&mtree->buf, s, x - s);
+			archive_strncat(&mtree->buf, " \\\n", 3);
+			for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
+				archive_strappend_char(&mtree->buf, ' ');
+			s = r = ++x;
+			x = NULL;
+		}
+	}
+	if (fn) {
+		for (i = 0; i < nd + pd; i++)
+			archive_strappend_char(&mtree->buf, ' ');
+		archive_strcat(&mtree->buf, s);
+		s += strlen(s);
+	}
+	if (x != NULL && pd + strlen(s) > MAXLINELEN - 3 - INDENTNAMELEN) {
+		/* Last keyword is longer. */
+		archive_strncat(&mtree->buf, s, x - s);
+		archive_strncat(&mtree->buf, " \\\n", 3);
+		for (i = 0; i < (INDENTNAMELEN + 1 + pd); i++)
+			archive_strappend_char(&mtree->buf, ' ');
+		s = ++x;
+	}
+	archive_strcat(&mtree->buf, s);
+	archive_string_empty(&mtree->ebuf);
+}
+
+/*
+ * Write /set keyword.
+ * Set the most used value of uid, gid, mode and fflags, which are
+ * collected by the attr_counter_set_collect() function.
+ */
+static void
+write_global(struct mtree_writer *mtree)
+{
+	struct archive_string setstr;
+	struct archive_string unsetstr;
+	struct att_counter_set *acs;
+	int keys, oldkeys, effkeys;
+
+	archive_string_init(&setstr);
+	archive_string_init(&unsetstr);
+	keys = mtree->keys & SET_KEYS;
+	oldkeys = mtree->set.keys;
+	effkeys = keys;
+	acs = &mtree->acs;
+	if (mtree->set.processing) {
+		/*
+		 * Check if the global data needs updating.
+		 */
+		effkeys &= ~F_TYPE;
+		if (acs->uid_list == NULL)
+			effkeys &= ~(F_UNAME | F_UID);
+		else if (oldkeys & (F_UNAME | F_UID)) {
+			if (acs->uid_list->count < 2 ||
+			    mtree->set.uid == acs->uid_list->m_entry->uid)
+				effkeys &= ~(F_UNAME | F_UID);
+		}
+		if (acs->gid_list == NULL)
+			effkeys &= ~(F_GNAME | F_GID);
+		else if (oldkeys & (F_GNAME | F_GID)) {
+			if (acs->gid_list->count < 2 ||
+			    mtree->set.gid == acs->gid_list->m_entry->gid)
+				effkeys &= ~(F_GNAME | F_GID);
+		}
+		if (acs->mode_list == NULL)
+			effkeys &= ~F_MODE;
+		else if (oldkeys & F_MODE) {
+			if (acs->mode_list->count < 2 ||
+			    mtree->set.mode == acs->mode_list->m_entry->mode)
+				effkeys &= ~F_MODE;
+		}
+		if (acs->flags_list == NULL)
+			effkeys &= ~F_FLAGS;
+		else if ((oldkeys & F_FLAGS) != 0) {
+			if (acs->flags_list->count < 2 ||
+			    (acs->flags_list->m_entry->fflags_set ==
+				mtree->set.fflags_set &&
+			     acs->flags_list->m_entry->fflags_clear ==
+				mtree->set.fflags_clear))
+				effkeys &= ~F_FLAGS;
+		}
+	} else {
+		if (acs->uid_list == NULL)
+			keys &= ~(F_UNAME | F_UID);
+		if (acs->gid_list == NULL)
+			keys &= ~(F_GNAME | F_GID);
+		if (acs->mode_list == NULL)
+			keys &= ~F_MODE;
+		if (acs->flags_list == NULL)
+			keys &= ~F_FLAGS;
+	}
+	if ((keys & effkeys & F_TYPE) != 0) {
+		if (mtree->dironly) {
+			archive_strcat(&setstr, " type=dir");
+			mtree->set.type = AE_IFDIR;
+		} else {
+			archive_strcat(&setstr, " type=file");
+			mtree->set.type = AE_IFREG;
+		}
+	}
+	if ((keys & effkeys & F_UNAME) != 0) {
+		if (archive_strlen(&(acs->uid_list->m_entry->uname)) > 0) {
+			archive_strcat(&setstr, " uname=");
+			mtree_quote(&setstr, acs->uid_list->m_entry->uname.s);
+		} else {
+			keys &= ~F_UNAME;
+			if ((oldkeys & F_UNAME) != 0)
+				archive_strcat(&unsetstr, " uname");
+		}
+	}
+	if ((keys & effkeys & F_UID) != 0) {
+		mtree->set.uid = acs->uid_list->m_entry->uid;
+		archive_string_sprintf(&setstr, " uid=%jd",
+		    (intmax_t)mtree->set.uid);
+	}
+	if ((keys & effkeys & F_GNAME) != 0) {
+		if (archive_strlen(&(acs->gid_list->m_entry->gname)) > 0) {
+			archive_strcat(&setstr, " gname=");
+			mtree_quote(&setstr, acs->gid_list->m_entry->gname.s);
+		} else {
+			keys &= ~F_GNAME;
+			if ((oldkeys & F_GNAME) != 0)
+				archive_strcat(&unsetstr, " gname");
+		}
+	}
+	if ((keys & effkeys & F_GID) != 0) {
+		mtree->set.gid = acs->gid_list->m_entry->gid;
+		archive_string_sprintf(&setstr, " gid=%jd",
+		    (intmax_t)mtree->set.gid);
+	}
+	if ((keys & effkeys & F_MODE) != 0) {
+		mtree->set.mode = acs->mode_list->m_entry->mode;
+		archive_string_sprintf(&setstr, " mode=%o",
+		    (unsigned int)mtree->set.mode);
+	}
+	if ((keys & effkeys & F_FLAGS) != 0) {
+		if (archive_strlen(
+		    &(acs->flags_list->m_entry->fflags_text)) > 0) {
+			archive_strcat(&setstr, " flags=");
+			mtree_quote(&setstr,
+			    acs->flags_list->m_entry->fflags_text.s);
+			mtree->set.fflags_set =
+			    acs->flags_list->m_entry->fflags_set;
+			mtree->set.fflags_clear =
+			    acs->flags_list->m_entry->fflags_clear;
+		} else {
+			keys &= ~F_FLAGS;
+			if ((oldkeys & F_FLAGS) != 0)
+				archive_strcat(&unsetstr, " flags");
+		}
+	}
+	if (unsetstr.length > 0)
+		archive_string_sprintf(&mtree->buf, "/unset%s\n", unsetstr.s);
+	archive_string_free(&unsetstr);
+	if (setstr.length > 0)
+		archive_string_sprintf(&mtree->buf, "/set%s\n", setstr.s);
+	archive_string_free(&setstr);
+	mtree->set.keys = keys;
+	mtree->set.processing = 1;
+}
+
+static struct attr_counter *
+attr_counter_new(struct mtree_entry *me, struct attr_counter *prev)
+{
+	struct attr_counter *ac;
+
+	ac = malloc(sizeof(*ac));
+	if (ac != NULL) {
+		ac->prev = prev;
+		ac->next = NULL;
+		ac->count = 1;
+		ac->m_entry = me;
+	}
+	return (ac);
+}
+
+static void
+attr_counter_free(struct attr_counter **top)
+{
+	struct attr_counter *ac, *tac;
+
+	if (*top == NULL)
+		return;
+	ac = *top;
+        while (ac != NULL) {
+		tac = ac->next;
+		free(ac);
+		ac = tac;
+	}
+	*top = NULL;
+}
+
+static int
+attr_counter_inc(struct attr_counter **top, struct attr_counter *ac,
+    struct attr_counter *last, struct mtree_entry *me)
+{
+	struct attr_counter *pac;
+
+	if (ac != NULL) {
+		ac->count++;
+		if (*top == ac || ac->prev->count >= ac->count)
+			return (0);
+		for (pac = ac->prev; pac; pac = pac->prev) {
+			if (pac->count >= ac->count)
+				break;
+		}
+		ac->prev->next = ac->next;
+		if (ac->next != NULL)
+			ac->next->prev = ac->prev;
+		if (pac != NULL) {
+			ac->prev = pac;
+			ac->next = pac->next;
+			pac->next = ac;
+			if (ac->next != NULL)
+				ac->next->prev = ac;
+		} else {
+			ac->prev = NULL;
+			ac->next = *top;
+			*top = ac;
+			ac->next->prev = ac;
+		}
+	} else if (last != NULL) {
+		ac = attr_counter_new(me, last);
+		if (ac == NULL)
+			return (-1);
+		last->next = ac;
+	}
+	return (0);
+}
+
+/*
+ * Tabulate uid, gid, mode and fflags of a entry in order to be used for /set.
+ */
+static int
+attr_counter_set_collect(struct mtree_writer *mtree, struct mtree_entry *me)
+{
+	struct attr_counter *ac, *last;
+	struct att_counter_set *acs = &mtree->acs;
+	int keys = mtree->keys;
+
+	if (keys & (F_UNAME | F_UID)) {
+		if (acs->uid_list == NULL) {
+			acs->uid_list = attr_counter_new(me, NULL);
+			if (acs->uid_list == NULL)
+				return (-1);
+		} else {
+			last = NULL;
+			for (ac = acs->uid_list; ac; ac = ac->next) {
+				if (ac->m_entry->uid == me->uid)
+					break;
+				last = ac;
+			}
+			if (attr_counter_inc(&acs->uid_list, ac, last, me) < 0)
+				return (-1);
+		}
+	}
+	if (keys & (F_GNAME | F_GID)) {
+		if (acs->gid_list == NULL) {
+			acs->gid_list = attr_counter_new(me, NULL);
+			if (acs->gid_list == NULL)
+				return (-1);
+		} else {
+			last = NULL;
+			for (ac = acs->gid_list; ac; ac = ac->next) {
+				if (ac->m_entry->gid == me->gid)
+					break;
+				last = ac;
+			}
+			if (attr_counter_inc(&acs->gid_list, ac, last, me) < 0)
+				return (-1);
+		}
+	}
+	if (keys & F_MODE) {
+		if (acs->mode_list == NULL) {
+			acs->mode_list = attr_counter_new(me, NULL);
+			if (acs->mode_list == NULL)
+				return (-1);
+		} else {
+			last = NULL;
+			for (ac = acs->mode_list; ac; ac = ac->next) {
+				if (ac->m_entry->mode == me->mode)
+					break;
+				last = ac;
+			}
+			if (attr_counter_inc(&acs->mode_list, ac, last, me) < 0)
+				return (-1);
+		}
+	}
+	if (keys & F_FLAGS) {
+		if (acs->flags_list == NULL) {
+			acs->flags_list = attr_counter_new(me, NULL);
+			if (acs->flags_list == NULL)
+				return (-1);
+		} else {
+			last = NULL;
+			for (ac = acs->flags_list; ac; ac = ac->next) {
+				if (ac->m_entry->fflags_set == me->fflags_set &&
+				    ac->m_entry->fflags_clear ==
+							me->fflags_clear)
+					break;
+				last = ac;
+			}
+			if (attr_counter_inc(&acs->flags_list, ac, last, me) < 0)
+				return (-1);
+		}
+	}
+
+	return (0);
+}
+
+static void
+attr_counter_set_free(struct mtree_writer *mtree)
+{
+	struct att_counter_set *acs = &mtree->acs;
+
+	attr_counter_free(&acs->uid_list);
+	attr_counter_free(&acs->gid_list);
+	attr_counter_free(&acs->mode_list);
+	attr_counter_free(&acs->flags_list);
+}
+
+static int
+get_global_set_keys(struct mtree_writer *mtree, struct mtree_entry *me)
+{
+	int keys;
+
+	keys = mtree->keys;
+
+	/*
+	 * If a keyword has been set by /set, we do not need to
+	 * output it.
+	 */
+	if (mtree->set.keys == 0)
+		return (keys);/* /set is not used. */
+
+	if ((mtree->set.keys & (F_GNAME | F_GID)) != 0 &&
+	     mtree->set.gid == me->gid)
+		keys &= ~(F_GNAME | F_GID);
+	if ((mtree->set.keys & (F_UNAME | F_UID)) != 0 &&
+	     mtree->set.uid == me->uid)
+		keys &= ~(F_UNAME | F_UID);
+	if (mtree->set.keys & F_FLAGS) {
+		if (mtree->set.fflags_set == me->fflags_set &&
+		    mtree->set.fflags_clear == me->fflags_clear)
+			keys &= ~F_FLAGS;
+	}
+	if ((mtree->set.keys & F_MODE) != 0 && mtree->set.mode == me->mode)
+		keys &= ~F_MODE;
+
+	switch (me->filetype) {
+	case AE_IFLNK: case AE_IFSOCK: case AE_IFCHR:
+	case AE_IFBLK: case AE_IFIFO:
+		break;
+	case AE_IFDIR:
+		if ((mtree->set.keys & F_TYPE) != 0 &&
+		    mtree->set.type == AE_IFDIR)
+			keys &= ~F_TYPE;
+		break;
+	case AE_IFREG:
+	default:	/* Handle unknown file types as regular files. */
+		if ((mtree->set.keys & F_TYPE) != 0 &&
+		    mtree->set.type == AE_IFREG)
+			keys &= ~F_TYPE;
+		break;
+	}
+
+	return (keys);
+}
+
+static int
+mtree_entry_new(struct archive_write *a, struct archive_entry *entry,
+    struct mtree_entry **m_entry)
+{
+	struct mtree_entry *me;
+	const char *s;
+	int r;
+	static const struct archive_rb_tree_ops rb_ops = {
+		mtree_entry_cmp_node, mtree_entry_cmp_key
+	};
+
+	me = calloc(1, sizeof(*me));
+	if (me == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory for a mtree entry");
+		*m_entry = NULL;
+		return (ARCHIVE_FATAL);
+	}
+
+	r = mtree_entry_setup_filenames(a, me, entry);
+	if (r < ARCHIVE_WARN) {
+		mtree_entry_free(me);
+		*m_entry = NULL;
+		return (r);
+	}
+
+	if ((s = archive_entry_symlink(entry)) != NULL)
+		archive_strcpy(&me->symlink, s);
+	me->nlink = archive_entry_nlink(entry);
+	me->filetype = archive_entry_filetype(entry);
+	me->mode = archive_entry_mode(entry) & 07777;
+	me->uid = archive_entry_uid(entry);
+	me->gid = archive_entry_gid(entry);
+	if ((s = archive_entry_uname(entry)) != NULL)
+		archive_strcpy(&me->uname, s);
+	if ((s = archive_entry_gname(entry)) != NULL)
+		archive_strcpy(&me->gname, s);
+	if ((s = archive_entry_fflags_text(entry)) != NULL)
+		archive_strcpy(&me->fflags_text, s);
+	archive_entry_fflags(entry, &me->fflags_set, &me->fflags_clear);
+	me->mtime = archive_entry_mtime(entry);
+	me->mtime_nsec = archive_entry_mtime_nsec(entry);
+	me->rdevmajor = archive_entry_rdevmajor(entry);
+	me->rdevminor = archive_entry_rdevminor(entry);
+	me->devmajor = archive_entry_devmajor(entry);
+	me->devminor = archive_entry_devminor(entry);
+	me->ino = archive_entry_ino(entry);
+	me->size = archive_entry_size(entry);
+	if (me->filetype == AE_IFDIR) {
+		me->dir_info = calloc(1, sizeof(*me->dir_info));
+		if (me->dir_info == NULL) {
+			mtree_entry_free(me);
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for a mtree entry");
+			*m_entry = NULL;
+			return (ARCHIVE_FATAL);
+		}
+		__archive_rb_tree_init(&me->dir_info->rbtree, &rb_ops);
+		me->dir_info->children.first = NULL;
+		me->dir_info->children.last = &(me->dir_info->children.first);
+		me->dir_info->chnext = NULL;
+	} else if (me->filetype == AE_IFREG) {
+		me->reg_info = calloc(1, sizeof(*me->reg_info));
+		if (me->reg_info == NULL) {
+			mtree_entry_free(me);
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for a mtree entry");
+			*m_entry = NULL;
+			return (ARCHIVE_FATAL);
+		}
+		me->reg_info->compute_sum = 0;
+	}
+
+	*m_entry = me;
+	return (ARCHIVE_OK);
+}
+
+static void
+mtree_entry_free(struct mtree_entry *me)
+{
+	archive_string_free(&me->parentdir);
+	archive_string_free(&me->basename);
+	archive_string_free(&me->pathname);
+	archive_string_free(&me->symlink);
+	archive_string_free(&me->uname);
+	archive_string_free(&me->gname);
+	archive_string_free(&me->fflags_text);
+	free(me->dir_info);
+	free(me->reg_info);
+	free(me);
+}
+
+static int
+archive_write_mtree_header(struct archive_write *a,
+    struct archive_entry *entry)
+{
+	struct mtree_writer *mtree= a->format_data;
+	struct mtree_entry *mtree_entry;
+	int r, r2;
+
+	if (mtree->first) {
+		mtree->first = 0;
+		archive_strcat(&mtree->buf, "#mtree\n");
+		if ((mtree->keys & SET_KEYS) == 0)
+			mtree->output_global_set = 0;/* Disabled. */
+	}
+
+	mtree->entry_bytes_remaining = archive_entry_size(entry);
+
+	/* While directory only mode, we do not handle non directory files. */
+	if (mtree->dironly && archive_entry_filetype(entry) != AE_IFDIR)
+		return (ARCHIVE_OK);
+
+	r2 = mtree_entry_new(a, entry, &mtree_entry);
+	if (r2 < ARCHIVE_WARN)
+		return (r2);
+	r = mtree_entry_tree_add(a, &mtree_entry);
+	if (r < ARCHIVE_WARN) {
+		mtree_entry_free(mtree_entry);
+		return (r);
+	}
+	mtree->mtree_entry = mtree_entry;
+
+	/* If the current file is a regular file, we have to
+	 * compute the sum of its content.
+	 * Initialize a bunch of checksum context. */
+	if (mtree_entry->reg_info)
+		sum_init(mtree);
+
+	return (r2);
+}
+
+static int
+write_mtree_entry(struct archive_write *a, struct mtree_entry *me)
+{
+	struct mtree_writer *mtree = a->format_data;
+	struct archive_string *str;
+	int keys, ret;
+
+	if (me->dir_info) {
+		if (mtree->classic) {
+			/*
+			 * Output a comment line to describe the full
+			 * pathname of the entry as mtree utility does
+			 * while generating classic format.
+			 */
+			if (!mtree->dironly)
+				archive_strappend_char(&mtree->buf, '\n');
+			if (me->parentdir.s)
+				archive_string_sprintf(&mtree->buf,
+				    "# %s/%s\n",
+				    me->parentdir.s, me->basename.s);
+			else
+				archive_string_sprintf(&mtree->buf,
+				    "# %s\n",
+				    me->basename.s);
+		}
+		if (mtree->output_global_set)
+			write_global(mtree);
+	}
+	archive_string_empty(&mtree->ebuf);
+	str = (mtree->indent || mtree->classic)? &mtree->ebuf : &mtree->buf;
+
+	if (!mtree->classic && me->parentdir.s) {
+		/*
+		 * If generating format is not classic one(v1), output
+		 * a full pathname.
+		 */
+		mtree_quote(str, me->parentdir.s);
+		archive_strappend_char(str, '/');
+	}
+	mtree_quote(str, me->basename.s);
+
+	keys = get_global_set_keys(mtree, me);
+	if ((keys & F_NLINK) != 0 &&
+	    me->nlink != 1 && me->filetype != AE_IFDIR)
+		archive_string_sprintf(str, " nlink=%u", me->nlink);
+
+	if ((keys & F_GNAME) != 0 && archive_strlen(&me->gname) > 0) {
+		archive_strcat(str, " gname=");
+		mtree_quote(str, me->gname.s);
+	}
+	if ((keys & F_UNAME) != 0 && archive_strlen(&me->uname) > 0) {
+		archive_strcat(str, " uname=");
+		mtree_quote(str, me->uname.s);
+	}
+	if ((keys & F_FLAGS) != 0) {
+		if (archive_strlen(&me->fflags_text) > 0) {
+			archive_strcat(str, " flags=");
+			mtree_quote(str, me->fflags_text.s);
+		} else if (mtree->set.processing &&
+		    (mtree->set.keys & F_FLAGS) != 0)
+			/* Overwrite the global parameter. */
+			archive_strcat(str, " flags=none");
+	}
+	if ((keys & F_TIME) != 0)
+		archive_string_sprintf(str, " time=%jd.%jd",
+		    (intmax_t)me->mtime, (intmax_t)me->mtime_nsec);
+	if ((keys & F_MODE) != 0)
+		archive_string_sprintf(str, " mode=%o", (unsigned int)me->mode);
+	if ((keys & F_GID) != 0)
+		archive_string_sprintf(str, " gid=%jd", (intmax_t)me->gid);
+	if ((keys & F_UID) != 0)
+		archive_string_sprintf(str, " uid=%jd", (intmax_t)me->uid);
+
+	if ((keys & F_INO) != 0)
+		archive_string_sprintf(str, " inode=%jd", (intmax_t)me->ino);
+	if ((keys & F_RESDEV) != 0) {
+		archive_string_sprintf(str,
+		    " resdevice=native,%ju,%ju",
+		    (uintmax_t)me->devmajor,
+		    (uintmax_t)me->devminor);
+	}
+
+	switch (me->filetype) {
+	case AE_IFLNK:
+		if ((keys & F_TYPE) != 0)
+			archive_strcat(str, " type=link");
+		if ((keys & F_SLINK) != 0) {
+			archive_strcat(str, " link=");
+			mtree_quote(str, me->symlink.s);
+		}
+		break;
+	case AE_IFSOCK:
+		if ((keys & F_TYPE) != 0)
+			archive_strcat(str, " type=socket");
+		break;
+	case AE_IFCHR:
+		if ((keys & F_TYPE) != 0)
+			archive_strcat(str, " type=char");
+		if ((keys & F_DEV) != 0) {
+			archive_string_sprintf(str,
+			    " device=native,%ju,%ju",
+			    (uintmax_t)me->rdevmajor,
+			    (uintmax_t)me->rdevminor);
+		}
+		break;
+	case AE_IFBLK:
+		if ((keys & F_TYPE) != 0)
+			archive_strcat(str, " type=block");
+		if ((keys & F_DEV) != 0) {
+			archive_string_sprintf(str,
+			    " device=native,%ju,%ju",
+			    (uintmax_t)me->rdevmajor,
+			    (uintmax_t)me->rdevminor);
+		}
+		break;
+	case AE_IFDIR:
+		if ((keys & F_TYPE) != 0)
+			archive_strcat(str, " type=dir");
+		break;
+	case AE_IFIFO:
+		if ((keys & F_TYPE) != 0)
+			archive_strcat(str, " type=fifo");
+		break;
+	case AE_IFREG:
+	default:	/* Handle unknown file types as regular files. */
+		if ((keys & F_TYPE) != 0)
+			archive_strcat(str, " type=file");
+		if ((keys & F_SIZE) != 0)
+			archive_string_sprintf(str, " size=%jd",
+			    (intmax_t)me->size);
+		break;
+	}
+
+	/* Write a bunch of sum. */
+	if (me->reg_info)
+		sum_write(str, me->reg_info);
+
+	archive_strappend_char(str, '\n');
+	if (mtree->indent || mtree->classic)
+		mtree_indent(mtree);
+
+	if (mtree->buf.length > 32768) {
+		ret = __archive_write_output(
+			a, mtree->buf.s, mtree->buf.length);
+		archive_string_empty(&mtree->buf);
+	} else
+		ret = ARCHIVE_OK;
+	return (ret);
+}
+
+static int
+write_dot_dot_entry(struct archive_write *a, struct mtree_entry *n)
+{
+	struct mtree_writer *mtree = a->format_data;
+	int ret;
+
+	if (n->parentdir.s) {
+		if (mtree->indent) {
+			int i, pd = mtree->depth * 4;
+			for (i = 0; i < pd; i++)
+				archive_strappend_char(&mtree->buf, ' ');
+		}
+		archive_string_sprintf(&mtree->buf, "# %s/%s\n",
+			n->parentdir.s, n->basename.s);
+	}
+
+	if (mtree->indent) {
+		archive_string_empty(&mtree->ebuf);
+		archive_strncat(&mtree->ebuf, "..\n\n", (mtree->dironly)?3:4);
+		mtree_indent(mtree);
+	} else
+		archive_strncat(&mtree->buf, "..\n\n", (mtree->dironly)?3:4);
+
+	if (mtree->buf.length > 32768) {
+		ret = __archive_write_output(
+			a, mtree->buf.s, mtree->buf.length);
+		archive_string_empty(&mtree->buf);
+	} else
+		ret = ARCHIVE_OK;
+	return (ret);
+}
+
+/*
+ * Write mtree entries saved at attr_counter_set_collect() function.
+ */
+static int
+write_mtree_entry_tree(struct archive_write *a)
+{
+	struct mtree_writer *mtree = a->format_data;
+	struct mtree_entry *np = mtree->root;
+	struct archive_rb_node *n;
+	int ret;
+
+	do {
+		if (mtree->output_global_set) {
+			/*
+			 * Collect attribute information to know which value
+			 * is frequently used among the children.
+			 */
+			attr_counter_set_reset(mtree);
+			ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
+				struct mtree_entry *e = (struct mtree_entry *)n;
+				if (attr_counter_set_collect(mtree, e) < 0) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "Can't allocate memory");
+					return (ARCHIVE_FATAL);
+				}
+			}
+		}
+		if (!np->dir_info->virtual || mtree->classic) {
+			ret = write_mtree_entry(a, np);
+			if (ret != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		} else {
+			/* Whenever output_global_set is enabled
+			 * output global value(/set keywords)
+			 * even if the directory entry is not allowed
+			 * to be written because the global values
+			 * can be used for the children. */
+			if (mtree->output_global_set)
+				write_global(mtree);
+		}
+		/*
+		 * Output the attribute of all files except directory files.
+		 */
+		mtree->depth++;
+		ARCHIVE_RB_TREE_FOREACH(n, &(np->dir_info->rbtree)) {
+			struct mtree_entry *e = (struct mtree_entry *)n;
+
+			if (e->dir_info)
+				mtree_entry_add_child_tail(np, e);
+			else {
+				ret = write_mtree_entry(a, e);
+				if (ret != ARCHIVE_OK)
+					return (ARCHIVE_FATAL);
+			}
+		}
+		mtree->depth--;
+
+		if (np->dir_info->children.first != NULL) {
+			/*
+			 * Descend the tree.
+			 */
+			np = np->dir_info->children.first;
+			if (mtree->indent)
+				mtree->depth++;
+			continue;
+		} else if (mtree->classic) {
+			/*
+			 * While printing mtree classic, if there are not
+			 * any directory files(except "." and "..") in the
+			 * directory, output two dots ".." as returning
+			 * the parent directory.
+			 */
+			ret = write_dot_dot_entry(a, np);
+			if (ret != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+		}
+
+		while (np != np->parent) {
+			if (np->dir_info->chnext == NULL) {
+				/*
+				 * Ascend the tree; go back to the parent.
+				 */
+				if (mtree->indent)
+					mtree->depth--;
+				if (mtree->classic) {
+					ret = write_dot_dot_entry(a,
+						np->parent);
+					if (ret != ARCHIVE_OK)
+						return (ARCHIVE_FATAL);
+				}
+				np = np->parent;
+			} else {
+				/*
+				 * Switch to next mtree entry in the directory.
+				 */
+				np = np->dir_info->chnext;
+				break;
+			}
+		}
+	} while (np != np->parent); 
+
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_mtree_finish_entry(struct archive_write *a)
+{
+	struct mtree_writer *mtree = a->format_data;
+	struct mtree_entry *me;
+
+	if ((me = mtree->mtree_entry) == NULL)
+		return (ARCHIVE_OK);
+	mtree->mtree_entry = NULL;
+
+	if (me->reg_info)
+		sum_final(mtree, me->reg_info);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_mtree_close(struct archive_write *a)
+{
+	struct mtree_writer *mtree= a->format_data;
+	int ret;
+
+	if (mtree->root != NULL) {
+		ret = write_mtree_entry_tree(a);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	archive_write_set_bytes_in_last_block(&a->archive, 1);
+
+	return __archive_write_output(a, mtree->buf.s, mtree->buf.length);
+}
+
+static ssize_t
+archive_write_mtree_data(struct archive_write *a, const void *buff, size_t n)
+{
+	struct mtree_writer *mtree= a->format_data;
+
+	if (n > mtree->entry_bytes_remaining)
+		n = (size_t)mtree->entry_bytes_remaining;
+	mtree->entry_bytes_remaining -= n;
+
+	/* We don't need to compute a regular file sum */
+	if (mtree->mtree_entry == NULL)
+		return (n);
+
+	if (mtree->mtree_entry->filetype == AE_IFREG)
+		sum_update(mtree, buff, n);
+
+	return (n);
+}
+
+static int
+archive_write_mtree_free(struct archive_write *a)
+{
+	struct mtree_writer *mtree= a->format_data;
+
+	if (mtree == NULL)
+		return (ARCHIVE_OK);
+
+	/* Make sure we do not leave any entries. */
+	mtree_entry_register_free(mtree);
+	archive_string_free(&mtree->cur_dirstr);
+	archive_string_free(&mtree->ebuf);
+	archive_string_free(&mtree->buf);
+	attr_counter_set_free(mtree);
+	free(mtree);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_mtree_options(struct archive_write *a, const char *key,
+    const char *value)
+{
+	struct mtree_writer *mtree= a->format_data;
+	int keybit = 0;
+
+	switch (key[0]) {
+	case 'a':
+		if (strcmp(key, "all") == 0)
+			keybit = ~0;
+		break;
+	case 'c':
+		if (strcmp(key, "cksum") == 0)
+			keybit = F_CKSUM;
+		break;
+	case 'd':
+		if (strcmp(key, "device") == 0)
+			keybit = F_DEV;
+		else if (strcmp(key, "dironly") == 0) {
+			mtree->dironly = (value != NULL)? 1: 0;
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'f':
+		if (strcmp(key, "flags") == 0)
+			keybit = F_FLAGS;
+		break;
+	case 'g':
+		if (strcmp(key, "gid") == 0)
+			keybit = F_GID;
+		else if (strcmp(key, "gname") == 0)
+			keybit = F_GNAME;
+		break;
+	case 'i':
+		if (strcmp(key, "indent") == 0) {
+			mtree->indent = (value != NULL)? 1: 0;
+			return (ARCHIVE_OK);
+		} else if (strcmp(key, "inode") == 0) {
+			keybit = F_INO;
+		}
+		break;
+	case 'l':
+		if (strcmp(key, "link") == 0)
+			keybit = F_SLINK;
+		break;
+	case 'm':
+		if (strcmp(key, "md5") == 0 ||
+		    strcmp(key, "md5digest") == 0)
+			keybit = F_MD5;
+		if (strcmp(key, "mode") == 0)
+			keybit = F_MODE;
+		break;
+	case 'n':
+		if (strcmp(key, "nlink") == 0)
+			keybit = F_NLINK;
+		break;
+	case 'r':
+		if (strcmp(key, "resdevice") == 0) {
+			keybit = F_RESDEV;
+		} else if (strcmp(key, "ripemd160digest") == 0 ||
+		    strcmp(key, "rmd160") == 0 ||
+		    strcmp(key, "rmd160digest") == 0)
+			keybit = F_RMD160;
+		break;
+	case 's':
+		if (strcmp(key, "sha1") == 0 ||
+		    strcmp(key, "sha1digest") == 0)
+			keybit = F_SHA1;
+		if (strcmp(key, "sha256") == 0 ||
+		    strcmp(key, "sha256digest") == 0)
+			keybit = F_SHA256;
+		if (strcmp(key, "sha384") == 0 ||
+		    strcmp(key, "sha384digest") == 0)
+			keybit = F_SHA384;
+		if (strcmp(key, "sha512") == 0 ||
+		    strcmp(key, "sha512digest") == 0)
+			keybit = F_SHA512;
+		if (strcmp(key, "size") == 0)
+			keybit = F_SIZE;
+		break;
+	case 't':
+		if (strcmp(key, "time") == 0)
+			keybit = F_TIME;
+		else if (strcmp(key, "type") == 0)
+			keybit = F_TYPE;
+		break;
+	case 'u':
+		if (strcmp(key, "uid") == 0)
+			keybit = F_UID;
+		else if (strcmp(key, "uname") == 0)
+			keybit = F_UNAME;
+		else if (strcmp(key, "use-set") == 0) {
+			mtree->output_global_set = (value != NULL)? 1: 0;
+			return (ARCHIVE_OK);
+		}
+		break;
+	}
+	if (keybit != 0) {
+		if (value != NULL)
+			mtree->keys |= keybit;
+		else
+			mtree->keys &= ~keybit;
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_set_format_mtree_default(struct archive *_a, const char *fn)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct mtree_writer *mtree;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW, fn);
+
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	if ((mtree = calloc(1, sizeof(*mtree))) == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate mtree data");
+		return (ARCHIVE_FATAL);
+	}
+
+	mtree->mtree_entry = NULL;
+	mtree->first = 1;
+	memset(&(mtree->set), 0, sizeof(mtree->set));
+	mtree->keys = DEFAULT_KEYS;
+	mtree->dironly = 0;
+	mtree->indent = 0;
+	archive_string_init(&mtree->ebuf);
+	archive_string_init(&mtree->buf);
+	mtree_entry_register_init(mtree);
+	a->format_data = mtree;
+	a->format_free = archive_write_mtree_free;
+	a->format_name = "mtree";
+	a->format_options = archive_write_mtree_options;
+	a->format_write_header = archive_write_mtree_header;
+	a->format_close = archive_write_mtree_close;
+	a->format_write_data = archive_write_mtree_data;
+	a->format_finish_entry = archive_write_mtree_finish_entry;
+	a->archive.archive_format = ARCHIVE_FORMAT_MTREE;
+	a->archive.archive_format_name = "mtree";
+
+	return (ARCHIVE_OK);
+}
+
+int
+archive_write_set_format_mtree(struct archive *_a)
+{
+	return archive_write_set_format_mtree_default(_a,
+		"archive_write_set_format_mtree");
+}
+
+int
+archive_write_set_format_mtree_classic(struct archive *_a)
+{
+	int r;
+
+	r = archive_write_set_format_mtree_default(_a,
+		"archive_write_set_format_mtree_classic");
+	if (r == ARCHIVE_OK) {
+		struct archive_write *a = (struct archive_write *)_a;
+		struct mtree_writer *mtree;
+
+		mtree = (struct mtree_writer *)a->format_data;
+
+		/* Set to output a mtree archive in classic format. */
+		mtree->classic = 1;
+		/* Basically, mtree classic format uses '/set' global
+		 * value. */
+		mtree->output_global_set = 1;
+	}
+	return (r);
+}
+
+static void
+sum_init(struct mtree_writer *mtree)
+{
+
+	mtree->compute_sum = 0;
+
+	if (mtree->keys & F_CKSUM) {
+		mtree->compute_sum |= F_CKSUM;
+		mtree->crc = 0;
+		mtree->crc_len = 0;
+	}
+#ifdef ARCHIVE_HAS_MD5
+	if (mtree->keys & F_MD5) {
+		if (archive_md5_init(&mtree->md5ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_MD5;
+		else
+			mtree->keys &= ~F_MD5;/* Not supported. */
+	}
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if (mtree->keys & F_RMD160) {
+		if (archive_rmd160_init(&mtree->rmd160ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_RMD160;
+		else
+			mtree->keys &= ~F_RMD160;/* Not supported. */
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if (mtree->keys & F_SHA1) {
+		if (archive_sha1_init(&mtree->sha1ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_SHA1;
+		else
+			mtree->keys &= ~F_SHA1;/* Not supported. */
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if (mtree->keys & F_SHA256) {
+		if (archive_sha256_init(&mtree->sha256ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_SHA256;
+		else
+			mtree->keys &= ~F_SHA256;/* Not supported. */
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if (mtree->keys & F_SHA384) {
+		if (archive_sha384_init(&mtree->sha384ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_SHA384;
+		else
+			mtree->keys &= ~F_SHA384;/* Not supported. */
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if (mtree->keys & F_SHA512) {
+		if (archive_sha512_init(&mtree->sha512ctx) == ARCHIVE_OK)
+			mtree->compute_sum |= F_SHA512;
+		else
+			mtree->keys &= ~F_SHA512;/* Not supported. */
+	}
+#endif
+}
+
+static void
+sum_update(struct mtree_writer *mtree, const void *buff, size_t n)
+{
+	if (mtree->compute_sum & F_CKSUM) {
+		/*
+		 * Compute a POSIX 1003.2 checksum
+		 */
+		const unsigned char *p;
+		size_t nn;
+
+		for (nn = n, p = buff; nn--; ++p)
+			COMPUTE_CRC(mtree->crc, *p);
+		mtree->crc_len += n;
+	}
+#ifdef ARCHIVE_HAS_MD5
+	if (mtree->compute_sum & F_MD5)
+		archive_md5_update(&mtree->md5ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if (mtree->compute_sum & F_RMD160)
+		archive_rmd160_update(&mtree->rmd160ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if (mtree->compute_sum & F_SHA1)
+		archive_sha1_update(&mtree->sha1ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if (mtree->compute_sum & F_SHA256)
+		archive_sha256_update(&mtree->sha256ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if (mtree->compute_sum & F_SHA384)
+		archive_sha384_update(&mtree->sha384ctx, buff, n);
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if (mtree->compute_sum & F_SHA512)
+		archive_sha512_update(&mtree->sha512ctx, buff, n);
+#endif
+}
+
+static void
+sum_final(struct mtree_writer *mtree, struct reg_info *reg)
+{
+
+	if (mtree->compute_sum & F_CKSUM) {
+		uint64_t len;
+		/* Include the length of the file. */
+		for (len = mtree->crc_len; len != 0; len >>= 8)
+			COMPUTE_CRC(mtree->crc, len & 0xff);
+		reg->crc = ~mtree->crc;
+	}
+#ifdef ARCHIVE_HAS_MD5
+	if (mtree->compute_sum & F_MD5)
+		archive_md5_final(&mtree->md5ctx, reg->buf_md5);
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if (mtree->compute_sum & F_RMD160)
+		archive_rmd160_final(&mtree->rmd160ctx, reg->buf_rmd160);
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if (mtree->compute_sum & F_SHA1)
+		archive_sha1_final(&mtree->sha1ctx, reg->buf_sha1);
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if (mtree->compute_sum & F_SHA256)
+		archive_sha256_final(&mtree->sha256ctx, reg->buf_sha256);
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if (mtree->compute_sum & F_SHA384)
+		archive_sha384_final(&mtree->sha384ctx, reg->buf_sha384);
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if (mtree->compute_sum & F_SHA512)
+		archive_sha512_final(&mtree->sha512ctx, reg->buf_sha512);
+#endif
+	/* Save what types of sum are computed. */
+	reg->compute_sum = mtree->compute_sum;
+}
+
+#if defined(ARCHIVE_HAS_MD5) || defined(ARCHIVE_HAS_RMD160) || \
+    defined(ARCHIVE_HAS_SHA1) || defined(ARCHIVE_HAS_SHA256) || \
+    defined(ARCHIVE_HAS_SHA384) || defined(ARCHIVE_HAS_SHA512)
+static void
+strappend_bin(struct archive_string *s, const unsigned char *bin, int n)
+{
+	static const char hex[] = "0123456789abcdef";
+	int i;
+
+	for (i = 0; i < n; i++) {
+		archive_strappend_char(s, hex[bin[i] >> 4]);
+		archive_strappend_char(s, hex[bin[i] & 0x0f]);
+	}
+}
+#endif
+
+static void
+sum_write(struct archive_string *str, struct reg_info *reg)
+{
+
+	if (reg->compute_sum & F_CKSUM) {
+		archive_string_sprintf(str, " cksum=%ju",
+		    (uintmax_t)reg->crc);
+	}
+#ifdef ARCHIVE_HAS_MD5
+	if (reg->compute_sum & F_MD5) {
+		archive_strcat(str, " md5digest=");
+		strappend_bin(str, reg->buf_md5, sizeof(reg->buf_md5));
+	}
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if (reg->compute_sum & F_RMD160) {
+		archive_strcat(str, " rmd160digest=");
+		strappend_bin(str, reg->buf_rmd160, sizeof(reg->buf_rmd160));
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if (reg->compute_sum & F_SHA1) {
+		archive_strcat(str, " sha1digest=");
+		strappend_bin(str, reg->buf_sha1, sizeof(reg->buf_sha1));
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if (reg->compute_sum & F_SHA256) {
+		archive_strcat(str, " sha256digest=");
+		strappend_bin(str, reg->buf_sha256, sizeof(reg->buf_sha256));
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if (reg->compute_sum & F_SHA384) {
+		archive_strcat(str, " sha384digest=");
+		strappend_bin(str, reg->buf_sha384, sizeof(reg->buf_sha384));
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if (reg->compute_sum & F_SHA512) {
+		archive_strcat(str, " sha512digest=");
+		strappend_bin(str, reg->buf_sha512, sizeof(reg->buf_sha512));
+	}
+#endif
+}
+
+static int
+mtree_entry_cmp_node(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	const struct mtree_entry *e1 = (const struct mtree_entry *)n1;
+	const struct mtree_entry *e2 = (const struct mtree_entry *)n2;
+
+	return (strcmp(e2->basename.s, e1->basename.s));
+}
+
+static int
+mtree_entry_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+	const struct mtree_entry *e = (const struct mtree_entry *)n;
+
+	return (strcmp((const char *)key, e->basename.s));
+}
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+static int
+cleanup_backslash_1(char *p)
+{
+	int mb, dos;
+
+	mb = dos = 0;
+	while (*p) {
+		if (*(unsigned char *)p > 127)
+			mb = 1;
+		if (*p == '\\') {
+			/* If we have not met any multi-byte characters,
+			 * we can replace '\' with '/'. */
+			if (!mb)
+				*p = '/';
+			dos = 1;
+		}
+		p++;
+	}
+	if (!mb || !dos)
+		return (0);
+	return (-1);
+}
+
+static void
+cleanup_backslash_2(wchar_t *p)
+{
+
+	/* Convert a path-separator from '\' to  '/' */
+	while (*p != L'\0') {
+		if (*p == L'\\')
+			*p = L'/';
+		p++;
+	}
+}
+#endif
+
+/*
+ * Generate a parent directory name and a base name from a pathname.
+ */
+static int
+mtree_entry_setup_filenames(struct archive_write *a, struct mtree_entry *file,
+    struct archive_entry *entry)
+{
+	const char *pathname;
+	char *p, *dirname, *slash;
+	size_t len;
+	int ret = ARCHIVE_OK;
+
+	archive_strcpy(&file->pathname, archive_entry_pathname(entry));
+#if defined(_WIN32) || defined(__CYGWIN__)
+	/*
+	 * Convert a path-separator from '\' to  '/'
+	 */
+	if (cleanup_backslash_1(file->pathname.s) != 0) {
+		const wchar_t *wp = archive_entry_pathname_w(entry);
+		struct archive_wstring ws;
+
+		if (wp != NULL) {
+			int r;
+			archive_string_init(&ws);
+			archive_wstrcpy(&ws, wp);
+			cleanup_backslash_2(ws.s);
+			archive_string_empty(&(file->pathname));
+			r = archive_string_append_from_wcs(&(file->pathname),
+			    ws.s, ws.length);
+			archive_wstring_free(&ws);
+			if (r < 0 && errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory");
+				return (ARCHIVE_FATAL);
+			}
+		}
+	}
+#else
+	(void)a; /* UNUSED */
+#endif
+	pathname =  file->pathname.s;
+	if (strcmp(pathname, ".") == 0) {
+		archive_strcpy(&file->basename, ".");
+		return (ARCHIVE_OK);
+	}
+
+	archive_strcpy(&(file->parentdir), pathname);
+
+	len = file->parentdir.length;
+	p = dirname = file->parentdir.s;
+
+	/*
+	 * Remove leading '/' and '../' elements
+	 */
+	while (*p) {
+		if (p[0] == '/') {
+			p++;
+			len--;
+		} else if (p[0] != '.')
+			break;
+		else if (p[1] == '.' && p[2] == '/') {
+			p += 3;
+			len -= 3;
+		} else
+			break;
+	}
+	if (p != dirname) {
+		memmove(dirname, p, len+1);
+		p = dirname;
+	}
+	/*
+	 * Remove "/","/." and "/.." elements from tail.
+	 */
+	while (len > 0) {
+		size_t ll = len;
+
+		if (len > 0 && p[len-1] == '/') {
+			p[len-1] = '\0';
+			len--;
+		}
+		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
+			p[len-2] = '\0';
+			len -= 2;
+		}
+		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
+		    p[len-1] == '.') {
+			p[len-3] = '\0';
+			len -= 3;
+		}
+		if (ll == len)
+			break;
+	}
+	while (*p) {
+		if (p[0] == '/') {
+			if (p[1] == '/')
+				/* Convert '//' --> '/' */
+				memmove(p, p+1, strlen(p+1) + 1);
+			else if (p[1] == '.' && p[2] == '/')
+				/* Convert '/./' --> '/' */
+				memmove(p, p+2, strlen(p+2) + 1);
+			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
+				/* Convert 'dir/dir1/../dir2/'
+				 *     --> 'dir/dir2/'
+				 */
+				char *rp = p -1;
+				while (rp >= dirname) {
+					if (*rp == '/')
+						break;
+					--rp;
+				}
+				if (rp > dirname) {
+					strcpy(rp, p+3);
+					p = rp;
+				} else {
+					strcpy(dirname, p+4);
+					p = dirname;
+				}
+			} else
+				p++;
+		} else
+			p++;
+	}
+	p = dirname;
+	len = strlen(p);
+
+	/*
+	 * Add "./" prefix.
+	 * NOTE: If the pathname does not have a path separator, we have
+	 * to add "./" to the head of the pathname because mtree reader
+	 * will suppose that it is v1(a.k.a classic) mtree format and
+	 * change the directory unexpectedly and so it will make a wrong
+	 * path.
+	 */
+	if (strcmp(p, ".") != 0 && strncmp(p, "./", 2) != 0) {
+		struct archive_string as;
+		archive_string_init(&as);
+		archive_strcpy(&as, "./");
+		archive_strncat(&as, p, len);
+		archive_string_empty(&file->parentdir);
+		archive_string_concat(&file->parentdir, &as);
+		archive_string_free(&as);
+		p = file->parentdir.s;
+		len = archive_strlen(&file->parentdir);
+	}
+
+	/*
+	 * Find out the position which points the last position of
+	 * path separator('/').
+	 */
+	slash = NULL;
+	for (; *p != '\0'; p++) {
+		if (*p == '/')
+			slash = p;
+	}
+	if (slash == NULL) {
+		/* The pathname doesn't have a parent directory. */
+		file->parentdir.length = len;
+		archive_string_copy(&(file->basename), &(file->parentdir));
+		archive_string_empty(&(file->parentdir));
+		*file->parentdir.s = '\0';
+		return (ret);
+	}
+
+	/* Make a basename from file->parentdir.s and slash */
+	*slash  = '\0';
+	file->parentdir.length = slash - file->parentdir.s;
+	archive_strcpy(&(file->basename),  slash + 1);
+	return (ret);
+}
+
+static int
+mtree_entry_create_virtual_dir(struct archive_write *a, const char *pathname,
+    struct mtree_entry **m_entry)
+{
+	struct archive_entry *entry;
+	struct mtree_entry *file;
+	int r;
+
+	entry = archive_entry_new();
+	if (entry == NULL) {
+		*m_entry = NULL;
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	archive_entry_copy_pathname(entry, pathname);
+	archive_entry_set_mode(entry, AE_IFDIR | 0755);
+	archive_entry_set_mtime(entry, time(NULL), 0);
+
+	r = mtree_entry_new(a, entry, &file);
+	archive_entry_free(entry);
+	if (r < ARCHIVE_WARN) {
+		*m_entry = NULL;
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	file->dir_info->virtual = 1;
+
+	*m_entry = file;
+	return (ARCHIVE_OK);
+}
+
+static void
+mtree_entry_register_add(struct mtree_writer *mtree, struct mtree_entry *file)
+{
+        file->next = NULL;
+        *mtree->file_list.last = file;
+        mtree->file_list.last = &(file->next);
+}
+
+static void
+mtree_entry_register_init(struct mtree_writer *mtree)
+{
+	mtree->file_list.first = NULL;
+	mtree->file_list.last = &(mtree->file_list.first);
+}
+
+static void
+mtree_entry_register_free(struct mtree_writer *mtree)
+{
+	struct mtree_entry *file, *file_next;
+
+	file = mtree->file_list.first;
+	while (file != NULL) {
+		file_next = file->next;
+		mtree_entry_free(file);
+		file = file_next;
+	}
+}
+
+static int
+mtree_entry_add_child_tail(struct mtree_entry *parent,
+    struct mtree_entry *child)
+{
+	child->dir_info->chnext = NULL;
+	*parent->dir_info->children.last = child;
+	parent->dir_info->children.last = &(child->dir_info->chnext);
+	return (1);
+}
+
+/*
+ * Find a entry from a parent entry with the name.
+ */
+static struct mtree_entry *
+mtree_entry_find_child(struct mtree_entry *parent, const char *child_name)
+{
+	struct mtree_entry *np;
+
+	if (parent == NULL)
+		return (NULL);
+	np = (struct mtree_entry *)__archive_rb_tree_find_node(
+	    &(parent->dir_info->rbtree), child_name);
+	return (np);
+}
+
+static int
+get_path_component(char *name, size_t n, const char *fn)
+{
+	char *p;
+	size_t l;
+
+	p = strchr(fn, '/');
+	if (p == NULL) {
+		if ((l = strlen(fn)) == 0)
+			return (0);
+	} else
+		l = p - fn;
+	if (l > n -1)
+		return (-1);
+	memcpy(name, fn, l);
+	name[l] = '\0';
+
+	return ((int)l);
+}
+
+/*
+ * Add a new entry into the tree.
+ */
+static int
+mtree_entry_tree_add(struct archive_write *a, struct mtree_entry **filep)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+	char name[NAME_MAX+1];
+#else
+	char name[256];
+#endif
+	struct mtree_writer *mtree = (struct mtree_writer *)a->format_data;
+	struct mtree_entry *dent, *file, *np;
+	const char *fn, *p;
+	int l, r;
+
+	file = *filep;
+	if (file->parentdir.length == 0 && file->basename.length == 1 &&
+	    file->basename.s[0] == '.') {
+		file->parent = file;
+		if (mtree->root != NULL) {
+			np = mtree->root;
+			goto same_entry;
+		}
+		mtree->root = file;
+		mtree_entry_register_add(mtree, file);
+		return (ARCHIVE_OK);
+	}
+
+	if (file->parentdir.length == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal programming error "
+		    "in generating canonical name for %s",
+		    file->pathname.s);
+		return (ARCHIVE_FAILED);
+	}
+
+	fn = p = file->parentdir.s;
+
+	/*
+	 * If the path of the parent directory of `file' entry is
+	 * the same as the path of `cur_dirent', add `file' entry to
+	 * `cur_dirent'.
+	 */
+	if (archive_strlen(&(mtree->cur_dirstr))
+	      == archive_strlen(&(file->parentdir)) &&
+	    strcmp(mtree->cur_dirstr.s, fn) == 0) {
+		if (!__archive_rb_tree_insert_node(
+		    &(mtree->cur_dirent->dir_info->rbtree),
+		    (struct archive_rb_node *)file)) {
+			/* There is the same name in the tree. */
+			np = (struct mtree_entry *)__archive_rb_tree_find_node(
+			    &(mtree->cur_dirent->dir_info->rbtree),
+			    file->basename.s);
+			goto same_entry;
+		}
+		file->parent = mtree->cur_dirent;
+		mtree_entry_register_add(mtree, file);
+		return (ARCHIVE_OK);
+	}
+
+	dent = mtree->root;
+	for (;;) {
+		l = get_path_component(name, sizeof(name), fn);
+		if (l == 0) {
+			np = NULL;
+			break;
+		}
+		if (l < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "A name buffer is too small");
+			return (ARCHIVE_FATAL);
+		}
+		if (l == 1 && name[0] == '.' && dent != NULL &&
+		    dent == mtree->root) {
+			fn += l;
+			if (fn[0] == '/')
+				fn++;
+			continue;
+		}
+
+		np = mtree_entry_find_child(dent, name);
+		if (np == NULL || fn[0] == '\0')
+			break;
+
+		/* Find next sub directory. */
+		if (!np->dir_info) {
+			/* NOT Directory! */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "`%s' is not directory, we cannot insert `%s' ",
+			    np->pathname.s, file->pathname.s);
+			return (ARCHIVE_FAILED);
+		}
+		fn += l;
+		if (fn[0] == '/')
+			fn++;
+		dent = np;
+	}
+	if (np == NULL) {
+		/*
+		 * Create virtual parent directories.
+		 */
+		while (fn[0] != '\0') {
+			struct mtree_entry *vp;
+			struct archive_string as;
+
+			archive_string_init(&as);
+			archive_strncat(&as, p, fn - p + l);
+			if (as.s[as.length-1] == '/') {
+				as.s[as.length-1] = '\0';
+				as.length--;
+			}
+			r = mtree_entry_create_virtual_dir(a, as.s, &vp);
+			archive_string_free(&as);
+			if (r < ARCHIVE_WARN)
+				return (r);
+
+			if (strcmp(vp->pathname.s, ".") == 0) {
+				vp->parent = vp;
+				mtree->root = vp;
+			} else {
+				__archive_rb_tree_insert_node(
+				    &(dent->dir_info->rbtree),
+				    (struct archive_rb_node *)vp);
+				vp->parent = dent;
+			}
+			mtree_entry_register_add(mtree, vp);
+			np = vp;
+
+			fn += l;
+			if (fn[0] == '/')
+				fn++;
+			l = get_path_component(name, sizeof(name), fn);
+			if (l < 0) {
+				archive_string_free(&as);
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "A name buffer is too small");
+				return (ARCHIVE_FATAL);
+			}
+			dent = np;
+		}
+
+		/* Found out the parent directory where `file' can be
+		 * inserted. */
+		mtree->cur_dirent = dent;
+		archive_string_empty(&(mtree->cur_dirstr));
+		archive_string_ensure(&(mtree->cur_dirstr),
+		    archive_strlen(&(dent->parentdir)) +
+		    archive_strlen(&(dent->basename)) + 2);
+		if (archive_strlen(&(dent->parentdir)) +
+		    archive_strlen(&(dent->basename)) == 0)
+			mtree->cur_dirstr.s[0] = 0;
+		else {
+			if (archive_strlen(&(dent->parentdir)) > 0) {
+				archive_string_copy(&(mtree->cur_dirstr),
+				    &(dent->parentdir));
+				archive_strappend_char(
+				    &(mtree->cur_dirstr), '/');
+			}
+			archive_string_concat(&(mtree->cur_dirstr),
+			    &(dent->basename));
+		}
+
+		if (!__archive_rb_tree_insert_node(
+		    &(dent->dir_info->rbtree),
+		    (struct archive_rb_node *)file)) {
+			np = (struct mtree_entry *)__archive_rb_tree_find_node(
+			    &(dent->dir_info->rbtree), file->basename.s);
+			goto same_entry;
+		}
+		file->parent = dent;
+		mtree_entry_register_add(mtree, file);
+		return (ARCHIVE_OK);
+	}
+
+same_entry:
+	/*
+	 * We have already has the entry the filename of which is
+	 * the same.
+	 */
+	r = mtree_entry_exchange_same_entry(a, np, file);
+	if (r < ARCHIVE_WARN)
+		return (r);
+	if (np->dir_info)
+		np->dir_info->virtual = 0;
+	*filep = np;
+	mtree_entry_free(file);
+	return (ARCHIVE_WARN);
+}
+
+static int
+mtree_entry_exchange_same_entry(struct archive_write *a, struct mtree_entry *np,
+    struct mtree_entry *file)
+{
+
+	if ((np->mode & AE_IFMT) != (file->mode & AE_IFMT)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Found duplicate entries `%s' and its file type is "
+		    "different",
+		    np->pathname.s);
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Update the existent mtree entry's attributes by the new one's. */
+	archive_string_empty(&np->symlink);
+	archive_string_concat(&np->symlink, &file->symlink);
+	archive_string_empty(&np->uname);
+	archive_string_concat(&np->uname, &file->uname);
+	archive_string_empty(&np->gname);
+	archive_string_concat(&np->gname, &file->gname);
+	archive_string_empty(&np->fflags_text);
+	archive_string_concat(&np->fflags_text, &file->fflags_text);
+	np->nlink = file->nlink;
+	np->filetype = file->filetype;
+	np->mode = file->mode;
+	np->size = file->size;
+	np->uid = file->uid;
+	np->gid = file->gid;
+	np->fflags_set = file->fflags_set;
+	np->fflags_clear = file->fflags_clear;
+	np->mtime = file->mtime;
+	np->mtime_nsec = file->mtime_nsec;
+	np->rdevmajor = file->rdevmajor;
+	np->rdevminor = file->rdevminor;
+	np->devmajor = file->devmajor;
+	np->devminor = file->devminor;
+	np->ino = file->ino;
+
+	return (ARCHIVE_WARN);
+}
diff --git a/c/archive_write_set_format_pax.c b/c/archive_write_set_format_pax.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_pax.c
@@ -0,0 +1,2044 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * Copyright (c) 2016 Martin Matuska
+ * 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: head/lib/libarchive/archive_write_set_format_pax.c 201162 2009-12-29 05:47:46Z kientzle $");
+
+#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_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct sparse_block {
+	struct sparse_block	*next;
+	int		is_hole;
+	uint64_t	offset;
+	uint64_t	remaining;
+};
+
+struct pax {
+	uint64_t	entry_bytes_remaining;
+	uint64_t	entry_padding;
+	struct archive_string	l_url_encoded_name;
+	struct archive_string	pax_header;
+	struct archive_string	sparse_map;
+	size_t			sparse_map_padding;
+	struct sparse_block	*sparse_list;
+	struct sparse_block	*sparse_tail;
+	struct archive_string_conv *sconv_utf8;
+	int			 opt_binary;
+
+	unsigned flags;
+#define WRITE_SCHILY_XATTR       (1 << 0)
+#define WRITE_LIBARCHIVE_XATTR   (1 << 1)
+};
+
+static void		 add_pax_attr(struct archive_string *, const char *key,
+			     const char *value);
+static void		 add_pax_attr_binary(struct archive_string *,
+			     const char *key,
+			     const char *value, size_t value_len);
+static void		 add_pax_attr_int(struct archive_string *,
+			     const char *key, int64_t value);
+static void		 add_pax_attr_time(struct archive_string *,
+			     const char *key, int64_t sec,
+			     unsigned long nanos);
+static int		 add_pax_acl(struct archive_write *,
+			    struct archive_entry *, struct pax *, int);
+static ssize_t		 archive_write_pax_data(struct archive_write *,
+			     const void *, size_t);
+static int		 archive_write_pax_close(struct archive_write *);
+static int		 archive_write_pax_free(struct archive_write *);
+static int		 archive_write_pax_finish_entry(struct archive_write *);
+static int		 archive_write_pax_header(struct archive_write *,
+			     struct archive_entry *);
+static int		 archive_write_pax_options(struct archive_write *,
+			     const char *, const char *);
+static char		*base64_encode(const char *src, size_t len);
+static char		*build_gnu_sparse_name(char *dest, const char *src);
+static char		*build_pax_attribute_name(char *dest, const char *src);
+static char		*build_ustar_entry_name(char *dest, const char *src,
+			     size_t src_length, const char *insert);
+static char		*format_int(char *dest, int64_t);
+static int		 has_non_ASCII(const char *);
+static void		 sparse_list_clear(struct pax *);
+static int		 sparse_list_add(struct pax *, int64_t, int64_t);
+static char		*url_encode(const char *in);
+
+/*
+ * Set output format to 'restricted pax' format.
+ *
+ * This is the same as normal 'pax', but tries to suppress
+ * the pax header whenever possible.  This is the default for
+ * bsdtar, for instance.
+ */
+int
+archive_write_set_format_pax_restricted(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int r;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_pax_restricted");
+
+	r = archive_write_set_format_pax(&a->archive);
+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED;
+	a->archive.archive_format_name = "restricted POSIX pax interchange";
+	return (r);
+}
+
+/*
+ * Set output format to 'pax' format.
+ */
+int
+archive_write_set_format_pax(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct pax *pax;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_pax");
+
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	pax = (struct pax *)calloc(1, sizeof(*pax));
+	if (pax == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate pax data");
+		return (ARCHIVE_FATAL);
+	}
+	pax->flags = WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR;
+
+	a->format_data = pax;
+	a->format_name = "pax";
+	a->format_options = archive_write_pax_options;
+	a->format_write_header = archive_write_pax_header;
+	a->format_write_data = archive_write_pax_data;
+	a->format_close = archive_write_pax_close;
+	a->format_free = archive_write_pax_free;
+	a->format_finish_entry = archive_write_pax_finish_entry;
+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+	a->archive.archive_format_name = "POSIX pax interchange";
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_pax_options(struct archive_write *a, const char *key,
+    const char *val)
+{
+	struct pax *pax = (struct pax *)a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	if (strcmp(key, "hdrcharset")  == 0) {
+		/*
+		 * The character-set we can use are defined in
+		 * IEEE Std 1003.1-2001
+		 */
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "pax: hdrcharset option needs a character-set name");
+		else if (strcmp(val, "BINARY") == 0 ||
+		    strcmp(val, "binary") == 0) {
+			/*
+			 * Specify binary mode. We will not convert
+			 * filenames, uname and gname to any charsets.
+			 */
+			pax->opt_binary = 1;
+			ret = ARCHIVE_OK;
+		} else if (strcmp(val, "UTF-8") == 0) {
+			/*
+			 * Specify UTF-8 character-set to be used for
+			 * filenames. This is almost the test that
+			 * running platform supports the string conversion.
+			 * Especially libarchive_test needs this trick for
+			 * its test.
+			 */
+			pax->sconv_utf8 = archive_string_conversion_to_charset(
+			    &(a->archive), "UTF-8", 0);
+			if (pax->sconv_utf8 == NULL)
+				ret = ARCHIVE_FATAL;
+			else
+				ret = ARCHIVE_OK;
+		} else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "pax: invalid charset name");
+		return (ret);
+	} else if (strcmp(key, "xattrheader") == 0) {
+		if (val == NULL || val[0] == 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "pax: xattrheader requires a value");
+		} else if (strcmp(val, "ALL") == 0 ||
+		    strcmp(val, "all") == 0) {
+			pax->flags |= WRITE_LIBARCHIVE_XATTR | WRITE_SCHILY_XATTR;
+			ret = ARCHIVE_OK;
+		} else if (strcmp(val, "SCHILY") == 0 ||
+		    strcmp(val, "schily") == 0) {
+			pax->flags |= WRITE_SCHILY_XATTR;
+			pax->flags &= ~WRITE_LIBARCHIVE_XATTR;
+			ret = ARCHIVE_OK;
+		} else if (strcmp(val, "LIBARCHIVE") == 0 ||
+		    strcmp(val, "libarchive") == 0) {
+			pax->flags |= WRITE_LIBARCHIVE_XATTR;
+			pax->flags &= ~WRITE_SCHILY_XATTR;
+			ret = ARCHIVE_OK;
+		} else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "pax: invalid xattr header name");
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+/*
+ * Note: This code assumes that 'nanos' has the same sign as 'sec',
+ * which implies that sec=-1, nanos=200000000 represents -1.2 seconds
+ * and not -0.8 seconds.  This is a pretty pedantic point, as we're
+ * unlikely to encounter many real files created before Jan 1, 1970,
+ * much less ones with timestamps recorded to sub-second resolution.
+ */
+static void
+add_pax_attr_time(struct archive_string *as, const char *key,
+    int64_t sec, unsigned long nanos)
+{
+	int digit, i;
+	char *t;
+	/*
+	 * Note that each byte contributes fewer than 3 base-10
+	 * digits, so this will always be big enough.
+	 */
+	char tmp[1 + 3*sizeof(sec) + 1 + 3*sizeof(nanos)];
+
+	tmp[sizeof(tmp) - 1] = 0;
+	t = tmp + sizeof(tmp) - 1;
+
+	/* Skip trailing zeros in the fractional part. */
+	for (digit = 0, i = 10; i > 0 && digit == 0; i--) {
+		digit = nanos % 10;
+		nanos /= 10;
+	}
+
+	/* Only format the fraction if it's non-zero. */
+	if (i > 0) {
+		while (i > 0) {
+			*--t = "0123456789"[digit];
+			digit = nanos % 10;
+			nanos /= 10;
+			i--;
+		}
+		*--t = '.';
+	}
+	t = format_int(t, sec);
+
+	add_pax_attr(as, key, t);
+}
+
+static char *
+format_int(char *t, int64_t i)
+{
+	uint64_t ui;
+
+	if (i < 0) 
+		ui = (i == INT64_MIN) ? (uint64_t)(INT64_MAX) + 1 : (uint64_t)(-i);
+	else
+		ui = i;
+
+	do {
+		*--t = "0123456789"[ui % 10];
+	} while (ui /= 10);
+	if (i < 0)
+		*--t = '-';
+	return (t);
+}
+
+static void
+add_pax_attr_int(struct archive_string *as, const char *key, int64_t value)
+{
+	char tmp[1 + 3 * sizeof(value)];
+
+	tmp[sizeof(tmp) - 1] = 0;
+	add_pax_attr(as, key, format_int(tmp + sizeof(tmp) - 1, value));
+}
+
+/*
+ * Add a key/value attribute to the pax header.  This function handles
+ * the length field and various other syntactic requirements.
+ */
+static void
+add_pax_attr(struct archive_string *as, const char *key, const char *value)
+{
+	add_pax_attr_binary(as, key, value, strlen(value));
+}
+
+/*
+ * Add a key/value attribute to the pax header.  This function handles
+ * binary values.
+ */
+static void
+add_pax_attr_binary(struct archive_string *as, const char *key,
+		    const char *value, size_t value_len)
+{
+	int digits, i, len, next_ten;
+	char tmp[1 + 3 * sizeof(int)];	/* < 3 base-10 digits per byte */
+
+	/*-
+	 * PAX attributes have the following layout:
+	 *     <len> <space> <key> <=> <value> <nl>
+	 */
+	len = 1 + (int)strlen(key) + 1 + (int)value_len + 1;
+
+	/*
+	 * The <len> field includes the length of the <len> field, so
+	 * computing the correct length is tricky.  I start by
+	 * counting the number of base-10 digits in 'len' and
+	 * computing the next higher power of 10.
+	 */
+	next_ten = 1;
+	digits = 0;
+	i = len;
+	while (i > 0) {
+		i = i / 10;
+		digits++;
+		next_ten = next_ten * 10;
+	}
+	/*
+	 * For example, if string without the length field is 99
+	 * chars, then adding the 2 digit length "99" will force the
+	 * total length past 100, requiring an extra digit.  The next
+	 * statement adjusts for this effect.
+	 */
+	if (len + digits >= next_ten)
+		digits++;
+
+	/* Now, we have the right length so we can build the line. */
+	tmp[sizeof(tmp) - 1] = 0;	/* Null-terminate the work area. */
+	archive_strcat(as, format_int(tmp + sizeof(tmp) - 1, len + digits));
+	archive_strappend_char(as, ' ');
+	archive_strcat(as, key);
+	archive_strappend_char(as, '=');
+	archive_array_append(as, value, value_len);
+	archive_strappend_char(as, '\n');
+}
+
+static void
+archive_write_pax_header_xattr(struct pax *pax, const char *encoded_name,
+    const void *value, size_t value_len)
+{
+	struct archive_string s;
+	char *encoded_value;
+
+	if (pax->flags & WRITE_LIBARCHIVE_XATTR) {
+		encoded_value = base64_encode((const char *)value, value_len);
+
+		if (encoded_name != NULL && encoded_value != NULL) {
+			archive_string_init(&s);
+			archive_strcpy(&s, "LIBARCHIVE.xattr.");
+			archive_strcat(&s, encoded_name);
+			add_pax_attr(&(pax->pax_header), s.s, encoded_value);
+			archive_string_free(&s);
+		}
+		free(encoded_value);
+	}
+	if (pax->flags & WRITE_SCHILY_XATTR) {
+		archive_string_init(&s);
+		archive_strcpy(&s, "SCHILY.xattr.");
+		archive_strcat(&s, encoded_name);
+		add_pax_attr_binary(&(pax->pax_header), s.s, value, value_len);
+		archive_string_free(&s);
+	}
+}
+
+static int
+archive_write_pax_header_xattrs(struct archive_write *a,
+    struct pax *pax, struct archive_entry *entry)
+{
+	int i = archive_entry_xattr_reset(entry);
+
+	while (i--) {
+		const char *name;
+		const void *value;
+		char *url_encoded_name = NULL, *encoded_name = NULL;
+		size_t size;
+		int r;
+
+		archive_entry_xattr_next(entry, &name, &value, &size);
+		url_encoded_name = url_encode(name);
+		if (url_encoded_name != NULL) {
+			/* Convert narrow-character to UTF-8. */
+			r = archive_strcpy_l(&(pax->l_url_encoded_name),
+			    url_encoded_name, pax->sconv_utf8);
+			free(url_encoded_name); /* Done with this. */
+			if (r == 0)
+				encoded_name = pax->l_url_encoded_name.s;
+			else if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Linkname");
+				return (ARCHIVE_FATAL);
+			}
+		}
+
+		archive_write_pax_header_xattr(pax, encoded_name,
+		    value, size);
+
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+get_entry_hardlink(struct archive_write *a, struct archive_entry *entry,
+    const char **name, size_t *length, struct archive_string_conv *sc)
+{
+	int r;
+	
+	r = archive_entry_hardlink_l(entry, name, length, sc);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+get_entry_pathname(struct archive_write *a, struct archive_entry *entry,
+    const char **name, size_t *length, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_entry_pathname_l(entry, name, length, sc);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+get_entry_uname(struct archive_write *a, struct archive_entry *entry,
+    const char **name, size_t *length, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_entry_uname_l(entry, name, length, sc);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Uname");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+get_entry_gname(struct archive_write *a, struct archive_entry *entry,
+    const char **name, size_t *length, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_entry_gname_l(entry, name, length, sc);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Gname");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+get_entry_symlink(struct archive_write *a, struct archive_entry *entry,
+    const char **name, size_t *length, struct archive_string_conv *sc)
+{
+	int r;
+
+	r = archive_entry_symlink_l(entry, name, length, sc);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			return (ARCHIVE_FATAL);
+		}
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+/* Add ACL to pax header */
+static int
+add_pax_acl(struct archive_write *a,
+    struct archive_entry *entry, struct pax *pax, int flags)
+{
+	char *p;
+	const char *attr;
+	int acl_types;
+
+	acl_types = archive_entry_acl_types(entry);
+
+	if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)
+		attr = "SCHILY.acl.ace";
+	else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
+		attr = "SCHILY.acl.access";
+	else if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
+		attr = "SCHILY.acl.default";
+	else
+		return (ARCHIVE_FATAL);
+
+	p = archive_entry_acl_to_text_l(entry, NULL, flags, pax->sconv_utf8);
+	if (p == NULL) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM, "%s %s",
+			    "Can't allocate memory for ", attr);
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT, "%s %s %s",
+		    "Can't translate ", attr, " to UTF-8");
+		return(ARCHIVE_WARN);
+	}
+
+	if (*p != '\0') {
+		add_pax_attr(&(pax->pax_header),
+		    attr, p);
+	}
+	free(p);
+	return(ARCHIVE_OK);
+}
+
+/*
+ * TODO: Consider adding 'comment' and 'charset' fields to
+ * archive_entry so that clients can specify them.  Also, consider
+ * adding generic key/value tags so clients can add arbitrary
+ * key/value data.
+ *
+ * TODO: Break up this 700-line function!!!!  Yowza!
+ */
+static int
+archive_write_pax_header(struct archive_write *a,
+    struct archive_entry *entry_original)
+{
+	struct archive_entry *entry_main;
+	const char *p;
+	const char *suffix;
+	int need_extension, r, ret;
+	int acl_types;
+	int sparse_count;
+	uint64_t sparse_total, real_size;
+	struct pax *pax;
+	const char *hardlink;
+	const char *path = NULL, *linkpath = NULL;
+	const char *uname = NULL, *gname = NULL;
+	const void *mac_metadata;
+	size_t mac_metadata_size;
+	struct archive_string_conv *sconv;
+	size_t hardlink_length, path_length, linkpath_length;
+	size_t uname_length, gname_length;
+
+	char paxbuff[512];
+	char ustarbuff[512];
+	char ustar_entry_name[256];
+	char pax_entry_name[256];
+	char gnu_sparse_name[256];
+	struct archive_string entry_name;
+
+	ret = ARCHIVE_OK;
+	need_extension = 0;
+	pax = (struct pax *)a->format_data;
+
+	/* Sanity check. */
+	if (archive_entry_pathname(entry_original) == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			  "Can't record entry in tar file without pathname");
+		return (ARCHIVE_FAILED);
+	}
+
+	/*
+	 * Choose a header encoding.
+	 */
+	if (pax->opt_binary)
+		sconv = NULL;/* Binary mode. */
+	else {
+		/* Header encoding is UTF-8. */
+		if (pax->sconv_utf8 == NULL) {
+			/* Initialize the string conversion object
+			 * we must need */
+			pax->sconv_utf8 = archive_string_conversion_to_charset(
+			    &(a->archive), "UTF-8", 1);
+			if (pax->sconv_utf8 == NULL)
+				/* Couldn't allocate memory */
+				return (ARCHIVE_FAILED);
+		}
+		sconv = pax->sconv_utf8;
+	}
+
+	r = get_entry_hardlink(a, entry_original, &hardlink,
+	    &hardlink_length, sconv);
+	if (r == ARCHIVE_FATAL)
+		return (r);
+	else if (r != ARCHIVE_OK) {
+		r = get_entry_hardlink(a, entry_original, &hardlink,
+		    &hardlink_length, NULL);
+		if (r == ARCHIVE_FATAL)
+			return (r);
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate linkname '%s' to %s", hardlink,
+		    archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+		sconv = NULL;/* The header charset switches to binary mode. */
+	}
+
+	/* Make sure this is a type of entry that we can handle here */
+	if (hardlink == NULL) {
+		switch (archive_entry_filetype(entry_original)) {
+		case AE_IFBLK:
+		case AE_IFCHR:
+		case AE_IFIFO:
+		case AE_IFLNK:
+		case AE_IFREG:
+			break;
+		case AE_IFDIR:
+		{
+			/*
+			 * Ensure a trailing '/'.  Modify the original
+			 * entry so the client sees the change.
+			 */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+			const wchar_t *wp;
+
+			wp = archive_entry_pathname_w(entry_original);
+			if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+				struct archive_wstring ws;
+
+				archive_string_init(&ws);
+				path_length = wcslen(wp);
+				if (archive_wstring_ensure(&ws,
+				    path_length + 2) == NULL) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "Can't allocate pax data");
+					archive_wstring_free(&ws);
+					return(ARCHIVE_FATAL);
+				}
+				/* Should we keep '\' ? */
+				if (wp[path_length -1] == L'\\')
+					path_length--;
+				archive_wstrncpy(&ws, wp, path_length);
+				archive_wstrappend_wchar(&ws, L'/');
+				archive_entry_copy_pathname_w(
+				    entry_original, ws.s);
+				archive_wstring_free(&ws);
+				p = NULL;
+			} else
+#endif
+				p = archive_entry_pathname(entry_original);
+			/*
+			 * On Windows, this is a backup operation just in
+			 * case getting WCS failed. On POSIX, this is a
+			 * normal operation.
+			 */
+			if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+				struct archive_string as;
+
+				archive_string_init(&as);
+				path_length = strlen(p);
+				if (archive_string_ensure(&as,
+				    path_length + 2) == NULL) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "Can't allocate pax data");
+					archive_string_free(&as);
+					return(ARCHIVE_FATAL);
+				}
+#if defined(_WIN32) && !defined(__CYGWIN__)
+				/* NOTE: This might break the pathname
+				 * if the current code page is CP932 and
+				 * the pathname includes a character '\'
+				 * as a part of its multibyte pathname. */
+				if (p[strlen(p) -1] == '\\')
+					path_length--;
+				else
+#endif
+				archive_strncpy(&as, p, path_length);
+				archive_strappend_char(&as, '/');
+				archive_entry_copy_pathname(
+				    entry_original, as.s);
+				archive_string_free(&as);
+			}
+			break;
+		}
+		default: /* AE_IFSOCK and unknown */
+			__archive_write_entry_filetype_unsupported(
+			    &a->archive, entry_original, "pax");
+			return (ARCHIVE_FAILED);
+		}
+	}
+
+	/*
+	 * If Mac OS metadata blob is here, recurse to write that
+	 * as a separate entry.  This is really a pretty poor design:
+	 * In particular, it doubles the overhead for long filenames.
+	 * TODO: Help Apple folks design something better and figure
+	 * out how to transition from this legacy format.
+	 *
+	 * Note that this code is present on every platform; clients
+	 * on non-Mac are unlikely to ever provide this data, but
+	 * applications that copy entries from one archive to another
+	 * should not lose data just because the local filesystem
+	 * can't store it.
+	 */
+	mac_metadata =
+	    archive_entry_mac_metadata(entry_original, &mac_metadata_size);
+	if (mac_metadata != NULL) {
+		const char *oname;
+		char *name, *bname;
+		size_t name_length;
+		struct archive_entry *extra = archive_entry_new2(&a->archive);
+
+		oname = archive_entry_pathname(entry_original);
+		name_length = strlen(oname);
+		name = malloc(name_length + 3);
+		if (name == NULL || extra == NULL) {
+			/* XXX error message */
+			archive_entry_free(extra);
+			free(name);
+			return (ARCHIVE_FAILED);
+		}
+		strcpy(name, oname);
+		/* Find last '/'; strip trailing '/' characters */
+		bname = strrchr(name, '/');
+		while (bname != NULL && bname[1] == '\0') {
+			*bname = '\0';
+			bname = strrchr(name, '/');
+		}
+		if (bname == NULL) {
+			memmove(name + 2, name, name_length + 1);
+			memmove(name, "._", 2);
+		} else {
+			bname += 1;
+			memmove(bname + 2, bname, strlen(bname) + 1);
+			memmove(bname, "._", 2);
+		}
+		archive_entry_copy_pathname(extra, name);
+		free(name);
+
+		archive_entry_set_size(extra, mac_metadata_size);
+		archive_entry_set_filetype(extra, AE_IFREG);
+		archive_entry_set_perm(extra,
+		    archive_entry_perm(entry_original));
+		archive_entry_set_mtime(extra,
+		    archive_entry_mtime(entry_original),
+		    archive_entry_mtime_nsec(entry_original));
+		archive_entry_set_gid(extra,
+		    archive_entry_gid(entry_original));
+		archive_entry_set_gname(extra,
+		    archive_entry_gname(entry_original));
+		archive_entry_set_uid(extra,
+		    archive_entry_uid(entry_original));
+		archive_entry_set_uname(extra,
+		    archive_entry_uname(entry_original));
+
+		/* Recurse to write the special copyfile entry. */
+		r = archive_write_pax_header(a, extra);
+		archive_entry_free(extra);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if (r < ret)
+			ret = r;
+		r = (int)archive_write_pax_data(a, mac_metadata,
+		    mac_metadata_size);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if (r < ret)
+			ret = r;
+		r = archive_write_pax_finish_entry(a);
+		if (r < ARCHIVE_WARN)
+			return (r);
+		if (r < ret)
+			ret = r;
+	}
+
+	/* Copy entry so we can modify it as needed. */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* Make sure the path separators in pathname, hardlink and symlink
+	 * are all slash '/', not the Windows path separator '\'. */
+	entry_main = __la_win_entry_in_posix_pathseparator(entry_original);
+	if (entry_main == entry_original)
+		entry_main = archive_entry_clone(entry_original);
+#else
+	entry_main = archive_entry_clone(entry_original);
+#endif
+	if (entry_main == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate pax data");
+		return(ARCHIVE_FATAL);
+	}
+	archive_string_empty(&(pax->pax_header)); /* Blank our work area. */
+	archive_string_empty(&(pax->sparse_map));
+	sparse_total = 0;
+	sparse_list_clear(pax);
+
+	if (hardlink == NULL &&
+	    archive_entry_filetype(entry_main) == AE_IFREG)
+		sparse_count = archive_entry_sparse_reset(entry_main);
+	else
+		sparse_count = 0;
+	if (sparse_count) {
+		int64_t offset, length, last_offset = 0;
+		/* Get the last entry of sparse block. */
+		while (archive_entry_sparse_next(
+		    entry_main, &offset, &length) == ARCHIVE_OK)
+			last_offset = offset + length;
+
+		/* If the last sparse block does not reach the end of file,
+		 * We have to add a empty sparse block as the last entry to
+		 * manage storing file data. */
+		if (last_offset < archive_entry_size(entry_main))
+			archive_entry_sparse_add_entry(entry_main,
+			    archive_entry_size(entry_main), 0);
+		sparse_count = archive_entry_sparse_reset(entry_main);
+	}
+
+	/*
+	 * First, check the name fields and see if any of them
+	 * require binary coding.  If any of them does, then all of
+	 * them do.
+	 */
+	r = get_entry_pathname(a, entry_main, &path, &path_length, sconv);
+	if (r == ARCHIVE_FATAL) {
+		archive_entry_free(entry_main);
+		return (r);
+	} else if (r != ARCHIVE_OK) {
+		r = get_entry_pathname(a, entry_main, &path,
+		    &path_length, NULL);
+		if (r == ARCHIVE_FATAL) {
+			archive_entry_free(entry_main);
+			return (r);
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate pathname '%s' to %s", path,
+		    archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+		sconv = NULL;/* The header charset switches to binary mode. */
+	}
+	r = get_entry_uname(a, entry_main, &uname, &uname_length, sconv);
+	if (r == ARCHIVE_FATAL) {
+		archive_entry_free(entry_main);
+		return (r);
+	} else if (r != ARCHIVE_OK) {
+		r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL);
+		if (r == ARCHIVE_FATAL) {
+			archive_entry_free(entry_main);
+			return (r);
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate uname '%s' to %s", uname,
+		    archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+		sconv = NULL;/* The header charset switches to binary mode. */
+	}
+	r = get_entry_gname(a, entry_main, &gname, &gname_length, sconv);
+	if (r == ARCHIVE_FATAL) {
+		archive_entry_free(entry_main);
+		return (r);
+	} else if (r != ARCHIVE_OK) {
+		r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL);
+		if (r == ARCHIVE_FATAL) {
+			archive_entry_free(entry_main);
+			return (r);
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate gname '%s' to %s", gname,
+		    archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+		sconv = NULL;/* The header charset switches to binary mode. */
+	}
+	linkpath = hardlink;
+	linkpath_length = hardlink_length;
+	if (linkpath == NULL) {
+		r = get_entry_symlink(a, entry_main, &linkpath,
+		    &linkpath_length, sconv);
+		if (r == ARCHIVE_FATAL) {
+			archive_entry_free(entry_main);
+			return (r);
+		} else if (r != ARCHIVE_OK) {
+			r = get_entry_symlink(a, entry_main, &linkpath,
+			    &linkpath_length, NULL);
+			if (r == ARCHIVE_FATAL) {
+				archive_entry_free(entry_main);
+				return (r);
+			}
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Can't translate linkname '%s' to %s", linkpath,
+			    archive_string_conversion_charset_name(sconv));
+			ret = ARCHIVE_WARN;
+			sconv = NULL;
+		}
+	}
+
+	/* If any string conversions failed, get all attributes
+	 * in binary-mode. */
+	if (sconv == NULL && !pax->opt_binary) {
+		if (hardlink != NULL) {
+			r = get_entry_hardlink(a, entry_main, &hardlink,
+			    &hardlink_length, NULL);
+			if (r == ARCHIVE_FATAL) {
+				archive_entry_free(entry_main);
+				return (r);
+			}
+			linkpath = hardlink;
+			linkpath_length = hardlink_length;
+		}
+		r = get_entry_pathname(a, entry_main, &path,
+		    &path_length, NULL);
+		if (r == ARCHIVE_FATAL) {
+			archive_entry_free(entry_main);
+			return (r);
+		}
+		r = get_entry_uname(a, entry_main, &uname, &uname_length, NULL);
+		if (r == ARCHIVE_FATAL) {
+			archive_entry_free(entry_main);
+			return (r);
+		}
+		r = get_entry_gname(a, entry_main, &gname, &gname_length, NULL);
+		if (r == ARCHIVE_FATAL) {
+			archive_entry_free(entry_main);
+			return (r);
+		}
+	}
+
+	/* Store the header encoding first, to be nice to readers. */
+	if (sconv == NULL)
+		add_pax_attr(&(pax->pax_header), "hdrcharset", "BINARY");
+
+
+	/*
+	 * If name is too long, or has non-ASCII characters, add
+	 * 'path' to pax extended attrs.  (Note that an unconvertible
+	 * name must have non-ASCII characters.)
+	 */
+	if (has_non_ASCII(path)) {
+		/* We have non-ASCII characters. */
+		add_pax_attr(&(pax->pax_header), "path", path);
+		archive_entry_set_pathname(entry_main,
+		    build_ustar_entry_name(ustar_entry_name,
+			path, path_length, NULL));
+		need_extension = 1;
+	} else {
+		/* We have an all-ASCII path; we'd like to just store
+		 * it in the ustar header if it will fit.  Yes, this
+		 * duplicates some of the logic in
+		 * archive_write_set_format_ustar.c
+		 */
+		if (path_length <= 100) {
+			/* Fits in the old 100-char tar name field. */
+		} else {
+			/* Find largest suffix that will fit. */
+			/* Note: strlen() > 100, so strlen() - 100 - 1 >= 0 */
+			suffix = strchr(path + path_length - 100 - 1, '/');
+			/* Don't attempt an empty prefix. */
+			if (suffix == path)
+				suffix = strchr(suffix + 1, '/');
+			/* We can put it in the ustar header if it's
+			 * all ASCII and it's either <= 100 characters
+			 * or can be split at a '/' into a prefix <=
+			 * 155 chars and a suffix <= 100 chars.  (Note
+			 * the strchr() above will return NULL exactly
+			 * when the path can't be split.)
+			 */
+			if (suffix == NULL       /* Suffix > 100 chars. */
+			    || suffix[1] == '\0'    /* empty suffix */
+			    || suffix - path > 155)  /* Prefix > 155 chars */
+			{
+				add_pax_attr(&(pax->pax_header), "path", path);
+				archive_entry_set_pathname(entry_main,
+				    build_ustar_entry_name(ustar_entry_name,
+					path, path_length, NULL));
+				need_extension = 1;
+			}
+		}
+	}
+
+	if (linkpath != NULL) {
+		/* If link name is too long or has non-ASCII characters, add
+		 * 'linkpath' to pax extended attrs. */
+		if (linkpath_length > 100 || has_non_ASCII(linkpath)) {
+			add_pax_attr(&(pax->pax_header), "linkpath", linkpath);
+			if (linkpath_length > 100) {
+				if (hardlink != NULL)
+					archive_entry_set_hardlink(entry_main,
+					    "././@LongHardLink");
+				else
+					archive_entry_set_symlink(entry_main,
+					    "././@LongSymLink");
+			}
+			need_extension = 1;
+		}
+	}
+	/* Save a pathname since it will be renamed if `entry_main` has
+	 * sparse blocks. */
+	archive_string_init(&entry_name);
+	archive_strcpy(&entry_name, archive_entry_pathname(entry_main));
+
+	/* If file size is too large, add 'size' to pax extended attrs. */
+	if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) {
+		add_pax_attr_int(&(pax->pax_header), "size",
+		    archive_entry_size(entry_main));
+		need_extension = 1;
+	}
+
+	/* If numeric GID is too large, add 'gid' to pax extended attrs. */
+	if ((unsigned int)archive_entry_gid(entry_main) >= (1 << 18)) {
+		add_pax_attr_int(&(pax->pax_header), "gid",
+		    archive_entry_gid(entry_main));
+		need_extension = 1;
+	}
+
+	/* If group name is too large or has non-ASCII characters, add
+	 * 'gname' to pax extended attrs. */
+	if (gname != NULL) {
+		if (gname_length > 31 || has_non_ASCII(gname)) {
+			add_pax_attr(&(pax->pax_header), "gname", gname);
+			need_extension = 1;
+		}
+	}
+
+	/* If numeric UID is too large, add 'uid' to pax extended attrs. */
+	if ((unsigned int)archive_entry_uid(entry_main) >= (1 << 18)) {
+		add_pax_attr_int(&(pax->pax_header), "uid",
+		    archive_entry_uid(entry_main));
+		need_extension = 1;
+	}
+
+	/* Add 'uname' to pax extended attrs if necessary. */
+	if (uname != NULL) {
+		if (uname_length > 31 || has_non_ASCII(uname)) {
+			add_pax_attr(&(pax->pax_header), "uname", uname);
+			need_extension = 1;
+		}
+	}
+
+	/*
+	 * POSIX/SUSv3 doesn't provide a standard key for large device
+	 * numbers.  I use the same keys here that Joerg Schilling
+	 * used for 'star.'  (Which, somewhat confusingly, are called
+	 * "devXXX" even though they code "rdev" values.)  No doubt,
+	 * other implementations use other keys.  Note that there's no
+	 * reason we can't write the same information into a number of
+	 * different keys.
+	 *
+	 * Of course, this is only needed for block or char device entries.
+	 */
+	if (archive_entry_filetype(entry_main) == AE_IFBLK
+	    || archive_entry_filetype(entry_main) == AE_IFCHR) {
+		/*
+		 * If rdevmajor is too large, add 'SCHILY.devmajor' to
+		 * extended attributes.
+		 */
+		int rdevmajor, rdevminor;
+		rdevmajor = archive_entry_rdevmajor(entry_main);
+		rdevminor = archive_entry_rdevminor(entry_main);
+		if (rdevmajor >= (1 << 18)) {
+			add_pax_attr_int(&(pax->pax_header), "SCHILY.devmajor",
+			    rdevmajor);
+			/*
+			 * Non-strict formatting below means we don't
+			 * have to truncate here.  Not truncating improves
+			 * the chance that some more modern tar archivers
+			 * (such as GNU tar 1.13) can restore the full
+			 * value even if they don't understand the pax
+			 * extended attributes.  See my rant below about
+			 * file size fields for additional details.
+			 */
+			/* archive_entry_set_rdevmajor(entry_main,
+			   rdevmajor & ((1 << 18) - 1)); */
+			need_extension = 1;
+		}
+
+		/*
+		 * If devminor is too large, add 'SCHILY.devminor' to
+		 * extended attributes.
+		 */
+		if (rdevminor >= (1 << 18)) {
+			add_pax_attr_int(&(pax->pax_header), "SCHILY.devminor",
+			    rdevminor);
+			/* Truncation is not necessary here, either. */
+			/* archive_entry_set_rdevminor(entry_main,
+			   rdevminor & ((1 << 18) - 1)); */
+			need_extension = 1;
+		}
+	}
+
+	/*
+	 * Technically, the mtime field in the ustar header can
+	 * support 33 bits, but many platforms use signed 32-bit time
+	 * values.  The cutoff of 0x7fffffff here is a compromise.
+	 * Yes, this check is duplicated just below; this helps to
+	 * avoid writing an mtime attribute just to handle a
+	 * high-resolution timestamp in "restricted pax" mode.
+	 */
+	if (!need_extension &&
+	    ((archive_entry_mtime(entry_main) < 0)
+		|| (archive_entry_mtime(entry_main) >= 0x7fffffff)))
+		need_extension = 1;
+
+	/* I use a star-compatible file flag attribute. */
+	p = archive_entry_fflags_text(entry_main);
+	if (!need_extension && p != NULL  &&  *p != '\0')
+		need_extension = 1;
+
+	/* If there are extended attributes, we need an extension */
+	if (!need_extension && archive_entry_xattr_count(entry_original) > 0)
+		need_extension = 1;
+
+	/* If there are sparse info, we need an extension */
+	if (!need_extension && sparse_count > 0)
+		need_extension = 1;
+
+	acl_types = archive_entry_acl_types(entry_original);
+
+	/* If there are any ACL entries, we need an extension */
+	if (!need_extension && acl_types != 0)
+		need_extension = 1;
+
+	/* If the symlink type is defined, we need an extension */
+	if (!need_extension && archive_entry_symlink_type(entry_main) > 0)
+		need_extension = 1;
+
+	/*
+	 * Libarchive used to include these in extended headers for
+	 * restricted pax format, but that confused people who
+	 * expected ustar-like time semantics.  So now we only include
+	 * them in full pax format.
+	 */
+	if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED) {
+		if (archive_entry_ctime(entry_main) != 0  ||
+		    archive_entry_ctime_nsec(entry_main) != 0)
+			add_pax_attr_time(&(pax->pax_header), "ctime",
+			    archive_entry_ctime(entry_main),
+			    archive_entry_ctime_nsec(entry_main));
+
+		if (archive_entry_atime(entry_main) != 0 ||
+		    archive_entry_atime_nsec(entry_main) != 0)
+			add_pax_attr_time(&(pax->pax_header), "atime",
+			    archive_entry_atime(entry_main),
+			    archive_entry_atime_nsec(entry_main));
+
+		/* Store birth/creationtime only if it's earlier than mtime */
+		if (archive_entry_birthtime_is_set(entry_main) &&
+		    archive_entry_birthtime(entry_main)
+		    < archive_entry_mtime(entry_main))
+			add_pax_attr_time(&(pax->pax_header),
+			    "LIBARCHIVE.creationtime",
+			    archive_entry_birthtime(entry_main),
+			    archive_entry_birthtime_nsec(entry_main));
+	}
+
+	/*
+	 * The following items are handled differently in "pax
+	 * restricted" format.  In particular, in "pax restricted"
+	 * format they won't be added unless need_extension is
+	 * already set (we're already generating an extended header, so
+	 * may as well include these).
+	 */
+	if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_RESTRICTED ||
+	    need_extension) {
+		if (archive_entry_mtime(entry_main) < 0  ||
+		    archive_entry_mtime(entry_main) >= 0x7fffffff  ||
+		    archive_entry_mtime_nsec(entry_main) != 0)
+			add_pax_attr_time(&(pax->pax_header), "mtime",
+			    archive_entry_mtime(entry_main),
+			    archive_entry_mtime_nsec(entry_main));
+
+		/* I use a star-compatible file flag attribute. */
+		p = archive_entry_fflags_text(entry_main);
+		if (p != NULL  &&  *p != '\0')
+			add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p);
+
+		/* I use star-compatible ACL attributes. */
+		if ((acl_types & ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {
+			ret = add_pax_acl(a, entry_original, pax,
+			    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+			    ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA |
+			    ARCHIVE_ENTRY_ACL_STYLE_COMPACT);
+			if (ret == ARCHIVE_FATAL) {
+				archive_entry_free(entry_main);
+				archive_string_free(&entry_name);
+				return (ARCHIVE_FATAL);
+			}
+		}
+		if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
+			ret = add_pax_acl(a, entry_original, pax,
+			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
+			    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+			    ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+			if (ret == ARCHIVE_FATAL) {
+				archive_entry_free(entry_main);
+				archive_string_free(&entry_name);
+				return (ARCHIVE_FATAL);
+			}
+		}
+		if (acl_types & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) {
+			ret = add_pax_acl(a, entry_original, pax,
+			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT |
+			    ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID |
+			    ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA);
+			if (ret == ARCHIVE_FATAL) {
+				archive_entry_free(entry_main);
+				archive_string_free(&entry_name);
+				return (ARCHIVE_FATAL);
+			}
+		}
+
+		/* We use GNU-tar-compatible sparse attributes. */
+		if (sparse_count > 0) {
+			int64_t soffset, slength;
+
+			add_pax_attr_int(&(pax->pax_header),
+			    "GNU.sparse.major", 1);
+			add_pax_attr_int(&(pax->pax_header),
+			    "GNU.sparse.minor", 0);
+			/*
+			 * Make sure to store the original path, since
+			 * truncation to ustar limit happened already.
+			 */
+			add_pax_attr(&(pax->pax_header),
+			    "GNU.sparse.name", path);
+			add_pax_attr_int(&(pax->pax_header),
+			    "GNU.sparse.realsize",
+			    archive_entry_size(entry_main));
+
+			/* Rename the file name which will be used for
+			 * ustar header to a special name, which GNU
+			 * PAX Format 1.0 requires */
+			archive_entry_set_pathname(entry_main,
+			    build_gnu_sparse_name(gnu_sparse_name,
+			        entry_name.s));
+
+			/*
+			 * - Make a sparse map, which will precede a file data.
+			 * - Get the total size of available data of sparse.
+			 */
+			archive_string_sprintf(&(pax->sparse_map), "%d\n",
+			    sparse_count);
+			while (archive_entry_sparse_next(entry_main,
+			    &soffset, &slength) == ARCHIVE_OK) {
+				archive_string_sprintf(&(pax->sparse_map),
+				    "%jd\n%jd\n",
+				    (intmax_t)soffset,
+				    (intmax_t)slength);
+				sparse_total += slength;
+				if (sparse_list_add(pax, soffset, slength)
+				    != ARCHIVE_OK) {
+					archive_set_error(&a->archive,
+					    ENOMEM,
+					    "Can't allocate memory");
+					archive_entry_free(entry_main);
+					archive_string_free(&entry_name);
+					return (ARCHIVE_FATAL);
+				}
+			}
+		}
+
+		/* Store extended attributes */
+		if (archive_write_pax_header_xattrs(a, pax, entry_original)
+		    == ARCHIVE_FATAL) {
+			archive_entry_free(entry_main);
+			archive_string_free(&entry_name);
+			return (ARCHIVE_FATAL);
+		}
+
+		/* Store extended symlink information */
+		if (archive_entry_symlink_type(entry_main) ==
+		    AE_SYMLINK_TYPE_FILE) {
+			add_pax_attr(&(pax->pax_header),
+			    "LIBARCHIVE.symlinktype", "file");
+		} else if (archive_entry_symlink_type(entry_main) ==
+		    AE_SYMLINK_TYPE_DIRECTORY) {
+			add_pax_attr(&(pax->pax_header),
+			    "LIBARCHIVE.symlinktype", "dir");
+		}
+	}
+
+	/* Only regular files have data. */
+	if (archive_entry_filetype(entry_main) != AE_IFREG)
+		archive_entry_set_size(entry_main, 0);
+
+	/*
+	 * Pax-restricted does not store data for hardlinks, in order
+	 * to improve compatibility with ustar.
+	 */
+	if (a->archive.archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE &&
+	    hardlink != NULL)
+		archive_entry_set_size(entry_main, 0);
+
+	/*
+	 * XXX Full pax interchange format does permit a hardlink
+	 * entry to have data associated with it.  I'm not supporting
+	 * that here because the client expects me to tell them whether
+	 * or not this format expects data for hardlinks.  If I
+	 * don't check here, then every pax archive will end up with
+	 * duplicated data for hardlinks.  Someday, there may be
+	 * need to select this behavior, in which case the following
+	 * will need to be revisited. XXX
+	 */
+	if (hardlink != NULL)
+		archive_entry_set_size(entry_main, 0);
+
+	/* Save a real file size. */
+	real_size = archive_entry_size(entry_main);
+	/*
+	 * Overwrite a file size by the total size of sparse blocks and
+	 * the size of sparse map info. That file size is the length of
+	 * the data, which we will exactly store into an archive file.
+	 */
+	if (archive_strlen(&(pax->sparse_map))) {
+		size_t mapsize = archive_strlen(&(pax->sparse_map));
+		pax->sparse_map_padding = 0x1ff & (-(ssize_t)mapsize);
+		archive_entry_set_size(entry_main,
+		    mapsize + pax->sparse_map_padding + sparse_total);
+	}
+
+	/* Format 'ustar' header for main entry.
+	 *
+	 * The trouble with file size: If the reader can't understand
+	 * the file size, they may not be able to locate the next
+	 * entry and the rest of the archive is toast.  Pax-compliant
+	 * readers are supposed to ignore the file size in the main
+	 * header, so the question becomes how to maximize portability
+	 * for readers that don't support pax attribute extensions.
+	 * For maximum compatibility, I permit numeric extensions in
+	 * the main header so that the file size stored will always be
+	 * correct, even if it's in a format that only some
+	 * implementations understand.  The technique used here is:
+	 *
+	 *  a) If possible, follow the standard exactly.  This handles
+	 *  files up to 8 gigabytes minus 1.
+	 *
+	 *  b) If that fails, try octal but omit the field terminator.
+	 *  That handles files up to 64 gigabytes minus 1.
+	 *
+	 *  c) Otherwise, use base-256 extensions.  That handles files
+	 *  up to 2^63 in this implementation, with the potential to
+	 *  go up to 2^94.  That should hold us for a while. ;-)
+	 *
+	 * The non-strict formatter uses similar logic for other
+	 * numeric fields, though they're less critical.
+	 */
+	if (__archive_write_format_header_ustar(a, ustarbuff, entry_main, -1, 0,
+	    NULL) == ARCHIVE_FATAL) {
+		archive_entry_free(entry_main);
+		archive_string_free(&entry_name);
+		return (ARCHIVE_FATAL);
+	}
+
+	/* If we built any extended attributes, write that entry first. */
+	if (archive_strlen(&(pax->pax_header)) > 0) {
+		struct archive_entry *pax_attr_entry;
+		time_t s;
+		int64_t uid, gid;
+		int mode;
+
+		pax_attr_entry = archive_entry_new2(&a->archive);
+		p = entry_name.s;
+		archive_entry_set_pathname(pax_attr_entry,
+		    build_pax_attribute_name(pax_entry_name, p));
+		archive_entry_set_size(pax_attr_entry,
+		    archive_strlen(&(pax->pax_header)));
+		/* Copy uid/gid (but clip to ustar limits). */
+		uid = archive_entry_uid(entry_main);
+		if (uid >= 1 << 18)
+			uid = (1 << 18) - 1;
+		archive_entry_set_uid(pax_attr_entry, uid);
+		gid = archive_entry_gid(entry_main);
+		if (gid >= 1 << 18)
+			gid = (1 << 18) - 1;
+		archive_entry_set_gid(pax_attr_entry, gid);
+		/* Copy mode over (but not setuid/setgid bits) */
+		mode = archive_entry_mode(entry_main);
+#ifdef S_ISUID
+		mode &= ~S_ISUID;
+#endif
+#ifdef S_ISGID
+		mode &= ~S_ISGID;
+#endif
+#ifdef S_ISVTX
+		mode &= ~S_ISVTX;
+#endif
+		archive_entry_set_mode(pax_attr_entry, mode);
+
+		/* Copy uname/gname. */
+		archive_entry_set_uname(pax_attr_entry,
+		    archive_entry_uname(entry_main));
+		archive_entry_set_gname(pax_attr_entry,
+		    archive_entry_gname(entry_main));
+
+		/* Copy mtime, but clip to ustar limits. */
+		s = archive_entry_mtime(entry_main);
+		if (s < 0) { s = 0; }
+		if (s >= 0x7fffffff) { s = 0x7fffffff; }
+		archive_entry_set_mtime(pax_attr_entry, s, 0);
+
+		/* Standard ustar doesn't support atime. */
+		archive_entry_set_atime(pax_attr_entry, 0, 0);
+
+		/* Standard ustar doesn't support ctime. */
+		archive_entry_set_ctime(pax_attr_entry, 0, 0);
+
+		r = __archive_write_format_header_ustar(a, paxbuff,
+		    pax_attr_entry, 'x', 1, NULL);
+
+		archive_entry_free(pax_attr_entry);
+
+		/* Note that the 'x' header shouldn't ever fail to format */
+		if (r < ARCHIVE_WARN) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "archive_write_pax_header: "
+			    "'x' header failed?!  This can't happen.\n");
+			archive_entry_free(entry_main);
+			archive_string_free(&entry_name);
+			return (ARCHIVE_FATAL);
+		} else if (r < ret)
+			ret = r;
+		r = __archive_write_output(a, paxbuff, 512);
+		if (r != ARCHIVE_OK) {
+			sparse_list_clear(pax);
+			pax->entry_bytes_remaining = 0;
+			pax->entry_padding = 0;
+			archive_entry_free(entry_main);
+			archive_string_free(&entry_name);
+			return (ARCHIVE_FATAL);
+		}
+
+		pax->entry_bytes_remaining = archive_strlen(&(pax->pax_header));
+		pax->entry_padding =
+		    0x1ff & (-(int64_t)pax->entry_bytes_remaining);
+
+		r = __archive_write_output(a, pax->pax_header.s,
+		    archive_strlen(&(pax->pax_header)));
+		if (r != ARCHIVE_OK) {
+			/* If a write fails, we're pretty much toast. */
+			archive_entry_free(entry_main);
+			archive_string_free(&entry_name);
+			return (ARCHIVE_FATAL);
+		}
+		/* Pad out the end of the entry. */
+		r = __archive_write_nulls(a, (size_t)pax->entry_padding);
+		if (r != ARCHIVE_OK) {
+			/* If a write fails, we're pretty much toast. */
+			archive_entry_free(entry_main);
+			archive_string_free(&entry_name);
+			return (ARCHIVE_FATAL);
+		}
+		pax->entry_bytes_remaining = pax->entry_padding = 0;
+	}
+
+	/* Write the header for main entry. */
+	r = __archive_write_output(a, ustarbuff, 512);
+	if (r != ARCHIVE_OK) {
+		archive_entry_free(entry_main);
+		archive_string_free(&entry_name);
+		return (r);
+	}
+
+	/*
+	 * Inform the client of the on-disk size we're using, so
+	 * they can avoid unnecessarily writing a body for something
+	 * that we're just going to ignore.
+	 */
+	archive_entry_set_size(entry_original, real_size);
+	if (pax->sparse_list == NULL && real_size > 0) {
+		/* This is not a sparse file but we handle its data as
+		 * a sparse block. */
+		sparse_list_add(pax, 0, real_size);
+		sparse_total = real_size;
+	}
+	pax->entry_padding = 0x1ff & (-(int64_t)sparse_total);
+	archive_entry_free(entry_main);
+	archive_string_free(&entry_name);
+
+	return (ret);
+}
+
+/*
+ * We need a valid name for the regular 'ustar' entry.  This routine
+ * tries to hack something more-or-less reasonable.
+ *
+ * The approach here tries to preserve leading dir names.  We do so by
+ * working with four sections:
+ *   1) "prefix" directory names,
+ *   2) "suffix" directory names,
+ *   3) inserted dir name (optional),
+ *   4) filename.
+ *
+ * These sections must satisfy the following requirements:
+ *   * Parts 1 & 2 together form an initial portion of the dir name.
+ *   * Part 3 is specified by the caller.  (It should not contain a leading
+ *     or trailing '/'.)
+ *   * Part 4 forms an initial portion of the base filename.
+ *   * The filename must be <= 99 chars to fit the ustar 'name' field.
+ *   * Parts 2, 3, 4 together must be <= 99 chars to fit the ustar 'name' fld.
+ *   * Part 1 must be <= 155 chars to fit the ustar 'prefix' field.
+ *   * If the original name ends in a '/', the new name must also end in a '/'
+ *   * Trailing '/.' sequences may be stripped.
+ *
+ * Note: Recall that the ustar format does not store the '/' separating
+ * parts 1 & 2, but does store the '/' separating parts 2 & 3.
+ */
+static char *
+build_ustar_entry_name(char *dest, const char *src, size_t src_length,
+    const char *insert)
+{
+	const char *prefix, *prefix_end;
+	const char *suffix, *suffix_end;
+	const char *filename, *filename_end;
+	char *p;
+	int need_slash = 0; /* Was there a trailing slash? */
+	size_t suffix_length = 99;
+	size_t insert_length;
+
+	/* Length of additional dir element to be added. */
+	if (insert == NULL)
+		insert_length = 0;
+	else
+		/* +2 here allows for '/' before and after the insert. */
+		insert_length = strlen(insert) + 2;
+
+	/* Step 0: Quick bailout in a common case. */
+	if (src_length < 100 && insert == NULL) {
+		strncpy(dest, src, src_length);
+		dest[src_length] = '\0';
+		return (dest);
+	}
+
+	/* Step 1: Locate filename and enforce the length restriction. */
+	filename_end = src + src_length;
+	/* Remove trailing '/' chars and '/.' pairs. */
+	for (;;) {
+		if (filename_end > src && filename_end[-1] == '/') {
+			filename_end --;
+			need_slash = 1; /* Remember to restore trailing '/'. */
+			continue;
+		}
+		if (filename_end > src + 1 && filename_end[-1] == '.'
+		    && filename_end[-2] == '/') {
+			filename_end -= 2;
+			need_slash = 1; /* "foo/." will become "foo/" */
+			continue;
+		}
+		break;
+	}
+	if (need_slash)
+		suffix_length--;
+	/* Find start of filename. */
+	filename = filename_end - 1;
+	while ((filename > src) && (*filename != '/'))
+		filename --;
+	if ((*filename == '/') && (filename < filename_end - 1))
+		filename ++;
+	/* Adjust filename_end so that filename + insert fits in 99 chars. */
+	suffix_length -= insert_length;
+	if (filename_end > filename + suffix_length)
+		filename_end = filename + suffix_length;
+	/* Calculate max size for "suffix" section (#3 above). */
+	suffix_length -= filename_end - filename;
+
+	/* Step 2: Locate the "prefix" section of the dirname, including
+	 * trailing '/'. */
+	prefix = src;
+	prefix_end = prefix + 155;
+	if (prefix_end > filename)
+		prefix_end = filename;
+	while (prefix_end > prefix && *prefix_end != '/')
+		prefix_end--;
+	if ((prefix_end < filename) && (*prefix_end == '/'))
+		prefix_end++;
+
+	/* Step 3: Locate the "suffix" section of the dirname,
+	 * including trailing '/'. */
+	suffix = prefix_end;
+	suffix_end = suffix + suffix_length; /* Enforce limit. */
+	if (suffix_end > filename)
+		suffix_end = filename;
+	if (suffix_end < suffix)
+		suffix_end = suffix;
+	while (suffix_end > suffix && *suffix_end != '/')
+		suffix_end--;
+	if ((suffix_end < filename) && (*suffix_end == '/'))
+		suffix_end++;
+
+	/* Step 4: Build the new name. */
+	/* The OpenBSD strlcpy function is safer, but less portable. */
+	/* Rather than maintain two versions, just use the strncpy version. */
+	p = dest;
+	if (prefix_end > prefix) {
+		strncpy(p, prefix, prefix_end - prefix);
+		p += prefix_end - prefix;
+	}
+	if (suffix_end > suffix) {
+		strncpy(p, suffix, suffix_end - suffix);
+		p += suffix_end - suffix;
+	}
+	if (insert != NULL) {
+		/* Note: assume insert does not have leading or trailing '/' */
+		strcpy(p, insert);
+		p += strlen(insert);
+		*p++ = '/';
+	}
+	strncpy(p, filename, filename_end - filename);
+	p += filename_end - filename;
+	if (need_slash)
+		*p++ = '/';
+	*p = '\0';
+
+	return (dest);
+}
+
+/*
+ * The ustar header for the pax extended attributes must have a
+ * reasonable name:  SUSv3 requires 'dirname'/PaxHeader.'pid'/'filename'
+ * where 'pid' is the PID of the archiving process.  Unfortunately,
+ * that makes testing a pain since the output varies for each run,
+ * so I'm sticking with the simpler 'dirname'/PaxHeader/'filename'
+ * for now.  (Someday, I'll make this settable.  Then I can use the
+ * SUS recommendation as default and test harnesses can override it
+ * to get predictable results.)
+ *
+ * Joerg Schilling has argued that this is unnecessary because, in
+ * practice, if the pax extended attributes get extracted as regular
+ * files, no one is going to bother reading those attributes to
+ * manually restore them.  Based on this, 'star' uses
+ * /tmp/PaxHeader/'basename' as the ustar header name.  This is a
+ * tempting argument, in part because it's simpler than the SUSv3
+ * recommendation, but I'm not entirely convinced.  I'm also
+ * uncomfortable with the fact that "/tmp" is a Unix-ism.
+ *
+ * The following routine leverages build_ustar_entry_name() above and
+ * so is simpler than you might think.  It just needs to provide the
+ * additional path element and handle a few pathological cases).
+ */
+static char *
+build_pax_attribute_name(char *dest, const char *src)
+{
+	char buff[64];
+	const char *p;
+
+	/* Handle the null filename case. */
+	if (src == NULL || *src == '\0') {
+		strcpy(dest, "PaxHeader/blank");
+		return (dest);
+	}
+
+	/* Prune final '/' and other unwanted final elements. */
+	p = src + strlen(src);
+	for (;;) {
+		/* Ends in "/", remove the '/' */
+		if (p > src && p[-1] == '/') {
+			--p;
+			continue;
+		}
+		/* Ends in "/.", remove the '.' */
+		if (p > src + 1 && p[-1] == '.'
+		    && p[-2] == '/') {
+			--p;
+			continue;
+		}
+		break;
+	}
+
+	/* Pathological case: After above, there was nothing left.
+	 * This includes "/." "/./." "/.//./." etc. */
+	if (p == src) {
+		strcpy(dest, "/PaxHeader/rootdir");
+		return (dest);
+	}
+
+	/* Convert unadorned "." into a suitable filename. */
+	if (*src == '.' && p == src + 1) {
+		strcpy(dest, "PaxHeader/currentdir");
+		return (dest);
+	}
+
+	/*
+	 * TODO: Push this string into the 'pax' structure to avoid
+	 * recomputing it every time.  That will also open the door
+	 * to having clients override it.
+	 */
+#if HAVE_GETPID && 0  /* Disable this for now; see above comment. */
+	sprintf(buff, "PaxHeader.%d", getpid());
+#else
+	/* If the platform can't fetch the pid, don't include it. */
+	strcpy(buff, "PaxHeader");
+#endif
+	/* General case: build a ustar-compatible name adding
+	 * "/PaxHeader/". */
+	build_ustar_entry_name(dest, src, p - src, buff);
+
+	return (dest);
+}
+
+/*
+ * GNU PAX Format 1.0 requires the special name, which pattern is:
+ * <dir>/GNUSparseFile.<pid>/<original file name>
+ *
+ * Since reproducible archives are more important, use 0 as pid.
+ *
+ * This function is used for only Sparse file, a file type of which
+ * is regular file.
+ */
+static char *
+build_gnu_sparse_name(char *dest, const char *src)
+{
+	const char *p;
+
+	/* Handle the null filename case. */
+	if (src == NULL || *src == '\0') {
+		strcpy(dest, "GNUSparseFile/blank");
+		return (dest);
+	}
+
+	/* Prune final '/' and other unwanted final elements. */
+	p = src + strlen(src);
+	for (;;) {
+		/* Ends in "/", remove the '/' */
+		if (p > src && p[-1] == '/') {
+			--p;
+			continue;
+		}
+		/* Ends in "/.", remove the '.' */
+		if (p > src + 1 && p[-1] == '.'
+		    && p[-2] == '/') {
+			--p;
+			continue;
+		}
+		break;
+	}
+
+	/* General case: build a ustar-compatible name adding
+	 * "/GNUSparseFile/". */
+	build_ustar_entry_name(dest, src, p - src, "GNUSparseFile.0");
+
+	return (dest);
+}
+
+/* Write two null blocks for the end of archive */
+static int
+archive_write_pax_close(struct archive_write *a)
+{
+	return (__archive_write_nulls(a, 512 * 2));
+}
+
+static int
+archive_write_pax_free(struct archive_write *a)
+{
+	struct pax *pax;
+
+	pax = (struct pax *)a->format_data;
+	if (pax == NULL)
+		return (ARCHIVE_OK);
+
+	archive_string_free(&pax->pax_header);
+	archive_string_free(&pax->sparse_map);
+	archive_string_free(&pax->l_url_encoded_name);
+	sparse_list_clear(pax);
+	free(pax);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_pax_finish_entry(struct archive_write *a)
+{
+	struct pax *pax;
+	uint64_t remaining;
+	int ret;
+
+	pax = (struct pax *)a->format_data;
+	remaining = pax->entry_bytes_remaining;
+	if (remaining == 0) {
+		while (pax->sparse_list) {
+			struct sparse_block *sb;
+			if (!pax->sparse_list->is_hole)
+				remaining += pax->sparse_list->remaining;
+			sb = pax->sparse_list->next;
+			free(pax->sparse_list);
+			pax->sparse_list = sb;
+		}
+	}
+	ret = __archive_write_nulls(a, (size_t)(remaining + pax->entry_padding));
+	pax->entry_bytes_remaining = pax->entry_padding = 0;
+	return (ret);
+}
+
+static ssize_t
+archive_write_pax_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct pax *pax;
+	size_t ws;
+	size_t total;
+	int ret;
+
+	pax = (struct pax *)a->format_data;
+
+	/*
+	 * According to GNU PAX format 1.0, write a sparse map
+	 * before the body.
+	 */
+	if (archive_strlen(&(pax->sparse_map))) {
+		ret = __archive_write_output(a, pax->sparse_map.s,
+		    archive_strlen(&(pax->sparse_map)));
+		if (ret != ARCHIVE_OK)
+			return (ret);
+		ret = __archive_write_nulls(a, pax->sparse_map_padding);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+		archive_string_empty(&(pax->sparse_map));
+	}
+
+	total = 0;
+	while (total < s) {
+		const unsigned char *p;
+
+		while (pax->sparse_list != NULL &&
+		    pax->sparse_list->remaining == 0) {
+			struct sparse_block *sb = pax->sparse_list->next;
+			free(pax->sparse_list);
+			pax->sparse_list = sb;
+		}
+
+		if (pax->sparse_list == NULL)
+			return (total);
+
+		p = ((const unsigned char *)buff) + total;
+		ws = s - total;
+		if (ws > pax->sparse_list->remaining)
+			ws = (size_t)pax->sparse_list->remaining;
+
+		if (pax->sparse_list->is_hole) {
+			/* Current block is hole thus we do not write
+			 * the body. */
+			pax->sparse_list->remaining -= ws;
+			total += ws;
+			continue;
+		}
+
+		ret = __archive_write_output(a, p, ws);
+		pax->sparse_list->remaining -= ws;
+		total += ws;
+		if (ret != ARCHIVE_OK)
+			return (ret);
+	}
+	return (total);
+}
+
+static int
+has_non_ASCII(const char *_p)
+{
+	const unsigned char *p = (const unsigned char *)_p;
+
+	if (p == NULL)
+		return (1);
+	while (*p != '\0' && *p < 128)
+		p++;
+	return (*p != '\0');
+}
+
+/*
+ * Used by extended attribute support; encodes the name
+ * so that there will be no '=' characters in the result.
+ */
+static char *
+url_encode(const char *in)
+{
+	const char *s;
+	char *d;
+	int out_len = 0;
+	char *out;
+
+	for (s = in; *s != '\0'; s++) {
+		if (*s < 33 || *s > 126 || *s == '%' || *s == '=')
+			out_len += 3;
+		else
+			out_len++;
+	}
+
+	out = (char *)malloc(out_len + 1);
+	if (out == NULL)
+		return (NULL);
+
+	for (s = in, d = out; *s != '\0'; s++) {
+		/* encode any non-printable ASCII character or '%' or '=' */
+		if (*s < 33 || *s > 126 || *s == '%' || *s == '=') {
+			/* URL encoding is '%' followed by two hex digits */
+			*d++ = '%';
+			*d++ = "0123456789ABCDEF"[0x0f & (*s >> 4)];
+			*d++ = "0123456789ABCDEF"[0x0f & *s];
+		} else {
+			*d++ = *s;
+		}
+	}
+	*d = '\0';
+	return (out);
+}
+
+/*
+ * Encode a sequence of bytes into a C string using base-64 encoding.
+ *
+ * Returns a null-terminated C string allocated with malloc(); caller
+ * is responsible for freeing the result.
+ */
+static char *
+base64_encode(const char *s, size_t len)
+{
+	static const char digits[64] =
+	    { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
+	      'P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d',
+	      'e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',
+	      't','u','v','w','x','y','z','0','1','2','3','4','5','6','7',
+	      '8','9','+','/' };
+	int v;
+	char *d, *out;
+
+	/* 3 bytes becomes 4 chars, but round up and allow for trailing NUL */
+	out = (char *)malloc((len * 4 + 2) / 3 + 1);
+	if (out == NULL)
+		return (NULL);
+	d = out;
+
+	/* Convert each group of 3 bytes into 4 characters. */
+	while (len >= 3) {
+		v = (((int)s[0] << 16) & 0xff0000)
+		    | (((int)s[1] << 8) & 0xff00)
+		    | (((int)s[2]) & 0x00ff);
+		s += 3;
+		len -= 3;
+		*d++ = digits[(v >> 18) & 0x3f];
+		*d++ = digits[(v >> 12) & 0x3f];
+		*d++ = digits[(v >> 6) & 0x3f];
+		*d++ = digits[(v) & 0x3f];
+	}
+	/* Handle final group of 1 byte (2 chars) or 2 bytes (3 chars). */
+	switch (len) {
+	case 0: break;
+	case 1:
+		v = (((int)s[0] << 16) & 0xff0000);
+		*d++ = digits[(v >> 18) & 0x3f];
+		*d++ = digits[(v >> 12) & 0x3f];
+		break;
+	case 2:
+		v = (((int)s[0] << 16) & 0xff0000)
+		    | (((int)s[1] << 8) & 0xff00);
+		*d++ = digits[(v >> 18) & 0x3f];
+		*d++ = digits[(v >> 12) & 0x3f];
+		*d++ = digits[(v >> 6) & 0x3f];
+		break;
+	}
+	/* Add trailing NUL character so output is a valid C string. */
+	*d = '\0';
+	return (out);
+}
+
+static void
+sparse_list_clear(struct pax *pax)
+{
+	while (pax->sparse_list != NULL) {
+		struct sparse_block *sb = pax->sparse_list;
+		pax->sparse_list = sb->next;
+		free(sb);
+	}
+	pax->sparse_tail = NULL;
+}
+
+static int
+_sparse_list_add_block(struct pax *pax, int64_t offset, int64_t length,
+    int is_hole)
+{
+	struct sparse_block *sb;
+
+	sb = (struct sparse_block *)malloc(sizeof(*sb));
+	if (sb == NULL)
+		return (ARCHIVE_FATAL);
+	sb->next = NULL;
+	sb->is_hole = is_hole;
+	sb->offset = offset;
+	sb->remaining = length;
+	if (pax->sparse_list == NULL || pax->sparse_tail == NULL)
+		pax->sparse_list = pax->sparse_tail = sb;
+	else {
+		pax->sparse_tail->next = sb;
+		pax->sparse_tail = sb;
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+sparse_list_add(struct pax *pax, int64_t offset, int64_t length)
+{
+	int64_t last_offset;
+	int r;
+
+	if (pax->sparse_tail == NULL)
+		last_offset = 0;
+	else {
+		last_offset = pax->sparse_tail->offset +
+		    pax->sparse_tail->remaining;
+	}
+	if (last_offset < offset) {
+		/* Add a hole block. */
+		r = _sparse_list_add_block(pax, last_offset,
+		    offset - last_offset, 1);
+		if (r != ARCHIVE_OK)
+			return (r);
+	}
+	/* Add data block. */
+	return (_sparse_list_add_block(pax, offset, length, 0));
+}
+
diff --git a/c/archive_write_set_format_private.h b/c/archive_write_set_format_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_private.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2020 Martin Matuska
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED
+#define ARCHIVE_WRITE_SET_FORMAT_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+
+void __archive_write_entry_filetype_unsupported(struct archive *a,
+    struct archive_entry *entry, const char *format);
+#endif
diff --git a/c/archive_write_set_format_raw.c b/c/archive_write_set_format_raw.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_raw.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2013 Marek Kubica
+ * 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"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "archive_entry.h"
+#include "archive_write_private.h"
+
+static ssize_t	archive_write_raw_data(struct archive_write *,
+		    const void *buff, size_t s);
+static int	archive_write_raw_free(struct archive_write *);
+static int	archive_write_raw_header(struct archive_write *,
+		    struct archive_entry *);
+
+struct raw {
+        int entries_written;
+};
+
+/*
+ * Set output format to 'raw' format.
+ */
+int
+archive_write_set_format_raw(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct raw *raw;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_raw");
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	raw = (struct raw *)calloc(1, sizeof(*raw));
+	if (raw == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate raw data");
+		return (ARCHIVE_FATAL);
+	}
+	raw->entries_written = 0;
+	a->format_data = raw;
+	a->format_name = "raw";
+        /* no options exist for this format */
+	a->format_options = NULL;
+	a->format_write_header = archive_write_raw_header;
+	a->format_write_data = archive_write_raw_data;
+	a->format_finish_entry = NULL;
+        /* nothing needs to be done on closing */
+	a->format_close = NULL;
+	a->format_free = archive_write_raw_free;
+	a->archive.archive_format = ARCHIVE_FORMAT_RAW;
+	a->archive.archive_format_name = "RAW";
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_raw_header(struct archive_write *a, struct archive_entry *entry)
+{
+	struct raw *raw = (struct raw *)a->format_data;
+
+	if (archive_entry_filetype(entry) != AE_IFREG) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Raw format only supports filetype AE_IFREG");
+		return (ARCHIVE_FATAL);
+	}
+
+
+	if (raw->entries_written > 0) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Raw format only supports one entry per archive");
+		return (ARCHIVE_FATAL);
+	}
+	raw->entries_written++;
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_write_raw_data(struct archive_write *a, const void *buff, size_t s)
+{
+	int ret;
+
+	ret = __archive_write_output(a, buff, s);
+	if (ret >= 0)
+		return (s);
+	else
+		return (ret);
+}
+
+static int
+archive_write_raw_free(struct archive_write *a)
+{
+	struct raw *raw;
+
+	raw = (struct raw *)a->format_data;
+	free(raw);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_write_set_format_shar.c b/c/archive_write_set_format_shar.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_shar.c
@@ -0,0 +1,641 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2008 Joerg Sonnenberger
+ * 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: head/lib/libarchive/archive_write_set_format_shar.c 189438 2009-03-06 05:58:56Z kientzle $");
+
+#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
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct shar {
+	int			 dump;
+	int			 end_of_line;
+	struct archive_entry	*entry;
+	int			 has_data;
+	char			*last_dir;
+
+	/* Line buffer for uuencoded dump format */
+	char			 outbuff[45];
+	size_t			 outpos;
+
+	int			 wrote_header;
+	struct archive_string	 work;
+	struct archive_string	 quoted_name;
+};
+
+static int	archive_write_shar_close(struct archive_write *);
+static int	archive_write_shar_free(struct archive_write *);
+static int	archive_write_shar_header(struct archive_write *,
+		    struct archive_entry *);
+static ssize_t	archive_write_shar_data_sed(struct archive_write *,
+		    const void * buff, size_t);
+static ssize_t	archive_write_shar_data_uuencode(struct archive_write *,
+		    const void * buff, size_t);
+static int	archive_write_shar_finish_entry(struct archive_write *);
+
+/*
+ * Copy the given string to the buffer, quoting all shell meta characters
+ * found.
+ */
+static void
+shar_quote(struct archive_string *buf, const char *str, int in_shell)
+{
+	static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~";
+	size_t len;
+
+	while (*str != '\0') {
+		if ((len = strcspn(str, meta)) != 0) {
+			archive_strncat(buf, str, len);
+			str += len;
+		} else if (*str == '\n') {
+			if (in_shell)
+				archive_strcat(buf, "\"\n\"");
+			else
+				archive_strcat(buf, "\\n");
+			++str;
+		} else {
+			archive_strappend_char(buf, '\\');
+			archive_strappend_char(buf, *str);
+			++str;
+		}
+	}
+}
+
+/*
+ * Set output format to 'shar' format.
+ */
+int
+archive_write_set_format_shar(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct shar *shar;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_shar");
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	shar = (struct shar *)calloc(1, sizeof(*shar));
+	if (shar == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Can't allocate shar data");
+		return (ARCHIVE_FATAL);
+	}
+	archive_string_init(&shar->work);
+	archive_string_init(&shar->quoted_name);
+	a->format_data = shar;
+	a->format_name = "shar";
+	a->format_write_header = archive_write_shar_header;
+	a->format_close = archive_write_shar_close;
+	a->format_free = archive_write_shar_free;
+	a->format_write_data = archive_write_shar_data_sed;
+	a->format_finish_entry = archive_write_shar_finish_entry;
+	a->archive.archive_format = ARCHIVE_FORMAT_SHAR_BASE;
+	a->archive.archive_format_name = "shar";
+	return (ARCHIVE_OK);
+}
+
+/*
+ * An alternate 'shar' that uses uudecode instead of 'sed' to encode
+ * file contents and can therefore be used to archive binary files.
+ * In addition, this variant also attempts to restore ownership, file modes,
+ * and other extended file information.
+ */
+int
+archive_write_set_format_shar_dump(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct shar *shar;
+
+	archive_write_set_format_shar(&a->archive);
+	shar = (struct shar *)a->format_data;
+	shar->dump = 1;
+	a->format_write_data = archive_write_shar_data_uuencode;
+	a->archive.archive_format = ARCHIVE_FORMAT_SHAR_DUMP;
+	a->archive.archive_format_name = "shar dump";
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_shar_header(struct archive_write *a, struct archive_entry *entry)
+{
+	const char *linkname;
+	const char *name;
+	char *p, *pp;
+	struct shar *shar;
+
+	shar = (struct shar *)a->format_data;
+	if (!shar->wrote_header) {
+		archive_strcat(&shar->work, "#!/bin/sh\n");
+		archive_strcat(&shar->work, "# This is a shell archive\n");
+		shar->wrote_header = 1;
+	}
+
+	/* Save the entry for the closing. */
+	archive_entry_free(shar->entry);
+	shar->entry = archive_entry_clone(entry);
+	name = archive_entry_pathname(entry);
+
+	/* Handle some preparatory issues. */
+	switch(archive_entry_filetype(entry)) {
+	case AE_IFREG:
+		/* Only regular files have non-zero size. */
+		break;
+	case AE_IFDIR:
+		archive_entry_set_size(entry, 0);
+		/* Don't bother trying to recreate '.' */
+		if (strcmp(name, ".") == 0  ||  strcmp(name, "./") == 0)
+			return (ARCHIVE_OK);
+		break;
+	case AE_IFIFO:
+	case AE_IFCHR:
+	case AE_IFBLK:
+		/* All other file types have zero size in the archive. */
+		archive_entry_set_size(entry, 0);
+		break;
+	default:
+		archive_entry_set_size(entry, 0);
+		if (archive_entry_hardlink(entry) == NULL &&
+		    archive_entry_symlink(entry) == NULL) {
+			__archive_write_entry_filetype_unsupported(
+			    &a->archive, entry, "shar");
+			return (ARCHIVE_WARN);
+		}
+	}
+
+	archive_string_empty(&shar->quoted_name);
+	shar_quote(&shar->quoted_name, name, 1);
+
+	/* Stock preparation for all file types. */
+	archive_string_sprintf(&shar->work, "echo x %s\n", shar->quoted_name.s);
+
+	if (archive_entry_filetype(entry) != AE_IFDIR) {
+		/* Try to create the dir. */
+		p = strdup(name);
+		pp = strrchr(p, '/');
+		/* If there is a / character, try to create the dir. */
+		if (pp != NULL) {
+			*pp = '\0';
+
+			/* Try to avoid a lot of redundant mkdir commands. */
+			if (strcmp(p, ".") == 0) {
+				/* Don't try to "mkdir ." */
+				free(p);
+			} else if (shar->last_dir == NULL) {
+				archive_strcat(&shar->work, "mkdir -p ");
+				shar_quote(&shar->work, p, 1);
+				archive_strcat(&shar->work,
+				    " > /dev/null 2>&1\n");
+				shar->last_dir = p;
+			} else if (strcmp(p, shar->last_dir) == 0) {
+				/* We've already created this exact dir. */
+				free(p);
+			} else if (strlen(p) < strlen(shar->last_dir) &&
+			    strncmp(p, shar->last_dir, strlen(p)) == 0) {
+				/* We've already created a subdir. */
+				free(p);
+			} else {
+				archive_strcat(&shar->work, "mkdir -p ");
+				shar_quote(&shar->work, p, 1);
+				archive_strcat(&shar->work,
+				    " > /dev/null 2>&1\n");
+				shar->last_dir = p;
+			}
+		} else {
+			free(p);
+		}
+	}
+
+	/* Handle file-type specific issues. */
+	shar->has_data = 0;
+	if ((linkname = archive_entry_hardlink(entry)) != NULL) {
+		archive_strcat(&shar->work, "ln -f ");
+		shar_quote(&shar->work, linkname, 1);
+		archive_string_sprintf(&shar->work, " %s\n",
+		    shar->quoted_name.s);
+	} else if ((linkname = archive_entry_symlink(entry)) != NULL) {
+		archive_strcat(&shar->work, "ln -fs ");
+		shar_quote(&shar->work, linkname, 1);
+		archive_string_sprintf(&shar->work, " %s\n",
+		    shar->quoted_name.s);
+	} else {
+		switch(archive_entry_filetype(entry)) {
+		case AE_IFREG:
+			if (archive_entry_size(entry) == 0) {
+				/* More portable than "touch." */
+				archive_string_sprintf(&shar->work,
+				    "test -e \"%s\" || :> \"%s\"\n",
+				    shar->quoted_name.s, shar->quoted_name.s);
+			} else {
+				if (shar->dump) {
+					unsigned int mode = archive_entry_mode(entry) & 0777;
+					archive_string_sprintf(&shar->work,
+					    "uudecode -p > %s << 'SHAR_END'\n",
+					    shar->quoted_name.s);
+					archive_string_sprintf(&shar->work,
+					    "begin %o ", mode);
+					shar_quote(&shar->work, name, 0);
+					archive_strcat(&shar->work, "\n");
+				} else {
+					archive_string_sprintf(&shar->work,
+					    "sed 's/^X//' > %s << 'SHAR_END'\n",
+					    shar->quoted_name.s);
+				}
+				shar->has_data = 1;
+				shar->end_of_line = 1;
+				shar->outpos = 0;
+			}
+			break;
+		case AE_IFDIR:
+			archive_string_sprintf(&shar->work,
+			    "mkdir -p %s > /dev/null 2>&1\n",
+			    shar->quoted_name.s);
+			/* Record that we just created this directory. */
+			free(shar->last_dir);
+
+			shar->last_dir = strdup(name);
+			/* Trim a trailing '/'. */
+			pp = strrchr(shar->last_dir, '/');
+			if (pp != NULL && pp[1] == '\0')
+				*pp = '\0';
+			/*
+			 * TODO: Put dir name/mode on a list to be fixed
+			 * up at end of archive.
+			 */
+			break;
+		case AE_IFIFO:
+			archive_string_sprintf(&shar->work,
+			    "mkfifo %s\n", shar->quoted_name.s);
+			break;
+		case AE_IFCHR:
+			archive_string_sprintf(&shar->work,
+			    "mknod %s c %ju %ju\n", shar->quoted_name.s,
+			    (uintmax_t)archive_entry_rdevmajor(entry),
+			    (uintmax_t)archive_entry_rdevminor(entry));
+			break;
+		case AE_IFBLK:
+			archive_string_sprintf(&shar->work,
+			    "mknod %s b %ju %ju\n", shar->quoted_name.s,
+			    (uintmax_t)archive_entry_rdevmajor(entry),
+			    (uintmax_t)archive_entry_rdevminor(entry));
+			break;
+		default:
+			return (ARCHIVE_WARN);
+		}
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+archive_write_shar_data_sed(struct archive_write *a, const void *buff, size_t n)
+{
+	static const size_t ensured = 65533;
+	struct shar *shar;
+	const char *src;
+	char *buf, *buf_end;
+	int ret;
+	size_t written = n;
+
+	shar = (struct shar *)a->format_data;
+	if (!shar->has_data || n == 0)
+		return (0);
+
+	src = (const char *)buff;
+
+	/*
+	 * ensure is the number of bytes in buffer before expanding the
+	 * current character.  Each operation writes the current character
+	 * and optionally the start-of-new-line marker.  This can happen
+	 * twice before entering the loop, so make sure three additional
+	 * bytes can be written.
+	 */
+	if (archive_string_ensure(&shar->work, ensured + 3) == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (shar->work.length > ensured) {
+		ret = __archive_write_output(a, shar->work.s,
+		    shar->work.length);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		archive_string_empty(&shar->work);
+	}
+	buf = shar->work.s + shar->work.length;
+	buf_end = shar->work.s + ensured;
+
+	if (shar->end_of_line) {
+		*buf++ = 'X';
+		shar->end_of_line = 0;
+	}
+
+	while (n-- != 0) {
+		if ((*buf++ = *src++) == '\n') {
+			if (n == 0)
+				shar->end_of_line = 1;
+			else
+				*buf++ = 'X';
+		}
+
+		if (buf >= buf_end) {
+			shar->work.length = buf - shar->work.s;
+			ret = __archive_write_output(a, shar->work.s,
+			    shar->work.length);
+			if (ret != ARCHIVE_OK)
+				return (ARCHIVE_FATAL);
+			archive_string_empty(&shar->work);
+			buf = shar->work.s;
+		}
+	}
+
+	shar->work.length = buf - shar->work.s;
+
+	return (written);
+}
+
+#define	UUENC(c)	(((c)!=0) ? ((c) & 077) + ' ': '`')
+
+static void
+uuencode_group(const char _in[3], char out[4])
+{
+	const unsigned char *in = (const unsigned char *)_in;
+	int t;
+
+	t = (in[0] << 16) | (in[1] << 8) | in[2];
+	out[0] = UUENC( 0x3f & (t >> 18) );
+	out[1] = UUENC( 0x3f & (t >> 12) );
+	out[2] = UUENC( 0x3f & (t >> 6) );
+	out[3] = UUENC( 0x3f & t );
+}
+
+static int
+_uuencode_line(struct archive_write *a, struct shar *shar, const char *inbuf, size_t len)
+{
+	char *buf;
+	size_t alloc_len;
+
+	/* len <= 45 -> expanded to 60 + len byte + new line */
+	alloc_len = shar->work.length + 62;
+	if (archive_string_ensure(&shar->work, alloc_len) == NULL) {
+		archive_set_error(&a->archive, ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	buf = shar->work.s + shar->work.length;
+	*buf++ = UUENC(len);
+	while (len >= 3) {
+		uuencode_group(inbuf, buf);
+		len -= 3;
+		inbuf += 3;
+		buf += 4;
+	}
+	if (len != 0) {
+		char tmp_buf[3];
+		tmp_buf[0] = inbuf[0];
+		if (len == 1)
+			tmp_buf[1] = '\0';
+		else
+			tmp_buf[1] = inbuf[1];
+		tmp_buf[2] = '\0';
+		uuencode_group(tmp_buf, buf);
+		buf += 4;
+	}
+	*buf++ = '\n';
+	if ((buf - shar->work.s) > (ptrdiff_t)(shar->work.length + 62)) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC, "Buffer overflow");
+		return (ARCHIVE_FATAL);
+	}
+	shar->work.length = buf - shar->work.s;
+	return (ARCHIVE_OK);
+}
+
+#define uuencode_line(__a, __shar, __inbuf, __len) \
+	do { \
+		int r = _uuencode_line(__a, __shar, __inbuf, __len); \
+		if (r != ARCHIVE_OK) \
+			return (ARCHIVE_FATAL); \
+	} while (0)
+
+static ssize_t
+archive_write_shar_data_uuencode(struct archive_write *a, const void *buff,
+    size_t length)
+{
+	struct shar *shar;
+	const char *src;
+	size_t n;
+	int ret;
+
+	shar = (struct shar *)a->format_data;
+	if (!shar->has_data)
+		return (ARCHIVE_OK);
+	src = (const char *)buff;
+
+	if (shar->outpos != 0) {
+		n = 45 - shar->outpos;
+		if (n > length)
+			n = length;
+		memcpy(shar->outbuff + shar->outpos, src, n);
+		if (shar->outpos + n < 45) {
+			shar->outpos += n;
+			return length;
+		}
+		uuencode_line(a, shar, shar->outbuff, 45);
+		src += n;
+		n = length - n;
+	} else {
+		n = length;
+	}
+
+	while (n >= 45) {
+		uuencode_line(a, shar, src, 45);
+		src += 45;
+		n -= 45;
+
+		if (shar->work.length < 65536)
+			continue;
+		ret = __archive_write_output(a, shar->work.s,
+		    shar->work.length);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		archive_string_empty(&shar->work);
+	}
+	if (n != 0) {
+		memcpy(shar->outbuff, src, n);
+		shar->outpos = n;
+	}
+	return (length);
+}
+
+static int
+archive_write_shar_finish_entry(struct archive_write *a)
+{
+	const char *g, *p, *u;
+	struct shar *shar;
+	int ret;
+
+	shar = (struct shar *)a->format_data;
+	if (shar->entry == NULL)
+		return (0);
+
+	if (shar->dump) {
+		/* Finish uuencoded data. */
+		if (shar->has_data) {
+			if (shar->outpos > 0)
+				uuencode_line(a, shar, shar->outbuff,
+				    shar->outpos);
+			archive_strcat(&shar->work, "`\nend\n");
+			archive_strcat(&shar->work, "SHAR_END\n");
+		}
+		/* Restore file mode, owner, flags. */
+		/*
+		 * TODO: Don't immediately restore mode for
+		 * directories; defer that to end of script.
+		 */
+		archive_string_sprintf(&shar->work, "chmod %o ",
+		    (unsigned int)(archive_entry_mode(shar->entry) & 07777));
+		shar_quote(&shar->work, archive_entry_pathname(shar->entry), 1);
+		archive_strcat(&shar->work, "\n");
+
+		u = archive_entry_uname(shar->entry);
+		g = archive_entry_gname(shar->entry);
+		if (u != NULL || g != NULL) {
+			archive_strcat(&shar->work, "chown ");
+			if (u != NULL)
+				shar_quote(&shar->work, u, 1);
+			if (g != NULL) {
+				archive_strcat(&shar->work, ":");
+				shar_quote(&shar->work, g, 1);
+			}
+			archive_strcat(&shar->work, " ");
+			shar_quote(&shar->work,
+			    archive_entry_pathname(shar->entry), 1);
+			archive_strcat(&shar->work, "\n");
+		}
+
+		if ((p = archive_entry_fflags_text(shar->entry)) != NULL) {
+			archive_string_sprintf(&shar->work, "chflags %s ", p);
+			shar_quote(&shar->work,
+			    archive_entry_pathname(shar->entry), 1);
+			archive_strcat(&shar->work, "\n");
+		}
+
+		/* TODO: restore ACLs */
+
+	} else {
+		if (shar->has_data) {
+			/* Finish sed-encoded data:  ensure last line ends. */
+			if (!shar->end_of_line)
+				archive_strappend_char(&shar->work, '\n');
+			archive_strcat(&shar->work, "SHAR_END\n");
+		}
+	}
+
+	archive_entry_free(shar->entry);
+	shar->entry = NULL;
+
+	if (shar->work.length < 65536)
+		return (ARCHIVE_OK);
+
+	ret = __archive_write_output(a, shar->work.s, shar->work.length);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	archive_string_empty(&shar->work);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_shar_close(struct archive_write *a)
+{
+	struct shar *shar;
+	int ret;
+
+	/*
+	 * TODO: Accumulate list of directory names/modes and
+	 * fix them all up at end-of-archive.
+	 */
+
+	shar = (struct shar *)a->format_data;
+
+	/*
+	 * Only write the end-of-archive markers if the archive was
+	 * actually started.  This avoids problems if someone sets
+	 * shar format, then sets another format (which would invoke
+	 * shar_finish to free the format-specific data).
+	 */
+	if (shar->wrote_header == 0)
+		return (ARCHIVE_OK);
+
+	archive_strcat(&shar->work, "exit\n");
+
+	ret = __archive_write_output(a, shar->work.s, shar->work.length);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* Shar output is never padded. */
+	archive_write_set_bytes_in_last_block(&a->archive, 1);
+	/*
+	 * TODO: shar should also suppress padding of
+	 * uncompressed data within gzip/bzip2 streams.
+	 */
+
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_shar_free(struct archive_write *a)
+{
+	struct shar *shar;
+
+	shar = (struct shar *)a->format_data;
+	if (shar == NULL)
+		return (ARCHIVE_OK);
+
+	archive_entry_free(shar->entry);
+	free(shar->last_dir);
+	archive_string_free(&(shar->work));
+	archive_string_free(&(shar->quoted_name));
+	free(shar);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
diff --git a/c/archive_write_set_format_ustar.c b/c/archive_write_set_format_ustar.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_ustar.c
@@ -0,0 +1,758 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * 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: head/lib/libarchive/archive_write_set_format_ustar.c 191579 2009-04-27 18:35:03Z kientzle $");
+
+
+#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
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct ustar {
+	uint64_t	entry_bytes_remaining;
+	uint64_t	entry_padding;
+
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv_default;
+	int	init_default_conversion;
+};
+
+/*
+ * Define structure of POSIX 'ustar' tar header.
+ */
+#define	USTAR_name_offset 0
+#define	USTAR_name_size 100
+#define	USTAR_mode_offset 100
+#define	USTAR_mode_size 6
+#define	USTAR_mode_max_size 8
+#define	USTAR_uid_offset 108
+#define	USTAR_uid_size 6
+#define	USTAR_uid_max_size 8
+#define	USTAR_gid_offset 116
+#define	USTAR_gid_size 6
+#define	USTAR_gid_max_size 8
+#define	USTAR_size_offset 124
+#define	USTAR_size_size 11
+#define	USTAR_size_max_size 12
+#define	USTAR_mtime_offset 136
+#define	USTAR_mtime_size 11
+#define	USTAR_mtime_max_size 11
+#define	USTAR_checksum_offset 148
+#define	USTAR_checksum_size 8
+#define	USTAR_typeflag_offset 156
+#define	USTAR_typeflag_size 1
+#define	USTAR_linkname_offset 157
+#define	USTAR_linkname_size 100
+#define	USTAR_magic_offset 257
+#define	USTAR_magic_size 6
+#define	USTAR_version_offset 263
+#define	USTAR_version_size 2
+#define	USTAR_uname_offset 265
+#define	USTAR_uname_size 32
+#define	USTAR_gname_offset 297
+#define	USTAR_gname_size 32
+#define	USTAR_rdevmajor_offset 329
+#define	USTAR_rdevmajor_size 6
+#define	USTAR_rdevmajor_max_size 8
+#define	USTAR_rdevminor_offset 337
+#define	USTAR_rdevminor_size 6
+#define	USTAR_rdevminor_max_size 8
+#define	USTAR_prefix_offset 345
+#define	USTAR_prefix_size 155
+#define	USTAR_padding_offset 500
+#define	USTAR_padding_size 12
+
+/*
+ * A filled-in copy of the header for initialization.
+ */
+static const char template_header[] = {
+	/* name: 100 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,
+	/* Mode, space-null termination: 8 bytes */
+	'0','0','0','0','0','0', ' ','\0',
+	/* uid, space-null termination: 8 bytes */
+	'0','0','0','0','0','0', ' ','\0',
+	/* gid, space-null termination: 8 bytes */
+	'0','0','0','0','0','0', ' ','\0',
+	/* size, space termination: 12 bytes */
+	'0','0','0','0','0','0','0','0','0','0','0', ' ',
+	/* mtime, space termination: 12 bytes */
+	'0','0','0','0','0','0','0','0','0','0','0', ' ',
+	/* Initial checksum value: 8 spaces */
+	' ',' ',' ',' ',' ',' ',' ',' ',
+	/* Typeflag: 1 byte */
+	'0',			/* '0' = regular file */
+	/* Linkname: 100 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,
+	/* Magic: 6 bytes, Version: 2 bytes */
+	'u','s','t','a','r','\0', '0','0',
+	/* Uname: 32 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	/* Gname: 32 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	/* rdevmajor + space/null padding: 8 bytes */
+	'0','0','0','0','0','0', ' ','\0',
+	/* rdevminor + space/null padding: 8 bytes */
+	'0','0','0','0','0','0', ' ','\0',
+	/* Prefix: 155 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,
+	/* Padding: 12 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0
+};
+
+static ssize_t	archive_write_ustar_data(struct archive_write *a, const void *buff,
+		    size_t s);
+static int	archive_write_ustar_free(struct archive_write *);
+static int	archive_write_ustar_close(struct archive_write *);
+static int	archive_write_ustar_finish_entry(struct archive_write *);
+static int	archive_write_ustar_header(struct archive_write *,
+		    struct archive_entry *entry);
+static int	archive_write_ustar_options(struct archive_write *,
+		    const char *, const char *);
+static int	format_256(int64_t, char *, int);
+static int	format_number(int64_t, char *, int size, int max, int strict);
+static int	format_octal(int64_t, char *, int);
+
+/*
+ * Set output format to 'ustar' format.
+ */
+int
+archive_write_set_format_ustar(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct ustar *ustar;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_ustar");
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	/* Basic internal sanity test. */
+	if (sizeof(template_header) != 512) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal: template_header wrong size: %zu should be 512",
+		    sizeof(template_header));
+		return (ARCHIVE_FATAL);
+	}
+
+	ustar = (struct ustar *)calloc(1, sizeof(*ustar));
+	if (ustar == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate ustar data");
+		return (ARCHIVE_FATAL);
+	}
+	a->format_data = ustar;
+	a->format_name = "ustar";
+	a->format_options = archive_write_ustar_options;
+	a->format_write_header = archive_write_ustar_header;
+	a->format_write_data = archive_write_ustar_data;
+	a->format_close = archive_write_ustar_close;
+	a->format_free = archive_write_ustar_free;
+	a->format_finish_entry = archive_write_ustar_finish_entry;
+	a->archive.archive_format = ARCHIVE_FORMAT_TAR_USTAR;
+	a->archive.archive_format_name = "POSIX ustar";
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ustar_options(struct archive_write *a, const char *key,
+    const char *val)
+{
+	struct ustar *ustar = (struct ustar *)a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: hdrcharset option needs a character-set name",
+			    a->format_name);
+		else {
+			ustar->opt_sconv = archive_string_conversion_to_charset(
+			    &a->archive, val, 0);
+			if (ustar->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_ustar_header(struct archive_write *a, struct archive_entry *entry)
+{
+	char buff[512];
+	int ret, ret2;
+	struct ustar *ustar;
+	struct archive_entry *entry_main;
+	struct archive_string_conv *sconv;
+
+	ustar = (struct ustar *)a->format_data;
+
+	/* Setup default string conversion. */
+	if (ustar->opt_sconv == NULL) {
+		if (!ustar->init_default_conversion) {
+			ustar->sconv_default =
+			    archive_string_default_conversion_for_write(&(a->archive));
+			ustar->init_default_conversion = 1;
+		}
+		sconv = ustar->sconv_default;
+	} else
+		sconv = ustar->opt_sconv;
+
+	/* Sanity check. */
+	if (archive_entry_pathname(entry) == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Can't record entry in tar file without pathname");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Only regular files (not hardlinks) have data. */
+	if (archive_entry_hardlink(entry) != NULL ||
+	    archive_entry_symlink(entry) != NULL ||
+	    !(archive_entry_filetype(entry) == AE_IFREG))
+		archive_entry_set_size(entry, 0);
+
+	if (AE_IFDIR == archive_entry_filetype(entry)) {
+		const char *p;
+		size_t path_length;
+		/*
+		 * Ensure a trailing '/'.  Modify the entry so
+		 * the client sees the change.
+		 */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		const wchar_t *wp;
+
+		wp = archive_entry_pathname_w(entry);
+		if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+			struct archive_wstring ws;
+
+			archive_string_init(&ws);
+			path_length = wcslen(wp);
+			if (archive_wstring_ensure(&ws,
+			    path_length + 2) == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate ustar data");
+				archive_wstring_free(&ws);
+				return(ARCHIVE_FATAL);
+			}
+			/* Should we keep '\' ? */
+			if (wp[path_length -1] == L'\\')
+				path_length--;
+			archive_wstrncpy(&ws, wp, path_length);
+			archive_wstrappend_wchar(&ws, L'/');
+			archive_entry_copy_pathname_w(entry, ws.s);
+			archive_wstring_free(&ws);
+			p = NULL;
+		} else
+#endif
+			p = archive_entry_pathname(entry);
+		/*
+		 * On Windows, this is a backup operation just in
+		 * case getting WCS failed. On POSIX, this is a
+		 * normal operation.
+		 */
+		if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+			struct archive_string as;
+
+			archive_string_init(&as);
+			path_length = strlen(p);
+			if (archive_string_ensure(&as,
+			    path_length + 2) == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate ustar data");
+				archive_string_free(&as);
+				return(ARCHIVE_FATAL);
+			}
+#if defined(_WIN32) && !defined(__CYGWIN__)
+			/* NOTE: This might break the pathname
+			 * if the current code page is CP932 and
+			 * the pathname includes a character '\'
+			 * as a part of its multibyte pathname. */
+			if (p[strlen(p) -1] == '\\')
+				path_length--;
+			else
+#endif
+			archive_strncpy(&as, p, path_length);
+			archive_strappend_char(&as, '/');
+			archive_entry_copy_pathname(entry, as.s);
+			archive_string_free(&as);
+		}
+	}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* Make sure the path separators in pathname, hardlink and symlink
+	 * are all slash '/', not the Windows path separator '\'. */
+	entry_main = __la_win_entry_in_posix_pathseparator(entry);
+	if (entry_main == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate ustar data");
+		return(ARCHIVE_FATAL);
+	}
+	if (entry != entry_main)
+		entry = entry_main;
+	else
+		entry_main = NULL;
+#else
+	entry_main = NULL;
+#endif
+	ret = __archive_write_format_header_ustar(a, buff, entry, -1, 1, sconv);
+	if (ret < ARCHIVE_WARN) {
+		archive_entry_free(entry_main);
+		return (ret);
+	}
+	ret2 = __archive_write_output(a, buff, 512);
+	if (ret2 < ARCHIVE_WARN) {
+		archive_entry_free(entry_main);
+		return (ret2);
+	}
+	if (ret2 < ret)
+		ret = ret2;
+
+	ustar->entry_bytes_remaining = archive_entry_size(entry);
+	ustar->entry_padding = 0x1ff & (-(int64_t)ustar->entry_bytes_remaining);
+	archive_entry_free(entry_main);
+	return (ret);
+}
+
+/*
+ * Format a basic 512-byte "ustar" header.
+ *
+ * Returns -1 if format failed (due to field overflow).
+ * Note that this always formats as much of the header as possible.
+ * If "strict" is set to zero, it will extend numeric fields as
+ * necessary (overwriting terminators or using base-256 extensions).
+ *
+ * This is exported so that other 'tar' formats can use it.
+ */
+int
+__archive_write_format_header_ustar(struct archive_write *a, char h[512],
+    struct archive_entry *entry, int tartype, int strict,
+    struct archive_string_conv *sconv)
+{
+	unsigned int checksum;
+	int i, r, ret;
+	size_t copy_length;
+	const char *p, *pp;
+	int mytartype;
+
+	ret = 0;
+	mytartype = -1;
+	/*
+	 * The "template header" already includes the "ustar"
+	 * signature, various end-of-field markers and other required
+	 * elements.
+	 */
+	memcpy(h, &template_header, 512);
+
+	/*
+	 * Because the block is already null-filled, and strings
+	 * are allowed to exactly fill their destination (without null),
+	 * I use memcpy(dest, src, strlen()) here a lot to copy strings.
+	 */
+	r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate pathname '%s' to %s",
+		    pp, archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+	}
+	if (copy_length <= USTAR_name_size)
+		memcpy(h + USTAR_name_offset, pp, copy_length);
+	else {
+		/* Store in two pieces, splitting at a '/'. */
+		p = strchr(pp + copy_length - USTAR_name_size - 1, '/');
+		/*
+		 * Look for the next '/' if we chose the first character
+		 * as the separator.  (ustar format doesn't permit
+		 * an empty prefix.)
+		 */
+		if (p == pp)
+			p = strchr(p + 1, '/');
+		/* Fail if the name won't fit. */
+		if (!p) {
+			/* No separator. */
+			archive_set_error(&a->archive, ENAMETOOLONG,
+			    "Pathname too long");
+			ret = ARCHIVE_FAILED;
+		} else if (p[1] == '\0') {
+			/*
+			 * The only feasible separator is a final '/';
+			 * this would result in a non-empty prefix and
+			 * an empty name, which POSIX doesn't
+			 * explicitly forbid, but it just feels wrong.
+			 */
+			archive_set_error(&a->archive, ENAMETOOLONG,
+			    "Pathname too long");
+			ret = ARCHIVE_FAILED;
+		} else if (p  > pp + USTAR_prefix_size) {
+			/* Prefix is too long. */
+			archive_set_error(&a->archive, ENAMETOOLONG,
+			    "Pathname too long");
+			ret = ARCHIVE_FAILED;
+		} else {
+			/* Copy prefix and remainder to appropriate places */
+			memcpy(h + USTAR_prefix_offset, pp, p - pp);
+			memcpy(h + USTAR_name_offset, p + 1,
+			    pp + copy_length - p - 1);
+		}
+	}
+
+	r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate linkname '%s' to %s",
+		    p, archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+	}
+	if (copy_length > 0)
+		mytartype = '1';
+	else {
+		r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);
+		if (r != 0) {
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Linkname");
+				return (ARCHIVE_FATAL);
+			}
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Can't translate linkname '%s' to %s",
+			    p, archive_string_conversion_charset_name(sconv));
+			ret = ARCHIVE_WARN;
+		}
+	}
+	if (copy_length > 0) {
+		if (copy_length > USTAR_linkname_size) {
+			archive_set_error(&a->archive, ENAMETOOLONG,
+			    "Link contents too long");
+			ret = ARCHIVE_FAILED;
+			copy_length = USTAR_linkname_size;
+		}
+		memcpy(h + USTAR_linkname_offset, p, copy_length);
+	}
+
+	r = archive_entry_uname_l(entry, &p, &copy_length, sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Uname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate uname '%s' to %s",
+		    p, archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+	}
+	if (copy_length > 0) {
+		if (copy_length > USTAR_uname_size) {
+			if (tartype != 'x') {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC, "Username too long");
+				ret = ARCHIVE_FAILED;
+			}
+			copy_length = USTAR_uname_size;
+		}
+		memcpy(h + USTAR_uname_offset, p, copy_length);
+	}
+
+	r = archive_entry_gname_l(entry, &p, &copy_length, sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Gname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate gname '%s' to %s",
+		    p, archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+	}
+	if (copy_length > 0) {
+		if (strlen(p) > USTAR_gname_size) {
+			if (tartype != 'x') {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC, "Group name too long");
+				ret = ARCHIVE_FAILED;
+			}
+			copy_length = USTAR_gname_size;
+		}
+		memcpy(h + USTAR_gname_offset, p, copy_length);
+	}
+
+	if (format_number(archive_entry_mode(entry) & 07777,
+	    h + USTAR_mode_offset, USTAR_mode_size, USTAR_mode_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric mode too large");
+		ret = ARCHIVE_FAILED;
+	}
+
+	if (format_number(archive_entry_uid(entry),
+	    h + USTAR_uid_offset, USTAR_uid_size, USTAR_uid_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric user ID too large");
+		ret = ARCHIVE_FAILED;
+	}
+
+	if (format_number(archive_entry_gid(entry),
+	    h + USTAR_gid_offset, USTAR_gid_size, USTAR_gid_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric group ID too large");
+		ret = ARCHIVE_FAILED;
+	}
+
+	if (format_number(archive_entry_size(entry),
+	    h + USTAR_size_offset, USTAR_size_size, USTAR_size_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File size out of range");
+		ret = ARCHIVE_FAILED;
+	}
+
+	if (format_number(archive_entry_mtime(entry),
+	    h + USTAR_mtime_offset, USTAR_mtime_size, USTAR_mtime_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File modification time too large");
+		ret = ARCHIVE_FAILED;
+	}
+
+	if (archive_entry_filetype(entry) == AE_IFBLK
+	    || archive_entry_filetype(entry) == AE_IFCHR) {
+		if (format_number(archive_entry_rdevmajor(entry),
+		    h + USTAR_rdevmajor_offset, USTAR_rdevmajor_size,
+		    USTAR_rdevmajor_max_size, strict)) {
+			archive_set_error(&a->archive, ERANGE,
+			    "Major device number too large");
+			ret = ARCHIVE_FAILED;
+		}
+
+		if (format_number(archive_entry_rdevminor(entry),
+		    h + USTAR_rdevminor_offset, USTAR_rdevminor_size,
+		    USTAR_rdevminor_max_size, strict)) {
+			archive_set_error(&a->archive, ERANGE,
+			    "Minor device number too large");
+			ret = ARCHIVE_FAILED;
+		}
+	}
+
+	if (tartype >= 0) {
+		h[USTAR_typeflag_offset] = tartype;
+	} else if (mytartype >= 0) {
+		h[USTAR_typeflag_offset] = mytartype;
+	} else {
+		switch (archive_entry_filetype(entry)) {
+		case AE_IFREG: h[USTAR_typeflag_offset] = '0' ; break;
+		case AE_IFLNK: h[USTAR_typeflag_offset] = '2' ; break;
+		case AE_IFCHR: h[USTAR_typeflag_offset] = '3' ; break;
+		case AE_IFBLK: h[USTAR_typeflag_offset] = '4' ; break;
+		case AE_IFDIR: h[USTAR_typeflag_offset] = '5' ; break;
+		case AE_IFIFO: h[USTAR_typeflag_offset] = '6' ; break;
+		default: /* AE_IFSOCK and unknown */
+			__archive_write_entry_filetype_unsupported(
+			    &a->archive, entry, "ustar");
+			ret = ARCHIVE_FAILED;
+		}
+	}
+
+	checksum = 0;
+	for (i = 0; i < 512; i++)
+		checksum += 255 & (unsigned int)h[i];
+	h[USTAR_checksum_offset + 6] = '\0'; /* Can't be pre-set in the template. */
+	/* h[USTAR_checksum_offset + 7] = ' '; */ /* This is pre-set in the template. */
+	format_octal(checksum, h + USTAR_checksum_offset, 6);
+	return (ret);
+}
+
+/*
+ * Format a number into a field, with some intelligence.
+ */
+static int
+format_number(int64_t v, char *p, int s, int maxsize, int strict)
+{
+	int64_t limit;
+
+	limit = ((int64_t)1 << (s*3));
+
+	/* "Strict" only permits octal values with proper termination. */
+	if (strict)
+		return (format_octal(v, p, s));
+
+	/*
+	 * In non-strict mode, we allow the number to overwrite one or
+	 * more bytes of the field termination.  Even old tar
+	 * implementations should be able to handle this with no
+	 * problem.
+	 */
+	if (v >= 0) {
+		while (s <= maxsize) {
+			if (v < limit)
+				return (format_octal(v, p, s));
+			s++;
+			limit <<= 3;
+		}
+	}
+
+	/* Base-256 can handle any number, positive or negative. */
+	return (format_256(v, p, maxsize));
+}
+
+/*
+ * Format a number into the specified field using base-256.
+ */
+static int
+format_256(int64_t v, char *p, int s)
+{
+	p += s;
+	while (s-- > 0) {
+		*--p = (char)(v & 0xff);
+		v >>= 8;
+	}
+	*p |= 0x80; /* Set the base-256 marker bit. */
+	return (0);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+	int len;
+
+	len = s;
+
+	/* Octal values can't be negative, so use 0. */
+	if (v < 0) {
+		while (len-- > 0)
+			*p++ = '0';
+		return (-1);
+	}
+
+	p += s;		/* Start at the end and work backwards. */
+	while (s-- > 0) {
+		*--p = (char)('0' + (v & 7));
+		v >>= 3;
+	}
+
+	if (v == 0)
+		return (0);
+
+	/* If it overflowed, fill field with max value. */
+	while (len-- > 0)
+		*p++ = '7';
+
+	return (-1);
+}
+
+static int
+archive_write_ustar_close(struct archive_write *a)
+{
+	return (__archive_write_nulls(a, 512*2));
+}
+
+static int
+archive_write_ustar_free(struct archive_write *a)
+{
+	struct ustar *ustar;
+
+	ustar = (struct ustar *)a->format_data;
+	free(ustar);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_ustar_finish_entry(struct archive_write *a)
+{
+	struct ustar *ustar;
+	int ret;
+
+	ustar = (struct ustar *)a->format_data;
+	ret = __archive_write_nulls(a,
+	    (size_t)(ustar->entry_bytes_remaining + ustar->entry_padding));
+	ustar->entry_bytes_remaining = ustar->entry_padding = 0;
+	return (ret);
+}
+
+static ssize_t
+archive_write_ustar_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct ustar *ustar;
+	int ret;
+
+	ustar = (struct ustar *)a->format_data;
+	if (s > ustar->entry_bytes_remaining)
+		s = (size_t)ustar->entry_bytes_remaining;
+	ret = __archive_write_output(a, buff, s);
+	ustar->entry_bytes_remaining -= s;
+	if (ret != ARCHIVE_OK)
+		return (ret);
+	return (s);
+}
diff --git a/c/archive_write_set_format_v7tar.c b/c/archive_write_set_format_v7tar.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_v7tar.c
@@ -0,0 +1,638 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
+ * 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$");
+
+
+#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
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct v7tar {
+	uint64_t	entry_bytes_remaining;
+	uint64_t	entry_padding;
+
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv_default;
+	int	init_default_conversion;
+};
+
+/*
+ * Define structure of POSIX 'v7tar' tar header.
+ */
+#define	V7TAR_name_offset 0
+#define	V7TAR_name_size 100
+#define	V7TAR_mode_offset 100
+#define	V7TAR_mode_size 6
+#define	V7TAR_mode_max_size 8
+#define	V7TAR_uid_offset 108
+#define	V7TAR_uid_size 6
+#define	V7TAR_uid_max_size 8
+#define	V7TAR_gid_offset 116
+#define	V7TAR_gid_size 6
+#define	V7TAR_gid_max_size 8
+#define	V7TAR_size_offset 124
+#define	V7TAR_size_size 11
+#define	V7TAR_size_max_size 12
+#define	V7TAR_mtime_offset 136
+#define	V7TAR_mtime_size 11
+#define	V7TAR_mtime_max_size 12
+#define	V7TAR_checksum_offset 148
+#define	V7TAR_checksum_size 8
+#define	V7TAR_typeflag_offset 156
+#define	V7TAR_typeflag_size 1
+#define	V7TAR_linkname_offset 157
+#define	V7TAR_linkname_size 100
+#define	V7TAR_padding_offset 257
+#define	V7TAR_padding_size 255
+
+/*
+ * A filled-in copy of the header for initialization.
+ */
+static const char template_header[] = {
+	/* name: 100 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,
+	/* Mode, space-null termination: 8 bytes */
+	'0','0','0','0','0','0', ' ','\0',
+	/* uid, space-null termination: 8 bytes */
+	'0','0','0','0','0','0', ' ','\0',
+	/* gid, space-null termination: 8 bytes */
+	'0','0','0','0','0','0', ' ','\0',
+	/* size, space termination: 12 bytes */
+	'0','0','0','0','0','0','0','0','0','0','0', ' ',
+	/* mtime, space termination: 12 bytes */
+	'0','0','0','0','0','0','0','0','0','0','0', ' ',
+	/* Initial checksum value: 8 spaces */
+	' ',' ',' ',' ',' ',' ',' ',' ',
+	/* Typeflag: 1 byte */
+	0,
+	/* Linkname: 100 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,
+	/* Padding: 255 bytes */
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
+	0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0
+};
+
+static ssize_t	archive_write_v7tar_data(struct archive_write *a, const void *buff,
+		    size_t s);
+static int	archive_write_v7tar_free(struct archive_write *);
+static int	archive_write_v7tar_close(struct archive_write *);
+static int	archive_write_v7tar_finish_entry(struct archive_write *);
+static int	archive_write_v7tar_header(struct archive_write *,
+		    struct archive_entry *entry);
+static int	archive_write_v7tar_options(struct archive_write *,
+		    const char *, const char *);
+static int	format_256(int64_t, char *, int);
+static int	format_number(int64_t, char *, int size, int max, int strict);
+static int	format_octal(int64_t, char *, int);
+static int	format_header_v7tar(struct archive_write *, char h[512],
+		    struct archive_entry *, int, struct archive_string_conv *);
+
+/*
+ * Set output format to 'v7tar' format.
+ */
+int
+archive_write_set_format_v7tar(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct v7tar *v7tar;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_v7tar");
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	/* Basic internal sanity test. */
+	if (sizeof(template_header) != 512) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Internal: template_header wrong size: %zu should be 512",
+		    sizeof(template_header));
+		return (ARCHIVE_FATAL);
+	}
+
+	v7tar = (struct v7tar *)calloc(1, sizeof(*v7tar));
+	if (v7tar == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate v7tar data");
+		return (ARCHIVE_FATAL);
+	}
+	a->format_data = v7tar;
+	a->format_name = "tar (non-POSIX)";
+	a->format_options = archive_write_v7tar_options;
+	a->format_write_header = archive_write_v7tar_header;
+	a->format_write_data = archive_write_v7tar_data;
+	a->format_close = archive_write_v7tar_close;
+	a->format_free = archive_write_v7tar_free;
+	a->format_finish_entry = archive_write_v7tar_finish_entry;
+	a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+	a->archive.archive_format_name = "tar (non-POSIX)";
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_v7tar_options(struct archive_write *a, const char *key,
+    const char *val)
+{
+	struct v7tar *v7tar = (struct v7tar *)a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	if (strcmp(key, "hdrcharset")  == 0) {
+		if (val == NULL || val[0] == 0)
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: hdrcharset option needs a character-set name",
+			    a->format_name);
+		else {
+			v7tar->opt_sconv = archive_string_conversion_to_charset(
+			    &a->archive, val, 0);
+			if (v7tar->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+archive_write_v7tar_header(struct archive_write *a, struct archive_entry *entry)
+{
+	char buff[512];
+	int ret, ret2;
+	struct v7tar *v7tar;
+	struct archive_entry *entry_main;
+	struct archive_string_conv *sconv;
+
+	v7tar = (struct v7tar *)a->format_data;
+
+	/* Setup default string conversion. */
+	if (v7tar->opt_sconv == NULL) {
+		if (!v7tar->init_default_conversion) {
+			v7tar->sconv_default =
+			    archive_string_default_conversion_for_write(
+				&(a->archive));
+			v7tar->init_default_conversion = 1;
+		}
+		sconv = v7tar->sconv_default;
+	} else
+		sconv = v7tar->opt_sconv;
+
+	/* Sanity check. */
+	if (archive_entry_pathname(entry) == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Can't record entry in tar file without pathname");
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Only regular files (not hardlinks) have data. */
+	if (archive_entry_hardlink(entry) != NULL ||
+	    archive_entry_symlink(entry) != NULL ||
+	    !(archive_entry_filetype(entry) == AE_IFREG))
+		archive_entry_set_size(entry, 0);
+
+	if (AE_IFDIR == archive_entry_filetype(entry)) {
+		const char *p;
+		size_t path_length;
+		/*
+		 * Ensure a trailing '/'.  Modify the entry so
+		 * the client sees the change.
+		 */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		const wchar_t *wp;
+
+		wp = archive_entry_pathname_w(entry);
+		if (wp != NULL && wp[wcslen(wp) -1] != L'/') {
+			struct archive_wstring ws;
+
+			archive_string_init(&ws);
+			path_length = wcslen(wp);
+			if (archive_wstring_ensure(&ws,
+			    path_length + 2) == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate v7tar data");
+				archive_wstring_free(&ws);
+				return(ARCHIVE_FATAL);
+			}
+			/* Should we keep '\' ? */
+			if (wp[path_length -1] == L'\\')
+				path_length--;
+			archive_wstrncpy(&ws, wp, path_length);
+			archive_wstrappend_wchar(&ws, L'/');
+			archive_entry_copy_pathname_w(entry, ws.s);
+			archive_wstring_free(&ws);
+			p = NULL;
+		} else
+#endif
+			p = archive_entry_pathname(entry);
+		/*
+		 * On Windows, this is a backup operation just in
+		 * case getting WCS failed. On POSIX, this is a
+		 * normal operation.
+		 */
+		if (p != NULL && p[0] != '\0' && p[strlen(p) - 1] != '/') {
+			struct archive_string as;
+
+			archive_string_init(&as);
+			path_length = strlen(p);
+			if (archive_string_ensure(&as,
+			    path_length + 2) == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate v7tar data");
+				archive_string_free(&as);
+				return(ARCHIVE_FATAL);
+			}
+#if defined(_WIN32) && !defined(__CYGWIN__)
+			/* NOTE: This might break the pathname
+			 * if the current code page is CP932 and
+			 * the pathname includes a character '\'
+			 * as a part of its multibyte pathname. */
+			if (p[strlen(p) -1] == '\\')
+				path_length--;
+			else
+#endif
+			archive_strncpy(&as, p, path_length);
+			archive_strappend_char(&as, '/');
+			archive_entry_copy_pathname(entry, as.s);
+			archive_string_free(&as);
+		}
+	}
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* Make sure the path separators in pathname, hardlink and symlink
+	 * are all slash '/', not the Windows path separator '\'. */
+	entry_main = __la_win_entry_in_posix_pathseparator(entry);
+	if (entry_main == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate v7tar data");
+		return(ARCHIVE_FATAL);
+	}
+	if (entry != entry_main)
+		entry = entry_main;
+	else
+		entry_main = NULL;
+#else
+	entry_main = NULL;
+#endif
+	ret = format_header_v7tar(a, buff, entry, 1, sconv);
+	if (ret < ARCHIVE_WARN) {
+		archive_entry_free(entry_main);
+		return (ret);
+	}
+	ret2 = __archive_write_output(a, buff, 512);
+	if (ret2 < ARCHIVE_WARN) {
+		archive_entry_free(entry_main);
+		return (ret2);
+	}
+	if (ret2 < ret)
+		ret = ret2;
+
+	v7tar->entry_bytes_remaining = archive_entry_size(entry);
+	v7tar->entry_padding = 0x1ff & (-(int64_t)v7tar->entry_bytes_remaining);
+	archive_entry_free(entry_main);
+	return (ret);
+}
+
+/*
+ * Format a basic 512-byte "v7tar" header.
+ *
+ * Returns -1 if format failed (due to field overflow).
+ * Note that this always formats as much of the header as possible.
+ * If "strict" is set to zero, it will extend numeric fields as
+ * necessary (overwriting terminators or using base-256 extensions).
+ *
+ */
+static int
+format_header_v7tar(struct archive_write *a, char h[512],
+    struct archive_entry *entry, int strict,
+    struct archive_string_conv *sconv)
+{
+	unsigned int checksum;
+	int i, r, ret;
+	size_t copy_length;
+	const char *p, *pp;
+	int mytartype;
+
+	ret = 0;
+	mytartype = -1;
+	/*
+	 * The "template header" already includes the "v7tar"
+	 * signature, various end-of-field markers and other required
+	 * elements.
+	 */
+	memcpy(h, &template_header, 512);
+
+	/*
+	 * Because the block is already null-filled, and strings
+	 * are allowed to exactly fill their destination (without null),
+	 * I use memcpy(dest, src, strlen()) here a lot to copy strings.
+	 */
+	r = archive_entry_pathname_l(entry, &pp, &copy_length, sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate pathname '%s' to %s",
+		    pp, archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+	}
+	if (strict && copy_length < V7TAR_name_size)
+		memcpy(h + V7TAR_name_offset, pp, copy_length);
+	else if (!strict && copy_length <= V7TAR_name_size)
+		memcpy(h + V7TAR_name_offset, pp, copy_length);
+	else {
+		/* Prefix is too long. */
+		archive_set_error(&a->archive, ENAMETOOLONG,
+		    "Pathname too long");
+		ret = ARCHIVE_FAILED;
+	}
+
+	r = archive_entry_hardlink_l(entry, &p, &copy_length, sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Linkname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate linkname '%s' to %s",
+		    p, archive_string_conversion_charset_name(sconv));
+		ret = ARCHIVE_WARN;
+	}
+	if (copy_length > 0)
+		mytartype = '1';
+	else {
+		r = archive_entry_symlink_l(entry, &p, &copy_length, sconv);
+		if (r != 0) {
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Linkname");
+				return (ARCHIVE_FATAL);
+			}
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Can't translate linkname '%s' to %s",
+			    p, archive_string_conversion_charset_name(sconv));
+			ret = ARCHIVE_WARN;
+		}
+	}
+	if (copy_length > 0) {
+		if (copy_length >= V7TAR_linkname_size) {
+			archive_set_error(&a->archive, ENAMETOOLONG,
+			    "Link contents too long");
+			ret = ARCHIVE_FAILED;
+			copy_length = V7TAR_linkname_size;
+		}
+		memcpy(h + V7TAR_linkname_offset, p, copy_length);
+	}
+
+	if (format_number(archive_entry_mode(entry) & 07777,
+	    h + V7TAR_mode_offset, V7TAR_mode_size,
+	    V7TAR_mode_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric mode too large");
+		ret = ARCHIVE_FAILED;
+	}
+
+	if (format_number(archive_entry_uid(entry),
+	    h + V7TAR_uid_offset, V7TAR_uid_size, V7TAR_uid_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric user ID too large");
+		ret = ARCHIVE_FAILED;
+	}
+
+	if (format_number(archive_entry_gid(entry),
+	    h + V7TAR_gid_offset, V7TAR_gid_size, V7TAR_gid_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Numeric group ID too large");
+		ret = ARCHIVE_FAILED;
+	}
+
+	if (format_number(archive_entry_size(entry),
+	    h + V7TAR_size_offset, V7TAR_size_size,
+	    V7TAR_size_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File size out of range");
+		ret = ARCHIVE_FAILED;
+	}
+
+	if (format_number(archive_entry_mtime(entry),
+	    h + V7TAR_mtime_offset, V7TAR_mtime_size,
+	    V7TAR_mtime_max_size, strict)) {
+		archive_set_error(&a->archive, ERANGE,
+		    "File modification time too large");
+		ret = ARCHIVE_FAILED;
+	}
+
+	if (mytartype >= 0) {
+		h[V7TAR_typeflag_offset] = mytartype;
+	} else {
+		switch (archive_entry_filetype(entry)) {
+		case AE_IFREG: case AE_IFDIR:
+			break;
+		case AE_IFLNK:
+			h[V7TAR_typeflag_offset] = '2';
+			break;
+		default:
+			/* AE_IFBLK, AE_IFCHR, AE_IFIFO, AE_IFSOCK
+			 * and unknown */
+			__archive_write_entry_filetype_unsupported(
+			    &a->archive, entry, "v7tar");
+			ret = ARCHIVE_FAILED;
+		}
+	}
+
+	checksum = 0;
+	for (i = 0; i < 512; i++)
+		checksum += 255 & (unsigned int)h[i];
+	format_octal(checksum, h + V7TAR_checksum_offset, 6);
+	/* Can't be pre-set in the template. */
+	h[V7TAR_checksum_offset + 6] = '\0';
+	return (ret);
+}
+
+/*
+ * Format a number into a field, with some intelligence.
+ */
+static int
+format_number(int64_t v, char *p, int s, int maxsize, int strict)
+{
+	int64_t limit;
+
+	limit = ((int64_t)1 << (s*3));
+
+	/* "Strict" only permits octal values with proper termination. */
+	if (strict)
+		return (format_octal(v, p, s));
+
+	/*
+	 * In non-strict mode, we allow the number to overwrite one or
+	 * more bytes of the field termination.  Even old tar
+	 * implementations should be able to handle this with no
+	 * problem.
+	 */
+	if (v >= 0) {
+		while (s <= maxsize) {
+			if (v < limit)
+				return (format_octal(v, p, s));
+			s++;
+			limit <<= 3;
+		}
+	}
+
+	/* Base-256 can handle any number, positive or negative. */
+	return (format_256(v, p, maxsize));
+}
+
+/*
+ * Format a number into the specified field using base-256.
+ */
+static int
+format_256(int64_t v, char *p, int s)
+{
+	p += s;
+	while (s-- > 0) {
+		*--p = (char)(v & 0xff);
+		v >>= 8;
+	}
+	*p |= 0x80; /* Set the base-256 marker bit. */
+	return (0);
+}
+
+/*
+ * Format a number into the specified field.
+ */
+static int
+format_octal(int64_t v, char *p, int s)
+{
+	int len;
+
+	len = s;
+
+	/* Octal values can't be negative, so use 0. */
+	if (v < 0) {
+		while (len-- > 0)
+			*p++ = '0';
+		return (-1);
+	}
+
+	p += s;		/* Start at the end and work backwards. */
+	while (s-- > 0) {
+		*--p = (char)('0' + (v & 7));
+		v >>= 3;
+	}
+
+	if (v == 0)
+		return (0);
+
+	/* If it overflowed, fill field with max value. */
+	while (len-- > 0)
+		*p++ = '7';
+
+	return (-1);
+}
+
+static int
+archive_write_v7tar_close(struct archive_write *a)
+{
+	return (__archive_write_nulls(a, 512*2));
+}
+
+static int
+archive_write_v7tar_free(struct archive_write *a)
+{
+	struct v7tar *v7tar;
+
+	v7tar = (struct v7tar *)a->format_data;
+	free(v7tar);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_v7tar_finish_entry(struct archive_write *a)
+{
+	struct v7tar *v7tar;
+	int ret;
+
+	v7tar = (struct v7tar *)a->format_data;
+	ret = __archive_write_nulls(a,
+	    (size_t)(v7tar->entry_bytes_remaining + v7tar->entry_padding));
+	v7tar->entry_bytes_remaining = v7tar->entry_padding = 0;
+	return (ret);
+}
+
+static ssize_t
+archive_write_v7tar_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct v7tar *v7tar;
+	int ret;
+
+	v7tar = (struct v7tar *)a->format_data;
+	if (s > v7tar->entry_bytes_remaining)
+		s = (size_t)v7tar->entry_bytes_remaining;
+	ret = __archive_write_output(a, buff, s);
+	v7tar->entry_bytes_remaining -= s;
+	if (ret != ARCHIVE_OK)
+		return (ret);
+	return (s);
+}
diff --git a/c/archive_write_set_format_warc.c b/c/archive_write_set_format_warc.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_warc.c
@@ -0,0 +1,453 @@
+/*-
+ * Copyright (c) 2014 Sebastian Freundt
+ * Author: Sebastian Freundt  <devel@fresse.org>
+ *
+ * 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$");
+
+#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_TIME_H
+#include <time.h>
+#endif
+
+#include "archive.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_random_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+struct warc_s {
+	unsigned int omit_warcinfo:1;
+
+	time_t now;
+	mode_t typ;
+	unsigned int rng;
+	/* populated size */
+	uint64_t populz;
+};
+
+static const char warcinfo[] =
+    "software: libarchive/" ARCHIVE_VERSION_ONLY_STRING "\r\n"
+    "format: WARC file version 1.0\r\n";
+
+typedef enum {
+	WT_NONE,
+	/* warcinfo */
+	WT_INFO,
+	/* metadata */
+	WT_META,
+	/* resource */
+	WT_RSRC,
+	/* request, unsupported */
+	WT_REQ,
+	/* response, unsupported */
+	WT_RSP,
+	/* revisit, unsupported */
+	WT_RVIS,
+	/* conversion, unsupported */
+	WT_CONV,
+	/* continuation, unsupported at the moment */
+	WT_CONT,
+	/* invalid type */
+	LAST_WT
+} warc_type_t;
+
+typedef struct {
+	warc_type_t type;
+	const char *tgturi;
+	const char *recid;
+	time_t rtime;
+	time_t mtime;
+	const char *cnttyp;
+	uint64_t cntlen;
+} warc_essential_hdr_t;
+
+typedef struct {
+	unsigned int u[4U];
+} warc_uuid_t;
+
+static int _warc_options(struct archive_write*, const char *key, const char *v);
+static int _warc_header(struct archive_write *a, struct archive_entry *entry);
+static ssize_t _warc_data(struct archive_write *a, const void *buf, size_t sz);
+static int _warc_finish_entry(struct archive_write *a);
+static int _warc_close(struct archive_write *a);
+static int _warc_free(struct archive_write *a);
+
+/* private routines */
+static ssize_t _popul_ehdr(struct archive_string *t, size_t z, warc_essential_hdr_t);
+static int _gen_uuid(warc_uuid_t *tgt);
+
+
+/*
+ * Set output format to ISO 28500 (aka WARC) format.
+ */
+int
+archive_write_set_format_warc(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct warc_s *w;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_warc");
+
+	/* If another format was already registered, unregister it. */
+	if (a->format_free != NULL) {
+		(a->format_free)(a);
+	}
+
+	w = malloc(sizeof(*w));
+	if (w == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate warc data");
+		return (ARCHIVE_FATAL);
+	}
+	/* by default we're emitting a file wide header */
+	w->omit_warcinfo = 0U;
+	/* obtain current time for date fields */
+	w->now = time(NULL);
+	/* reset file type info */
+	w->typ = 0;
+	/* also initialise our rng */
+	w->rng = (unsigned int)w->now;
+
+	a->format_data = w;
+	a->format_name = "WARC/1.0";
+	a->format_options = _warc_options;
+	a->format_write_header = _warc_header;
+	a->format_write_data = _warc_data;
+	a->format_close = _warc_close;
+	a->format_free = _warc_free;
+	a->format_finish_entry = _warc_finish_entry;
+	a->archive.archive_format = ARCHIVE_FORMAT_WARC;
+	a->archive.archive_format_name = "WARC/1.0";
+	return (ARCHIVE_OK);
+}
+
+
+/* archive methods */
+static int
+_warc_options(struct archive_write *a, const char *key, const char *val)
+{
+	struct warc_s *w = a->format_data;
+
+	if (strcmp(key, "omit-warcinfo") == 0) {
+		if (val == NULL || strcmp(val, "true") == 0) {
+			/* great */
+			w->omit_warcinfo = 1U;
+			return (ARCHIVE_OK);
+		}
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+_warc_header(struct archive_write *a, struct archive_entry *entry)
+{
+	struct warc_s *w = a->format_data;
+	struct archive_string hdr;
+#define MAX_HDR_SIZE 512
+
+	/* check whether warcinfo record needs outputting */
+	if (!w->omit_warcinfo) {
+		ssize_t r;
+		warc_essential_hdr_t wi = {
+			WT_INFO,
+			/*uri*/NULL,
+			/*urn*/NULL,
+			/*rtm*/0,
+			/*mtm*/0,
+			/*cty*/"application/warc-fields",
+			/*len*/sizeof(warcinfo) - 1U,
+		};
+		wi.rtime = w->now;
+		wi.mtime = w->now;
+
+		archive_string_init(&hdr);
+		r = _popul_ehdr(&hdr, MAX_HDR_SIZE, wi);
+		if (r >= 0) {
+			/* jackpot! */
+			/* now also use HDR buffer for the actual warcinfo */
+			archive_strncat(&hdr, warcinfo, sizeof(warcinfo) -1);
+
+			/* append end-of-record indicator */
+			archive_strncat(&hdr, "\r\n\r\n", 4);
+
+			/* write to output stream */
+			__archive_write_output(a, hdr.s, archive_strlen(&hdr));
+		}
+		/* indicate we're done with file header writing */
+		w->omit_warcinfo = 1U;
+		archive_string_free(&hdr);
+	}
+
+	if (archive_entry_pathname(entry) == NULL) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Invalid filename");
+		return (ARCHIVE_WARN);
+	}
+
+	w->typ = archive_entry_filetype(entry);
+	w->populz = 0U;
+	if (w->typ == AE_IFREG) {
+		warc_essential_hdr_t rh = {
+			WT_RSRC,
+			/*uri*/NULL,
+			/*urn*/NULL,
+			/*rtm*/0,
+			/*mtm*/0,
+			/*cty*/NULL,
+			/*len*/0,
+		};
+		ssize_t r;
+		rh.tgturi = archive_entry_pathname(entry);
+		rh.rtime = w->now;
+		rh.mtime = archive_entry_mtime(entry);
+		rh.cntlen = (size_t)archive_entry_size(entry);
+
+		archive_string_init(&hdr);
+		r = _popul_ehdr(&hdr, MAX_HDR_SIZE, rh);
+		if (r < 0) {
+			/* don't bother */
+			archive_set_error(
+				&a->archive,
+				ARCHIVE_ERRNO_FILE_FORMAT,
+				"cannot archive file");
+			return (ARCHIVE_WARN);
+		}
+		/* otherwise append to output stream */
+		__archive_write_output(a, hdr.s, r);
+		/* and let subsequent calls to _data() know about the size */
+		w->populz = rh.cntlen;
+		archive_string_free(&hdr);
+		return (ARCHIVE_OK);
+	}
+	/* just resort to erroring as per Tim's advice */
+	__archive_write_entry_filetype_unsupported(
+	    &a->archive, entry, "WARC");
+	return (ARCHIVE_FAILED);
+}
+
+static ssize_t
+_warc_data(struct archive_write *a, const void *buf, size_t len)
+{
+	struct warc_s *w = a->format_data;
+
+	if (w->typ == AE_IFREG) {
+		int rc;
+
+		/* never write more bytes than announced */
+		if (len > w->populz) {
+			len = (size_t)w->populz;
+		}
+
+		/* now then, out we put the whole shebang */
+		rc = __archive_write_output(a, buf, len);
+		if (rc != ARCHIVE_OK) {
+			return rc;
+		}
+	}
+	return len;
+}
+
+static int
+_warc_finish_entry(struct archive_write *a)
+{
+	static const char _eor[] = "\r\n\r\n";
+	struct warc_s *w = a->format_data;
+
+	if (w->typ == AE_IFREG) {
+		int rc = __archive_write_output(a, _eor, sizeof(_eor) - 1U);
+
+		if (rc != ARCHIVE_OK) {
+			return rc;
+		}
+	}
+	/* reset type info */
+	w->typ = 0;
+	return (ARCHIVE_OK);
+}
+
+static int
+_warc_close(struct archive_write *a)
+{
+	(void)a; /* UNUSED */
+	return (ARCHIVE_OK);
+}
+
+static int
+_warc_free(struct archive_write *a)
+{
+	struct warc_s *w = a->format_data;
+
+	free(w);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+
+/* private routines */
+static void
+xstrftime(struct archive_string *as, const char *fmt, time_t t)
+{
+/** like strftime(3) but for time_t objects */
+	struct tm *rt;
+#if defined(HAVE_GMTIME_R) || defined(HAVE__GMTIME64_S)
+	struct tm timeHere;
+#endif
+#if defined(HAVE__GMTIME64_S)
+	errno_t terr;
+	__time64_t tmptime;
+#endif
+	char strtime[100];
+	size_t len;
+
+#ifdef HAVE_GMTIME_R
+	if ((rt = gmtime_r(&t, &timeHere)) == NULL)
+		return;
+#elif defined(HAVE__GMTIME64_S)
+	tmptime = t;
+	terr = _gmtime64_s(&timeHere, &tmptime);
+	if (terr)
+		rt = NULL;
+	else
+		rt = &timeHere;
+#else
+	if ((rt = gmtime(&t)) == NULL)
+		return;
+#endif
+	/* leave the hard yacker to our role model strftime() */
+	len = strftime(strtime, sizeof(strtime)-1, fmt, rt);
+	archive_strncat(as, strtime, len);
+}
+
+static ssize_t
+_popul_ehdr(struct archive_string *tgt, size_t tsz, warc_essential_hdr_t hdr)
+{
+	static const char _ver[] = "WARC/1.0\r\n";
+	static const char * const _typ[LAST_WT] = {
+		NULL, "warcinfo", "metadata", "resource", NULL
+	};
+	char std_uuid[48U];
+
+	if (hdr.type == WT_NONE || hdr.type > WT_RSRC) {
+		/* brilliant, how exactly did we get here? */
+		return -1;
+	}
+
+	archive_strcpy(tgt, _ver);
+
+	archive_string_sprintf(tgt, "WARC-Type: %s\r\n", _typ[hdr.type]);
+
+	if (hdr.tgturi != NULL) {
+		/* check if there's a xyz:// */
+		static const char _uri[] = "";
+		static const char _fil[] = "file://";
+		const char *u;
+		char *chk = strchr(hdr.tgturi, ':');
+
+		if (chk != NULL && chk[1U] == '/' && chk[2U] == '/') {
+			/* yep, it's definitely a URI */
+			u = _uri;
+		} else {
+			/* hm, best to prepend file:// then */
+			u = _fil;
+		}
+		archive_string_sprintf(tgt,
+			"WARC-Target-URI: %s%s\r\n", u, hdr.tgturi);
+	}
+
+	/* record time is usually when the http is sent off,
+	 * just treat the archive writing as such for a moment */
+	xstrftime(tgt, "WARC-Date: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.rtime);
+
+	/* while we're at it, record the mtime */
+	xstrftime(tgt, "Last-Modified: %Y-%m-%dT%H:%M:%SZ\r\n", hdr.mtime);
+
+	if (hdr.recid == NULL) {
+		/* generate one, grrrr */
+		warc_uuid_t u;
+
+		_gen_uuid(&u);
+		/* Unfortunately, archive_string_sprintf does not
+		 * handle the minimum number following '%'.
+		 * So we have to use snprintf function here instead
+		 * of archive_string_snprintf function. */
+#if defined(_WIN32) && !defined(__CYGWIN__) && !( defined(_MSC_VER) && _MSC_VER >= 1900)
+#define snprintf _snprintf
+#endif
+		snprintf(
+			std_uuid, sizeof(std_uuid),
+			"<urn:uuid:%08x-%04x-%04x-%04x-%04x%08x>",
+			u.u[0U],
+			u.u[1U] >> 16U, u.u[1U] & 0xffffU,
+			u.u[2U] >> 16U, u.u[2U] & 0xffffU,
+			u.u[3U]);
+		hdr.recid = std_uuid;
+	}
+
+	/* record-id is mandatory, fingers crossed we won't fail */
+	archive_string_sprintf(tgt, "WARC-Record-ID: %s\r\n", hdr.recid);
+
+	if (hdr.cnttyp != NULL) {
+		archive_string_sprintf(tgt, "Content-Type: %s\r\n", hdr.cnttyp);
+	}
+
+	/* next one is mandatory */
+	archive_string_sprintf(tgt, "Content-Length: %ju\r\n", (uintmax_t)hdr.cntlen);
+	/**/
+	archive_strncat(tgt, "\r\n", 2);
+
+	return (archive_strlen(tgt) >= tsz)? -1: (ssize_t)archive_strlen(tgt);
+}
+
+static int
+_gen_uuid(warc_uuid_t *tgt)
+{
+	archive_random(tgt->u, sizeof(tgt->u));
+	/* obey uuid version 4 rules */
+	tgt->u[1U] &= 0xffff0fffU;
+	tgt->u[1U] |= 0x4000U;
+	tgt->u[2U] &= 0x3fffffffU;
+	tgt->u[2U] |= 0x80000000U;
+	return 0;
+}
+
+/* archive_write_set_format_warc.c ends here */
diff --git a/c/archive_write_set_format_xar.c b/c/archive_write_set_format_xar.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_xar.c
@@ -0,0 +1,3259 @@
+/*-
+ * Copyright (c) 2010-2012 Michihiro NAKAJIMA
+ * 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#include <stdlib.h>
+#if HAVE_LIBXML_XMLWRITER_H
+#include <libxml/xmlwriter.h>
+#endif
+#ifdef HAVE_BZLIB_H
+#include <bzlib.h>
+#endif
+#if HAVE_LZMA_H
+#include <lzma.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_digest_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_private.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+/*
+ * Differences to xar utility.
+ * - Subdocument is not supported yet.
+ * - ACL is not supported yet.
+ * - When writing an XML element <link type="<file-type>">, <file-type>
+ *   which is a file type a symbolic link is referencing is always marked
+ *   as "broken". Xar utility uses stat(2) to get the file type, but, in
+ *   libarchive format writer, we should not use it; if it is needed, we
+ *   should get about it at archive_read_disk.c.
+ * - It is possible to appear both <flags> and <ext2> elements.
+ *   Xar utility generates <flags> on BSD platform and <ext2> on Linux
+ *   platform.
+ *
+ */
+
+#if !(defined(HAVE_LIBXML_XMLWRITER_H) && defined(LIBXML_VERSION) &&\
+	LIBXML_VERSION >= 20703) ||\
+	!defined(HAVE_ZLIB_H) || \
+	!defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
+/*
+ * xar needs several external libraries.
+ *   o libxml2
+ *   o openssl or MD5/SHA1 hash function
+ *   o zlib
+ *   o bzlib2 (option)
+ *   o liblzma (option)
+ */
+int
+archive_write_set_format_xar(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+	    "Xar not supported on this platform");
+	return (ARCHIVE_WARN);
+}
+
+#else	/* Support xar format */
+
+/*#define DEBUG_PRINT_TOC		1 */
+
+#define BAD_CAST_CONST (const xmlChar *)
+
+#define HEADER_MAGIC	0x78617221
+#define HEADER_SIZE	28
+#define HEADER_VERSION	1
+
+enum sumalg {
+	CKSUM_NONE = 0,
+	CKSUM_SHA1 = 1,
+	CKSUM_MD5 = 2
+};
+
+#define MD5_SIZE	16
+#define SHA1_SIZE	20
+#define MAX_SUM_SIZE	20
+#define MD5_NAME	"md5"
+#define SHA1_NAME	"sha1"
+
+enum enctype {
+	NONE,
+	GZIP,
+	BZIP2,
+	LZMA,
+	XZ,
+};
+
+struct chksumwork {
+	enum sumalg		 alg;
+#ifdef ARCHIVE_HAS_MD5
+	archive_md5_ctx		 md5ctx;
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	archive_sha1_ctx	 sha1ctx;
+#endif
+};
+
+enum la_zaction {
+	ARCHIVE_Z_FINISH,
+	ARCHIVE_Z_RUN
+};
+
+/*
+ * Universal zstream.
+ */
+struct la_zstream {
+	const unsigned char	*next_in;
+	size_t			 avail_in;
+	uint64_t		 total_in;
+
+	unsigned char		*next_out;
+	size_t			 avail_out;
+	uint64_t		 total_out;
+
+	int			 valid;
+	void			*real_stream;
+	int			 (*code) (struct archive *a,
+				    struct la_zstream *lastrm,
+				    enum la_zaction action);
+	int			 (*end)(struct archive *a,
+				    struct la_zstream *lastrm);
+};
+
+struct chksumval {
+	enum sumalg		 alg;
+	size_t			 len;
+	unsigned char		 val[MAX_SUM_SIZE];
+};
+
+struct heap_data {
+	int			 id;
+	struct heap_data	*next;
+	uint64_t		 temp_offset;
+	uint64_t		 length;	/* archived size.	*/
+	uint64_t		 size;		/* extracted size.	*/
+	enum enctype		 compression;
+	struct chksumval	 a_sum;		/* archived checksum.	*/
+	struct chksumval	 e_sum;		/* extracted checksum.	*/
+};
+
+struct file {
+	struct archive_rb_node	 rbnode;
+
+	int			 id;
+	struct archive_entry	*entry;
+
+	struct archive_rb_tree	 rbtree;
+	struct file		*next;
+	struct file		*chnext;
+	struct file		*hlnext;
+	/* For hardlinked files.
+	 * Use only when archive_entry_nlink() > 1 */
+	struct file		*hardlink_target;
+	struct file		*parent;	/* parent directory entry */
+	/*
+	 * To manage sub directory files.
+	 * We use 'chnext' (a member of struct file) to chain.
+	 */
+	struct {
+		struct file	*first;
+		struct file	**last;
+	}			 children;
+
+	/* For making a directory tree. */
+        struct archive_string    parentdir;
+        struct archive_string    basename;
+        struct archive_string    symlink;
+
+	int			 ea_idx;
+	struct {
+		struct heap_data *first;
+		struct heap_data **last;
+	}			 xattr;
+	struct heap_data	 data;
+        struct archive_string    script;
+
+	signed int		 virtual:1;
+	signed int		 dir:1;
+};
+
+struct hardlink {
+	struct archive_rb_node	 rbnode;
+	int			 nlink;
+	struct {
+		struct file	*first;
+		struct file	**last;
+	}			 file_list;
+};
+
+struct xar {
+	int			 temp_fd;
+	uint64_t		 temp_offset;
+
+	int			 file_idx;
+	struct file		*root;
+	struct file		*cur_dirent;
+	struct archive_string	 cur_dirstr;
+	struct file		*cur_file;
+	uint64_t		 bytes_remaining;
+	struct archive_string	 tstr;
+	struct archive_string	 vstr;
+
+	enum sumalg		 opt_toc_sumalg;
+	enum sumalg		 opt_sumalg;
+	enum enctype		 opt_compression;
+	int			 opt_compression_level;
+	uint32_t		 opt_threads;
+
+	struct chksumwork	 a_sumwrk;	/* archived checksum.	*/
+	struct chksumwork	 e_sumwrk;	/* extracted checksum.	*/
+	struct la_zstream	 stream;
+	struct archive_string_conv *sconv;
+	/*
+	 * Compressed data buffer.
+	 */
+	unsigned char		 wbuff[1024 * 64];
+	size_t			 wbuff_remaining;
+
+	struct heap_data	 toc;
+	/*
+	 * The list of all file entries is used to manage struct file
+	 * objects.
+	 * We use 'next' (a member of struct file) to chain.
+	 */
+	struct {
+		struct file	*first;
+		struct file	**last;
+	}			 file_list;
+	/*
+	 * The list of hard-linked file entries.
+	 * We use 'hlnext' (a member of struct file) to chain.
+	 */
+	struct archive_rb_tree	 hardlink_rbtree;
+};
+
+static int	xar_options(struct archive_write *,
+		    const char *, const char *);
+static int	xar_write_header(struct archive_write *,
+		    struct archive_entry *);
+static ssize_t	xar_write_data(struct archive_write *,
+		    const void *, size_t);
+static int	xar_finish_entry(struct archive_write *);
+static int	xar_close(struct archive_write *);
+static int	xar_free(struct archive_write *);
+
+static struct file *file_new(struct archive_write *a, struct archive_entry *);
+static void	file_free(struct file *);
+static struct file *file_create_virtual_dir(struct archive_write *a, struct xar *,
+		    const char *);
+static int	file_add_child_tail(struct file *, struct file *);
+static struct file *file_find_child(struct file *, const char *);
+static int	file_gen_utility_names(struct archive_write *,
+		    struct file *);
+static int	get_path_component(char *, int, const char *);
+static int	file_tree(struct archive_write *, struct file **);
+static void	file_register(struct xar *, struct file *);
+static void	file_init_register(struct xar *);
+static void	file_free_register(struct xar *);
+static int	file_register_hardlink(struct archive_write *,
+		    struct file *);
+static void	file_connect_hardlink_files(struct xar *);
+static void	file_init_hardlinks(struct xar *);
+static void	file_free_hardlinks(struct xar *);
+
+static void	checksum_init(struct chksumwork *, enum sumalg);
+static void	checksum_update(struct chksumwork *, const void *, size_t);
+static void	checksum_final(struct chksumwork *, struct chksumval *);
+static int	compression_init_encoder_gzip(struct archive *,
+		    struct la_zstream *, int, int);
+static int	compression_code_gzip(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end_gzip(struct archive *, struct la_zstream *);
+static int	compression_init_encoder_bzip2(struct archive *,
+		    struct la_zstream *, int);
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int	compression_code_bzip2(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end_bzip2(struct archive *, struct la_zstream *);
+#endif
+static int	compression_init_encoder_lzma(struct archive *,
+		    struct la_zstream *, int);
+static int	compression_init_encoder_xz(struct archive *,
+		    struct la_zstream *, int, int);
+#if defined(HAVE_LZMA_H)
+static int	compression_code_lzma(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end_lzma(struct archive *, struct la_zstream *);
+#endif
+static int	xar_compression_init_encoder(struct archive_write *);
+static int	compression_code(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end(struct archive *,
+		    struct la_zstream *);
+static int	save_xattrs(struct archive_write *, struct file *);
+static int	getalgsize(enum sumalg);
+static const char *getalgname(enum sumalg);
+
+int
+archive_write_set_format_xar(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct xar *xar;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_xar");
+
+	/* If another format was already registered, unregister it. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	xar = calloc(1, sizeof(*xar));
+	if (xar == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate xar data");
+		return (ARCHIVE_FATAL);
+	}
+	xar->temp_fd = -1;
+	file_init_register(xar);
+	file_init_hardlinks(xar);
+	archive_string_init(&(xar->tstr));
+	archive_string_init(&(xar->vstr));
+
+	/*
+	 * Create the root directory.
+	 */
+	xar->root = file_create_virtual_dir(a, xar, "");
+	if (xar->root == NULL) {
+		free(xar);
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate xar data");
+		return (ARCHIVE_FATAL);
+	}
+	xar->root->parent = xar->root;
+	file_register(xar, xar->root);
+	xar->cur_dirent = xar->root;
+	archive_string_init(&(xar->cur_dirstr));
+	archive_string_ensure(&(xar->cur_dirstr), 1);
+	xar->cur_dirstr.s[0] = 0;
+
+	/*
+	 * Initialize option.
+	 */
+	/* Set default checksum type. */
+	xar->opt_toc_sumalg = CKSUM_SHA1;
+	xar->opt_sumalg = CKSUM_SHA1;
+	/* Set default compression type, level, and number of threads. */
+	xar->opt_compression = GZIP;
+	xar->opt_compression_level = 6;
+	xar->opt_threads = 1;
+
+	a->format_data = xar;
+
+	a->format_name = "xar";
+	a->format_options = xar_options;
+	a->format_write_header = xar_write_header;
+	a->format_write_data = xar_write_data;
+	a->format_finish_entry = xar_finish_entry;
+	a->format_close = xar_close;
+	a->format_free = xar_free;
+	a->archive.archive_format = ARCHIVE_FORMAT_XAR;
+	a->archive.archive_format_name = "xar";
+
+	return (ARCHIVE_OK);
+}
+
+static int
+xar_options(struct archive_write *a, const char *key, const char *value)
+{
+	struct xar *xar;
+
+	xar = (struct xar *)a->format_data;
+
+	if (strcmp(key, "checksum") == 0) {
+		if (value == NULL)
+			xar->opt_sumalg = CKSUM_NONE;
+		else if (strcmp(value, "none") == 0)
+			xar->opt_sumalg = CKSUM_NONE;
+		else if (strcmp(value, "sha1") == 0)
+			xar->opt_sumalg = CKSUM_SHA1;
+		else if (strcmp(value, "md5") == 0)
+			xar->opt_sumalg = CKSUM_MD5;
+		else {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "Unknown checksum name: `%s'",
+			    value);
+			return (ARCHIVE_FAILED);
+		}
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "compression") == 0) {
+		const char *name = NULL;
+
+		if (value == NULL)
+			xar->opt_compression = NONE;
+		else if (strcmp(value, "none") == 0)
+			xar->opt_compression = NONE;
+		else if (strcmp(value, "gzip") == 0)
+			xar->opt_compression = GZIP;
+		else if (strcmp(value, "bzip2") == 0)
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+			xar->opt_compression = BZIP2;
+#else
+			name = "bzip2";
+#endif
+		else if (strcmp(value, "lzma") == 0)
+#if HAVE_LZMA_H
+			xar->opt_compression = LZMA;
+#else
+			name = "lzma";
+#endif
+		else if (strcmp(value, "xz") == 0)
+#if HAVE_LZMA_H
+			xar->opt_compression = XZ;
+#else
+			name = "xz";
+#endif
+		else {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "Unknown compression name: `%s'",
+			    value);
+			return (ARCHIVE_FAILED);
+		}
+		if (name != NULL) {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "`%s' compression not supported "
+			    "on this platform",
+			    name);
+			return (ARCHIVE_FAILED);
+		}
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "compression-level") == 0) {
+		if (value == NULL ||
+		    !(value[0] >= '0' && value[0] <= '9') ||
+		    value[1] != '\0') {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "Illegal value `%s'",
+			    value);
+			return (ARCHIVE_FAILED);
+		}
+		xar->opt_compression_level = value[0] - '0';
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "toc-checksum") == 0) {
+		if (value == NULL)
+			xar->opt_toc_sumalg = CKSUM_NONE;
+		else if (strcmp(value, "none") == 0)
+			xar->opt_toc_sumalg = CKSUM_NONE;
+		else if (strcmp(value, "sha1") == 0)
+			xar->opt_toc_sumalg = CKSUM_SHA1;
+		else if (strcmp(value, "md5") == 0)
+			xar->opt_toc_sumalg = CKSUM_MD5;
+		else {
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "Unknown checksum name: `%s'",
+			    value);
+			return (ARCHIVE_FAILED);
+		}
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "threads") == 0) {
+		char *endptr;
+
+		if (value == NULL)
+			return (ARCHIVE_FAILED);
+		errno = 0;
+		xar->opt_threads = (int)strtoul(value, &endptr, 10);
+		if (errno != 0 || *endptr != '\0') {
+			xar->opt_threads = 1;
+			archive_set_error(&(a->archive),
+			    ARCHIVE_ERRNO_MISC,
+			    "Illegal value `%s'",
+			    value);
+			return (ARCHIVE_FAILED);
+		}
+		if (xar->opt_threads == 0) {
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+			xar->opt_threads = lzma_cputhreads();
+#else
+			xar->opt_threads = 1;
+#endif
+		}
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+static int
+xar_write_header(struct archive_write *a, struct archive_entry *entry)
+{
+	struct xar *xar;
+	struct file *file;
+	struct archive_entry *file_entry;
+	int r, r2;
+
+	xar = (struct xar *)a->format_data;
+	xar->cur_file = NULL;
+	xar->bytes_remaining = 0;
+
+	if (xar->sconv == NULL) {
+		xar->sconv = archive_string_conversion_to_charset(
+		    &a->archive, "UTF-8", 1);
+		if (xar->sconv == NULL)
+			return (ARCHIVE_FATAL);
+	}
+
+	file = file_new(a, entry);
+	if (file == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate data");
+		return (ARCHIVE_FATAL);
+	}
+	r2 = file_gen_utility_names(a, file);
+	if (r2 < ARCHIVE_WARN)
+		return (r2);
+
+	/*
+	 * Ignore a path which looks like the top of directory name
+	 * since we have already made the root directory of an Xar archive.
+	 */
+	if (archive_strlen(&(file->parentdir)) == 0 &&
+	    archive_strlen(&(file->basename)) == 0) {
+		file_free(file);
+		return (r2);
+	}
+
+	/* Add entry into tree */
+	file_entry = file->entry;
+	r = file_tree(a, &file);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/* There is the same file in tree and
+	 * the current file is older than the file in tree.
+	 * So we don't need the current file data anymore. */
+	if (file->entry != file_entry)
+		return (r2);
+	if (file->id == 0)
+		file_register(xar, file);
+
+	/* A virtual file, which is a directory, does not have
+	 * any contents and we won't store it into a archive
+	 * file other than its name. */
+	if (file->virtual)
+		return (r2);
+
+	/*
+	 * Prepare to save the contents of the file.
+	 */
+	if (xar->temp_fd == -1) {
+		int algsize;
+		xar->temp_offset = 0;
+		xar->temp_fd = __archive_mktemp(NULL);
+		if (xar->temp_fd < 0) {
+			archive_set_error(&a->archive, errno,
+			    "Couldn't create temporary file");
+			return (ARCHIVE_FATAL);
+		}
+		algsize = getalgsize(xar->opt_toc_sumalg);
+		if (algsize > 0) {
+			if (lseek(xar->temp_fd, algsize, SEEK_SET) < 0) {
+				archive_set_error(&(a->archive), errno,
+				    "lseek failed");
+				return (ARCHIVE_FATAL);
+			}
+			xar->temp_offset = algsize;
+		}
+	}
+
+	if (archive_entry_hardlink(file->entry) == NULL) {
+		r = save_xattrs(a, file);
+		if (r != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	/* Non regular files contents are unneeded to be saved to
+	 * a temporary file. */
+	if (archive_entry_filetype(file->entry) != AE_IFREG)
+		return (r2);
+
+	/*
+	 * Set the current file to cur_file to read its contents.
+	 */
+	xar->cur_file = file;
+
+	if (archive_entry_nlink(file->entry) > 1) {
+		r = file_register_hardlink(a, file);
+		if (r != ARCHIVE_OK)
+			return (r);
+		if (archive_entry_hardlink(file->entry) != NULL) {
+			archive_entry_unset_size(file->entry);
+			return (r2);
+		}
+	}
+
+	/* Save a offset of current file in temporary file. */
+	file->data.temp_offset = xar->temp_offset;
+	file->data.size = archive_entry_size(file->entry);
+	file->data.compression = xar->opt_compression;
+	xar->bytes_remaining = archive_entry_size(file->entry);
+	checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);
+	checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
+	r = xar_compression_init_encoder(a);
+
+	if (r != ARCHIVE_OK)
+		return (r);
+	else
+		return (r2);
+}
+
+static int
+write_to_temp(struct archive_write *a, const void *buff, size_t s)
+{
+	struct xar *xar;
+	const unsigned char *p;
+	ssize_t ws;
+
+	xar = (struct xar *)a->format_data;
+	p = (const unsigned char *)buff;
+	while (s) {
+		ws = write(xar->temp_fd, p, s);
+		if (ws < 0) {
+			archive_set_error(&(a->archive), errno,
+			    "fwrite function failed");
+			return (ARCHIVE_FATAL);
+		}
+		s -= ws;
+		p += ws;
+		xar->temp_offset += ws;
+	}
+	return (ARCHIVE_OK);
+}
+
+static ssize_t
+xar_write_data(struct archive_write *a, const void *buff, size_t s)
+{
+	struct xar *xar;
+	enum la_zaction run;
+	size_t size = 0;
+	size_t rsize;
+	int r;
+
+	xar = (struct xar *)a->format_data;
+
+	if (s > xar->bytes_remaining)
+		s = (size_t)xar->bytes_remaining;
+	if (s == 0 || xar->cur_file == NULL)
+		return (0);
+	if (xar->cur_file->data.compression == NONE) {
+		checksum_update(&(xar->e_sumwrk), buff, s);
+		checksum_update(&(xar->a_sumwrk), buff, s);
+		size = rsize = s;
+	} else {
+		xar->stream.next_in = (const unsigned char *)buff;
+		xar->stream.avail_in = s;
+		if (xar->bytes_remaining > s)
+			run = ARCHIVE_Z_RUN;
+		else
+			run = ARCHIVE_Z_FINISH;
+		/* Compress file data. */
+		for (;;) {
+			r = compression_code(&(a->archive), &(xar->stream),
+			    run);
+			if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
+				return (ARCHIVE_FATAL);
+			if (xar->stream.avail_out == 0 ||
+			    run == ARCHIVE_Z_FINISH) {
+				size = sizeof(xar->wbuff) -
+				    xar->stream.avail_out;
+				checksum_update(&(xar->a_sumwrk), xar->wbuff,
+				    size);
+				xar->cur_file->data.length += size;
+				if (write_to_temp(a, xar->wbuff,
+				    size) != ARCHIVE_OK)
+					return (ARCHIVE_FATAL);
+				if (r == ARCHIVE_OK) {
+					/* Output buffer was full */
+					xar->stream.next_out = xar->wbuff;
+					xar->stream.avail_out =
+					    sizeof(xar->wbuff);
+				} else {
+					/* ARCHIVE_EOF - We are done */
+					break;
+				}
+			} else {
+				/* Compressor wants more input */
+				break;
+			}
+		}
+		rsize = s - xar->stream.avail_in;
+		checksum_update(&(xar->e_sumwrk), buff, rsize);
+	}
+#if !defined(_WIN32) || defined(__CYGWIN__)
+	if (xar->bytes_remaining ==
+	    (uint64_t)archive_entry_size(xar->cur_file->entry)) {
+		/*
+		 * Get the path of a shell script if so.
+		 */
+		const unsigned char *b = (const unsigned char *)buff;
+
+		archive_string_empty(&(xar->cur_file->script));
+		if (rsize > 2 && b[0] == '#' && b[1] == '!') {
+			size_t i, end, off;
+
+			off = 2;
+			if (b[off] == ' ')
+				off++;
+#ifdef PATH_MAX
+			if ((rsize - off) > PATH_MAX)
+				end = off + PATH_MAX;
+			else
+#endif
+				end = rsize;
+			/* Find the end of a script path. */
+			for (i = off; i < end && b[i] != '\0' &&
+			    b[i] != '\n' && b[i] != '\r' &&
+			    b[i] != ' ' && b[i] != '\t'; i++)
+				;
+			archive_strncpy(&(xar->cur_file->script), b + off,
+			    i - off);
+		}
+	}
+#endif
+
+	if (xar->cur_file->data.compression == NONE) {
+		if (write_to_temp(a, buff, size) != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		xar->cur_file->data.length += size;
+	}
+	xar->bytes_remaining -= rsize;
+
+	return (rsize);
+}
+
+static int
+xar_finish_entry(struct archive_write *a)
+{
+	struct xar *xar;
+	struct file *file;
+	size_t s;
+	ssize_t w;
+
+	xar = (struct xar *)a->format_data;
+	if (xar->cur_file == NULL)
+		return (ARCHIVE_OK);
+
+	while (xar->bytes_remaining > 0) {
+		s = (size_t)xar->bytes_remaining;
+		if (s > a->null_length)
+			s = a->null_length;
+		w = xar_write_data(a, a->nulls, s);
+		if (w > 0)
+			xar->bytes_remaining -= w;
+		else
+			return (w);
+	}
+	file = xar->cur_file;
+	checksum_final(&(xar->e_sumwrk), &(file->data.e_sum));
+	checksum_final(&(xar->a_sumwrk), &(file->data.a_sum));
+	xar->cur_file = NULL;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_string_attr(struct archive_write *a, xmlTextWriterPtr writer,
+	const char *key, const char *value,
+	const char *attrkey, const char *attrvalue)
+{
+	int r;
+
+	r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key));
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xmlTextWriterStartElement() failed: %d", r);
+		return (ARCHIVE_FATAL);
+	}
+	if (attrkey != NULL && attrvalue != NULL) {
+		r = xmlTextWriterWriteAttribute(writer,
+		    BAD_CAST_CONST(attrkey), BAD_CAST_CONST(attrvalue));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterWriteAttribute() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+	if (value != NULL) {
+		r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterWriteString() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+	r = xmlTextWriterEndElement(writer);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xmlTextWriterEndElement() failed: %d", r);
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_string(struct archive_write *a, xmlTextWriterPtr writer,
+	const char *key, const char *value)
+{
+	int r;
+
+	if (value == NULL)
+		return (ARCHIVE_OK);
+
+	r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(key));
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xmlTextWriterStartElement() failed: %d", r);
+		return (ARCHIVE_FATAL);
+	}
+	if (value != NULL) {
+		r = xmlTextWriterWriteString(writer, BAD_CAST_CONST(value));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterWriteString() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+	r = xmlTextWriterEndElement(writer);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xmlTextWriterEndElement() failed: %d", r);
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_fstring(struct archive_write *a, xmlTextWriterPtr writer,
+	const char *key, const char *fmt, ...)
+{
+	struct xar *xar;
+	va_list ap;
+
+	xar = (struct xar *)a->format_data;
+	va_start(ap, fmt);
+	archive_string_empty(&xar->vstr);
+	archive_string_vsprintf(&xar->vstr, fmt, ap);
+	va_end(ap);
+	return (xmlwrite_string(a, writer, key, xar->vstr.s));
+}
+
+static int
+xmlwrite_time(struct archive_write *a, xmlTextWriterPtr writer,
+	const char *key, time_t t, int z)
+{
+	char timestr[100];
+	struct tm tm;
+#if defined(HAVE__GMTIME64_S)
+	__time64_t tmptime;
+#endif
+
+#if defined(HAVE_GMTIME_R)
+	gmtime_r(&t, &tm);
+#elif defined(HAVE__GMTIME64_S)
+	tmptime = t;
+	_gmtime64_s(&tm, &tmptime);
+#else
+	memcpy(&tm, gmtime(&t), sizeof(tm));
+#endif
+	memset(&timestr, 0, sizeof(timestr));
+	/* Do not use %F and %T for portability. */
+	strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S", &tm);
+	if (z)
+		strcat(timestr, "Z");
+	return (xmlwrite_string(a, writer, key, timestr));
+}
+
+static int
+xmlwrite_mode(struct archive_write *a, xmlTextWriterPtr writer,
+	const char *key, mode_t mode)
+{
+	char ms[5];
+
+	ms[0] = '0';
+	ms[1] = '0' + ((mode >> 6) & 07);
+	ms[2] = '0' + ((mode >> 3) & 07);
+	ms[3] = '0' + (mode & 07);
+	ms[4] = '\0';
+
+	return (xmlwrite_string(a, writer, key, ms));
+}
+
+static int
+xmlwrite_sum(struct archive_write *a, xmlTextWriterPtr writer,
+	const char *key, struct chksumval *sum)
+{
+	const char *algname;
+	int algsize;
+	char buff[MAX_SUM_SIZE*2 + 1];
+	char *p;
+	unsigned char *s;
+	int i, r;
+
+	if (sum->len > 0) {
+		algname = getalgname(sum->alg);
+		algsize = getalgsize(sum->alg);
+		if (algname != NULL) {
+			const char *hex = "0123456789abcdef";
+			p = buff;
+			s = sum->val;
+			for (i = 0; i < algsize; i++) {
+				*p++ = hex[(*s >> 4)];
+				*p++ = hex[(*s & 0x0f)];
+				s++;
+			}
+			*p = '\0';
+			r = xmlwrite_string_attr(a, writer,
+			    key, buff,
+			    "style", algname);
+			if (r < 0)
+				return (ARCHIVE_FATAL);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_heap(struct archive_write *a, xmlTextWriterPtr writer,
+	struct heap_data *heap)
+{
+	const char *encname;
+	int r;
+
+	r = xmlwrite_fstring(a, writer, "length", "%ju", heap->length);
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+	r = xmlwrite_fstring(a, writer, "offset", "%ju", heap->temp_offset);
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+	r = xmlwrite_fstring(a, writer, "size", "%ju", heap->size);
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+	switch (heap->compression) {
+	case GZIP:
+		encname = "application/x-gzip"; break;
+	case BZIP2:
+		encname = "application/x-bzip2"; break;
+	case LZMA:
+		encname = "application/x-lzma"; break;
+	case XZ:
+		encname = "application/x-xz"; break;
+	default:
+		encname = "application/octet-stream"; break;
+	}
+	r = xmlwrite_string_attr(a, writer, "encoding", NULL,
+	    "style", encname);
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+	r = xmlwrite_sum(a, writer, "archived-checksum", &(heap->a_sum));
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+	r = xmlwrite_sum(a, writer, "extracted-checksum", &(heap->e_sum));
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+	return (ARCHIVE_OK);
+}
+
+/*
+ * xar utility records fflags as following xml elements:
+ *   <flags>
+ *     <UserNoDump/>
+ *     .....
+ *   </flags>
+ * or
+ *   <ext2>
+ *     <NoDump/>
+ *     .....
+ *   </ext2>
+ * If xar is running on BSD platform, records <flags>..</flags>;
+ * if xar is running on linux platform, records <ext2>..</ext2>;
+ * otherwise does not record.
+ *
+ * Our implements records both <flags> and <ext2> if it's necessary.
+ */
+static int
+make_fflags_entry(struct archive_write *a, xmlTextWriterPtr writer,
+    const char *element, const char *fflags_text)
+{
+	static const struct flagentry {
+		const char	*name;
+		const char	*xarname;
+	}
+	flagbsd[] = {
+		{ "sappnd",	"SystemAppend"},
+		{ "sappend",	"SystemAppend"},
+		{ "arch",	"SystemArchived"},
+		{ "archived",	"SystemArchived"},
+		{ "schg",	"SystemImmutable"},
+		{ "schange",	"SystemImmutable"},
+		{ "simmutable",	"SystemImmutable"},
+		{ "nosunlnk",	"SystemNoUnlink"},
+		{ "nosunlink",	"SystemNoUnlink"},
+		{ "snapshot",	"SystemSnapshot"},
+		{ "uappnd",	"UserAppend"},
+		{ "uappend",	"UserAppend"},
+		{ "uchg",	"UserImmutable"},
+		{ "uchange",	"UserImmutable"},
+		{ "uimmutable",	"UserImmutable"},
+		{ "nodump",	"UserNoDump"},
+		{ "noopaque",	"UserOpaque"},
+		{ "nouunlnk",	"UserNoUnlink"},
+		{ "nouunlink",	"UserNoUnlink"},
+		{ NULL, NULL}
+	},
+	flagext2[] = {
+		{ "sappnd",	"AppendOnly"},
+		{ "sappend",	"AppendOnly"},
+		{ "schg",	"Immutable"},
+		{ "schange",	"Immutable"},
+		{ "simmutable",	"Immutable"},
+		{ "nodump",	"NoDump"},
+		{ "nouunlnk",	"Undelete"},
+		{ "nouunlink",	"Undelete"},
+		{ "btree",	"BTree"},
+		{ "comperr",	"CompError"},
+		{ "compress",	"Compress"},
+		{ "noatime",	"NoAtime"},
+		{ "compdirty",	"CompDirty"},
+		{ "comprblk",	"CompBlock"},
+		{ "dirsync",	"DirSync"},
+		{ "hashidx",	"HashIndexed"},
+		{ "imagic",	"iMagic"},
+		{ "journal",	"Journaled"},
+		{ "securedeletion",	"SecureDeletion"},
+		{ "sync",	"Synchronous"},
+		{ "notail",	"NoTail"},
+		{ "topdir",	"TopDir"},
+		{ "reserved",	"Reserved"},
+		{ NULL, NULL}
+	};
+	const struct flagentry *fe, *flagentry;
+#define FLAGENTRY_MAXSIZE ((sizeof(flagbsd)+sizeof(flagext2))/sizeof(flagbsd))
+	const struct flagentry *avail[FLAGENTRY_MAXSIZE];
+	const char *p;
+	int i, n, r;
+
+	if (strcmp(element, "ext2") == 0)
+		flagentry = flagext2;
+	else
+		flagentry = flagbsd;
+	n = 0;
+	p = fflags_text;
+	do {
+		const char *cp;
+
+		cp = strchr(p, ',');
+		if (cp == NULL)
+			cp = p + strlen(p);
+
+		for (fe = flagentry; fe->name != NULL; fe++) {
+			if (fe->name[cp - p] != '\0'
+			    || p[0] != fe->name[0])
+				continue;
+			if (strncmp(p, fe->name, cp - p) == 0) {
+				avail[n++] = fe;
+				break;
+			}
+		}
+		if (*cp == ',')
+			p = cp + 1;
+		else
+			p = NULL;
+	} while (p != NULL);
+
+	if (n > 0) {
+		r = xmlTextWriterStartElement(writer, BAD_CAST_CONST(element));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterStartElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		for (i = 0; i < n; i++) {
+			r = xmlwrite_string(a, writer,
+			    avail[i]->xarname, NULL);
+			if (r != ARCHIVE_OK)
+				return (r);
+		}
+
+		r = xmlTextWriterEndElement(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterEndElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+make_file_entry(struct archive_write *a, xmlTextWriterPtr writer,
+    struct file *file)
+{
+	struct xar *xar;
+	const char *filetype, *filelink, *fflags;
+	struct archive_string linkto;
+	struct heap_data *heap;
+	unsigned char *tmp;
+	const char *p;
+	size_t len;
+	int r, r2, l, ll;
+
+	xar = (struct xar *)a->format_data;
+	r2 = ARCHIVE_OK;
+
+	/*
+	 * Make a file name entry, "<name>".
+	 */
+	l = ll = archive_strlen(&(file->basename));
+	tmp = malloc(l);
+	if (tmp == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	r = UTF8Toisolat1(tmp, &l, BAD_CAST(file->basename.s), &ll);
+	free(tmp);
+	if (r < 0) {
+		r = xmlTextWriterStartElement(writer, BAD_CAST("name"));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterStartElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		r = xmlTextWriterWriteAttribute(writer,
+		    BAD_CAST("enctype"), BAD_CAST("base64"));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterWriteAttribute() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		r = xmlTextWriterWriteBase64(writer, file->basename.s,
+		    0, archive_strlen(&(file->basename)));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterWriteBase64() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		r = xmlTextWriterEndElement(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterEndElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	} else {
+		r = xmlwrite_string(a, writer, "name", file->basename.s);
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Make a file type entry, "<type>".
+	 */
+	filelink = NULL;
+	archive_string_init(&linkto);
+	switch (archive_entry_filetype(file->entry)) {
+	case AE_IFDIR:
+		filetype = "directory"; break;
+	case AE_IFLNK:
+		filetype = "symlink"; break;
+	case AE_IFCHR:
+		filetype = "character special"; break;
+	case AE_IFBLK:
+		filetype = "block special"; break;
+	case AE_IFSOCK:
+		filetype = "socket"; break;
+	case AE_IFIFO:
+		filetype = "fifo"; break;
+	case AE_IFREG:
+	default:
+		if (file->hardlink_target != NULL) {
+			filetype = "hardlink";
+			filelink = "link";
+			if (file->hardlink_target == file)
+				archive_strcpy(&linkto, "original");
+			else
+				archive_string_sprintf(&linkto, "%d",
+				    file->hardlink_target->id);
+		} else
+			filetype = "file";
+		break;
+	}
+	r = xmlwrite_string_attr(a, writer, "type", filetype,
+	    filelink, linkto.s);
+	archive_string_free(&linkto);
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+
+	/*
+	 * On a virtual directory, we record "name" and "type" only.
+	 */
+	if (file->virtual)
+		return (ARCHIVE_OK);
+
+	switch (archive_entry_filetype(file->entry)) {
+	case AE_IFLNK:
+		/*
+		 * xar utility has checked a file type, which
+		 * a symbolic-link file has referenced.
+		 * For example:
+		 *   <link type="directory">../ref/</link>
+		 *   The symlink target file is "../ref/" and its
+		 *   file type is a directory.
+		 *
+		 *   <link type="file">../f</link>
+		 *   The symlink target file is "../f" and its
+		 *   file type is a regular file.
+		 *
+		 * But our implementation cannot do it, and then we
+		 * always record that a attribute "type" is "broken",
+		 * for example:
+		 *   <link type="broken">foo/bar</link>
+		 *   It means "foo/bar" is not reachable.
+		 */
+		r = xmlwrite_string_attr(a, writer, "link",
+		    file->symlink.s,
+		    "type", "broken");
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+		break;
+	case AE_IFCHR:
+	case AE_IFBLK:
+		r = xmlTextWriterStartElement(writer, BAD_CAST("device"));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterStartElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		r = xmlwrite_fstring(a, writer, "major",
+		    "%d", archive_entry_rdevmajor(file->entry));
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+		r = xmlwrite_fstring(a, writer, "minor",
+		    "%d", archive_entry_rdevminor(file->entry));
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+		r = xmlTextWriterEndElement(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterEndElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		break;
+	default:
+		break;
+	}
+
+	/*
+	 * Make a inode entry, "<inode>".
+	 */
+	r = xmlwrite_fstring(a, writer, "inode",
+	    "%jd", archive_entry_ino64(file->entry));
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+	if (archive_entry_dev(file->entry) != 0) {
+		r = xmlwrite_fstring(a, writer, "deviceno",
+		    "%d", archive_entry_dev(file->entry));
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Make a file mode entry, "<mode>".
+	 */
+	r = xmlwrite_mode(a, writer, "mode",
+	    archive_entry_mode(file->entry));
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+
+	/*
+	 * Make a user entry, "<uid>" and "<user>.
+	 */
+	r = xmlwrite_fstring(a, writer, "uid",
+	    "%d", archive_entry_uid(file->entry));
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+	r = archive_entry_uname_l(file->entry, &p, &len, xar->sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Uname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate uname '%s' to UTF-8",
+		    archive_entry_uname(file->entry));
+		r2 = ARCHIVE_WARN;
+	}
+	if (len > 0) {
+		r = xmlwrite_string(a, writer, "user", p);
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Make a group entry, "<gid>" and "<group>.
+	 */
+	r = xmlwrite_fstring(a, writer, "gid",
+	    "%d", archive_entry_gid(file->entry));
+	if (r < 0)
+		return (ARCHIVE_FATAL);
+	r = archive_entry_gname_l(file->entry, &p, &len, xar->sconv);
+	if (r != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Gname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate gname '%s' to UTF-8",
+		    archive_entry_gname(file->entry));
+		r2 = ARCHIVE_WARN;
+	}
+	if (len > 0) {
+		r = xmlwrite_string(a, writer, "group", p);
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Make a ctime entry, "<ctime>".
+	 */
+	if (archive_entry_ctime_is_set(file->entry)) {
+		r = xmlwrite_time(a, writer, "ctime",
+		    archive_entry_ctime(file->entry), 1);
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Make a mtime entry, "<mtime>".
+	 */
+	if (archive_entry_mtime_is_set(file->entry)) {
+		r = xmlwrite_time(a, writer, "mtime",
+		    archive_entry_mtime(file->entry), 1);
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Make a atime entry, "<atime>".
+	 */
+	if (archive_entry_atime_is_set(file->entry)) {
+		r = xmlwrite_time(a, writer, "atime",
+		    archive_entry_atime(file->entry), 1);
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * Make fflags entries, "<flags>" and "<ext2>".
+	 */
+	fflags = archive_entry_fflags_text(file->entry);
+	if (fflags != NULL) {
+		r = make_fflags_entry(a, writer, "flags", fflags);
+		if (r < 0)
+			return (r);
+		r = make_fflags_entry(a, writer, "ext2", fflags);
+		if (r < 0)
+			return (r);
+	}
+
+	/*
+	 * Make extended attribute entries, "<ea>".
+	 */
+	archive_entry_xattr_reset(file->entry);
+	for (heap = file->xattr.first; heap != NULL; heap = heap->next) {
+		const char *name;
+		const void *value;
+		size_t size;
+
+		archive_entry_xattr_next(file->entry,
+		    &name, &value, &size);
+		r = xmlTextWriterStartElement(writer, BAD_CAST("ea"));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterStartElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		r = xmlTextWriterWriteFormatAttribute(writer,
+		    BAD_CAST("id"), "%d", heap->id);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterWriteAttribute() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		r = xmlwrite_heap(a, writer, heap);
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+		r = xmlwrite_string(a, writer, "name", name);
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+
+		r = xmlTextWriterEndElement(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterEndElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	/*
+	 * Make a file data entry, "<data>".
+	 */
+	if (file->data.length > 0) {
+		r = xmlTextWriterStartElement(writer, BAD_CAST("data"));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterStartElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+
+		r = xmlwrite_heap(a, writer, &(file->data));
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+
+		r = xmlTextWriterEndElement(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterEndElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	if (archive_strlen(&file->script) > 0) {
+		r = xmlTextWriterStartElement(writer, BAD_CAST("content"));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterStartElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+
+		r = xmlwrite_string(a, writer,
+		    "interpreter", file->script.s);
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+
+		r = xmlwrite_string(a, writer, "type", "script");
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+
+		r = xmlTextWriterEndElement(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterEndElement() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	return (r2);
+}
+
+/*
+ * Make the TOC
+ */
+static int
+make_toc(struct archive_write *a)
+{
+	struct xar *xar;
+	struct file *np;
+	xmlBufferPtr bp;
+	xmlTextWriterPtr writer;
+	int algsize;
+	int r, ret;
+
+	xar = (struct xar *)a->format_data;
+
+	ret = ARCHIVE_FATAL;
+
+	/*
+	 * Initialize xml writer.
+	 */
+	writer = NULL;
+	bp = xmlBufferCreate();
+	if (bp == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "xmlBufferCreate() "
+		    "couldn't create xml buffer");
+		goto exit_toc;
+	}
+	writer = xmlNewTextWriterMemory(bp, 0);
+	if (writer == NULL) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xmlNewTextWriterMemory() "
+		    "couldn't create xml writer");
+		goto exit_toc;
+	}
+	r = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xmlTextWriterStartDocument() failed: %d", r);
+		goto exit_toc;
+	}
+	r = xmlTextWriterSetIndent(writer, 4);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xmlTextWriterSetIndent() failed: %d", r);
+		goto exit_toc;
+	}
+
+	/*
+	 * Start recording TOC
+	 */
+	r = xmlTextWriterStartElement(writer, BAD_CAST("xar"));
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xmlTextWriterStartElement() failed: %d", r);
+		goto exit_toc;
+	}
+	r = xmlTextWriterStartElement(writer, BAD_CAST("toc"));
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xmlTextWriterStartDocument() failed: %d", r);
+		goto exit_toc;
+	}
+
+	/*
+	 * Record the creation time of the archive file.
+	 */
+	r = xmlwrite_time(a, writer, "creation-time", time(NULL), 0);
+	if (r < 0)
+		goto exit_toc;
+
+	/*
+	 * Record the checksum value of TOC
+	 */
+	algsize = getalgsize(xar->opt_toc_sumalg);
+	if (algsize) {
+		/*
+		 * Record TOC checksum
+		 */
+		r = xmlTextWriterStartElement(writer, BAD_CAST("checksum"));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterStartElement() failed: %d", r);
+			goto exit_toc;
+		}
+		r = xmlTextWriterWriteAttribute(writer, BAD_CAST("style"),
+		    BAD_CAST_CONST(getalgname(xar->opt_toc_sumalg)));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterWriteAttribute() failed: %d", r);
+			goto exit_toc;
+		}
+
+		/*
+		 * Record the offset of the value of checksum of TOC
+		 */
+		r = xmlwrite_string(a, writer, "offset", "0");
+		if (r < 0)
+			goto exit_toc;
+
+		/*
+		 * Record the size of the value of checksum of TOC
+		 */
+		r = xmlwrite_fstring(a, writer, "size", "%d", algsize);
+		if (r < 0)
+			goto exit_toc;
+
+		r = xmlTextWriterEndElement(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xmlTextWriterEndElement() failed: %d", r);
+			goto exit_toc;
+		}
+	}
+
+	np = xar->root;
+	do {
+		if (np != np->parent) {
+			r = make_file_entry(a, writer, np);
+			if (r != ARCHIVE_OK)
+				goto exit_toc;
+		}
+
+		if (np->dir && np->children.first != NULL) {
+			/* Enter to sub directories. */
+			np = np->children.first;
+			r = xmlTextWriterStartElement(writer,
+			    BAD_CAST("file"));
+			if (r < 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "xmlTextWriterStartElement() "
+				    "failed: %d", r);
+				goto exit_toc;
+			}
+			r = xmlTextWriterWriteFormatAttribute(
+			    writer, BAD_CAST("id"), "%d", np->id);
+			if (r < 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "xmlTextWriterWriteAttribute() "
+				    "failed: %d", r);
+				goto exit_toc;
+			}
+			continue;
+		}
+		while (np != np->parent) {
+			r = xmlTextWriterEndElement(writer);
+			if (r < 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "xmlTextWriterEndElement() "
+				    "failed: %d", r);
+				goto exit_toc;
+			}
+			if (np->chnext == NULL) {
+				/* Return to the parent directory. */
+				np = np->parent;
+			} else {
+				np = np->chnext;
+				r = xmlTextWriterStartElement(writer,
+				    BAD_CAST("file"));
+				if (r < 0) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_MISC,
+					    "xmlTextWriterStartElement() "
+					    "failed: %d", r);
+					goto exit_toc;
+				}
+				r = xmlTextWriterWriteFormatAttribute(
+				    writer, BAD_CAST("id"), "%d", np->id);
+				if (r < 0) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_MISC,
+					    "xmlTextWriterWriteAttribute() "
+					    "failed: %d", r);
+					goto exit_toc;
+				}
+				break;
+			}
+		}
+	} while (np != np->parent);
+
+	r = xmlTextWriterEndDocument(writer);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xmlTextWriterEndDocument() failed: %d", r);
+		goto exit_toc;
+	}
+#if DEBUG_PRINT_TOC
+	fprintf(stderr, "\n---TOC-- %d bytes --\n%s\n",
+	    strlen((const char *)bp->content), bp->content);
+#endif
+
+	/*
+	 * Compress the TOC and calculate the sum of the TOC.
+	 */
+	xar->toc.temp_offset = xar->temp_offset;
+	xar->toc.size = bp->use;
+	checksum_init(&(xar->a_sumwrk), xar->opt_toc_sumalg);
+
+	r = compression_init_encoder_gzip(&(a->archive),
+	    &(xar->stream), 6, 1);
+	if (r != ARCHIVE_OK)
+		goto exit_toc;
+	xar->stream.next_in = bp->content;
+	xar->stream.avail_in = bp->use;
+	xar->stream.total_in = 0;
+	xar->stream.next_out = xar->wbuff;
+	xar->stream.avail_out = sizeof(xar->wbuff);
+	xar->stream.total_out = 0;
+	for (;;) {
+		size_t size;
+
+		r = compression_code(&(a->archive),
+		    &(xar->stream), ARCHIVE_Z_FINISH);
+		if (r != ARCHIVE_OK && r != ARCHIVE_EOF)
+			goto exit_toc;
+		size = sizeof(xar->wbuff) - xar->stream.avail_out;
+		checksum_update(&(xar->a_sumwrk), xar->wbuff, size);
+		if (write_to_temp(a, xar->wbuff, size) != ARCHIVE_OK)
+			goto exit_toc;
+		if (r == ARCHIVE_EOF)
+			break;
+		xar->stream.next_out = xar->wbuff;
+		xar->stream.avail_out = sizeof(xar->wbuff);
+	}
+	r = compression_end(&(a->archive), &(xar->stream));
+	if (r != ARCHIVE_OK)
+		goto exit_toc;
+	xar->toc.length = xar->stream.total_out;
+	xar->toc.compression = GZIP;
+	checksum_final(&(xar->a_sumwrk), &(xar->toc.a_sum));
+
+	ret = ARCHIVE_OK;
+exit_toc:
+	if (writer)
+		xmlFreeTextWriter(writer);
+	if (bp)
+		xmlBufferFree(bp);
+
+	return (ret);
+}
+
+static int
+flush_wbuff(struct archive_write *a)
+{
+	struct xar *xar;
+	int r;
+	size_t s;
+
+	xar = (struct xar *)a->format_data;
+	s = sizeof(xar->wbuff) - xar->wbuff_remaining;
+	r = __archive_write_output(a, xar->wbuff, s);
+	if (r != ARCHIVE_OK)
+		return (r);
+	xar->wbuff_remaining = sizeof(xar->wbuff);
+	return (r);
+}
+
+static int
+copy_out(struct archive_write *a, uint64_t offset, uint64_t length)
+{
+	struct xar *xar;
+	int r;
+
+	xar = (struct xar *)a->format_data;
+	if (lseek(xar->temp_fd, offset, SEEK_SET) < 0) {
+		archive_set_error(&(a->archive), errno, "lseek failed");
+		return (ARCHIVE_FATAL);
+	}
+	while (length) {
+		size_t rsize;
+		ssize_t rs;
+		unsigned char *wb;
+
+		if (length > xar->wbuff_remaining)
+			rsize = xar->wbuff_remaining;
+		else
+			rsize = (size_t)length;
+		wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);
+		rs = read(xar->temp_fd, wb, rsize);
+		if (rs < 0) {
+			archive_set_error(&(a->archive), errno,
+			    "Can't read temporary file(%jd)",
+			    (intmax_t)rs);
+			return (ARCHIVE_FATAL);
+		}
+		if (rs == 0) {
+			archive_set_error(&(a->archive), 0,
+			    "Truncated xar archive");
+			return (ARCHIVE_FATAL);
+		}
+		xar->wbuff_remaining -= rs;
+		length -= rs;
+		if (xar->wbuff_remaining == 0) {
+			r = flush_wbuff(a);
+			if (r != ARCHIVE_OK)
+				return (r);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+xar_close(struct archive_write *a)
+{
+	struct xar *xar;
+	unsigned char *wb;
+	uint64_t length;
+	int r;
+
+	xar = (struct xar *)a->format_data;
+
+	/* Empty! */
+	if (xar->root->children.first == NULL)
+		return (ARCHIVE_OK);
+
+	/* Save the length of all file extended attributes and contents. */
+	length = xar->temp_offset;
+
+	/* Connect hardlinked files */
+	file_connect_hardlink_files(xar);
+
+	/* Make the TOC */
+	r = make_toc(a);
+	if (r != ARCHIVE_OK)
+		return (r);
+	/*
+	 * Make the xar header on wbuff(write buffer).
+	 */
+	wb = xar->wbuff;
+	xar->wbuff_remaining = sizeof(xar->wbuff);
+	archive_be32enc(&wb[0], HEADER_MAGIC);
+	archive_be16enc(&wb[4], HEADER_SIZE);
+	archive_be16enc(&wb[6], HEADER_VERSION);
+	archive_be64enc(&wb[8], xar->toc.length);
+	archive_be64enc(&wb[16], xar->toc.size);
+	archive_be32enc(&wb[24], xar->toc.a_sum.alg);
+	xar->wbuff_remaining -= HEADER_SIZE;
+
+	/*
+	 * Write the TOC
+	 */
+	r = copy_out(a, xar->toc.temp_offset, xar->toc.length);
+	if (r != ARCHIVE_OK)
+		return (r);
+
+	/* Write the checksum value of the TOC. */
+	if (xar->toc.a_sum.len) {
+		if (xar->wbuff_remaining < xar->toc.a_sum.len) {
+			r = flush_wbuff(a);
+			if (r != ARCHIVE_OK)
+				return (r);
+		}
+		wb = xar->wbuff + (sizeof(xar->wbuff) - xar->wbuff_remaining);
+		memcpy(wb, xar->toc.a_sum.val, xar->toc.a_sum.len);
+		xar->wbuff_remaining -= xar->toc.a_sum.len;
+	}
+
+	/*
+	 * Write all file extended attributes and contents.
+	 */
+	r = copy_out(a, xar->toc.a_sum.len, length);
+	if (r != ARCHIVE_OK)
+		return (r);
+	r = flush_wbuff(a);
+	return (r);
+}
+
+static int
+xar_free(struct archive_write *a)
+{
+	struct xar *xar;
+
+	xar = (struct xar *)a->format_data;
+
+	/* Close the temporary file. */
+	if (xar->temp_fd >= 0)
+		close(xar->temp_fd);
+
+	archive_string_free(&(xar->cur_dirstr));
+	archive_string_free(&(xar->tstr));
+	archive_string_free(&(xar->vstr));
+	file_free_hardlinks(xar);
+	file_free_register(xar);
+	compression_end(&(a->archive), &(xar->stream));
+	free(xar);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+file_cmp_node(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	const struct file *f1 = (const struct file *)n1;
+	const struct file *f2 = (const struct file *)n2;
+
+	return (strcmp(f1->basename.s, f2->basename.s));
+}
+
+static int
+file_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+	const struct file *f = (const struct file *)n;
+
+	return (strcmp(f->basename.s, (const char *)key));
+}
+
+static struct file *
+file_new(struct archive_write *a, struct archive_entry *entry)
+{
+	struct file *file;
+	static const struct archive_rb_tree_ops rb_ops = {
+		file_cmp_node, file_cmp_key
+	};
+
+	file = calloc(1, sizeof(*file));
+	if (file == NULL)
+		return (NULL);
+
+	if (entry != NULL)
+		file->entry = archive_entry_clone(entry);
+	else
+		file->entry = archive_entry_new2(&a->archive);
+	if (file->entry == NULL) {
+		free(file);
+		return (NULL);
+	}
+	__archive_rb_tree_init(&(file->rbtree), &rb_ops);
+	file->children.first = NULL;
+	file->children.last = &(file->children.first);
+	file->xattr.first = NULL;
+	file->xattr.last = &(file->xattr.first);
+	archive_string_init(&(file->parentdir));
+	archive_string_init(&(file->basename));
+	archive_string_init(&(file->symlink));
+	archive_string_init(&(file->script));
+	if (entry != NULL && archive_entry_filetype(entry) == AE_IFDIR)
+		file->dir = 1;
+
+	return (file);
+}
+
+static void
+file_free(struct file *file)
+{
+	struct heap_data *heap, *next_heap;
+
+	heap = file->xattr.first;
+	while (heap != NULL) {
+		next_heap = heap->next;
+		free(heap);
+		heap = next_heap;
+	}
+	archive_string_free(&(file->parentdir));
+	archive_string_free(&(file->basename));
+	archive_string_free(&(file->symlink));
+	archive_string_free(&(file->script));
+	archive_entry_free(file->entry);
+	free(file);
+}
+
+static struct file *
+file_create_virtual_dir(struct archive_write *a, struct xar *xar,
+    const char *pathname)
+{
+	struct file *file;
+
+	(void)xar; /* UNUSED */
+
+	file = file_new(a, NULL);
+	if (file == NULL)
+		return (NULL);
+	archive_entry_set_pathname(file->entry, pathname);
+	archive_entry_set_mode(file->entry, 0555 | AE_IFDIR);
+
+	file->dir = 1;
+	file->virtual = 1;
+
+	return (file);
+}
+
+static int
+file_add_child_tail(struct file *parent, struct file *child)
+{
+	if (!__archive_rb_tree_insert_node(
+	    &(parent->rbtree), (struct archive_rb_node *)child))
+		return (0);
+	child->chnext = NULL;
+	*parent->children.last = child;
+	parent->children.last = &(child->chnext);
+	child->parent = parent;
+	return (1);
+}
+
+/*
+ * Find a entry from `parent'
+ */
+static struct file *
+file_find_child(struct file *parent, const char *child_name)
+{
+	struct file *np;
+
+	np = (struct file *)__archive_rb_tree_find_node(
+	    &(parent->rbtree), child_name);
+	return (np);
+}
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+static void
+cleanup_backslash(char *utf8, size_t len)
+{
+
+	/* Convert a path-separator from '\' to  '/' */
+	while (*utf8 != '\0' && len) {
+		if (*utf8 == '\\')
+			*utf8 = '/';
+		++utf8;
+		--len;
+	}
+}
+#else
+#define cleanup_backslash(p, len)	/* nop */
+#endif
+
+/*
+ * Generate a parent directory name and a base name from a pathname.
+ */
+static int
+file_gen_utility_names(struct archive_write *a, struct file *file)
+{
+	struct xar *xar;
+	const char *pp;
+	char *p, *dirname, *slash;
+	size_t len;
+	int r = ARCHIVE_OK;
+
+	xar = (struct xar *)a->format_data;
+	archive_string_empty(&(file->parentdir));
+	archive_string_empty(&(file->basename));
+	archive_string_empty(&(file->symlink));
+
+	if (file->parent == file)/* virtual root */
+		return (ARCHIVE_OK);
+
+	if (archive_entry_pathname_l(file->entry, &pp, &len, xar->sconv)
+	    != 0) {
+		if (errno == ENOMEM) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for Pathname");
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Can't translate pathname '%s' to UTF-8",
+		    archive_entry_pathname(file->entry));
+		r = ARCHIVE_WARN;
+	}
+	archive_strncpy(&(file->parentdir), pp, len);
+	len = file->parentdir.length;
+	p = dirname = file->parentdir.s;
+	/*
+	 * Convert a path-separator from '\' to  '/'
+	 */
+	cleanup_backslash(p, len);
+
+	/*
+	 * Remove leading '/', '../' and './' elements
+	 */
+	while (*p) {
+		if (p[0] == '/') {
+			p++;
+			len--;
+		} else if (p[0] != '.')
+			break;
+		else if (p[1] == '.' && p[2] == '/') {
+			p += 3;
+			len -= 3;
+		} else if (p[1] == '/' || (p[1] == '.' && p[2] == '\0')) {
+			p += 2;
+			len -= 2;
+		} else if (p[1] == '\0') {
+			p++;
+			len--;
+		} else
+			break;
+	}
+	if (p != dirname) {
+		memmove(dirname, p, len+1);
+		p = dirname;
+	}
+	/*
+	 * Remove "/","/." and "/.." elements from tail.
+	 */
+	while (len > 0) {
+		size_t ll = len;
+
+		if (p[len-1] == '/') {
+			p[len-1] = '\0';
+			len--;
+		}
+		if (len > 1 && p[len-2] == '/' && p[len-1] == '.') {
+			p[len-2] = '\0';
+			len -= 2;
+		}
+		if (len > 2 && p[len-3] == '/' && p[len-2] == '.' &&
+		    p[len-1] == '.') {
+			p[len-3] = '\0';
+			len -= 3;
+		}
+		if (ll == len)
+			break;
+	}
+	while (*p) {
+		if (p[0] == '/') {
+			if (p[1] == '/')
+				/* Convert '//' --> '/' */
+				memmove(p, p+1, strlen(p+1) + 1);
+			else if (p[1] == '.' && p[2] == '/')
+				/* Convert '/./' --> '/' */
+				memmove(p, p+2, strlen(p+2) + 1);
+			else if (p[1] == '.' && p[2] == '.' && p[3] == '/') {
+				/* Convert 'dir/dir1/../dir2/'
+				 *     --> 'dir/dir2/'
+				 */
+				char *rp = p -1;
+				while (rp >= dirname) {
+					if (*rp == '/')
+						break;
+					--rp;
+				}
+				if (rp > dirname) {
+					strcpy(rp, p+3);
+					p = rp;
+				} else {
+					strcpy(dirname, p+4);
+					p = dirname;
+				}
+			} else
+				p++;
+		} else
+			p++;
+	}
+	p = dirname;
+	len = strlen(p);
+
+	if (archive_entry_filetype(file->entry) == AE_IFLNK) {
+		size_t len2;
+		/* Convert symlink name too. */
+		if (archive_entry_symlink_l(file->entry, &pp, &len2,
+		    xar->sconv) != 0) {
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Linkname");
+				return (ARCHIVE_FATAL);
+			}
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Can't translate symlink '%s' to UTF-8",
+			    archive_entry_symlink(file->entry));
+			r = ARCHIVE_WARN;
+		}
+		archive_strncpy(&(file->symlink), pp, len2);
+		cleanup_backslash(file->symlink.s, file->symlink.length);
+	}
+	/*
+	 * - Count up directory elements.
+	 * - Find out the position which points the last position of
+	 *   path separator('/').
+	 */
+	slash = NULL;
+	for (; *p != '\0'; p++)
+		if (*p == '/')
+			slash = p;
+	if (slash == NULL) {
+		/* The pathname doesn't have a parent directory. */
+		file->parentdir.length = len;
+		archive_string_copy(&(file->basename), &(file->parentdir));
+		archive_string_empty(&(file->parentdir));
+		*file->parentdir.s = '\0';
+		return (r);
+	}
+
+	/* Make a basename from dirname and slash */
+	*slash  = '\0';
+	file->parentdir.length = slash - dirname;
+	archive_strcpy(&(file->basename),  slash + 1);
+	return (r);
+}
+
+static int
+get_path_component(char *name, int n, const char *fn)
+{
+	char *p;
+	int l;
+
+	p = strchr(fn, '/');
+	if (p == NULL) {
+		if ((l = strlen(fn)) == 0)
+			return (0);
+	} else
+		l = p - fn;
+	if (l > n -1)
+		return (-1);
+	memcpy(name, fn, l);
+	name[l] = '\0';
+
+	return (l);
+}
+
+/*
+ * Add a new entry into the tree.
+ */
+static int
+file_tree(struct archive_write *a, struct file **filepp)
+{
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	char name[_MAX_FNAME];/* Included null terminator size. */
+#elif defined(NAME_MAX) && NAME_MAX >= 255
+	char name[NAME_MAX+1];
+#else
+	char name[256];
+#endif
+	struct xar *xar = (struct xar *)a->format_data;
+	struct file *dent, *file, *np;
+	struct archive_entry *ent;
+	const char *fn, *p;
+	int l;
+
+	file = *filepp;
+	dent = xar->root;
+	if (file->parentdir.length > 0)
+		fn = p = file->parentdir.s;
+	else
+		fn = p = "";
+
+	/*
+	 * If the path of the parent directory of `file' entry is
+	 * the same as the path of `cur_dirent', add isoent to
+	 * `cur_dirent'.
+	 */
+	if (archive_strlen(&(xar->cur_dirstr))
+	      == archive_strlen(&(file->parentdir)) &&
+	    strcmp(xar->cur_dirstr.s, fn) == 0) {
+		if (!file_add_child_tail(xar->cur_dirent, file)) {
+			np = (struct file *)__archive_rb_tree_find_node(
+			    &(xar->cur_dirent->rbtree),
+			    file->basename.s);
+			goto same_entry;
+		}
+		return (ARCHIVE_OK);
+	}
+
+	for (;;) {
+		l = get_path_component(name, sizeof(name), fn);
+		if (l == 0) {
+			np = NULL;
+			break;
+		}
+		if (l < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "A name buffer is too small");
+			file_free(file);
+			*filepp = NULL;
+			return (ARCHIVE_FATAL);
+		}
+
+		np = file_find_child(dent, name);
+		if (np == NULL || fn[0] == '\0')
+			break;
+
+		/* Find next subdirectory. */
+		if (!np->dir) {
+			/* NOT Directory! */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "`%s' is not directory, we cannot insert `%s' ",
+			    archive_entry_pathname(np->entry),
+			    archive_entry_pathname(file->entry));
+			file_free(file);
+			*filepp = NULL;
+			return (ARCHIVE_FAILED);
+		}
+		fn += l;
+		if (fn[0] == '/')
+			fn++;
+		dent = np;
+	}
+	if (np == NULL) {
+		/*
+		 * Create virtual parent directories.
+		 */
+		while (fn[0] != '\0') {
+			struct file *vp;
+			struct archive_string as;
+
+			archive_string_init(&as);
+			archive_strncat(&as, p, fn - p + l);
+			if (as.s[as.length-1] == '/') {
+				as.s[as.length-1] = '\0';
+				as.length--;
+			}
+			vp = file_create_virtual_dir(a, xar, as.s);
+			if (vp == NULL) {
+				archive_string_free(&as);
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory");
+				file_free(file);
+				*filepp = NULL;
+				return (ARCHIVE_FATAL);
+			}
+			archive_string_free(&as);
+			if (file_gen_utility_names(a, vp) <= ARCHIVE_FAILED)
+				return (ARCHIVE_FATAL);
+			file_add_child_tail(dent, vp);
+			file_register(xar, vp);
+			np = vp;
+
+			fn += l;
+			if (fn[0] == '/')
+				fn++;
+			l = get_path_component(name, sizeof(name), fn);
+			if (l < 0) {
+				archive_string_free(&as);
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "A name buffer is too small");
+				file_free(file);
+				*filepp = NULL;
+				return (ARCHIVE_FATAL);
+			}
+			dent = np;
+		}
+
+		/* Found out the parent directory where isoent can be
+		 * inserted. */
+		xar->cur_dirent = dent;
+		archive_string_empty(&(xar->cur_dirstr));
+		archive_string_ensure(&(xar->cur_dirstr),
+		    archive_strlen(&(dent->parentdir)) +
+		    archive_strlen(&(dent->basename)) + 2);
+		if (archive_strlen(&(dent->parentdir)) +
+		    archive_strlen(&(dent->basename)) == 0)
+			xar->cur_dirstr.s[0] = 0;
+		else {
+			if (archive_strlen(&(dent->parentdir)) > 0) {
+				archive_string_copy(&(xar->cur_dirstr),
+				    &(dent->parentdir));
+				archive_strappend_char(&(xar->cur_dirstr), '/');
+			}
+			archive_string_concat(&(xar->cur_dirstr),
+			    &(dent->basename));
+		}
+
+		if (!file_add_child_tail(dent, file)) {
+			np = (struct file *)__archive_rb_tree_find_node(
+			    &(dent->rbtree), file->basename.s);
+			goto same_entry;
+		}
+		return (ARCHIVE_OK);
+	}
+
+same_entry:
+	/*
+	 * We have already has the entry the filename of which is
+	 * the same.
+	 */
+	if (archive_entry_filetype(np->entry) !=
+	    archive_entry_filetype(file->entry)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Found duplicate entries `%s' and its file type is "
+		    "different",
+		    archive_entry_pathname(np->entry));
+		file_free(file);
+		*filepp = NULL;
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Swap files. */
+	ent = np->entry;
+	np->entry = file->entry;
+	file->entry = ent;
+	np->virtual = 0;
+
+	file_free(file);
+	*filepp = np;
+	return (ARCHIVE_OK);
+}
+
+static void
+file_register(struct xar *xar, struct file *file)
+{
+	file->id = xar->file_idx++;
+        file->next = NULL;
+        *xar->file_list.last = file;
+        xar->file_list.last = &(file->next);
+}
+
+static void
+file_init_register(struct xar *xar)
+{
+	xar->file_list.first = NULL;
+	xar->file_list.last = &(xar->file_list.first);
+}
+
+static void
+file_free_register(struct xar *xar)
+{
+	struct file *file, *file_next;
+
+	file = xar->file_list.first;
+	while (file != NULL) {
+		file_next = file->next;
+		file_free(file);
+		file = file_next;
+	}
+}
+
+/*
+ * Register entry to get a hardlink target.
+ */
+static int
+file_register_hardlink(struct archive_write *a, struct file *file)
+{
+	struct xar *xar = (struct xar *)a->format_data;
+	struct hardlink *hl;
+	const char *pathname;
+
+	archive_entry_set_nlink(file->entry, 1);
+	pathname = archive_entry_hardlink(file->entry);
+	if (pathname == NULL) {
+		/* This `file` is a hardlink target. */
+		hl = malloc(sizeof(*hl));
+		if (hl == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+			return (ARCHIVE_FATAL);
+		}
+		hl->nlink = 1;
+		/* A hardlink target must be the first position. */
+		file->hlnext = NULL;
+		hl->file_list.first = file;
+		hl->file_list.last = &(file->hlnext);
+		__archive_rb_tree_insert_node(&(xar->hardlink_rbtree),
+		    (struct archive_rb_node *)hl);
+	} else {
+		hl = (struct hardlink *)__archive_rb_tree_find_node(
+		    &(xar->hardlink_rbtree), pathname);
+		if (hl != NULL) {
+			/* Insert `file` entry into the tail. */
+			file->hlnext = NULL;
+			*hl->file_list.last = file;
+			hl->file_list.last = &(file->hlnext);
+			hl->nlink++;
+		}
+		archive_entry_unset_size(file->entry);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Hardlinked files have to have the same location of extent.
+ * We have to find out hardlink target entries for entries which
+ * have a hardlink target name.
+ */
+static void
+file_connect_hardlink_files(struct xar *xar)
+{
+	struct archive_rb_node *n;
+	struct hardlink *hl;
+	struct file *target, *nf;
+
+	ARCHIVE_RB_TREE_FOREACH(n, &(xar->hardlink_rbtree)) {
+		hl = (struct hardlink *)n;
+
+		/* The first entry must be a hardlink target. */
+		target = hl->file_list.first;
+		archive_entry_set_nlink(target->entry, hl->nlink);
+		if (hl->nlink > 1)
+			/* It means this file is a hardlink
+			 * target itself. */
+			target->hardlink_target = target;
+		for (nf = target->hlnext;
+		    nf != NULL; nf = nf->hlnext) {
+			nf->hardlink_target = target;
+			archive_entry_set_nlink(nf->entry, hl->nlink);
+		}
+	}
+}
+
+static int
+file_hd_cmp_node(const struct archive_rb_node *n1,
+    const struct archive_rb_node *n2)
+{
+	const struct hardlink *h1 = (const struct hardlink *)n1;
+	const struct hardlink *h2 = (const struct hardlink *)n2;
+
+	return (strcmp(archive_entry_pathname(h1->file_list.first->entry),
+		       archive_entry_pathname(h2->file_list.first->entry)));
+}
+
+static int
+file_hd_cmp_key(const struct archive_rb_node *n, const void *key)
+{
+	const struct hardlink *h = (const struct hardlink *)n;
+
+	return (strcmp(archive_entry_pathname(h->file_list.first->entry),
+		       (const char *)key));
+}
+
+
+static void
+file_init_hardlinks(struct xar *xar)
+{
+	static const struct archive_rb_tree_ops rb_ops = {
+		file_hd_cmp_node, file_hd_cmp_key,
+	};
+
+	__archive_rb_tree_init(&(xar->hardlink_rbtree), &rb_ops);
+}
+
+static void
+file_free_hardlinks(struct xar *xar)
+{
+	struct archive_rb_node *n, *tmp;
+
+	ARCHIVE_RB_TREE_FOREACH_SAFE(n, &(xar->hardlink_rbtree), tmp) {
+		__archive_rb_tree_remove_node(&(xar->hardlink_rbtree), n);
+		free(n);
+	}
+}
+
+static void
+checksum_init(struct chksumwork *sumwrk, enum sumalg sum_alg)
+{
+	sumwrk->alg = sum_alg;
+	switch (sum_alg) {
+	case CKSUM_NONE:
+		break;
+	case CKSUM_SHA1:
+		archive_sha1_init(&(sumwrk->sha1ctx));
+		break;
+	case CKSUM_MD5:
+		archive_md5_init(&(sumwrk->md5ctx));
+		break;
+	}
+}
+
+static void
+checksum_update(struct chksumwork *sumwrk, const void *buff, size_t size)
+{
+
+	switch (sumwrk->alg) {
+	case CKSUM_NONE:
+		break;
+	case CKSUM_SHA1:
+		archive_sha1_update(&(sumwrk->sha1ctx), buff, size);
+		break;
+	case CKSUM_MD5:
+		archive_md5_update(&(sumwrk->md5ctx), buff, size);
+		break;
+	}
+}
+
+static void
+checksum_final(struct chksumwork *sumwrk, struct chksumval *sumval)
+{
+
+	switch (sumwrk->alg) {
+	case CKSUM_NONE:
+		sumval->len = 0;
+		break;
+	case CKSUM_SHA1:
+		archive_sha1_final(&(sumwrk->sha1ctx), sumval->val);
+		sumval->len = SHA1_SIZE;
+		break;
+	case CKSUM_MD5:
+		archive_md5_final(&(sumwrk->md5ctx), sumval->val);
+		sumval->len = MD5_SIZE;
+		break;
+	}
+	sumval->alg = sumwrk->alg;
+}
+
+#if !defined(HAVE_BZLIB_H) || !defined(BZ_CONFIG_ERROR) || !defined(HAVE_LZMA_H)
+static int
+compression_unsupported_encoder(struct archive *a,
+    struct la_zstream *lastrm, const char *name)
+{
+
+	archive_set_error(a, ARCHIVE_ERRNO_MISC,
+	    "%s compression not supported on this platform", name);
+	lastrm->valid = 0;
+	lastrm->real_stream = NULL;
+	return (ARCHIVE_FAILED);
+}
+#endif
+
+static int
+compression_init_encoder_gzip(struct archive *a,
+    struct la_zstream *lastrm, int level, int withheader)
+{
+	z_stream *strm;
+
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	strm = calloc(1, sizeof(*strm));
+	if (strm == NULL) {
+		archive_set_error(a, ENOMEM,
+		    "Can't allocate memory for gzip stream");
+		return (ARCHIVE_FATAL);
+	}
+	/* zlib.h is not const-correct, so we need this one bit
+	 * of ugly hackery to convert a const * pointer to
+	 * a non-const pointer. */
+	strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+	strm->avail_in = lastrm->avail_in;
+	strm->total_in = (uLong)lastrm->total_in;
+	strm->next_out = lastrm->next_out;
+	strm->avail_out = lastrm->avail_out;
+	strm->total_out = (uLong)lastrm->total_out;
+	if (deflateInit2(strm, level, Z_DEFLATED,
+	    (withheader)?15:-15,
+	    8, Z_DEFAULT_STRATEGY) != Z_OK) {
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library");
+		return (ARCHIVE_FATAL);
+	}
+	lastrm->real_stream = strm;
+	lastrm->valid = 1;
+	lastrm->code = compression_code_gzip;
+	lastrm->end = compression_end_gzip;
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_code_gzip(struct archive *a,
+    struct la_zstream *lastrm, enum la_zaction action)
+{
+	z_stream *strm;
+	int r;
+
+	strm = (z_stream *)lastrm->real_stream;
+	/* zlib.h is not const-correct, so we need this one bit
+	 * of ugly hackery to convert a const * pointer to
+	 * a non-const pointer. */
+	strm->next_in = (Bytef *)(uintptr_t)(const void *)lastrm->next_in;
+	strm->avail_in = lastrm->avail_in;
+	strm->total_in = (uLong)lastrm->total_in;
+	strm->next_out = lastrm->next_out;
+	strm->avail_out = lastrm->avail_out;
+	strm->total_out = (uLong)lastrm->total_out;
+	r = deflate(strm,
+	    (action == ARCHIVE_Z_FINISH)? Z_FINISH: Z_NO_FLUSH);
+	lastrm->next_in = strm->next_in;
+	lastrm->avail_in = strm->avail_in;
+	lastrm->total_in = strm->total_in;
+	lastrm->next_out = strm->next_out;
+	lastrm->avail_out = strm->avail_out;
+	lastrm->total_out = strm->total_out;
+	switch (r) {
+	case Z_OK:
+		return (ARCHIVE_OK);
+	case Z_STREAM_END:
+		return (ARCHIVE_EOF);
+	default:
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "GZip compression failed:"
+		    " deflate() call returned status %d", r);
+		return (ARCHIVE_FATAL);
+	}
+}
+
+static int
+compression_end_gzip(struct archive *a, struct la_zstream *lastrm)
+{
+	z_stream *strm;
+	int r;
+
+	strm = (z_stream *)lastrm->real_stream;
+	r = deflateEnd(strm);
+	free(strm);
+	lastrm->real_stream = NULL;
+	lastrm->valid = 0;
+	if (r != Z_OK) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Failed to clean up compressor");
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+#if defined(HAVE_BZLIB_H) && defined(BZ_CONFIG_ERROR)
+static int
+compression_init_encoder_bzip2(struct archive *a,
+    struct la_zstream *lastrm, int level)
+{
+	bz_stream *strm;
+
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	strm = calloc(1, sizeof(*strm));
+	if (strm == NULL) {
+		archive_set_error(a, ENOMEM,
+		    "Can't allocate memory for bzip2 stream");
+		return (ARCHIVE_FATAL);
+	}
+	/* bzlib.h is not const-correct, so we need this one bit
+	 * of ugly hackery to convert a const * pointer to
+	 * a non-const pointer. */
+	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+	strm->avail_in = lastrm->avail_in;
+	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+	strm->next_out = (char *)lastrm->next_out;
+	strm->avail_out = lastrm->avail_out;
+	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+	if (BZ2_bzCompressInit(strm, level, 0, 30) != BZ_OK) {
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library");
+		return (ARCHIVE_FATAL);
+	}
+	lastrm->real_stream = strm;
+	lastrm->valid = 1;
+	lastrm->code = compression_code_bzip2;
+	lastrm->end = compression_end_bzip2;
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_code_bzip2(struct archive *a,
+    struct la_zstream *lastrm, enum la_zaction action)
+{
+	bz_stream *strm;
+	int r;
+
+	strm = (bz_stream *)lastrm->real_stream;
+	/* bzlib.h is not const-correct, so we need this one bit
+	 * of ugly hackery to convert a const * pointer to
+	 * a non-const pointer. */
+	strm->next_in = (char *)(uintptr_t)(const void *)lastrm->next_in;
+	strm->avail_in = lastrm->avail_in;
+	strm->total_in_lo32 = (uint32_t)(lastrm->total_in & 0xffffffff);
+	strm->total_in_hi32 = (uint32_t)(lastrm->total_in >> 32);
+	strm->next_out = (char *)lastrm->next_out;
+	strm->avail_out = lastrm->avail_out;
+	strm->total_out_lo32 = (uint32_t)(lastrm->total_out & 0xffffffff);
+	strm->total_out_hi32 = (uint32_t)(lastrm->total_out >> 32);
+	r = BZ2_bzCompress(strm,
+	    (action == ARCHIVE_Z_FINISH)? BZ_FINISH: BZ_RUN);
+	lastrm->next_in = (const unsigned char *)strm->next_in;
+	lastrm->avail_in = strm->avail_in;
+	lastrm->total_in =
+	    (((uint64_t)(uint32_t)strm->total_in_hi32) << 32)
+	    + (uint64_t)(uint32_t)strm->total_in_lo32;
+	lastrm->next_out = (unsigned char *)strm->next_out;
+	lastrm->avail_out = strm->avail_out;
+	lastrm->total_out =
+	    (((uint64_t)(uint32_t)strm->total_out_hi32) << 32)
+	    + (uint64_t)(uint32_t)strm->total_out_lo32;
+	switch (r) {
+	case BZ_RUN_OK:     /* Non-finishing */
+	case BZ_FINISH_OK:  /* Finishing: There's more work to do */
+		return (ARCHIVE_OK);
+	case BZ_STREAM_END: /* Finishing: all done */
+		/* Only occurs in finishing case */
+		return (ARCHIVE_EOF);
+	default:
+		/* Any other return value indicates an error */
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Bzip2 compression failed:"
+		    " BZ2_bzCompress() call returned status %d", r);
+		return (ARCHIVE_FATAL);
+	}
+}
+
+static int
+compression_end_bzip2(struct archive *a, struct la_zstream *lastrm)
+{
+	bz_stream *strm;
+	int r;
+
+	strm = (bz_stream *)lastrm->real_stream;
+	r = BZ2_bzCompressEnd(strm);
+	free(strm);
+	lastrm->real_stream = NULL;
+	lastrm->valid = 0;
+	if (r != BZ_OK) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Failed to clean up compressor");
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+#else
+static int
+compression_init_encoder_bzip2(struct archive *a,
+    struct la_zstream *lastrm, int level)
+{
+
+	(void) level; /* UNUSED */
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	return (compression_unsupported_encoder(a, lastrm, "bzip2"));
+}
+#endif
+
+#if defined(HAVE_LZMA_H)
+static int
+compression_init_encoder_lzma(struct archive *a,
+    struct la_zstream *lastrm, int level)
+{
+	static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
+	lzma_stream *strm;
+	lzma_options_lzma lzma_opt;
+	int r;
+
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	if (lzma_lzma_preset(&lzma_opt, level)) {
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ENOMEM,
+		    "Internal error initializing compression library");
+		return (ARCHIVE_FATAL);
+	}
+	strm = calloc(1, sizeof(*strm));
+	if (strm == NULL) {
+		archive_set_error(a, ENOMEM,
+		    "Can't allocate memory for lzma stream");
+		return (ARCHIVE_FATAL);
+	}
+	*strm = lzma_init_data;
+	r = lzma_alone_encoder(strm, &lzma_opt);
+	switch (r) {
+	case LZMA_OK:
+		lastrm->real_stream = strm;
+		lastrm->valid = 1;
+		lastrm->code = compression_code_lzma;
+		lastrm->end = compression_end_lzma;
+		r = ARCHIVE_OK;
+		break;
+	case LZMA_MEM_ERROR:
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ENOMEM,
+		    "Internal error initializing compression library: "
+		    "Cannot allocate memory");
+		r =  ARCHIVE_FATAL;
+		break;
+        default:
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library: "
+		    "It's a bug in liblzma");
+		r =  ARCHIVE_FATAL;
+		break;
+	}
+	return (r);
+}
+
+static int
+compression_init_encoder_xz(struct archive *a,
+    struct la_zstream *lastrm, int level, int threads)
+{
+	static const lzma_stream lzma_init_data = LZMA_STREAM_INIT;
+	lzma_stream *strm;
+	lzma_filter *lzmafilters;
+	lzma_options_lzma lzma_opt;
+	int r;
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+	lzma_mt mt_options;
+#endif
+
+	(void)threads; /* UNUSED (if multi-threaded LZMA library not avail) */
+
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	strm = calloc(1, sizeof(*strm) + sizeof(*lzmafilters) * 2);
+	if (strm == NULL) {
+		archive_set_error(a, ENOMEM,
+		    "Can't allocate memory for xz stream");
+		return (ARCHIVE_FATAL);
+	}
+	lzmafilters = (lzma_filter *)(strm+1);
+	if (level > 6)
+		level = 6;
+	if (lzma_lzma_preset(&lzma_opt, level)) {
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ENOMEM,
+		    "Internal error initializing compression library");
+		return (ARCHIVE_FATAL);
+	}
+	lzmafilters[0].id = LZMA_FILTER_LZMA2;
+	lzmafilters[0].options = &lzma_opt;
+	lzmafilters[1].id = LZMA_VLI_UNKNOWN;/* Terminate */
+
+	*strm = lzma_init_data;
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+	if (threads > 1) {
+		memset(&mt_options, 0, sizeof(mt_options));
+		mt_options.threads = threads;
+		mt_options.timeout = 300;
+		mt_options.filters = lzmafilters;
+		mt_options.check = LZMA_CHECK_CRC64;
+		r = lzma_stream_encoder_mt(strm, &mt_options);
+	} else
+#endif
+		r = lzma_stream_encoder(strm, lzmafilters, LZMA_CHECK_CRC64);
+	switch (r) {
+	case LZMA_OK:
+		lastrm->real_stream = strm;
+		lastrm->valid = 1;
+		lastrm->code = compression_code_lzma;
+		lastrm->end = compression_end_lzma;
+		r = ARCHIVE_OK;
+		break;
+	case LZMA_MEM_ERROR:
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ENOMEM,
+		    "Internal error initializing compression library: "
+		    "Cannot allocate memory");
+		r =  ARCHIVE_FATAL;
+		break;
+        default:
+		free(strm);
+		lastrm->real_stream = NULL;
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Internal error initializing compression library: "
+		    "It's a bug in liblzma");
+		r =  ARCHIVE_FATAL;
+		break;
+	}
+	return (r);
+}
+
+static int
+compression_code_lzma(struct archive *a,
+    struct la_zstream *lastrm, enum la_zaction action)
+{
+	lzma_stream *strm;
+	int r;
+
+	strm = (lzma_stream *)lastrm->real_stream;
+	strm->next_in = lastrm->next_in;
+	strm->avail_in = lastrm->avail_in;
+	strm->total_in = lastrm->total_in;
+	strm->next_out = lastrm->next_out;
+	strm->avail_out = lastrm->avail_out;
+	strm->total_out = lastrm->total_out;
+	r = lzma_code(strm,
+	    (action == ARCHIVE_Z_FINISH)? LZMA_FINISH: LZMA_RUN);
+	lastrm->next_in = strm->next_in;
+	lastrm->avail_in = strm->avail_in;
+	lastrm->total_in = strm->total_in;
+	lastrm->next_out = strm->next_out;
+	lastrm->avail_out = strm->avail_out;
+	lastrm->total_out = strm->total_out;
+	switch (r) {
+	case LZMA_OK:
+		/* Non-finishing case */
+		return (ARCHIVE_OK);
+	case LZMA_STREAM_END:
+		/* This return can only occur in finishing case. */
+		return (ARCHIVE_EOF);
+	case LZMA_MEMLIMIT_ERROR:
+		archive_set_error(a, ENOMEM,
+		    "lzma compression error:"
+		    " %ju MiB would have been needed",
+		    (uintmax_t)((lzma_memusage(strm) + 1024 * 1024 -1)
+			/ (1024 * 1024)));
+		return (ARCHIVE_FATAL);
+	default:
+		/* Any other return value indicates an error */
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "lzma compression failed:"
+		    " lzma_code() call returned status %d", r);
+		return (ARCHIVE_FATAL);
+	}
+}
+
+static int
+compression_end_lzma(struct archive *a, struct la_zstream *lastrm)
+{
+	lzma_stream *strm;
+
+	(void)a; /* UNUSED */
+	strm = (lzma_stream *)lastrm->real_stream;
+	lzma_end(strm);
+	free(strm);
+	lastrm->valid = 0;
+	lastrm->real_stream = NULL;
+	return (ARCHIVE_OK);
+}
+#else
+static int
+compression_init_encoder_lzma(struct archive *a,
+    struct la_zstream *lastrm, int level)
+{
+
+	(void) level; /* UNUSED */
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	return (compression_unsupported_encoder(a, lastrm, "lzma"));
+}
+static int
+compression_init_encoder_xz(struct archive *a,
+    struct la_zstream *lastrm, int level, int threads)
+{
+
+	(void) level; /* UNUSED */
+	(void) threads; /* UNUSED */
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+	return (compression_unsupported_encoder(a, lastrm, "xz"));
+}
+#endif
+
+static int
+xar_compression_init_encoder(struct archive_write *a)
+{
+	struct xar *xar;
+	int r;
+
+	xar = (struct xar *)a->format_data;
+	switch (xar->opt_compression) {
+	case GZIP:
+		r = compression_init_encoder_gzip(
+		    &(a->archive), &(xar->stream),
+		    xar->opt_compression_level, 1);
+		break;
+	case BZIP2:
+		r = compression_init_encoder_bzip2(
+		    &(a->archive), &(xar->stream),
+		    xar->opt_compression_level);
+		break;
+	case LZMA:
+		r = compression_init_encoder_lzma(
+		    &(a->archive), &(xar->stream),
+		    xar->opt_compression_level);
+		break;
+	case XZ:
+		r = compression_init_encoder_xz(
+		    &(a->archive), &(xar->stream),
+		    xar->opt_compression_level, xar->opt_threads);
+		break;
+	default:
+		r = ARCHIVE_OK;
+		break;
+	}
+	if (r == ARCHIVE_OK) {
+		xar->stream.total_in = 0;
+		xar->stream.next_out = xar->wbuff;
+		xar->stream.avail_out = sizeof(xar->wbuff);
+		xar->stream.total_out = 0;
+	}
+
+	return (r);
+}
+
+static int
+compression_code(struct archive *a, struct la_zstream *lastrm,
+    enum la_zaction action)
+{
+	if (lastrm->valid)
+		return (lastrm->code(a, lastrm, action));
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_end(struct archive *a, struct la_zstream *lastrm)
+{
+	if (lastrm->valid)
+		return (lastrm->end(a, lastrm));
+	return (ARCHIVE_OK);
+}
+
+
+static int
+save_xattrs(struct archive_write *a, struct file *file)
+{
+	struct xar *xar;
+	const char *name;
+	const void *value;
+	struct heap_data *heap;
+	size_t size;
+	int count, r;
+
+	xar = (struct xar *)a->format_data;
+	count = archive_entry_xattr_reset(file->entry);
+	if (count == 0)
+		return (ARCHIVE_OK);
+	while (count--) {
+		archive_entry_xattr_next(file->entry,
+		    &name, &value, &size);
+		checksum_init(&(xar->a_sumwrk), xar->opt_sumalg);
+		checksum_init(&(xar->e_sumwrk), xar->opt_sumalg);
+
+		heap = calloc(1, sizeof(*heap));
+		if (heap == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for xattr");
+			return (ARCHIVE_FATAL);
+		}
+		heap->id = file->ea_idx++;
+		heap->temp_offset = xar->temp_offset;
+		heap->size = size;/* save a extracted size */
+		heap->compression = xar->opt_compression;
+		/* Get a extracted sumcheck value. */
+		checksum_update(&(xar->e_sumwrk), value, size);
+		checksum_final(&(xar->e_sumwrk), &(heap->e_sum));
+
+		/*
+		 * Not compression to xattr is simple way.
+		 */
+		if (heap->compression == NONE) {
+			checksum_update(&(xar->a_sumwrk), value, size);
+			checksum_final(&(xar->a_sumwrk), &(heap->a_sum));
+			if (write_to_temp(a, value, size)
+			    != ARCHIVE_OK) {
+				free(heap);
+				return (ARCHIVE_FATAL);
+			}
+			heap->length = size;
+			/* Add heap to the tail of file->xattr. */
+			heap->next = NULL;
+			*file->xattr.last = heap;
+			file->xattr.last = &(heap->next);
+			/* Next xattr */
+			continue;
+		}
+
+		/*
+		 * Init compression library.
+		 */
+		r = xar_compression_init_encoder(a);
+		if (r != ARCHIVE_OK) {
+			free(heap);
+			return (ARCHIVE_FATAL);
+		}
+
+		xar->stream.next_in = (const unsigned char *)value;
+		xar->stream.avail_in = size;
+		for (;;) {
+			r = compression_code(&(a->archive),
+			    &(xar->stream), ARCHIVE_Z_FINISH);
+			if (r != ARCHIVE_OK && r != ARCHIVE_EOF) {
+				free(heap);
+				return (ARCHIVE_FATAL);
+			}
+			size = sizeof(xar->wbuff) - xar->stream.avail_out;
+			checksum_update(&(xar->a_sumwrk),
+			    xar->wbuff, size);
+			if (write_to_temp(a, xar->wbuff, size)
+			    != ARCHIVE_OK) {
+				free(heap);
+				return (ARCHIVE_FATAL);
+			}
+			if (r == ARCHIVE_OK) {
+				xar->stream.next_out = xar->wbuff;
+				xar->stream.avail_out = sizeof(xar->wbuff);
+			} else {
+				checksum_final(&(xar->a_sumwrk),
+				    &(heap->a_sum));
+				heap->length = xar->stream.total_out;
+				/* Add heap to the tail of file->xattr. */
+				heap->next = NULL;
+				*file->xattr.last = heap;
+				file->xattr.last = &(heap->next);
+				break;
+			}
+		}
+		/* Clean up compression library. */
+		r = compression_end(&(a->archive), &(xar->stream));
+		if (r != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+getalgsize(enum sumalg sumalg)
+{
+	switch (sumalg) {
+	default:
+	case CKSUM_NONE:
+		return (0);
+	case CKSUM_SHA1:
+		return (SHA1_SIZE);
+	case CKSUM_MD5:
+		return (MD5_SIZE);
+	}
+}
+
+static const char *
+getalgname(enum sumalg sumalg)
+{
+	switch (sumalg) {
+	default:
+	case CKSUM_NONE:
+		return (NULL);
+	case CKSUM_SHA1:
+		return (SHA1_NAME);
+	case CKSUM_MD5:
+		return (MD5_NAME);
+	}
+}
+
+#endif /* Support xar format */
diff --git a/c/archive_write_set_format_zip.c b/c/archive_write_set_format_zip.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_zip.c
@@ -0,0 +1,1695 @@
+/*-
+ * Copyright (c) 2008 Anselm Strauss
+ * Copyright (c) 2009 Joerg Sonnenberger
+ * Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA
+ * 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.
+ */
+
+/*
+ * Development supported by Google Summer of Code 2008.
+ */
+
+#include "archive_platform.h"
+__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_zip.c 201168 2009-12-29 06:15:32Z kientzle $");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZLIB_H
+#include <zlib.h>
+#endif
+
+#include "archive.h"
+#include "archive_cryptor_private.h"
+#include "archive_endian.h"
+#include "archive_entry.h"
+#include "archive_entry_locale.h"
+#include "archive_hmac_private.h"
+#include "archive_private.h"
+#include "archive_random_private.h"
+#include "archive_write_private.h"
+#include "archive_write_set_format_private.h"
+
+#ifndef HAVE_ZLIB_H
+#include "archive_crc32.h"
+#endif
+
+#define ZIP_ENTRY_FLAG_ENCRYPTED	(1<<0)
+#define ZIP_ENTRY_FLAG_LENGTH_AT_END	(1<<3)
+#define ZIP_ENTRY_FLAG_UTF8_NAME	(1 << 11)
+
+#define ZIP_4GB_MAX ARCHIVE_LITERAL_LL(0xffffffff)
+#define ZIP_4GB_MAX_UNCOMPRESSED ARCHIVE_LITERAL_LL(0xff000000)
+
+enum compression {
+	COMPRESSION_UNSPECIFIED = -1,
+	COMPRESSION_STORE = 0,
+	COMPRESSION_DEFLATE = 8
+};
+
+#ifdef HAVE_ZLIB_H
+#define COMPRESSION_DEFAULT	COMPRESSION_DEFLATE
+#else
+#define COMPRESSION_DEFAULT	COMPRESSION_STORE
+#endif
+
+enum encryption {
+	ENCRYPTION_NONE	= 0,
+	ENCRYPTION_TRADITIONAL, /* Traditional PKWARE encryption. */
+	ENCRYPTION_WINZIP_AES128, /* WinZIP AES-128 encryption. */
+	ENCRYPTION_WINZIP_AES256, /* WinZIP AES-256 encryption. */
+};
+
+#define TRAD_HEADER_SIZE	12
+/*
+ * See "WinZip - AES Encryption Information"
+ *     http://www.winzip.com/aes_info.htm
+ */
+/* Value used in compression method. */
+#define WINZIP_AES_ENCRYPTION	99
+/* A WinZip AES header size which is stored at the beginning of
+ * file contents. */
+#define WINZIP_AES128_HEADER_SIZE	(8 + 2)
+#define WINZIP_AES256_HEADER_SIZE	(16 + 2)
+/* AES vendor version. */
+#define AES_VENDOR_AE_1 0x0001
+#define AES_VENDOR_AE_2 0x0002
+/* Authentication code size. */
+#define AUTH_CODE_SIZE		10
+/**/
+#define MAX_DERIVED_KEY_BUF_SIZE (AES_MAX_KEY_SIZE * 2 + 2)
+
+struct cd_segment {
+	struct cd_segment *next;
+	size_t buff_size;
+	unsigned char *buff;
+	unsigned char *p;
+};
+
+struct trad_enc_ctx {
+	uint32_t keys[3];
+};
+
+struct zip {
+
+	int64_t entry_offset;
+	int64_t entry_compressed_size;
+	int64_t entry_uncompressed_size;
+	int64_t entry_compressed_written;
+	int64_t entry_uncompressed_written;
+	int64_t entry_uncompressed_limit;
+	struct archive_entry *entry;
+	uint32_t entry_crc32;
+	enum compression entry_compression;
+	enum encryption  entry_encryption;
+	int entry_flags;
+	int entry_uses_zip64;
+	int experiments;
+	struct trad_enc_ctx tctx;
+	char tctx_valid;
+	unsigned char trad_chkdat;
+	unsigned aes_vendor;
+	archive_crypto_ctx cctx;
+	char cctx_valid;
+	archive_hmac_sha1_ctx hctx;
+	char hctx_valid;
+
+	unsigned char *file_header;
+	size_t file_header_extra_offset;
+	unsigned long (*crc32func)(unsigned long crc, const void *buff, size_t len);
+
+	struct cd_segment *central_directory;
+	struct cd_segment *central_directory_last;
+	size_t central_directory_bytes;
+	size_t central_directory_entries;
+
+	int64_t written_bytes; /* Overall position in file. */
+
+	struct archive_string_conv *opt_sconv;
+	struct archive_string_conv *sconv_default;
+	enum compression requested_compression;
+	int deflate_compression_level;
+	int init_default_conversion;
+	enum encryption  encryption_type;
+
+#define ZIP_FLAG_AVOID_ZIP64 1
+#define ZIP_FLAG_FORCE_ZIP64 2
+#define ZIP_FLAG_EXPERIMENT_xl 4
+	int flags;
+
+#ifdef HAVE_ZLIB_H
+	z_stream stream;
+#endif
+	size_t len_buf;
+	unsigned char *buf;
+};
+
+/* Don't call this min or MIN, since those are already defined
+   on lots of platforms (but not all). */
+#define zipmin(a, b) ((a) > (b) ? (b) : (a))
+
+static ssize_t archive_write_zip_data(struct archive_write *,
+		   const void *buff, size_t s);
+static int archive_write_zip_close(struct archive_write *);
+static int archive_write_zip_free(struct archive_write *);
+static int archive_write_zip_finish_entry(struct archive_write *);
+static int archive_write_zip_header(struct archive_write *,
+	      struct archive_entry *);
+static int archive_write_zip_options(struct archive_write *,
+	      const char *, const char *);
+static unsigned int dos_time(const time_t);
+static size_t path_length(struct archive_entry *);
+static int write_path(struct archive_entry *, struct archive_write *);
+static void copy_path(struct archive_entry *, unsigned char *);
+static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *);
+static int trad_enc_init(struct trad_enc_ctx *, const char *, size_t);
+static unsigned trad_enc_encrypt_update(struct trad_enc_ctx *, const uint8_t *,
+    size_t, uint8_t *, size_t);
+static int init_traditional_pkware_encryption(struct archive_write *);
+static int is_traditional_pkware_encryption_supported(void);
+static int init_winzip_aes_encryption(struct archive_write *);
+static int is_winzip_aes_encryption_supported(int encryption);
+
+static unsigned char *
+cd_alloc(struct zip *zip, size_t length)
+{
+	unsigned char *p;
+
+	if (zip->central_directory == NULL
+	    || (zip->central_directory_last->p + length
+		> zip->central_directory_last->buff + zip->central_directory_last->buff_size)) {
+		struct cd_segment *segment = calloc(1, sizeof(*segment));
+		if (segment == NULL)
+			return NULL;
+		segment->buff_size = 64 * 1024;
+		segment->buff = malloc(segment->buff_size);
+		if (segment->buff == NULL) {
+			free(segment);
+			return NULL;
+		}
+		segment->p = segment->buff;
+
+		if (zip->central_directory == NULL) {
+			zip->central_directory
+			    = zip->central_directory_last
+			    = segment;
+		} else {
+			zip->central_directory_last->next = segment;
+			zip->central_directory_last = segment;
+		}
+	}
+
+	p = zip->central_directory_last->p;
+	zip->central_directory_last->p += length;
+	zip->central_directory_bytes += length;
+	return (p);
+}
+
+static unsigned long
+real_crc32(unsigned long crc, const void *buff, size_t len)
+{
+	return crc32(crc, buff, (unsigned int)len);
+}
+
+static unsigned long
+fake_crc32(unsigned long crc, const void *buff, size_t len)
+{
+	(void)crc; /* UNUSED */
+	(void)buff; /* UNUSED */
+	(void)len; /* UNUSED */
+	return 0;
+}
+
+static int
+archive_write_zip_options(struct archive_write *a, const char *key,
+    const char *val)
+{
+	struct zip *zip = a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	if (strcmp(key, "compression") == 0) {
+		/*
+		 * Set compression to use on all future entries.
+		 * This only affects regular files.
+		 */
+		if (val == NULL || val[0] == 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: compression option needs a compression name",
+			    a->format_name);
+		} else if (strcmp(val, "deflate") == 0) {
+#ifdef HAVE_ZLIB_H
+			zip->requested_compression = COMPRESSION_DEFLATE;
+			ret = ARCHIVE_OK;
+#else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "deflate compression not supported");
+#endif
+		} else if (strcmp(val, "store") == 0) {
+			zip->requested_compression = COMPRESSION_STORE;
+			ret = ARCHIVE_OK;
+		}
+		return (ret);
+	} else if (strcmp(key, "compression-level") == 0) {
+		if (val == NULL || !(val[0] >= '0' && val[0] <= '9') || val[1] != '\0') {
+			return ARCHIVE_WARN;
+		}
+
+		if (val[0] == '0') {
+			zip->requested_compression = COMPRESSION_STORE;
+			return ARCHIVE_OK;
+		} else {
+#ifdef HAVE_ZLIB_H
+			zip->requested_compression = COMPRESSION_DEFLATE;
+			zip->deflate_compression_level = val[0] - '0';
+			return ARCHIVE_OK;
+#else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "deflate compression not supported");
+#endif
+		}
+	} else if (strcmp(key, "encryption") == 0) {
+		if (val == NULL) {
+			zip->encryption_type = ENCRYPTION_NONE;
+			ret = ARCHIVE_OK;
+		} else if (val[0] == '1' || strcmp(val, "traditional") == 0
+		    || strcmp(val, "zipcrypt") == 0
+		    || strcmp(val, "ZipCrypt") == 0) {
+			if (is_traditional_pkware_encryption_supported()) {
+				zip->encryption_type = ENCRYPTION_TRADITIONAL;
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "encryption not supported");
+			}
+		} else if (strcmp(val, "aes128") == 0) {
+			if (is_winzip_aes_encryption_supported(
+			    ENCRYPTION_WINZIP_AES128)) {
+				zip->encryption_type = ENCRYPTION_WINZIP_AES128;
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "encryption not supported");
+			}
+		} else if (strcmp(val, "aes256") == 0) {
+			if (is_winzip_aes_encryption_supported(
+			    ENCRYPTION_WINZIP_AES256)) {
+				zip->encryption_type = ENCRYPTION_WINZIP_AES256;
+				ret = ARCHIVE_OK;
+			} else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "encryption not supported");
+			}
+		} else {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: unknown encryption '%s'",
+			    a->format_name, val);
+		}
+		return (ret);
+	} else if (strcmp(key, "experimental") == 0) {
+		if (val == NULL || val[0] == 0) {
+			zip->flags &= ~ ZIP_FLAG_EXPERIMENT_xl;
+		} else {
+			zip->flags |= ZIP_FLAG_EXPERIMENT_xl;
+		}
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "fakecrc32") == 0) {
+		/*
+		 * FOR TESTING ONLY:  disable CRC calculation to speed up
+		 * certain complex tests.
+		 */
+		if (val == NULL || val[0] == 0) {
+			zip->crc32func = real_crc32;
+		} else {
+			zip->crc32func = fake_crc32;
+		}
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "hdrcharset")  == 0) {
+		/*
+		 * Set the character set used in translating filenames.
+		 */
+		if (val == NULL || val[0] == 0) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "%s: hdrcharset option needs a character-set name",
+			    a->format_name);
+		} else {
+			zip->opt_sconv = archive_string_conversion_to_charset(
+			    &a->archive, val, 0);
+			if (zip->opt_sconv != NULL)
+				ret = ARCHIVE_OK;
+			else
+				ret = ARCHIVE_FATAL;
+		}
+		return (ret);
+	} else if (strcmp(key, "zip64") == 0) {
+		/*
+		 * Bias decisions about Zip64: force them to be
+		 * generated in certain cases where they are not
+		 * forbidden or avoid them in certain cases where they
+		 * are not strictly required.
+		 */
+		if (val != NULL && *val != '\0') {
+			zip->flags |= ZIP_FLAG_FORCE_ZIP64;
+			zip->flags &= ~ZIP_FLAG_AVOID_ZIP64;
+		} else {
+			zip->flags &= ~ZIP_FLAG_FORCE_ZIP64;
+			zip->flags |= ZIP_FLAG_AVOID_ZIP64;
+		}
+		return (ARCHIVE_OK);
+	}
+
+	/* Note: The "warn" return is just to inform the options
+	 * supervisor that we didn't handle it.  It will generate
+	 * a suitable error if no one used this option. */
+	return (ARCHIVE_WARN);
+}
+
+int
+archive_write_zip_set_compression_deflate(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	int ret = ARCHIVE_FAILED;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+		ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+		"archive_write_zip_set_compression_deflate");
+	if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		"Can only use archive_write_zip_set_compression_deflate"
+		" with zip format");
+		ret = ARCHIVE_FATAL;
+	} else {
+#ifdef HAVE_ZLIB_H
+		struct zip *zip = a->format_data;
+		zip->requested_compression = COMPRESSION_DEFLATE;
+		ret = ARCHIVE_OK;
+#else
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"deflate compression not supported");
+		ret = ARCHIVE_FAILED;
+#endif
+	}
+	return (ret);
+}
+
+int
+archive_write_zip_set_compression_store(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct zip *zip = a->format_data;
+	int ret = ARCHIVE_FAILED;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+		ARCHIVE_STATE_NEW | ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
+		"archive_write_zip_set_compression_deflate");
+	if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"Can only use archive_write_zip_set_compression_store"
+			" with zip format");
+		ret = ARCHIVE_FATAL;
+	} else {
+		zip->requested_compression = COMPRESSION_STORE;
+		ret = ARCHIVE_OK;
+	}
+	return (ret);
+}
+
+int
+archive_write_set_format_zip(struct archive *_a)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct zip *zip;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_zip");
+
+	/* If another format was already registered, unregister it. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	zip = (struct zip *) calloc(1, sizeof(*zip));
+	if (zip == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate zip data");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* "Unspecified" lets us choose the appropriate compression. */
+	zip->requested_compression = COMPRESSION_UNSPECIFIED;
+#ifdef HAVE_ZLIB_H
+	zip->deflate_compression_level = Z_DEFAULT_COMPRESSION;
+#endif
+	zip->crc32func = real_crc32;
+
+	/* A buffer used for both compression and encryption. */
+	zip->len_buf = 65536;
+	zip->buf = malloc(zip->len_buf);
+	if (zip->buf == NULL) {
+		free(zip);
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate compression buffer");
+		return (ARCHIVE_FATAL);
+	}
+
+	a->format_data = zip;
+	a->format_name = "zip";
+	a->format_options = archive_write_zip_options;
+	a->format_write_header = archive_write_zip_header;
+	a->format_write_data = archive_write_zip_data;
+	a->format_finish_entry = archive_write_zip_finish_entry;
+	a->format_close = archive_write_zip_close;
+	a->format_free = archive_write_zip_free;
+	a->archive.archive_format = ARCHIVE_FORMAT_ZIP;
+	a->archive.archive_format_name = "ZIP";
+
+	return (ARCHIVE_OK);
+}
+
+static int
+is_all_ascii(const char *p)
+{
+	const unsigned char *pp = (const unsigned char *)p;
+
+	while (*pp) {
+		if (*pp++ > 127)
+			return (0);
+	}
+	return (1);
+}
+
+static int
+archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
+{
+	unsigned char local_header[32];
+	unsigned char local_extra[144];
+	struct zip *zip = a->format_data;
+	unsigned char *e;
+	unsigned char *cd_extra;
+	size_t filename_length;
+	const char *slink = NULL;
+	size_t slink_size = 0;
+	struct archive_string_conv *sconv = get_sconv(a, zip);
+	int ret, ret2 = ARCHIVE_OK;
+	mode_t type;
+	int version_needed = 10;
+
+	/* Ignore types of entries that we don't support. */
+	type = archive_entry_filetype(entry);
+	if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) {
+		__archive_write_entry_filetype_unsupported(
+		    &a->archive, entry, "zip");
+		return ARCHIVE_FAILED;
+	};
+
+	/* If we're not using Zip64, reject large files. */
+	if (zip->flags & ZIP_FLAG_AVOID_ZIP64) {
+		/* Reject entries over 4GB. */
+		if (archive_entry_size_is_set(entry)
+		    && (archive_entry_size(entry) > ZIP_4GB_MAX)) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Files > 4GB require Zip64 extensions");
+			return ARCHIVE_FAILED;
+		}
+		/* Reject entries if archive is > 4GB. */
+		if (zip->written_bytes > ZIP_4GB_MAX) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Archives > 4GB require Zip64 extensions");
+			return ARCHIVE_FAILED;
+		}
+	}
+
+	/* Only regular files can have size > 0. */
+	if (type != AE_IFREG)
+		archive_entry_set_size(entry, 0);
+
+
+	/* Reset information from last entry. */
+	zip->entry_offset = zip->written_bytes;
+	zip->entry_uncompressed_limit = INT64_MAX;
+	zip->entry_compressed_size = 0;
+	zip->entry_uncompressed_size = 0;
+	zip->entry_compressed_written = 0;
+	zip->entry_uncompressed_written = 0;
+	zip->entry_flags = 0;
+	zip->entry_uses_zip64 = 0;
+	zip->entry_crc32 = zip->crc32func(0, NULL, 0);
+	zip->entry_encryption = 0;
+	archive_entry_free(zip->entry);
+	zip->entry = NULL;
+
+	if (zip->cctx_valid)
+		archive_encrypto_aes_ctr_release(&zip->cctx);
+	if (zip->hctx_valid)
+		archive_hmac_sha1_cleanup(&zip->hctx);
+	zip->tctx_valid = zip->cctx_valid = zip->hctx_valid = 0;
+
+	if (type == AE_IFREG
+		    &&(!archive_entry_size_is_set(entry)
+			|| archive_entry_size(entry) > 0)) {
+		switch (zip->encryption_type) {
+		case ENCRYPTION_TRADITIONAL:
+		case ENCRYPTION_WINZIP_AES128:
+		case ENCRYPTION_WINZIP_AES256:
+			zip->entry_flags |= ZIP_ENTRY_FLAG_ENCRYPTED;
+			zip->entry_encryption = zip->encryption_type;
+			break;
+		default:
+			break;
+		}
+	}
+
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* Make sure the path separators in pathname, hardlink and symlink
+	 * are all slash '/', not the Windows path separator '\'. */
+	zip->entry = __la_win_entry_in_posix_pathseparator(entry);
+	if (zip->entry == entry)
+		zip->entry = archive_entry_clone(entry);
+#else
+	zip->entry = archive_entry_clone(entry);
+#endif
+	if (zip->entry == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate zip header data");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (sconv != NULL) {
+		const char *p;
+		size_t len;
+
+		if (archive_entry_pathname_l(entry, &p, &len, sconv) != 0) {
+			if (errno == ENOMEM) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate memory for Pathname");
+				return (ARCHIVE_FATAL);
+			}
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Can't translate Pathname '%s' to %s",
+			    archive_entry_pathname(entry),
+			    archive_string_conversion_charset_name(sconv));
+			ret2 = ARCHIVE_WARN;
+		}
+		if (len > 0)
+			archive_entry_set_pathname(zip->entry, p);
+
+		/*
+		 * There is no standard for symlink handling; we convert
+		 * it using the same character-set translation that we use
+		 * for filename.
+		 */
+		if (type == AE_IFLNK) {
+			if (archive_entry_symlink_l(entry, &p, &len, sconv)) {
+				if (errno == ENOMEM) {
+					archive_set_error(&a->archive, ENOMEM,
+					    "Can't allocate memory "
+					    " for Symlink");
+					return (ARCHIVE_FATAL);
+				}
+				/* No error if we can't convert. */
+			} else if (len > 0)
+				archive_entry_set_symlink(zip->entry, p);
+		}
+	}
+
+	/* If filename isn't ASCII and we can use UTF-8, set the UTF-8 flag. */
+	if (!is_all_ascii(archive_entry_pathname(zip->entry))) {
+		if (zip->opt_sconv != NULL) {
+			if (strcmp(archive_string_conversion_charset_name(
+					zip->opt_sconv), "UTF-8") == 0)
+				zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME;
+#if HAVE_NL_LANGINFO
+		} else if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {
+			zip->entry_flags |= ZIP_ENTRY_FLAG_UTF8_NAME;
+#endif
+		}
+	}
+	filename_length = path_length(zip->entry);
+
+	/* Determine appropriate compression and size for this entry. */
+	if (type == AE_IFLNK) {
+		slink = archive_entry_symlink(zip->entry);
+		if (slink != NULL)
+			slink_size = strlen(slink);
+		else
+			slink_size = 0;
+		zip->entry_uncompressed_limit = slink_size;
+		zip->entry_compressed_size = slink_size;
+		zip->entry_uncompressed_size = slink_size;
+		zip->entry_crc32 = zip->crc32func(zip->entry_crc32,
+		    (const unsigned char *)slink, slink_size);
+		zip->entry_compression = COMPRESSION_STORE;
+		version_needed = 20;
+	} else if (type != AE_IFREG) {
+		zip->entry_compression = COMPRESSION_STORE;
+		zip->entry_uncompressed_limit = 0;
+		version_needed = 20;
+	} else if (archive_entry_size_is_set(zip->entry)) {
+		int64_t size = archive_entry_size(zip->entry);
+		int64_t additional_size = 0;
+
+		zip->entry_uncompressed_limit = size;
+		zip->entry_compression = zip->requested_compression;
+		if (zip->entry_compression == COMPRESSION_UNSPECIFIED) {
+			zip->entry_compression = COMPRESSION_DEFAULT;
+		}
+		if (zip->entry_compression == COMPRESSION_STORE) {
+			zip->entry_compressed_size = size;
+			zip->entry_uncompressed_size = size;
+			version_needed = 10;
+		} else {
+			zip->entry_uncompressed_size = size;
+			version_needed = 20;
+		}
+
+		if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+			switch (zip->entry_encryption) {
+			case ENCRYPTION_TRADITIONAL:
+				additional_size = TRAD_HEADER_SIZE;
+				version_needed = 20;
+				break;
+			case ENCRYPTION_WINZIP_AES128:
+				additional_size = WINZIP_AES128_HEADER_SIZE
+				    + AUTH_CODE_SIZE;
+				version_needed = 20;
+				break;
+			case ENCRYPTION_WINZIP_AES256:
+				additional_size = WINZIP_AES256_HEADER_SIZE
+				    + AUTH_CODE_SIZE;
+				version_needed = 20;
+				break;
+			default:
+				break;
+			}
+			if (zip->entry_compression == COMPRESSION_STORE)
+				zip->entry_compressed_size += additional_size;
+		}
+
+		/*
+		 * Set Zip64 extension in any of the following cases
+		 * (this was suggested by discussion on info-zip-dev
+		 * mailing list):
+		 *  = Zip64 is being forced by user
+		 *  = File is over 4GiB uncompressed
+		 *    (including encryption header, if any)
+		 *  = File is close to 4GiB and is being compressed
+		 *    (compression might make file larger)
+		 */
+		if ((zip->flags & ZIP_FLAG_FORCE_ZIP64)
+		    || (zip->entry_uncompressed_size + additional_size > ZIP_4GB_MAX)
+		    || (zip->entry_uncompressed_size > ZIP_4GB_MAX_UNCOMPRESSED
+			&& zip->entry_compression != COMPRESSION_STORE)) {
+			zip->entry_uses_zip64 = 1;
+			version_needed = 45;
+		}
+
+		/* We may know the size, but never the CRC. */
+		zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END;
+	} else {
+		/* We don't know the size.  In this case, we prefer
+		 * deflate (it has a clear end-of-data marker which
+		 * makes length-at-end more reliable) and will
+		 * enable Zip64 extensions unless we're told not to.
+		 */
+		zip->entry_compression = COMPRESSION_DEFAULT;
+		zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END;
+		if ((zip->flags & ZIP_FLAG_AVOID_ZIP64) == 0) {
+			zip->entry_uses_zip64 = 1;
+			version_needed = 45;
+		} else if (zip->entry_compression == COMPRESSION_STORE) {
+			version_needed = 10;
+		} else {
+			version_needed = 20;
+		}
+
+		if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+			switch (zip->entry_encryption) {
+			case ENCRYPTION_TRADITIONAL:
+			case ENCRYPTION_WINZIP_AES128:
+			case ENCRYPTION_WINZIP_AES256:
+				if (version_needed < 20)
+					version_needed = 20;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	/* Format the local header. */
+	memset(local_header, 0, sizeof(local_header));
+	memcpy(local_header, "PK\003\004", 4);
+	archive_le16enc(local_header + 4, version_needed);
+	archive_le16enc(local_header + 6, zip->entry_flags);
+	if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128
+	    || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)
+		archive_le16enc(local_header + 8, WINZIP_AES_ENCRYPTION);
+	else
+		archive_le16enc(local_header + 8, zip->entry_compression);
+	archive_le32enc(local_header + 10,
+		dos_time(archive_entry_mtime(zip->entry)));
+	archive_le32enc(local_header + 14, zip->entry_crc32);
+	if (zip->entry_uses_zip64) {
+		/* Zip64 data in the local header "must" include both
+		 * compressed and uncompressed sizes AND those fields
+		 * are included only if these are 0xffffffff;
+		 * THEREFORE these must be set this way, even if we
+		 * know one of them is smaller. */
+		archive_le32enc(local_header + 18, ZIP_4GB_MAX);
+		archive_le32enc(local_header + 22, ZIP_4GB_MAX);
+	} else {
+		archive_le32enc(local_header + 18, (uint32_t)zip->entry_compressed_size);
+		archive_le32enc(local_header + 22, (uint32_t)zip->entry_uncompressed_size);
+	}
+	archive_le16enc(local_header + 26, (uint16_t)filename_length);
+
+	if (zip->entry_encryption == ENCRYPTION_TRADITIONAL) {
+		if (zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END)
+			zip->trad_chkdat = local_header[11];
+		else
+			zip->trad_chkdat = local_header[17];
+	}
+
+	/* Format as much of central directory file header as we can: */
+	zip->file_header = cd_alloc(zip, 46);
+	/* If (zip->file_header == NULL) XXXX */
+	++zip->central_directory_entries;
+	memset(zip->file_header, 0, 46);
+	memcpy(zip->file_header, "PK\001\002", 4);
+	/* "Made by PKZip 2.0 on Unix." */
+	archive_le16enc(zip->file_header + 4, 3 * 256 + version_needed);
+	archive_le16enc(zip->file_header + 6, version_needed);
+	archive_le16enc(zip->file_header + 8, zip->entry_flags);
+	if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128
+	    || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)
+		archive_le16enc(zip->file_header + 10, WINZIP_AES_ENCRYPTION);
+	else
+		archive_le16enc(zip->file_header + 10, zip->entry_compression);
+	archive_le32enc(zip->file_header + 12,
+		dos_time(archive_entry_mtime(zip->entry)));
+	archive_le16enc(zip->file_header + 28, (uint16_t)filename_length);
+	/* Following Info-Zip, store mode in the "external attributes" field. */
+	archive_le32enc(zip->file_header + 38,
+	    ((uint32_t)archive_entry_mode(zip->entry)) << 16);
+	e = cd_alloc(zip, filename_length);
+	/* If (e == NULL) XXXX */
+	copy_path(zip->entry, e);
+
+	/* Format extra data. */
+	memset(local_extra, 0, sizeof(local_extra));
+	e = local_extra;
+
+	/* First, extra blocks that are the same between
+	 * the local file header and the central directory.
+	 * We format them once and then duplicate them. */
+
+	/* UT timestamp, length depends on what timestamps are set. */
+	memcpy(e, "UT", 2);
+	archive_le16enc(e + 2,
+	    1
+	    + (archive_entry_mtime_is_set(entry) ? 4 : 0)
+	    + (archive_entry_atime_is_set(entry) ? 4 : 0)
+	    + (archive_entry_ctime_is_set(entry) ? 4 : 0));
+	e += 4;
+	*e++ =
+	    (archive_entry_mtime_is_set(entry) ? 1 : 0)
+	    | (archive_entry_atime_is_set(entry) ? 2 : 0)
+	    | (archive_entry_ctime_is_set(entry) ? 4 : 0);
+	if (archive_entry_mtime_is_set(entry)) {
+		archive_le32enc(e, (uint32_t)archive_entry_mtime(entry));
+		e += 4;
+	}
+	if (archive_entry_atime_is_set(entry)) {
+		archive_le32enc(e, (uint32_t)archive_entry_atime(entry));
+		e += 4;
+	}
+	if (archive_entry_ctime_is_set(entry)) {
+		archive_le32enc(e, (uint32_t)archive_entry_ctime(entry));
+		e += 4;
+	}
+
+	/* ux Unix extra data, length 11, version 1 */
+	/* TODO: If uid < 64k, use 2 bytes, ditto for gid. */
+	memcpy(e, "ux\013\000\001", 5);
+	e += 5;
+	*e++ = 4; /* Length of following UID */
+	archive_le32enc(e, (uint32_t)archive_entry_uid(entry));
+	e += 4;
+	*e++ = 4; /* Length of following GID */
+	archive_le32enc(e, (uint32_t)archive_entry_gid(entry));
+	e += 4;
+
+	/* AES extra data field: WinZIP AES information, ID=0x9901 */
+	if ((zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED)
+	    && (zip->entry_encryption == ENCRYPTION_WINZIP_AES128
+	        || zip->entry_encryption == ENCRYPTION_WINZIP_AES256)) {
+
+		memcpy(e, "\001\231\007\000\001\000AE", 8);
+		/* AES vendor version AE-2 does not store a CRC.
+		 * WinZip 11 uses AE-1, which does store the CRC,
+		 * but it does not store the CRC when the file size
+		 * is less than 20 bytes. So we simulate what
+		 * WinZip 11 does.
+		 * NOTE: WinZip 9.0 and 10.0 uses AE-2 by default. */
+		if (archive_entry_size_is_set(zip->entry)
+		    && archive_entry_size(zip->entry) < 20) {
+			archive_le16enc(e+4, AES_VENDOR_AE_2);
+			zip->aes_vendor = AES_VENDOR_AE_2;/* no CRC. */
+		} else
+			zip->aes_vendor = AES_VENDOR_AE_1;
+		e += 8;
+		/* AES encryption strength. */
+		*e++ = (zip->entry_encryption == ENCRYPTION_WINZIP_AES128)?1:3;
+		/* Actual compression method. */
+		archive_le16enc(e, zip->entry_compression);
+		e += 2;
+	}
+
+	/* Copy UT ,ux, and AES-extra into central directory as well. */
+	zip->file_header_extra_offset = zip->central_directory_bytes;
+	cd_extra = cd_alloc(zip, e - local_extra);
+	memcpy(cd_extra, local_extra, e - local_extra);
+
+	/*
+	 * Following extra blocks vary between local header and
+	 * central directory. These are the local header versions.
+	 * Central directory versions get formatted in
+	 * archive_write_zip_finish_entry() below.
+	 */
+
+	/* "[Zip64 entry] in the local header MUST include BOTH
+	 * original [uncompressed] and compressed size fields." */
+	if (zip->entry_uses_zip64) {
+		unsigned char *zip64_start = e;
+		memcpy(e, "\001\000\020\000", 4);
+		e += 4;
+		archive_le64enc(e, zip->entry_uncompressed_size);
+		e += 8;
+		archive_le64enc(e, zip->entry_compressed_size);
+		e += 8;
+		archive_le16enc(zip64_start + 2, (uint16_t)(e - (zip64_start + 4)));
+	}
+
+	if (zip->flags & ZIP_FLAG_EXPERIMENT_xl) {
+		/* Experimental 'xl' extension to improve streaming. */
+		unsigned char *external_info = e;
+		int included = 7;
+		memcpy(e, "xl\000\000", 4); // 0x6c65 + 2-byte length
+		e += 4;
+		e[0] = included; /* bitmap of included fields */
+		e += 1;
+		if (included & 1) {
+			archive_le16enc(e, /* "Version created by" */
+			    3 * 256 + version_needed);
+			e += 2;
+		}
+		if (included & 2) {
+			archive_le16enc(e, 0); /* internal file attributes */
+			e += 2;
+		}
+		if (included & 4) {
+			archive_le32enc(e,  /* external file attributes */
+			    ((uint32_t)archive_entry_mode(zip->entry)) << 16);
+			e += 4;
+		}
+		if (included & 8) {
+			// Libarchive does not currently support file comments.
+		}
+		archive_le16enc(external_info + 2, (uint16_t)(e - (external_info + 4)));
+	}
+
+	/* Update local header with size of extra data and write it all out: */
+	archive_le16enc(local_header + 28, (uint16_t)(e - local_extra));
+
+	ret = __archive_write_output(a, local_header, 30);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	zip->written_bytes += 30;
+
+	ret = write_path(zip->entry, a);
+	if (ret <= ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	zip->written_bytes += ret;
+
+	ret = __archive_write_output(a, local_extra, e - local_extra);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	zip->written_bytes += e - local_extra;
+
+	/* For symlinks, write the body now. */
+	if (slink != NULL) {
+		ret = __archive_write_output(a, slink, slink_size);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		zip->entry_compressed_written += slink_size;
+		zip->entry_uncompressed_written += slink_size;
+		zip->written_bytes += slink_size;
+	}
+
+#ifdef HAVE_ZLIB_H
+	if (zip->entry_compression == COMPRESSION_DEFLATE) {
+		zip->stream.zalloc = Z_NULL;
+		zip->stream.zfree = Z_NULL;
+		zip->stream.opaque = Z_NULL;
+		zip->stream.next_out = zip->buf;
+		zip->stream.avail_out = (uInt)zip->len_buf;
+		if (deflateInit2(&zip->stream, zip->deflate_compression_level,
+		    Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't init deflate compressor");
+			return (ARCHIVE_FATAL);
+		}
+	}
+#endif
+
+	return (ret2);
+}
+
+static ssize_t
+archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
+{
+	int ret;
+	struct zip *zip = a->format_data;
+
+	if ((int64_t)s > zip->entry_uncompressed_limit)
+		s = (size_t)zip->entry_uncompressed_limit;
+	zip->entry_uncompressed_written += s;
+
+	if (s == 0) return 0;
+
+	if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+		switch (zip->entry_encryption) {
+		case ENCRYPTION_TRADITIONAL:
+			/* Initialize traditional PKWARE encryption context. */
+			if (!zip->tctx_valid) {
+				ret = init_traditional_pkware_encryption(a);
+				if (ret != ARCHIVE_OK)
+					return (ret);
+				zip->tctx_valid = 1;
+			}
+			break;
+		case ENCRYPTION_WINZIP_AES128:
+		case ENCRYPTION_WINZIP_AES256:
+			if (!zip->cctx_valid) {
+				ret = init_winzip_aes_encryption(a);
+				if (ret != ARCHIVE_OK)
+					return (ret);
+				zip->cctx_valid = zip->hctx_valid = 1;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	switch (zip->entry_compression) {
+	case COMPRESSION_STORE:
+		if (zip->tctx_valid || zip->cctx_valid) {
+			const uint8_t *rb = (const uint8_t *)buff;
+			const uint8_t * const re = rb + s;
+
+			while (rb < re) {
+				size_t l;
+
+				if (zip->tctx_valid) {
+					l = trad_enc_encrypt_update(&zip->tctx,
+					    rb, re - rb,
+					    zip->buf, zip->len_buf);
+				} else {
+					l = zip->len_buf;
+					ret = archive_encrypto_aes_ctr_update(
+					    &zip->cctx,
+					    rb, re - rb, zip->buf, &l);
+					if (ret < 0) {
+						archive_set_error(&a->archive,
+						    ARCHIVE_ERRNO_MISC,
+						    "Failed to encrypt file");
+						return (ARCHIVE_FAILED);
+					}
+					archive_hmac_sha1_update(&zip->hctx,
+					    zip->buf, l);
+				}
+				ret = __archive_write_output(a, zip->buf, l);
+				if (ret != ARCHIVE_OK)
+					return (ret);
+				zip->entry_compressed_written += l;
+				zip->written_bytes += l;
+				rb += l;
+			}
+		} else {
+			ret = __archive_write_output(a, buff, s);
+			if (ret != ARCHIVE_OK)
+				return (ret);
+			zip->written_bytes += s;
+			zip->entry_compressed_written += s;
+		}
+		break;
+#if HAVE_ZLIB_H
+	case COMPRESSION_DEFLATE:
+		zip->stream.next_in = (unsigned char*)(uintptr_t)buff;
+		zip->stream.avail_in = (uInt)s;
+		do {
+			ret = deflate(&zip->stream, Z_NO_FLUSH);
+			if (ret == Z_STREAM_ERROR)
+				return (ARCHIVE_FATAL);
+			if (zip->stream.avail_out == 0) {
+				if (zip->tctx_valid) {
+					trad_enc_encrypt_update(&zip->tctx,
+					    zip->buf, zip->len_buf,
+					    zip->buf, zip->len_buf);
+				} else if (zip->cctx_valid) {
+					size_t outl = zip->len_buf;
+					ret = archive_encrypto_aes_ctr_update(
+					    &zip->cctx,
+					    zip->buf, zip->len_buf,
+					    zip->buf, &outl);
+					if (ret < 0) {
+						archive_set_error(&a->archive,
+						    ARCHIVE_ERRNO_MISC,
+						    "Failed to encrypt file");
+						return (ARCHIVE_FAILED);
+					}
+					archive_hmac_sha1_update(&zip->hctx,
+					    zip->buf, zip->len_buf);
+				}
+				ret = __archive_write_output(a, zip->buf,
+					zip->len_buf);
+				if (ret != ARCHIVE_OK)
+					return (ret);
+				zip->entry_compressed_written += zip->len_buf;
+				zip->written_bytes += zip->len_buf;
+				zip->stream.next_out = zip->buf;
+				zip->stream.avail_out = (uInt)zip->len_buf;
+			}
+		} while (zip->stream.avail_in != 0);
+		break;
+#endif
+
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Invalid ZIP compression type");
+		return ARCHIVE_FATAL;
+	}
+
+	zip->entry_uncompressed_limit -= s;
+	if (!zip->cctx_valid || zip->aes_vendor != AES_VENDOR_AE_2)
+		zip->entry_crc32 =
+		    zip->crc32func(zip->entry_crc32, buff, (unsigned)s);
+	return (s);
+
+}
+
+static int
+archive_write_zip_finish_entry(struct archive_write *a)
+{
+	struct zip *zip = a->format_data;
+	int ret;
+
+#if HAVE_ZLIB_H
+	if (zip->entry_compression == COMPRESSION_DEFLATE) {
+		for (;;) {
+			size_t remainder;
+
+			ret = deflate(&zip->stream, Z_FINISH);
+			if (ret == Z_STREAM_ERROR)
+				return (ARCHIVE_FATAL);
+			remainder = zip->len_buf - zip->stream.avail_out;
+			if (zip->tctx_valid) {
+				trad_enc_encrypt_update(&zip->tctx,
+				    zip->buf, remainder, zip->buf, remainder);
+			} else if (zip->cctx_valid) {
+				size_t outl = remainder;
+				ret = archive_encrypto_aes_ctr_update(
+				    &zip->cctx, zip->buf, remainder,
+				    zip->buf, &outl);
+				if (ret < 0) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_MISC,
+					    "Failed to encrypt file");
+					return (ARCHIVE_FAILED);
+				}
+				archive_hmac_sha1_update(&zip->hctx,
+				    zip->buf, remainder);
+			}
+			ret = __archive_write_output(a, zip->buf, remainder);
+			if (ret != ARCHIVE_OK)
+				return (ret);
+			zip->entry_compressed_written += remainder;
+			zip->written_bytes += remainder;
+			zip->stream.next_out = zip->buf;
+			if (zip->stream.avail_out != 0)
+				break;
+			zip->stream.avail_out = (uInt)zip->len_buf;
+		}
+		deflateEnd(&zip->stream);
+	}
+#endif
+	if (zip->hctx_valid) {
+		uint8_t hmac[20];
+		size_t hmac_len = 20;
+
+		archive_hmac_sha1_final(&zip->hctx, hmac, &hmac_len);
+		ret = __archive_write_output(a, hmac, AUTH_CODE_SIZE);
+		if (ret != ARCHIVE_OK)
+			return (ret);
+		zip->entry_compressed_written += AUTH_CODE_SIZE;
+		zip->written_bytes += AUTH_CODE_SIZE;
+	}
+
+	/* Write trailing data descriptor. */
+	if ((zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) != 0) {
+		char d[24];
+		memcpy(d, "PK\007\010", 4);
+		if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2)
+			archive_le32enc(d + 4, 0);/* no CRC.*/
+		else
+			archive_le32enc(d + 4, zip->entry_crc32);
+		if (zip->entry_uses_zip64) {
+			archive_le64enc(d + 8,
+				(uint64_t)zip->entry_compressed_written);
+			archive_le64enc(d + 16,
+				(uint64_t)zip->entry_uncompressed_written);
+			ret = __archive_write_output(a, d, 24);
+			zip->written_bytes += 24;
+		} else {
+			archive_le32enc(d + 8,
+				(uint32_t)zip->entry_compressed_written);
+			archive_le32enc(d + 12,
+				(uint32_t)zip->entry_uncompressed_written);
+			ret = __archive_write_output(a, d, 16);
+			zip->written_bytes += 16;
+		}
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+	}
+
+	/* Append Zip64 extra data to central directory information. */
+	if (zip->entry_compressed_written > ZIP_4GB_MAX
+	    || zip->entry_uncompressed_written > ZIP_4GB_MAX
+	    || zip->entry_offset > ZIP_4GB_MAX) {
+		unsigned char zip64[32];
+		unsigned char *z = zip64, *zd;
+		memcpy(z, "\001\000\000\000", 4);
+		z += 4;
+		if (zip->entry_uncompressed_written >= ZIP_4GB_MAX) {
+			archive_le64enc(z, zip->entry_uncompressed_written);
+			z += 8;
+		}
+		if (zip->entry_compressed_written >= ZIP_4GB_MAX) {
+			archive_le64enc(z, zip->entry_compressed_written);
+			z += 8;
+		}
+		if (zip->entry_offset >= ZIP_4GB_MAX) {
+			archive_le64enc(z, zip->entry_offset);
+			z += 8;
+		}
+		archive_le16enc(zip64 + 2, (uint16_t)(z - (zip64 + 4)));
+		zd = cd_alloc(zip, z - zip64);
+		if (zd == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+				"Can't allocate zip data");
+			return (ARCHIVE_FATAL);
+		}
+		memcpy(zd, zip64, z - zip64);
+		/* Zip64 means version needs to be set to at least 4.5 */
+		if (archive_le16dec(zip->file_header + 6) < 45)
+			archive_le16enc(zip->file_header + 6, 45);
+	}
+
+	/* Fix up central directory file header. */
+	if (zip->cctx_valid && zip->aes_vendor == AES_VENDOR_AE_2)
+		archive_le32enc(zip->file_header + 16, 0);/* no CRC.*/
+	else
+		archive_le32enc(zip->file_header + 16, zip->entry_crc32);
+	archive_le32enc(zip->file_header + 20,
+		(uint32_t)zipmin(zip->entry_compressed_written,
+				 ZIP_4GB_MAX));
+	archive_le32enc(zip->file_header + 24,
+		(uint32_t)zipmin(zip->entry_uncompressed_written,
+				 ZIP_4GB_MAX));
+	archive_le16enc(zip->file_header + 30,
+	    (uint16_t)(zip->central_directory_bytes - zip->file_header_extra_offset));
+	archive_le32enc(zip->file_header + 42,
+		(uint32_t)zipmin(zip->entry_offset,
+				 ZIP_4GB_MAX));
+
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_zip_close(struct archive_write *a)
+{
+	uint8_t buff[64];
+	int64_t offset_start, offset_end;
+	struct zip *zip = a->format_data;
+	struct cd_segment *segment;
+	int ret;
+
+	offset_start = zip->written_bytes;
+	segment = zip->central_directory;
+	while (segment != NULL) {
+		ret = __archive_write_output(a,
+		    segment->buff, segment->p - segment->buff);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		zip->written_bytes += segment->p - segment->buff;
+		segment = segment->next;
+	}
+	offset_end = zip->written_bytes;
+
+	/* If central dir info is too large, write Zip64 end-of-cd */
+	if (offset_end - offset_start > ZIP_4GB_MAX
+	    || offset_start > ZIP_4GB_MAX
+	    || zip->central_directory_entries > 0xffffUL
+	    || (zip->flags & ZIP_FLAG_FORCE_ZIP64)) {
+	  /* Zip64 end-of-cd record */
+	  memset(buff, 0, 56);
+	  memcpy(buff, "PK\006\006", 4);
+	  archive_le64enc(buff + 4, 44);
+	  archive_le16enc(buff + 12, 45);
+	  archive_le16enc(buff + 14, 45);
+	  /* This is disk 0 of 0. */
+	  archive_le64enc(buff + 24, zip->central_directory_entries);
+	  archive_le64enc(buff + 32, zip->central_directory_entries);
+	  archive_le64enc(buff + 40, offset_end - offset_start);
+	  archive_le64enc(buff + 48, offset_start);
+	  ret = __archive_write_output(a, buff, 56);
+	  if (ret != ARCHIVE_OK)
+		  return (ARCHIVE_FATAL);
+	  zip->written_bytes += 56;
+
+	  /* Zip64 end-of-cd locator record. */
+	  memset(buff, 0, 20);
+	  memcpy(buff, "PK\006\007", 4);
+	  archive_le32enc(buff + 4, 0);
+	  archive_le64enc(buff + 8, offset_end);
+	  archive_le32enc(buff + 16, 1);
+	  ret = __archive_write_output(a, buff, 20);
+	  if (ret != ARCHIVE_OK)
+		  return (ARCHIVE_FATAL);
+	  zip->written_bytes += 20;
+
+	}
+
+	/* Format and write end of central directory. */
+	memset(buff, 0, sizeof(buff));
+	memcpy(buff, "PK\005\006", 4);
+	archive_le16enc(buff + 8, (uint16_t)zipmin(0xffffU,
+		zip->central_directory_entries));
+	archive_le16enc(buff + 10, (uint16_t)zipmin(0xffffU,
+		zip->central_directory_entries));
+	archive_le32enc(buff + 12,
+		(uint32_t)zipmin(ZIP_4GB_MAX, (offset_end - offset_start)));
+	archive_le32enc(buff + 16,
+		(uint32_t)zipmin(ZIP_4GB_MAX, offset_start));
+	ret = __archive_write_output(a, buff, 22);
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	zip->written_bytes += 22;
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_zip_free(struct archive_write *a)
+{
+	struct zip *zip;
+	struct cd_segment *segment;
+
+	zip = a->format_data;
+	while (zip->central_directory != NULL) {
+		segment = zip->central_directory;
+		zip->central_directory = segment->next;
+		free(segment->buff);
+		free(segment);
+	}
+	free(zip->buf);
+	archive_entry_free(zip->entry);
+	if (zip->cctx_valid)
+		archive_encrypto_aes_ctr_release(&zip->cctx);
+	if (zip->hctx_valid)
+		archive_hmac_sha1_cleanup(&zip->hctx);
+	/* TODO: Free opt_sconv, sconv_default */
+
+	free(zip);
+	a->format_data = NULL;
+	return (ARCHIVE_OK);
+}
+
+/* Convert into MSDOS-style date/time. */
+static unsigned int
+dos_time(const time_t unix_time)
+{
+	struct tm *t;
+	unsigned int dt;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE__LOCALTIME64_S)
+	struct tm tmbuf;
+#endif
+#if defined(HAVE__LOCALTIME64_S)
+	errno_t terr;
+	__time64_t tmptime;
+#endif
+
+	/* This will not preserve time when creating/extracting the archive
+	 * on two systems with different time zones. */
+#if defined(HAVE_LOCALTIME_R)
+	t = localtime_r(&unix_time, &tmbuf);
+#elif defined(HAVE__LOCALTIME64_S)
+	tmptime = unix_time;
+	terr = _localtime64_s(&tmbuf, &tmptime);
+	if (terr)
+		t = NULL;
+	else
+		t = &tmbuf;
+#else
+	t = localtime(&unix_time);
+#endif
+
+	/* MSDOS-style date/time is only between 1980-01-01 and 2107-12-31 */
+	if (t->tm_year < 1980 - 1900)
+		/* Set minimum date/time '1980-01-01 00:00:00'. */
+		dt = 0x00210000U;
+	else if (t->tm_year > 2107 - 1900)
+		/* Set maximum date/time '2107-12-31 23:59:58'. */
+		dt = 0xff9fbf7dU;
+	else {
+		dt = 0;
+		dt += ((t->tm_year - 80) & 0x7f) << 9;
+		dt += ((t->tm_mon + 1) & 0x0f) << 5;
+		dt += (t->tm_mday & 0x1f);
+		dt <<= 16;
+		dt += (t->tm_hour & 0x1f) << 11;
+		dt += (t->tm_min & 0x3f) << 5;
+		dt += (t->tm_sec & 0x3e) >> 1; /* Only counting every 2 seconds. */
+	}
+	return dt;
+}
+
+static size_t
+path_length(struct archive_entry *entry)
+{
+	mode_t type;
+	const char *path;
+	size_t len;
+
+	type = archive_entry_filetype(entry);
+	path = archive_entry_pathname(entry);
+
+	if (path == NULL)
+		return (0);
+	len = strlen(path);
+	if (type == AE_IFDIR && (path[0] == '\0' || path[len - 1] != '/'))
+		++len; /* Space for the trailing / */
+	return len;
+}
+
+static int
+write_path(struct archive_entry *entry, struct archive_write *archive)
+{
+	int ret;
+	const char *path;
+	mode_t type;
+	size_t written_bytes;
+
+	path = archive_entry_pathname(entry);
+	type = archive_entry_filetype(entry);
+	written_bytes = 0;
+
+	if (path == NULL)
+		return (ARCHIVE_FATAL);
+
+	ret = __archive_write_output(archive, path, strlen(path));
+	if (ret != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+	written_bytes += strlen(path);
+
+	/* Folders are recognized by a trailing slash. */
+	if ((type == AE_IFDIR) & (path[strlen(path) - 1] != '/')) {
+		ret = __archive_write_output(archive, "/", 1);
+		if (ret != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+		written_bytes += 1;
+	}
+
+	return ((int)written_bytes);
+}
+
+static void
+copy_path(struct archive_entry *entry, unsigned char *p)
+{
+	const char *path;
+	size_t pathlen;
+	mode_t type;
+
+	path = archive_entry_pathname(entry);
+	pathlen = strlen(path);
+	type = archive_entry_filetype(entry);
+
+	memcpy(p, path, pathlen);
+
+	/* Folders are recognized by a trailing slash. */
+	if ((type == AE_IFDIR) && (path[pathlen - 1] != '/'))
+		p[pathlen] = '/';
+}
+
+
+static struct archive_string_conv *
+get_sconv(struct archive_write *a, struct zip *zip)
+{
+	if (zip->opt_sconv != NULL)
+		return (zip->opt_sconv);
+
+	if (!zip->init_default_conversion) {
+		zip->sconv_default =
+		    archive_string_default_conversion_for_write(&(a->archive));
+		zip->init_default_conversion = 1;
+	}
+	return (zip->sconv_default);
+}
+
+/*
+  Traditional PKWARE Decryption functions.
+ */
+
+static void
+trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c)
+{
+	uint8_t t;
+#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL)
+
+	ctx->keys[0] = CRC32(ctx->keys[0], c);
+	ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1;
+	t = (ctx->keys[1] >> 24) & 0xff;
+	ctx->keys[2] = CRC32(ctx->keys[2], t);
+#undef CRC32
+}
+
+static uint8_t
+trad_enc_decrypt_byte(struct trad_enc_ctx *ctx)
+{
+	unsigned temp = ctx->keys[2] | 2;
+	return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;
+}
+
+static unsigned
+trad_enc_encrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in,
+    size_t in_len, uint8_t *out, size_t out_len)
+{
+	unsigned i, max;
+
+	max = (unsigned)((in_len < out_len)? in_len: out_len);
+
+	for (i = 0; i < max; i++) {
+		uint8_t t = in[i];
+		out[i] = t ^ trad_enc_decrypt_byte(ctx);
+		trad_enc_update_keys(ctx, t);
+	}
+	return i;
+}
+
+static int
+trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len)
+{
+
+	ctx->keys[0] = 305419896L;
+	ctx->keys[1] = 591751049L;
+	ctx->keys[2] = 878082192L;
+
+	for (;pw_len; --pw_len)
+		trad_enc_update_keys(ctx, *pw++);
+	return 0;
+}
+
+static int
+is_traditional_pkware_encryption_supported(void)
+{
+	uint8_t key[TRAD_HEADER_SIZE];
+
+	if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK)
+		return (0);
+	return (1);
+}
+
+static int
+init_traditional_pkware_encryption(struct archive_write *a)
+{
+	struct zip *zip = a->format_data;
+	const char *passphrase;
+	uint8_t key[TRAD_HEADER_SIZE];
+	uint8_t key_encrypted[TRAD_HEADER_SIZE];
+	int ret;
+
+	passphrase = __archive_write_get_passphrase(a);
+	if (passphrase == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Encryption needs passphrase");
+		return ARCHIVE_FAILED;
+	}
+	if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Can't generate random number for encryption");
+		return ARCHIVE_FATAL;
+	}
+	trad_enc_init(&zip->tctx, passphrase, strlen(passphrase));
+	/* Set the last key code which will be used as a check code
+	 * for verifying passphrase in decryption. */
+	key[TRAD_HEADER_SIZE-1] = zip->trad_chkdat;
+	trad_enc_encrypt_update(&zip->tctx, key, TRAD_HEADER_SIZE,
+	    key_encrypted, TRAD_HEADER_SIZE);
+	/* Write encrypted keys in the top of the file content. */
+	ret = __archive_write_output(a, key_encrypted, TRAD_HEADER_SIZE);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+	zip->written_bytes += TRAD_HEADER_SIZE;
+	zip->entry_compressed_written += TRAD_HEADER_SIZE;
+	return (ret);
+}
+
+static int
+init_winzip_aes_encryption(struct archive_write *a)
+{
+	struct zip *zip = a->format_data;
+	const char *passphrase;
+	size_t key_len, salt_len;
+	uint8_t salt[16 + 2];
+	uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];
+	int ret;
+
+	passphrase = __archive_write_get_passphrase(a);
+	if (passphrase == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Encryption needs passphrase");
+		return (ARCHIVE_FAILED);
+	}
+	if (zip->entry_encryption == ENCRYPTION_WINZIP_AES128) {
+		salt_len = 8;
+		key_len = 16;
+	} else {
+		/* AES 256 */
+		salt_len = 16;
+		key_len = 32;
+	}
+	if (archive_random(salt, salt_len) != ARCHIVE_OK) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Can't generate random number for encryption");
+		return (ARCHIVE_FATAL);
+	}
+	archive_pbkdf2_sha1(passphrase, strlen(passphrase),
+	    salt, salt_len, 1000, derived_key, key_len * 2 + 2);
+
+	ret = archive_encrypto_aes_ctr_init(&zip->cctx, derived_key, key_len);
+	if (ret != 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Decryption is unsupported due to lack of crypto library");
+		return (ARCHIVE_FAILED);
+	}
+	ret = archive_hmac_sha1_init(&zip->hctx, derived_key + key_len,
+	    key_len);
+	if (ret != 0) {
+		archive_encrypto_aes_ctr_release(&zip->cctx);
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to initialize HMAC-SHA1");
+		return (ARCHIVE_FAILED);
+        }
+
+	/* Set a password verification value after the 'salt'. */
+	salt[salt_len] = derived_key[key_len * 2];
+	salt[salt_len + 1] = derived_key[key_len * 2 + 1];
+
+	/* Write encrypted keys in the top of the file content. */
+	ret = __archive_write_output(a, salt, salt_len + 2);
+	if (ret != ARCHIVE_OK)
+		return (ret);
+	zip->written_bytes += salt_len + 2;
+	zip->entry_compressed_written += salt_len + 2;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+is_winzip_aes_encryption_supported(int encryption)
+{
+	size_t key_len, salt_len;
+	uint8_t salt[16 + 2];
+	uint8_t derived_key[MAX_DERIVED_KEY_BUF_SIZE];
+	archive_crypto_ctx cctx;
+	archive_hmac_sha1_ctx hctx;
+	int ret;
+
+	if (encryption == ENCRYPTION_WINZIP_AES128) {
+		salt_len = 8;
+		key_len = 16;
+	} else {
+		/* AES 256 */
+		salt_len = 16;
+		key_len = 32;
+	}
+	if (archive_random(salt, salt_len) != ARCHIVE_OK)
+		return (0);
+	ret = archive_pbkdf2_sha1("p", 1, salt, salt_len, 1000,
+	    derived_key, key_len * 2 + 2);
+	if (ret != 0)
+		return (0);
+
+	ret = archive_encrypto_aes_ctr_init(&cctx, derived_key, key_len);
+	if (ret != 0)
+		return (0);
+	ret = archive_hmac_sha1_init(&hctx, derived_key + key_len,
+	    key_len);
+	archive_encrypto_aes_ctr_release(&cctx);
+	if (ret != 0)
+		return (0);
+	archive_hmac_sha1_cleanup(&hctx);
+	return (1);
+}
diff --git a/c/archive_write_set_options.c b/c/archive_write_set_options.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_options.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 2003-2010 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$");
+
+#include "archive_write_private.h"
+#include "archive_options_private.h"
+
+static int	archive_set_format_option(struct archive *a,
+		    const char *m, const char *o, const char *v);
+static int	archive_set_filter_option(struct archive *a,
+		    const char *m, const char *o, const char *v);
+static int	archive_set_option(struct archive *a,
+		    const char *m, const char *o, const char *v);
+
+int
+archive_write_set_format_option(struct archive *a, const char *m, const char *o,
+    const char *v)
+{
+	return _archive_set_option(a, m, o, v,
+	    ARCHIVE_WRITE_MAGIC, "archive_write_set_format_option",
+	    archive_set_format_option);
+}
+
+int
+archive_write_set_filter_option(struct archive *a, const char *m, const char *o,
+    const char *v)
+{
+	return _archive_set_option(a, m, o, v,
+	    ARCHIVE_WRITE_MAGIC, "archive_write_set_filter_option",
+	    archive_set_filter_option);
+}
+
+int
+archive_write_set_option(struct archive *a, const char *m, const char *o,
+    const char *v)
+{
+	return _archive_set_option(a, m, o, v,
+	    ARCHIVE_WRITE_MAGIC, "archive_write_set_option",
+	    archive_set_option);
+}
+
+int
+archive_write_set_options(struct archive *a, const char *options)
+{
+	return _archive_set_options(a, options,
+	    ARCHIVE_WRITE_MAGIC, "archive_write_set_options",
+	    archive_set_option);
+}
+
+static int
+archive_set_format_option(struct archive *_a, const char *m, const char *o,
+    const char *v)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+
+	if (a->format_name == NULL)
+		return (m == NULL)?ARCHIVE_FAILED:ARCHIVE_WARN - 1;
+	/* If the format name didn't match, return a special code for
+	 * _archive_set_option[s]. */
+	if (m != NULL && strcmp(m, a->format_name) != 0)
+		return (ARCHIVE_WARN - 1);
+	if (a->format_options == NULL)
+		return (ARCHIVE_WARN);
+	return a->format_options(a, o, v);
+}
+
+static int
+archive_set_filter_option(struct archive *_a, const char *m, const char *o,
+    const char *v)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct archive_write_filter *filter;
+	int r, rv = ARCHIVE_WARN;
+
+	for (filter = a->filter_first; filter != NULL; filter = filter->next_filter) {
+		if (filter->options == NULL)
+			continue;
+		if (m != NULL && strcmp(filter->name, m) != 0)
+			continue;
+
+		r = filter->options(filter, o, v);
+
+		if (r == ARCHIVE_FATAL)
+			return (ARCHIVE_FATAL);
+
+		if (m != NULL)
+			return (r);
+
+		if (r == ARCHIVE_OK)
+			rv = ARCHIVE_OK;
+	}
+	/* If the filter name didn't match, return a special code for
+	 * _archive_set_option[s]. */
+	if (rv == ARCHIVE_WARN && m != NULL)
+		rv = ARCHIVE_WARN - 1;
+	return (rv);
+}
+
+static int
+archive_set_option(struct archive *a, const char *m, const char *o,
+    const char *v)
+{
+	return _archive_set_either_option(a, m, o, v,
+	    archive_set_format_option,
+	    archive_set_filter_option);
+}
diff --git a/c/archive_write_set_passphrase.c b/c/archive_write_set_passphrase.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_passphrase.c
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * 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$");
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "archive_write_private.h"
+
+int
+archive_write_set_passphrase(struct archive *_a, const char *p)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW,
+		"archive_write_set_passphrase");
+
+	if (p == NULL || p[0] == '\0') {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Empty passphrase is unacceptable");
+		return (ARCHIVE_FAILED);
+	}
+	free(a->passphrase);
+	a->passphrase = strdup(p);
+	if (a->passphrase == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate data for passphrase");
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+
+int
+archive_write_set_passphrase_callback(struct archive *_a, void *client_data,
+    archive_passphrase_callback *cb)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC, ARCHIVE_STATE_NEW,
+		"archive_write_set_passphrase_callback");
+
+	a->passphrase_callback = cb;
+	a->passphrase_client_data = client_data;
+	return (ARCHIVE_OK);
+}
+
+
+const char *
+__archive_write_get_passphrase(struct archive_write *a)
+{
+
+	if (a->passphrase != NULL)
+		return (a->passphrase);
+
+	if (a->passphrase_callback != NULL) {
+		const char *p;
+		p = a->passphrase_callback(&a->archive,
+		    a->passphrase_client_data);
+		if (p != NULL) {
+			a->passphrase = strdup(p);
+			if (a->passphrase == NULL) {
+				archive_set_error(&a->archive, ENOMEM,
+				    "Can't allocate data for passphrase");
+				return (NULL);
+			}
+			return (a->passphrase);
+		}
+	}
+	return (NULL);
+}
diff --git a/c/archive_xxhash.h b/c/archive_xxhash.h
new file mode 100644
--- /dev/null
+++ b/c/archive_xxhash.h
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2014 Michihiro NAKAJIMA
+ * 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.
+ *
+ */
+
+#ifndef ARCHIVE_XXHASH_H_INCLUDED
+#define ARCHIVE_XXHASH_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
+
+struct archive_xxhash {
+	unsigned int  (*XXH32)(const void* input, unsigned int len,
+			unsigned int seed);
+	void*         (*XXH32_init)(unsigned int seed);
+	XXH_errorcode (*XXH32_update)(void* state, const void* input,
+			unsigned int len);
+	unsigned int  (*XXH32_digest)(void* state);
+};
+
+extern const struct archive_xxhash __archive_xxhash;
+
+#endif
diff --git a/c/autoconf-darwin/config.h b/c/autoconf-darwin/config.h
new file mode 100644
--- /dev/null
+++ b/c/autoconf-darwin/config.h
@@ -0,0 +1,1428 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Darwin ACL support */
+/* #undef ARCHIVE_ACL_DARWIN */
+
+/* FreeBSD ACL support */
+/* #undef ARCHIVE_ACL_FREEBSD */
+
+/* FreeBSD NFSv4 ACL support */
+/* #undef ARCHIVE_ACL_FREEBSD_NFS4 */
+
+/* Linux POSIX.1e ACL support via libacl */
+/* #undef ARCHIVE_ACL_LIBACL */
+
+/* Linux NFSv4 ACL support via librichacl */
+/* #undef ARCHIVE_ACL_LIBRICHACL */
+
+/* Solaris ACL support */
+/* #undef ARCHIVE_ACL_SUNOS */
+
+/* Solaris NFSv4 ACL support */
+/* #undef ARCHIVE_ACL_SUNOS_NFS4 */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBC */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBMD supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBMD */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */
+#define ARCHIVE_CRYPTO_MD5_LIBSYSTEM 1
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_MBEDTLS */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_OPENSSL */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_WIN */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_LIBC */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBMD supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_LIBMD */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_MBEDTLS */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_NETTLE */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_OPENSSL */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBC */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBMD supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBMD */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */
+#define ARCHIVE_CRYPTO_SHA1_LIBSYSTEM 1
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_MBEDTLS */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_OPENSSL */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_WIN */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC2 */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC3 */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBMD supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBMD */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */
+#define ARCHIVE_CRYPTO_SHA256_LIBSYSTEM 1
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_MBEDTLS */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_OPENSSL */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_WIN */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC2 */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC3 */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */
+#define ARCHIVE_CRYPTO_SHA384_LIBSYSTEM 1
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_MBEDTLS */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_OPENSSL */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_WIN */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC2 */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC3 */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBMD supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBMD */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */
+#define ARCHIVE_CRYPTO_SHA512_LIBSYSTEM 1
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_MBEDTLS */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_OPENSSL */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_WIN */
+
+/* AIX xattr support */
+/* #undef ARCHIVE_XATTR_AIX */
+
+/* Darwin xattr support */
+/* #undef ARCHIVE_XATTR_DARWIN */
+
+/* FreeBSD xattr support */
+/* #undef ARCHIVE_XATTR_FREEBSD */
+
+/* Linux xattr support */
+/* #undef ARCHIVE_XATTR_LINUX */
+
+/* Version number of bsdcat */
+#define BSDCAT_VERSION_STRING "3.4.3"
+
+/* Version number of bsdcpio */
+#define BSDCPIO_VERSION_STRING "3.4.3"
+
+/* Version number of bsdtar */
+#define BSDTAR_VERSION_STRING "3.4.3"
+
+/* Define to 1 if the system has the type `ace_t'. */
+/* #undef HAVE_ACE_T */
+
+/* Define to 1 if you have the `acl' function. */
+/* #undef HAVE_ACL */
+
+/* Define to 1 if the system has the type `aclent_t'. */
+/* #undef HAVE_ACLENT_T */
+
+/* Define to 1 if you have the `acl_add_flag_np' function. */
+/* #undef HAVE_ACL_ADD_FLAG_NP */
+
+/* Define to 1 if you have the `acl_add_perm' function. */
+/* #undef HAVE_ACL_ADD_PERM */
+
+/* Define to 1 if you have the `acl_clear_flags_np' function. */
+/* #undef HAVE_ACL_CLEAR_FLAGS_NP */
+
+/* Define to 1 if you have the `acl_clear_perms' function. */
+/* #undef HAVE_ACL_CLEAR_PERMS */
+
+/* Define to 1 if you have the `acl_create_entry' function. */
+/* #undef HAVE_ACL_CREATE_ENTRY */
+
+/* Define to 1 if you have the `acl_delete_def_file' function. */
+/* #undef HAVE_ACL_DELETE_DEF_FILE */
+
+/* Define to 1 if the system has the type `acl_entry_t'. */
+/* #undef HAVE_ACL_ENTRY_T */
+
+/* Define to 1 if you have the `acl_free' function. */
+/* #undef HAVE_ACL_FREE */
+
+/* Define to 1 if you have the `acl_get_brand_np' function. */
+/* #undef HAVE_ACL_GET_BRAND_NP */
+
+/* Define to 1 if you have the `acl_get_entry' function. */
+/* #undef HAVE_ACL_GET_ENTRY */
+
+/* Define to 1 if you have the `acl_get_entry_type_np' function. */
+/* #undef HAVE_ACL_GET_ENTRY_TYPE_NP */
+
+/* Define to 1 if you have the `acl_get_fd' function. */
+/* #undef HAVE_ACL_GET_FD */
+
+/* Define to 1 if you have the `acl_get_fd_np' function. */
+/* #undef HAVE_ACL_GET_FD_NP */
+
+/* Define to 1 if you have the `acl_get_file' function. */
+/* #undef HAVE_ACL_GET_FILE */
+
+/* Define to 1 if you have the `acl_get_flagset_np' function. */
+/* #undef HAVE_ACL_GET_FLAGSET_NP */
+
+/* Define to 1 if you have the `acl_get_flag_np' function. */
+/* #undef HAVE_ACL_GET_FLAG_NP */
+
+/* Define to 1 if you have the `acl_get_link_np' function. */
+/* #undef HAVE_ACL_GET_LINK_NP */
+
+/* Define to 1 if you have the `acl_get_perm' function. */
+/* #undef HAVE_ACL_GET_PERM */
+
+/* Define to 1 if you have the `acl_get_permset' function. */
+/* #undef HAVE_ACL_GET_PERMSET */
+
+/* Define to 1 if you have the `acl_get_perm_np' function. */
+/* #undef HAVE_ACL_GET_PERM_NP */
+
+/* Define to 1 if you have the `acl_get_qualifier' function. */
+/* #undef HAVE_ACL_GET_QUALIFIER */
+
+/* Define to 1 if you have the `acl_get_tag_type' function. */
+/* #undef HAVE_ACL_GET_TAG_TYPE */
+
+/* Define to 1 if you have the `acl_init' function. */
+/* #undef HAVE_ACL_INIT */
+
+/* Define to 1 if you have the `acl_is_trivial_np' function. */
+/* #undef HAVE_ACL_IS_TRIVIAL_NP */
+
+/* Define to 1 if you have the <acl/libacl.h> header file. */
+/* #undef HAVE_ACL_LIBACL_H */
+
+/* Define to 1 if the system has the type `acl_permset_t'. */
+/* #undef HAVE_ACL_PERMSET_T */
+
+/* Define to 1 if you have the `acl_set_entry_type_np' function. */
+/* #undef HAVE_ACL_SET_ENTRY_TYPE_NP */
+
+/* Define to 1 if you have the `acl_set_fd' function. */
+/* #undef HAVE_ACL_SET_FD */
+
+/* Define to 1 if you have the `acl_set_fd_np' function. */
+/* #undef HAVE_ACL_SET_FD_NP */
+
+/* Define to 1 if you have the `acl_set_file' function. */
+/* #undef HAVE_ACL_SET_FILE */
+
+/* Define to 1 if you have the `acl_set_link_np' function. */
+/* #undef HAVE_ACL_SET_LINK_NP */
+
+/* Define to 1 if you have the `acl_set_qualifier' function. */
+/* #undef HAVE_ACL_SET_QUALIFIER */
+
+/* Define to 1 if you have the `acl_set_tag_type' function. */
+/* #undef HAVE_ACL_SET_TAG_TYPE */
+
+/* Define to 1 if the system has the type `acl_t'. */
+/* #undef HAVE_ACL_T */
+
+/* Define to 1 if the system has the type `acl_tag_t'. */
+/* #undef HAVE_ACL_TAG_T */
+
+/* Define to 1 if you have the `arc4random_buf' function. */
+#define HAVE_ARC4RANDOM_BUF 1
+
+/* Define to 1 if you have the <attr/xattr.h> header file. */
+/* #undef HAVE_ATTR_XATTR_H */
+
+/* Define to 1 if you have the <bcrypt.h> header file. */
+/* #undef HAVE_BCRYPT_H */
+
+/* Define to 1 if you have the <blake2.h> header file. */
+/* #undef HAVE_BLAKE2_H */
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+/* #undef HAVE_BZLIB_H */
+
+/* Define to 1 if you have the `chflags' function. */
+#define HAVE_CHFLAGS 1
+
+/* Define to 1 if you have the `chown' function. */
+#define HAVE_CHOWN 1
+
+/* Define to 1 if you have the `chroot' function. */
+#define HAVE_CHROOT 1
+
+/* Define to 1 if you have the <copyfile.h> header file. */
+#define HAVE_COPYFILE_H 1
+
+/* Define to 1 if you have the `ctime_r' function. */
+#define HAVE_CTIME_R 1
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the `cygwin_conv_path' function. */
+/* #undef HAVE_CYGWIN_CONV_PATH */
+
+/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACE_GETACL */
+
+/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACE_GETACLCNT */
+
+/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACE_SETACL */
+
+/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if
+   you don't. */
+/* #undef HAVE_DECL_ACL_SYNCHRONIZE */
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if
+   you don't. */
+/* #undef HAVE_DECL_ACL_TYPE_EXTENDED */
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACL_TYPE_NFS4 */
+
+/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACL_USER */
+
+/* Define to 1 if you have the declaration of `EXTATTR_NAMESPACE_USER', and to
+   0 if you don't. */
+/* #undef HAVE_DECL_EXTATTR_NAMESPACE_USER */
+
+/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.
+   */
+/* #undef HAVE_DECL_GETACL */
+
+/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_GETACLCNT */
+
+/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT32_MAX 1
+
+/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT32_MIN 1
+
+/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT64_MAX 1
+
+/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT64_MIN 1
+
+/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_INTMAX_MAX 1
+
+/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you
+   don't. */
+#define HAVE_DECL_INTMAX_MIN 1
+
+/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.
+   */
+/* #undef HAVE_DECL_SETACL */
+
+/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_SIZE_MAX 1
+
+/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_SSIZE_MAX 1
+
+/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
+   don't. */
+#define HAVE_DECL_STRERROR_R 1
+
+/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_UINT32_MAX 1
+
+/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_UINT64_MAX 1
+
+/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_UINTMAX_MAX 1
+
+/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if
+   you don't. */
+/* #undef HAVE_DECL_XATTR_NOFOLLOW */
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+   */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have a dirfd function or macro */
+#define HAVE_DIRFD 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Define to 1 if nl_langinfo supports D_MD_ORDER */
+#define HAVE_D_MD_ORDER 1
+
+/* A possible errno value for invalid file format errors */
+#define HAVE_EFTYPE 1
+
+/* A possible errno value for invalid file format errors */
+#define HAVE_EILSEQ 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <expat.h> header file. */
+/* #undef HAVE_EXPAT_H */
+
+/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */
+/* #undef HAVE_EXT2FS_EXT2_FS_H */
+
+/* Define to 1 if you have the `extattr_get_fd' function. */
+/* #undef HAVE_EXTATTR_GET_FD */
+
+/* Define to 1 if you have the `extattr_get_file' function. */
+/* #undef HAVE_EXTATTR_GET_FILE */
+
+/* Define to 1 if you have the `extattr_get_link' function. */
+/* #undef HAVE_EXTATTR_GET_LINK */
+
+/* Define to 1 if you have the `extattr_list_fd' function. */
+/* #undef HAVE_EXTATTR_LIST_FD */
+
+/* Define to 1 if you have the `extattr_list_file' function. */
+/* #undef HAVE_EXTATTR_LIST_FILE */
+
+/* Define to 1 if you have the `extattr_list_link' function. */
+/* #undef HAVE_EXTATTR_LIST_LINK */
+
+/* Define to 1 if you have the `extattr_set_fd' function. */
+/* #undef HAVE_EXTATTR_SET_FD */
+
+/* Define to 1 if you have the `extattr_set_link' function. */
+/* #undef HAVE_EXTATTR_SET_LINK */
+
+/* Define to 1 if you have the `facl' function. */
+/* #undef HAVE_FACL */
+
+/* Define to 1 if you have the `fchdir' function. */
+#define HAVE_FCHDIR 1
+
+/* Define to 1 if you have the `fchflags' function. */
+#define HAVE_FCHFLAGS 1
+
+/* Define to 1 if you have the `fchmod' function. */
+#define HAVE_FCHMOD 1
+
+/* Define to 1 if you have the `fchown' function. */
+#define HAVE_FCHOWN 1
+
+/* Define to 1 if you have the `fcntl' function. */
+#define HAVE_FCNTL 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fdopendir' function. */
+#define HAVE_FDOPENDIR 1
+
+/* Define to 1 if you have the `fgetea' function. */
+/* #undef HAVE_FGETEA */
+
+/* Define to 1 if you have the `fgetxattr' function. */
+/* #undef HAVE_FGETXATTR */
+
+/* Define to 1 if you have the `flistea' function. */
+/* #undef HAVE_FLISTEA */
+
+/* Define to 1 if you have the `flistxattr' function. */
+/* #undef HAVE_FLISTXATTR */
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `fsetea' function. */
+/* #undef HAVE_FSETEA */
+
+/* Define to 1 if you have the `fsetxattr' function. */
+/* #undef HAVE_FSETXATTR */
+
+/* Define to 1 if you have the `fstat' function. */
+#define HAVE_FSTAT 1
+
+/* Define to 1 if you have the `fstatat' function. */
+#define HAVE_FSTATAT 1
+
+/* Define to 1 if you have the `fstatfs' function. */
+#define HAVE_FSTATFS 1
+
+/* Define to 1 if you have the `fstatvfs' function. */
+#define HAVE_FSTATVFS 1
+
+/* Define to 1 if you have the `ftruncate' function. */
+#define HAVE_FTRUNCATE 1
+
+/* Define to 1 if you have the `futimens' function. */
+#define HAVE_FUTIMENS 1
+
+/* Define to 1 if you have the `futimes' function. */
+#define HAVE_FUTIMES 1
+
+/* Define to 1 if you have the `futimesat' function. */
+/* #undef HAVE_FUTIMESAT */
+
+/* Define to 1 if you have the `getea' function. */
+/* #undef HAVE_GETEA */
+
+/* Define to 1 if you have the `geteuid' function. */
+#define HAVE_GETEUID 1
+
+/* Define to 1 if you have the `getgrgid_r' function. */
+#define HAVE_GETGRGID_R 1
+
+/* Define to 1 if you have the `getgrnam_r' function. */
+#define HAVE_GETGRNAM_R 1
+
+/* Define to 1 if you have the `getpid' function. */
+#define HAVE_GETPID 1
+
+/* Define to 1 if you have the `getpwnam_r' function. */
+#define HAVE_GETPWNAM_R 1
+
+/* Define to 1 if you have the `getpwuid_r' function. */
+#define HAVE_GETPWUID_R 1
+
+/* Define to 1 if you have the `getvfsbyname' function. */
+#define HAVE_GETVFSBYNAME 1
+
+/* Define to 1 if you have the `getxattr' function. */
+/* #undef HAVE_GETXATTR */
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#define HAVE_GMTIME_R 1
+
+/* Define to 1 if you have the <grp.h> header file. */
+#define HAVE_GRP_H 1
+
+/* Define if you have the iconv() function and it works. */
+/* #undef HAVE_ICONV */
+
+/* Define to 1 if you have the <iconv.h> header file. */
+/* #undef HAVE_ICONV_H */
+
+/* Define to 1 if the system has the type `intmax_t'. */
+#define HAVE_INTMAX_T 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <io.h> header file. */
+/* #undef HAVE_IO_H */
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define to 1 if you have the `lchflags' function. */
+#define HAVE_LCHFLAGS 1
+
+/* Define to 1 if you have the `lchmod' function. */
+#define HAVE_LCHMOD 1
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the `lgetea' function. */
+/* #undef HAVE_LGETEA */
+
+/* Define to 1 if you have the `lgetxattr' function. */
+/* #undef HAVE_LGETXATTR */
+
+/* Define to 1 if you have the `acl' library (-lacl). */
+/* #undef HAVE_LIBACL */
+
+/* Define to 1 if you have the `b2' library (-lb2). */
+/* #undef HAVE_LIBB2 */
+
+/* Define to 1 if you have the `bz2' library (-lbz2). */
+/* #undef HAVE_LIBBZ2 */
+
+/* Define to 1 if you have the `charset' library (-lcharset). */
+/* #undef HAVE_LIBCHARSET */
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+/* #undef HAVE_LIBCRYPTO */
+
+/* Define to 1 if you have the `eay32' library (-leay32). */
+/* #undef HAVE_LIBEAY32 */
+
+/* Define to 1 if you have the `eay64' library (-leay64). */
+/* #undef HAVE_LIBEAY64 */
+
+/* Define to 1 if you have the `expat' library (-lexpat). */
+/* #undef HAVE_LIBEXPAT */
+
+/* Define to 1 if you have the `lz4' library (-llz4). */
+/* #undef HAVE_LIBLZ4 */
+
+/* Define to 1 if you have the `lzma' library (-llzma). */
+/* #undef HAVE_LIBLZMA */
+
+/* Define to 1 if you have the `lzo2' library (-llzo2). */
+/* #undef HAVE_LIBLZO2 */
+
+/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */
+/* #undef HAVE_LIBMBEDCRYPTO */
+
+/* Define to 1 if you have the `md' library (-lmd). */
+/* #undef HAVE_LIBMD */
+
+/* Define to 1 if you have the `nettle' library (-lnettle). */
+/* #undef HAVE_LIBNETTLE */
+
+/* Define to 1 if you have the `pcre' library (-lpcre). */
+/* #undef HAVE_LIBPCRE */
+
+/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */
+/* #undef HAVE_LIBPCREPOSIX */
+
+/* Define to 1 if you have the `regex' library (-lregex). */
+/* #undef HAVE_LIBREGEX */
+
+/* Define to 1 if you have the `richacl' library (-lrichacl). */
+/* #undef HAVE_LIBRICHACL */
+
+/* Define to 1 if you have the `xml2' library (-lxml2). */
+/* #undef HAVE_LIBXML2 */
+
+/* Define to 1 if you have the <libxml/xmlreader.h> header file. */
+/* #undef HAVE_LIBXML_XMLREADER_H */
+
+/* Define to 1 if you have the <libxml/xmlwriter.h> header file. */
+/* #undef HAVE_LIBXML_XMLWRITER_H */
+
+/* Define to 1 if you have the `z' library (-lz). */
+/* #undef HAVE_LIBZ */
+
+/* Define to 1 if you have the `zstd' library (-lzstd). */
+/* #undef HAVE_LIBZSTD */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the `link' function. */
+#define HAVE_LINK 1
+
+/* Define to 1 if you have the <linux/fiemap.h> header file. */
+/* #undef HAVE_LINUX_FIEMAP_H */
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+/* #undef HAVE_LINUX_FS_H */
+
+/* Define to 1 if you have the <linux/magic.h> header file. */
+/* #undef HAVE_LINUX_MAGIC_H */
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+/* #undef HAVE_LINUX_TYPES_H */
+
+/* Define to 1 if you have the `listea' function. */
+/* #undef HAVE_LISTEA */
+
+/* Define to 1 if you have the `listxattr' function. */
+/* #undef HAVE_LISTXATTR */
+
+/* Define to 1 if you have the `llistea' function. */
+/* #undef HAVE_LLISTEA */
+
+/* Define to 1 if you have the `llistxattr' function. */
+/* #undef HAVE_LLISTXATTR */
+
+/* Define to 1 if you have the <localcharset.h> header file. */
+/* #undef HAVE_LOCALCHARSET_H */
+
+/* Define to 1 if you have the `locale_charset' function. */
+/* #undef HAVE_LOCALE_CHARSET */
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have the `localtime_r' function. */
+#define HAVE_LOCALTIME_R 1
+
+/* Define to 1 if the system has the type `long long int'. */
+#define HAVE_LONG_LONG_INT 1
+
+/* Define to 1 if you have the `lsetea' function. */
+/* #undef HAVE_LSETEA */
+
+/* Define to 1 if you have the `lsetxattr' function. */
+/* #undef HAVE_LSETXATTR */
+
+/* Define to 1 if you have the `lstat' function. */
+#define HAVE_LSTAT 1
+
+/* Define to 1 if `lstat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+/* #undef HAVE_LSTAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the `lutimes' function. */
+#define HAVE_LUTIMES 1
+
+/* Define to 1 if you have the <lz4hc.h> header file. */
+/* #undef HAVE_LZ4HC_H */
+
+/* Define to 1 if you have the <lz4.h> header file. */
+/* #undef HAVE_LZ4_H */
+
+/* Define to 1 if you have the <lzma.h> header file. */
+/* #undef HAVE_LZMA_H */
+
+/* Define to 1 if you have the `lzma_stream_encoder_mt' function. */
+/* #undef HAVE_LZMA_STREAM_ENCODER_MT */
+
+/* Define to 1 if you have the <lzo/lzo1x.h> header file. */
+/* #undef HAVE_LZO_LZO1X_H */
+
+/* Define to 1 if you have the <lzo/lzoconf.h> header file. */
+/* #undef HAVE_LZO_LZOCONF_H */
+
+/* Define to 1 if you have the <mbedtls/aes.h> header file. */
+/* #undef HAVE_MBEDTLS_AES_H */
+
+/* Define to 1 if you have the <mbedtls/md.h> header file. */
+/* #undef HAVE_MBEDTLS_MD_H */
+
+/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */
+/* #undef HAVE_MBEDTLS_PKCS5_H */
+
+/* Define to 1 if you have the `mbrtowc' function. */
+#define HAVE_MBRTOWC 1
+
+/* Define to 1 if you have the `mbr_gid_to_uuid' function. */
+/* #undef HAVE_MBR_GID_TO_UUID */
+
+/* Define to 1 if you have the `mbr_uid_to_uuid' function. */
+/* #undef HAVE_MBR_UID_TO_UUID */
+
+/* Define to 1 if you have the `mbr_uuid_to_id' function. */
+/* #undef HAVE_MBR_UUID_TO_ID */
+
+/* Define to 1 if you have the <md5.h> header file. */
+/* #undef HAVE_MD5_H */
+
+/* Define to 1 if you have the <membership.h> header file. */
+#define HAVE_MEMBERSHIP_H 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the `mkdir' function. */
+#define HAVE_MKDIR 1
+
+/* Define to 1 if you have the `mkfifo' function. */
+#define HAVE_MKFIFO 1
+
+/* Define to 1 if you have the `mknod' function. */
+#define HAVE_MKNOD 1
+
+/* Define to 1 if you have the `mkstemp' function. */
+#define HAVE_MKSTEMP 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the <nettle/aes.h> header file. */
+/* #undef HAVE_NETTLE_AES_H */
+
+/* Define to 1 if you have the <nettle/hmac.h> header file. */
+/* #undef HAVE_NETTLE_HMAC_H */
+
+/* Define to 1 if you have the <nettle/md5.h> header file. */
+/* #undef HAVE_NETTLE_MD5_H */
+
+/* Define to 1 if you have the <nettle/pbkdf2.h> header file. */
+/* #undef HAVE_NETTLE_PBKDF2_H */
+
+/* Define to 1 if you have the <nettle/ripemd160.h> header file. */
+/* #undef HAVE_NETTLE_RIPEMD160_H */
+
+/* Define to 1 if you have the <nettle/sha.h> header file. */
+/* #undef HAVE_NETTLE_SHA_H */
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#define HAVE_NL_LANGINFO 1
+
+/* Define to 1 if you have the `openat' function. */
+#define HAVE_OPENAT 1
+
+/* Define to 1 if you have the <openssl/evp.h> header file. */
+/* #undef HAVE_OPENSSL_EVP_H */
+
+/* Define to 1 if you have the <paths.h> header file. */
+#define HAVE_PATHS_H 1
+
+/* Define to 1 if you have the <pcreposix.h> header file. */
+/* #undef HAVE_PCREPOSIX_H */
+
+/* Define to 1 if you have the `pipe' function. */
+#define HAVE_PIPE 1
+
+/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */
+/* #undef HAVE_PKCS5_PBKDF2_HMAC_SHA1 */
+
+/* Define to 1 if you have the `poll' function. */
+#define HAVE_POLL 1
+
+/* Define to 1 if you have the <poll.h> header file. */
+#define HAVE_POLL_H 1
+
+/* Define to 1 if you have the `posix_spawnp' function. */
+#define HAVE_POSIX_SPAWNP 1
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#define HAVE_PTHREAD_H 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have a POSIX compatible readdir_r */
+#define HAVE_READDIR_R 1
+
+/* Define to 1 if you have the `readlink' function. */
+#define HAVE_READLINK 1
+
+/* Define to 1 if you have the `readlinkat' function. */
+#define HAVE_READLINKAT 1
+
+/* Define to 1 if you have the `readpassphrase' function. */
+#define HAVE_READPASSPHRASE 1
+
+/* Define to 1 if you have the <readpassphrase.h> header file. */
+#define HAVE_READPASSPHRASE_H 1
+
+/* Define to 1 if you have the <regex.h> header file. */
+/* #undef HAVE_REGEX_H */
+
+/* Define to 1 if you have the `richacl_alloc' function. */
+/* #undef HAVE_RICHACL_ALLOC */
+
+/* Define to 1 if you have the `richacl_equiv_mode' function. */
+/* #undef HAVE_RICHACL_EQUIV_MODE */
+
+/* Define to 1 if you have the `richacl_free' function. */
+/* #undef HAVE_RICHACL_FREE */
+
+/* Define to 1 if you have the `richacl_get_fd' function. */
+/* #undef HAVE_RICHACL_GET_FD */
+
+/* Define to 1 if you have the `richacl_get_file' function. */
+/* #undef HAVE_RICHACL_GET_FILE */
+
+/* Define to 1 if you have the `richacl_set_fd' function. */
+/* #undef HAVE_RICHACL_SET_FD */
+
+/* Define to 1 if you have the `richacl_set_file' function. */
+/* #undef HAVE_RICHACL_SET_FILE */
+
+/* Define to 1 if you have the <ripemd.h> header file. */
+/* #undef HAVE_RIPEMD_H */
+
+/* Define to 1 if you have the `select' function. */
+#define HAVE_SELECT 1
+
+/* Define to 1 if you have the `setenv' function. */
+#define HAVE_SETENV 1
+
+/* Define to 1 if you have the `setlocale' function. */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have the `setxattr' function. */
+/* #undef HAVE_SETXATTR */
+
+/* Define to 1 if you have the <sha256.h> header file. */
+/* #undef HAVE_SHA256_H */
+
+/* Define to 1 if you have the <sha512.h> header file. */
+/* #undef HAVE_SHA512_H */
+
+/* Define to 1 if you have the <sha.h> header file. */
+/* #undef HAVE_SHA_H */
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the <spawn.h> header file. */
+#define HAVE_SPAWN_H 1
+
+/* Define to 1 if you have the `statfs' function. */
+#define HAVE_STATFS 1
+
+/* Define to 1 if you have the `statvfs' function. */
+#define HAVE_STATVFS 1
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+/* #undef HAVE_STAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the `strerror_r' function. */
+#define HAVE_STRERROR_R 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strncpy_s' function. */
+/* #undef HAVE_STRNCPY_S */
+
+/* Define to 1 if you have the `strrchr' function. */
+#define HAVE_STRRCHR 1
+
+/* Define to 1 if the system has the type `struct richace'. */
+/* #undef HAVE_STRUCT_RICHACE */
+
+/* Define to 1 if the system has the type `struct richacl'. */
+/* #undef HAVE_STRUCT_RICHACL */
+
+/* Define to 1 if `f_namemax' is a member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_NAMEMAX */
+
+/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */
+/* #undef HAVE_STRUCT_STATVFS_F_IOSIZE */
+
+/* Define to 1 if `st_birthtime' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
+
+/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
+
+/* Define to 1 if `st_blksize' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+
+/* Define to 1 if `st_flags' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+
+/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
+
+/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIME_N */
+
+/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIME_USEC */
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC */
+
+/* Define to 1 if `st_umtime' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_UMTIME */
+
+/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */
+#define HAVE_STRUCT_TM_TM_GMTOFF 1
+
+/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */
+/* #undef HAVE_STRUCT_TM___TM_GMTOFF */
+
+/* Define to 1 if the system has the type `struct vfsconf'. */
+#define HAVE_STRUCT_VFSCONF 1
+
+/* Define to 1 if the system has the type `struct xvfsconf'. */
+/* #undef HAVE_STRUCT_XVFSCONF */
+
+/* Define to 1 if you have the `symlink' function. */
+#define HAVE_SYMLINK 1
+
+/* Define to 1 if you have the <sys/acl.h> header file. */
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#define HAVE_SYS_CDEFS_H 1
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/ea.h> header file. */
+/* #undef HAVE_SYS_EA_H */
+
+/* Define to 1 if you have the <sys/extattr.h> header file. */
+/* #undef HAVE_SYS_EXTATTR_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/mkdev.h> header file. */
+/* #undef HAVE_SYS_MKDEV_H */
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#define HAVE_SYS_MOUNT_H 1
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#define HAVE_SYS_POLL_H 1
+
+/* Define to 1 if you have the <sys/richacl.h> header file. */
+/* #undef HAVE_SYS_RICHACL_H */
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/statfs.h> header file. */
+/* #undef HAVE_SYS_STATFS_H */
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+#define HAVE_SYS_STATVFS_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/sysmacros.h> header file. */
+/* #undef HAVE_SYS_SYSMACROS_H */
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+/* #undef HAVE_SYS_UTIME_H */
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#define HAVE_SYS_UTSNAME_H 1
+
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+/* #undef HAVE_SYS_VFS_H */
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+#define HAVE_SYS_XATTR_H 1
+
+/* Define to 1 if you have the `timegm' function. */
+#define HAVE_TIMEGM 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the `tzset' function. */
+#define HAVE_TZSET 1
+
+/* Define to 1 if the system has the type `uintmax_t'. */
+#define HAVE_UINTMAX_T 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `unlinkat' function. */
+#define HAVE_UNLINKAT 1
+
+/* Define to 1 if you have the `unsetenv' function. */
+#define HAVE_UNSETENV 1
+
+/* Define to 1 if the system has the type `unsigned long long'. */
+#define HAVE_UNSIGNED_LONG_LONG 1
+
+/* Define to 1 if the system has the type `unsigned long long int'. */
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+
+/* Define to 1 if you have the `utime' function. */
+#define HAVE_UTIME 1
+
+/* Define to 1 if you have the `utimensat' function. */
+#define HAVE_UTIMENSAT 1
+
+/* Define to 1 if you have the `utimes' function. */
+#define HAVE_UTIMES 1
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if the system has the type `wchar_t'. */
+#define HAVE_WCHAR_T 1
+
+/* Define to 1 if you have the `wcrtomb' function. */
+#define HAVE_WCRTOMB 1
+
+/* Define to 1 if you have the `wcscmp' function. */
+#define HAVE_WCSCMP 1
+
+/* Define to 1 if you have the `wcscpy' function. */
+#define HAVE_WCSCPY 1
+
+/* Define to 1 if you have the `wcslen' function. */
+#define HAVE_WCSLEN 1
+
+/* Define to 1 if you have the `wctomb' function. */
+#define HAVE_WCTOMB 1
+
+/* Define to 1 if you have the <wctype.h> header file. */
+#define HAVE_WCTYPE_H 1
+
+/* Define to 1 if you have the <wincrypt.h> header file. */
+/* #undef HAVE_WINCRYPT_H */
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if you have the <winioctl.h> header file. */
+/* #undef HAVE_WINIOCTL_H */
+
+/* Define to 1 if you have the `wmemcmp' function. */
+#define HAVE_WMEMCMP 1
+
+/* Define to 1 if you have the `wmemcpy' function. */
+#define HAVE_WMEMCPY 1
+
+/* Define to 1 if you have the `wmemmove' function. */
+#define HAVE_WMEMMOVE 1
+
+/* Define to 1 if you have a working EXT2_IOC_GETFLAGS */
+/* #undef HAVE_WORKING_EXT2_IOC_GETFLAGS */
+
+/* Define to 1 if you have a working FS_IOC_GETFLAGS */
+/* #undef HAVE_WORKING_FS_IOC_GETFLAGS */
+
+/* Define to 1 if you have the <zlib.h> header file. */
+/* #undef HAVE_ZLIB_H */
+
+/* Define to 1 if you have the <zstd.h> header file. */
+/* #undef HAVE_ZSTD_H */
+
+/* Define to 1 if you have the `_ctime64_s' function. */
+/* #undef HAVE__CTIME64_S */
+
+/* Define to 1 if you have the `_fseeki64' function. */
+/* #undef HAVE__FSEEKI64 */
+
+/* Define to 1 if you have the `_get_timezone' function. */
+/* #undef HAVE__GET_TIMEZONE */
+
+/* Define to 1 if you have the `_gmtime64_s' function. */
+/* #undef HAVE__GMTIME64_S */
+
+/* Define to 1 if you have the `_localtime64_s' function. */
+/* #undef HAVE__LOCALTIME64_S */
+
+/* Define to 1 if you have the `_mkgmtime64' function. */
+/* #undef HAVE__MKGMTIME64 */
+
+/* Define as const if the declaration of iconv() needs const. */
+/* #undef ICONV_CONST */
+
+/* Version number of libarchive as a single integer */
+#define LIBARCHIVE_VERSION_NUMBER "3004003"
+
+/* Version number of libarchive */
+#define LIBARCHIVE_VERSION_STRING "3.4.3"
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+   slash. */
+/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+   */
+/* #undef MAJOR_IN_MKDEV */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+   <sysmacros.h>. */
+/* #undef MAJOR_IN_SYSMACROS */
+
+/* Define to '0x05020000' for Windows Server 2003 APIs. */
+/* #undef NTDDI_VERSION */
+
+/* Name of package */
+#define PACKAGE "libarchive"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "libarchive-discuss@googlegroups.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libarchive"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libarchive 3.4.3"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libarchive"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "3.4.3"
+
+/* Define to 1 if PCRE_STATIC needs to be defined. */
+/* #undef PCRE_STATIC */
+
+/* The size of `wchar_t', as computed by sizeof. */
+#define SIZEOF_WCHAR_T 4
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if strerror_r returns char *. */
+/* #undef STRERROR_R_CHAR_P */
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Version number of package */
+#define VERSION "3.4.3"
+
+/* Define to '0x0502' for Windows Server 2003 APIs. */
+/* #undef WINVER */
+
+/* Enable large inode numbers on Mac OS X 10.5.  */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to '0x0502' for Windows Server 2003 APIs. */
+/* #undef _WIN32_WINNT */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to match typeof st_gid field of struct stat if <sys/types.h> doesn't
+   define. */
+/* #undef gid_t */
+
+/* Define to `unsigned long' if <sys/types.h> does not define. */
+/* #undef id_t */
+
+/* Define to the type of a signed integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int16_t */
+
+/* Define to the type of a signed integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int32_t */
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int64_t */
+
+/* Define to the widest signed integer type if <stdint.h> and <inttypes.h> do
+   not define. */
+/* #undef intmax_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `long long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to match typeof st_uid field of struct stat if <sys/types.h> doesn't
+   define. */
+/* #undef uid_t */
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint16_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
+
+/* Define to the widest unsigned integer type if <stdint.h> and <inttypes.h>
+   do not define. */
+/* #undef uintmax_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef uintptr_t */
diff --git a/c/autoconf-freebsd/config.h b/c/autoconf-freebsd/config.h
new file mode 100644
--- /dev/null
+++ b/c/autoconf-freebsd/config.h
@@ -0,0 +1,1417 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Darwin ACL support */
+/* #undef ARCHIVE_ACL_DARWIN */
+
+/* FreeBSD ACL support */
+/* #undef ARCHIVE_ACL_FREEBSD */
+
+/* FreeBSD NFSv4 ACL support */
+/* #undef ARCHIVE_ACL_FREEBSD_NFS4 */
+
+/* Linux POSIX.1e ACL support via libacl */
+/* #undef ARCHIVE_ACL_LIBACL */
+
+/* Linux NFSv4 ACL support via librichacl */
+/* #undef ARCHIVE_ACL_LIBRICHACL */
+
+/* Solaris ACL support */
+/* #undef ARCHIVE_ACL_SUNOS */
+
+/* Solaris NFSv4 ACL support */
+/* #undef ARCHIVE_ACL_SUNOS_NFS4 */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBC */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBMD supported. */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBSYSTEM */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_MBEDTLS */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_OPENSSL */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_WIN */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_LIBC */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBMD supported. */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_MBEDTLS */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_NETTLE */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_OPENSSL */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBC */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBMD supported. */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBSYSTEM */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_MBEDTLS */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_OPENSSL */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_WIN */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC2 */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC3 */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBMD supported. */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBSYSTEM */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_MBEDTLS */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_OPENSSL */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_WIN */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC2 */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC3 */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBSYSTEM */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_MBEDTLS */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_OPENSSL */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_WIN */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC2 */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC3 */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBMD supported. */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBSYSTEM */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_MBEDTLS */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_OPENSSL */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_WIN */
+
+/* AIX xattr support */
+/* #undef ARCHIVE_XATTR_AIX */
+
+/* Darwin xattr support */
+/* #undef ARCHIVE_XATTR_DARWIN */
+
+/* FreeBSD xattr support */
+/* #undef ARCHIVE_XATTR_FREEBSD */
+
+/* Linux xattr support */
+/* #undef ARCHIVE_XATTR_LINUX */
+
+/* Version number of bsdcat */
+#define BSDCAT_VERSION_STRING "3.4.3"
+
+/* Version number of bsdcpio */
+#define BSDCPIO_VERSION_STRING "3.4.3"
+
+/* Version number of bsdtar */
+#define BSDTAR_VERSION_STRING "3.4.3"
+
+/* Define to 1 if the system has the type `ace_t'. */
+/* #undef HAVE_ACE_T */
+
+/* Define to 1 if you have the `acl' function. */
+/* #undef HAVE_ACL */
+
+/* Define to 1 if the system has the type `aclent_t'. */
+/* #undef HAVE_ACLENT_T */
+
+/* Define to 1 if you have the `acl_add_flag_np' function. */
+/* #undef HAVE_ACL_ADD_FLAG_NP */
+
+/* Define to 1 if you have the `acl_add_perm' function. */
+/* #undef HAVE_ACL_ADD_PERM */
+
+/* Define to 1 if you have the `acl_clear_flags_np' function. */
+/* #undef HAVE_ACL_CLEAR_FLAGS_NP */
+
+/* Define to 1 if you have the `acl_clear_perms' function. */
+/* #undef HAVE_ACL_CLEAR_PERMS */
+
+/* Define to 1 if you have the `acl_create_entry' function. */
+/* #undef HAVE_ACL_CREATE_ENTRY */
+
+/* Define to 1 if you have the `acl_delete_def_file' function. */
+/* #undef HAVE_ACL_DELETE_DEF_FILE */
+
+/* Define to 1 if the system has the type `acl_entry_t'. */
+/* #undef HAVE_ACL_ENTRY_T */
+
+/* Define to 1 if you have the `acl_free' function. */
+/* #undef HAVE_ACL_FREE */
+
+/* Define to 1 if you have the `acl_get_brand_np' function. */
+/* #undef HAVE_ACL_GET_BRAND_NP */
+
+/* Define to 1 if you have the `acl_get_entry' function. */
+/* #undef HAVE_ACL_GET_ENTRY */
+
+/* Define to 1 if you have the `acl_get_entry_type_np' function. */
+/* #undef HAVE_ACL_GET_ENTRY_TYPE_NP */
+
+/* Define to 1 if you have the `acl_get_fd' function. */
+/* #undef HAVE_ACL_GET_FD */
+
+/* Define to 1 if you have the `acl_get_fd_np' function. */
+/* #undef HAVE_ACL_GET_FD_NP */
+
+/* Define to 1 if you have the `acl_get_file' function. */
+/* #undef HAVE_ACL_GET_FILE */
+
+/* Define to 1 if you have the `acl_get_flagset_np' function. */
+/* #undef HAVE_ACL_GET_FLAGSET_NP */
+
+/* Define to 1 if you have the `acl_get_flag_np' function. */
+/* #undef HAVE_ACL_GET_FLAG_NP */
+
+/* Define to 1 if you have the `acl_get_link_np' function. */
+/* #undef HAVE_ACL_GET_LINK_NP */
+
+/* Define to 1 if you have the `acl_get_perm' function. */
+/* #undef HAVE_ACL_GET_PERM */
+
+/* Define to 1 if you have the `acl_get_permset' function. */
+/* #undef HAVE_ACL_GET_PERMSET */
+
+/* Define to 1 if you have the `acl_get_perm_np' function. */
+/* #undef HAVE_ACL_GET_PERM_NP */
+
+/* Define to 1 if you have the `acl_get_qualifier' function. */
+/* #undef HAVE_ACL_GET_QUALIFIER */
+
+/* Define to 1 if you have the `acl_get_tag_type' function. */
+/* #undef HAVE_ACL_GET_TAG_TYPE */
+
+/* Define to 1 if you have the `acl_init' function. */
+/* #undef HAVE_ACL_INIT */
+
+/* Define to 1 if you have the `acl_is_trivial_np' function. */
+/* #undef HAVE_ACL_IS_TRIVIAL_NP */
+
+/* Define to 1 if you have the <acl/libacl.h> header file. */
+/* #undef HAVE_ACL_LIBACL_H */
+
+/* Define to 1 if the system has the type `acl_permset_t'. */
+/* #undef HAVE_ACL_PERMSET_T */
+
+/* Define to 1 if you have the `acl_set_entry_type_np' function. */
+/* #undef HAVE_ACL_SET_ENTRY_TYPE_NP */
+
+/* Define to 1 if you have the `acl_set_fd' function. */
+/* #undef HAVE_ACL_SET_FD */
+
+/* Define to 1 if you have the `acl_set_fd_np' function. */
+/* #undef HAVE_ACL_SET_FD_NP */
+
+/* Define to 1 if you have the `acl_set_file' function. */
+/* #undef HAVE_ACL_SET_FILE */
+
+/* Define to 1 if you have the `acl_set_link_np' function. */
+/* #undef HAVE_ACL_SET_LINK_NP */
+
+/* Define to 1 if you have the `acl_set_qualifier' function. */
+/* #undef HAVE_ACL_SET_QUALIFIER */
+
+/* Define to 1 if you have the `acl_set_tag_type' function. */
+/* #undef HAVE_ACL_SET_TAG_TYPE */
+
+/* Define to 1 if the system has the type `acl_t'. */
+/* #undef HAVE_ACL_T */
+
+/* Define to 1 if the system has the type `acl_tag_t'. */
+/* #undef HAVE_ACL_TAG_T */
+
+/* Define to 1 if you have the `arc4random_buf' function. */
+#define HAVE_ARC4RANDOM_BUF 1
+
+/* Define to 1 if you have the <attr/xattr.h> header file. */
+/* #undef HAVE_ATTR_XATTR_H */
+
+/* Define to 1 if you have the <bcrypt.h> header file. */
+/* #undef HAVE_BCRYPT_H */
+
+/* Define to 1 if you have the <blake2.h> header file. */
+/* #undef HAVE_BLAKE2_H */
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+/* #undef HAVE_BZLIB_H */
+
+/* Define to 1 if you have the `chflags' function. */
+#define HAVE_CHFLAGS 1
+
+/* Define to 1 if you have the `chown' function. */
+#define HAVE_CHOWN 1
+
+/* Define to 1 if you have the `chroot' function. */
+#define HAVE_CHROOT 1
+
+/* Define to 1 if you have the <copyfile.h> header file. */
+/* #undef HAVE_COPYFILE_H */
+
+/* Define to 1 if you have the `ctime_r' function. */
+#define HAVE_CTIME_R 1
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the `cygwin_conv_path' function. */
+/* #undef HAVE_CYGWIN_CONV_PATH */
+
+/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACE_GETACL */
+
+/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACE_GETACLCNT */
+
+/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACE_SETACL */
+
+/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if
+   you don't. */
+/* #undef HAVE_DECL_ACL_SYNCHRONIZE */
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if
+   you don't. */
+/* #undef HAVE_DECL_ACL_TYPE_EXTENDED */
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACL_TYPE_NFS4 */
+
+/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACL_USER */
+
+/* Define to 1 if you have the declaration of `EXTATTR_NAMESPACE_USER', and to
+   0 if you don't. */
+/* #undef HAVE_DECL_EXTATTR_NAMESPACE_USER */
+
+/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.
+   */
+/* #undef HAVE_DECL_GETACL */
+
+/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_GETACLCNT */
+
+/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT32_MAX 1
+
+/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT32_MIN 1
+
+/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT64_MAX 1
+
+/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT64_MIN 1
+
+/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_INTMAX_MAX 1
+
+/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you
+   don't. */
+#define HAVE_DECL_INTMAX_MIN 1
+
+/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.
+   */
+/* #undef HAVE_DECL_SETACL */
+
+/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_SIZE_MAX 1
+
+/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_SSIZE_MAX 1
+
+/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
+   don't. */
+#define HAVE_DECL_STRERROR_R 1
+
+/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_UINT32_MAX 1
+
+/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_UINT64_MAX 1
+
+/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_UINTMAX_MAX 1
+
+/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if
+   you don't. */
+/* #undef HAVE_DECL_XATTR_NOFOLLOW */
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+   */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have a dirfd function or macro */
+#define HAVE_DIRFD 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Define to 1 if nl_langinfo supports D_MD_ORDER */
+#define HAVE_D_MD_ORDER 1
+
+/* A possible errno value for invalid file format errors */
+#define HAVE_EFTYPE 1
+
+/* A possible errno value for invalid file format errors */
+#define HAVE_EILSEQ 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <expat.h> header file. */
+/* #undef HAVE_EXPAT_H */
+
+/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */
+/* #undef HAVE_EXT2FS_EXT2_FS_H */
+
+/* Define to 1 if you have the `extattr_get_fd' function. */
+/* #undef HAVE_EXTATTR_GET_FD */
+
+/* Define to 1 if you have the `extattr_get_file' function. */
+/* #undef HAVE_EXTATTR_GET_FILE */
+
+/* Define to 1 if you have the `extattr_get_link' function. */
+/* #undef HAVE_EXTATTR_GET_LINK */
+
+/* Define to 1 if you have the `extattr_list_fd' function. */
+/* #undef HAVE_EXTATTR_LIST_FD */
+
+/* Define to 1 if you have the `extattr_list_file' function. */
+/* #undef HAVE_EXTATTR_LIST_FILE */
+
+/* Define to 1 if you have the `extattr_list_link' function. */
+/* #undef HAVE_EXTATTR_LIST_LINK */
+
+/* Define to 1 if you have the `extattr_set_fd' function. */
+/* #undef HAVE_EXTATTR_SET_FD */
+
+/* Define to 1 if you have the `extattr_set_link' function. */
+/* #undef HAVE_EXTATTR_SET_LINK */
+
+/* Define to 1 if you have the `facl' function. */
+/* #undef HAVE_FACL */
+
+/* Define to 1 if you have the `fchdir' function. */
+#define HAVE_FCHDIR 1
+
+/* Define to 1 if you have the `fchflags' function. */
+#define HAVE_FCHFLAGS 1
+
+/* Define to 1 if you have the `fchmod' function. */
+#define HAVE_FCHMOD 1
+
+/* Define to 1 if you have the `fchown' function. */
+#define HAVE_FCHOWN 1
+
+/* Define to 1 if you have the `fcntl' function. */
+#define HAVE_FCNTL 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fdopendir' function. */
+#define HAVE_FDOPENDIR 1
+
+/* Define to 1 if you have the `fgetea' function. */
+/* #undef HAVE_FGETEA */
+
+/* Define to 1 if you have the `fgetxattr' function. */
+/* #undef HAVE_FGETXATTR */
+
+/* Define to 1 if you have the `flistea' function. */
+/* #undef HAVE_FLISTEA */
+
+/* Define to 1 if you have the `flistxattr' function. */
+/* #undef HAVE_FLISTXATTR */
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `fsetea' function. */
+/* #undef HAVE_FSETEA */
+
+/* Define to 1 if you have the `fsetxattr' function. */
+/* #undef HAVE_FSETXATTR */
+
+/* Define to 1 if you have the `fstat' function. */
+#define HAVE_FSTAT 1
+
+/* Define to 1 if you have the `fstatat' function. */
+#define HAVE_FSTATAT 1
+
+/* Define to 1 if you have the `fstatfs' function. */
+#define HAVE_FSTATFS 1
+
+/* Define to 1 if you have the `fstatvfs' function. */
+#define HAVE_FSTATVFS 1
+
+/* Define to 1 if you have the `ftruncate' function. */
+#define HAVE_FTRUNCATE 1
+
+/* Define to 1 if you have the `futimens' function. */
+#define HAVE_FUTIMENS 1
+
+/* Define to 1 if you have the `futimes' function. */
+#define HAVE_FUTIMES 1
+
+/* Define to 1 if you have the `futimesat' function. */
+#define HAVE_FUTIMESAT 1
+
+/* Define to 1 if you have the `getea' function. */
+/* #undef HAVE_GETEA */
+
+/* Define to 1 if you have the `geteuid' function. */
+#define HAVE_GETEUID 1
+
+/* Define to 1 if you have the `getgrgid_r' function. */
+#define HAVE_GETGRGID_R 1
+
+/* Define to 1 if you have the `getgrnam_r' function. */
+#define HAVE_GETGRNAM_R 1
+
+/* Define to 1 if you have the `getpid' function. */
+#define HAVE_GETPID 1
+
+/* Define to 1 if you have the `getpwnam_r' function. */
+#define HAVE_GETPWNAM_R 1
+
+/* Define to 1 if you have the `getpwuid_r' function. */
+#define HAVE_GETPWUID_R 1
+
+/* Define to 1 if you have the `getvfsbyname' function. */
+#define HAVE_GETVFSBYNAME 1
+
+/* Define to 1 if you have the `getxattr' function. */
+/* #undef HAVE_GETXATTR */
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#define HAVE_GMTIME_R 1
+
+/* Define to 1 if you have the <grp.h> header file. */
+#define HAVE_GRP_H 1
+
+/* Define if you have the iconv() function and it works. */
+/* #undef HAVE_ICONV */
+
+/* Define to 1 if you have the <iconv.h> header file. */
+/* #undef HAVE_ICONV_H */
+
+/* Define to 1 if the system has the type `intmax_t'. */
+#define HAVE_INTMAX_T 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <io.h> header file. */
+/* #undef HAVE_IO_H */
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define to 1 if you have the `lchflags' function. */
+#define HAVE_LCHFLAGS 1
+
+/* Define to 1 if you have the `lchmod' function. */
+#define HAVE_LCHMOD 1
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the `lgetea' function. */
+/* #undef HAVE_LGETEA */
+
+/* Define to 1 if you have the `lgetxattr' function. */
+/* #undef HAVE_LGETXATTR */
+
+/* Define to 1 if you have the `acl' library (-lacl). */
+/* #undef HAVE_LIBACL */
+
+/* Define to 1 if you have the `b2' library (-lb2). */
+/* #undef HAVE_LIBB2 */
+
+/* Define to 1 if you have the `bz2' library (-lbz2). */
+/* #undef HAVE_LIBBZ2 */
+
+/* Define to 1 if you have the `charset' library (-lcharset). */
+/* #undef HAVE_LIBCHARSET */
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+/* #undef HAVE_LIBCRYPTO */
+
+/* Define to 1 if you have the `eay32' library (-leay32). */
+/* #undef HAVE_LIBEAY32 */
+
+/* Define to 1 if you have the `eay64' library (-leay64). */
+/* #undef HAVE_LIBEAY64 */
+
+/* Define to 1 if you have the `expat' library (-lexpat). */
+/* #undef HAVE_LIBEXPAT */
+
+/* Define to 1 if you have the `lz4' library (-llz4). */
+/* #undef HAVE_LIBLZ4 */
+
+/* Define to 1 if you have the `lzma' library (-llzma). */
+/* #undef HAVE_LIBLZMA */
+
+/* Define to 1 if you have the `lzo2' library (-llzo2). */
+/* #undef HAVE_LIBLZO2 */
+
+/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */
+/* #undef HAVE_LIBMBEDCRYPTO */
+
+/* Define to 1 if you have the `md' library (-lmd). */
+
+/* Define to 1 if you have the `nettle' library (-lnettle). */
+/* #undef HAVE_LIBNETTLE */
+
+/* Define to 1 if you have the `pcre' library (-lpcre). */
+/* #undef HAVE_LIBPCRE */
+
+/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */
+/* #undef HAVE_LIBPCREPOSIX */
+
+/* Define to 1 if you have the `regex' library (-lregex). */
+/* #undef HAVE_LIBREGEX */
+
+/* Define to 1 if you have the `richacl' library (-lrichacl). */
+/* #undef HAVE_LIBRICHACL */
+
+/* Define to 1 if you have the `xml2' library (-lxml2). */
+/* #undef HAVE_LIBXML2 */
+
+/* Define to 1 if you have the <libxml/xmlreader.h> header file. */
+/* #undef HAVE_LIBXML_XMLREADER_H */
+
+/* Define to 1 if you have the <libxml/xmlwriter.h> header file. */
+/* #undef HAVE_LIBXML_XMLWRITER_H */
+
+/* Define to 1 if you have the `z' library (-lz). */
+/* #undef HAVE_LIBZ */
+
+/* Define to 1 if you have the `zstd' library (-lzstd). */
+/* #undef HAVE_LIBZSTD */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the `link' function. */
+#define HAVE_LINK 1
+
+/* Define to 1 if you have the <linux/fiemap.h> header file. */
+/* #undef HAVE_LINUX_FIEMAP_H */
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+/* #undef HAVE_LINUX_FS_H */
+
+/* Define to 1 if you have the <linux/magic.h> header file. */
+/* #undef HAVE_LINUX_MAGIC_H */
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+/* #undef HAVE_LINUX_TYPES_H */
+
+/* Define to 1 if you have the `listea' function. */
+/* #undef HAVE_LISTEA */
+
+/* Define to 1 if you have the `listxattr' function. */
+/* #undef HAVE_LISTXATTR */
+
+/* Define to 1 if you have the `llistea' function. */
+/* #undef HAVE_LLISTEA */
+
+/* Define to 1 if you have the `llistxattr' function. */
+/* #undef HAVE_LLISTXATTR */
+
+/* Define to 1 if you have the <localcharset.h> header file. */
+/* #undef HAVE_LOCALCHARSET_H */
+
+/* Define to 1 if you have the `locale_charset' function. */
+/* #undef HAVE_LOCALE_CHARSET */
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have the `localtime_r' function. */
+#define HAVE_LOCALTIME_R 1
+
+/* Define to 1 if the system has the type `long long int'. */
+#define HAVE_LONG_LONG_INT 1
+
+/* Define to 1 if you have the `lsetea' function. */
+/* #undef HAVE_LSETEA */
+
+/* Define to 1 if you have the `lsetxattr' function. */
+/* #undef HAVE_LSETXATTR */
+
+/* Define to 1 if you have the `lstat' function. */
+#define HAVE_LSTAT 1
+
+/* Define to 1 if `lstat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+/* #undef HAVE_LSTAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the `lutimes' function. */
+#define HAVE_LUTIMES 1
+
+/* Define to 1 if you have the <lz4hc.h> header file. */
+/* #undef HAVE_LZ4HC_H */
+
+/* Define to 1 if you have the <lz4.h> header file. */
+/* #undef HAVE_LZ4_H */
+
+/* Define to 1 if you have the <lzma.h> header file. */
+/* #undef HAVE_LZMA_H */
+
+/* Define to 1 if you have the `lzma_stream_encoder_mt' function. */
+/* #undef HAVE_LZMA_STREAM_ENCODER_MT */
+
+/* Define to 1 if you have the <lzo/lzo1x.h> header file. */
+/* #undef HAVE_LZO_LZO1X_H */
+
+/* Define to 1 if you have the <lzo/lzoconf.h> header file. */
+/* #undef HAVE_LZO_LZOCONF_H */
+
+/* Define to 1 if you have the <mbedtls/aes.h> header file. */
+/* #undef HAVE_MBEDTLS_AES_H */
+
+/* Define to 1 if you have the <mbedtls/md.h> header file. */
+/* #undef HAVE_MBEDTLS_MD_H */
+
+/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */
+/* #undef HAVE_MBEDTLS_PKCS5_H */
+
+/* Define to 1 if you have the `mbrtowc' function. */
+#define HAVE_MBRTOWC 1
+
+/* Define to 1 if you have the `mbr_gid_to_uuid' function. */
+/* #undef HAVE_MBR_GID_TO_UUID */
+
+/* Define to 1 if you have the `mbr_uid_to_uuid' function. */
+/* #undef HAVE_MBR_UID_TO_UUID */
+
+/* Define to 1 if you have the `mbr_uuid_to_id' function. */
+/* #undef HAVE_MBR_UUID_TO_ID */
+
+/* Define to 1 if you have the <md5.h> header file. */
+
+/* Define to 1 if you have the <membership.h> header file. */
+/* #undef HAVE_MEMBERSHIP_H */
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the `mkdir' function. */
+#define HAVE_MKDIR 1
+
+/* Define to 1 if you have the `mkfifo' function. */
+#define HAVE_MKFIFO 1
+
+/* Define to 1 if you have the `mknod' function. */
+#define HAVE_MKNOD 1
+
+/* Define to 1 if you have the `mkstemp' function. */
+#define HAVE_MKSTEMP 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the <nettle/aes.h> header file. */
+/* #undef HAVE_NETTLE_AES_H */
+
+/* Define to 1 if you have the <nettle/hmac.h> header file. */
+/* #undef HAVE_NETTLE_HMAC_H */
+
+/* Define to 1 if you have the <nettle/md5.h> header file. */
+/* #undef HAVE_NETTLE_MD5_H */
+
+/* Define to 1 if you have the <nettle/pbkdf2.h> header file. */
+/* #undef HAVE_NETTLE_PBKDF2_H */
+
+/* Define to 1 if you have the <nettle/ripemd160.h> header file. */
+/* #undef HAVE_NETTLE_RIPEMD160_H */
+
+/* Define to 1 if you have the <nettle/sha.h> header file. */
+/* #undef HAVE_NETTLE_SHA_H */
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#define HAVE_NL_LANGINFO 1
+
+/* Define to 1 if you have the `openat' function. */
+#define HAVE_OPENAT 1
+
+/* Define to 1 if you have the <openssl/evp.h> header file. */
+/* #undef HAVE_OPENSSL_EVP_H */
+
+/* Define to 1 if you have the <paths.h> header file. */
+#define HAVE_PATHS_H 1
+
+/* Define to 1 if you have the <pcreposix.h> header file. */
+/* #undef HAVE_PCREPOSIX_H */
+
+/* Define to 1 if you have the `pipe' function. */
+#define HAVE_PIPE 1
+
+/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */
+/* #undef HAVE_PKCS5_PBKDF2_HMAC_SHA1 */
+
+/* Define to 1 if you have the `poll' function. */
+#define HAVE_POLL 1
+
+/* Define to 1 if you have the <poll.h> header file. */
+#define HAVE_POLL_H 1
+
+/* Define to 1 if you have the `posix_spawnp' function. */
+#define HAVE_POSIX_SPAWNP 1
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#define HAVE_PTHREAD_H 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have a POSIX compatible readdir_r */
+#define HAVE_READDIR_R 1
+
+/* Define to 1 if you have the `readlink' function. */
+#define HAVE_READLINK 1
+
+/* Define to 1 if you have the `readlinkat' function. */
+#define HAVE_READLINKAT 1
+
+/* Define to 1 if you have the `readpassphrase' function. */
+#define HAVE_READPASSPHRASE 1
+
+/* Define to 1 if you have the <readpassphrase.h> header file. */
+#define HAVE_READPASSPHRASE_H 1
+
+/* Define to 1 if you have the <regex.h> header file. */
+/* #undef HAVE_REGEX_H */
+
+/* Define to 1 if you have the `richacl_alloc' function. */
+/* #undef HAVE_RICHACL_ALLOC */
+
+/* Define to 1 if you have the `richacl_equiv_mode' function. */
+/* #undef HAVE_RICHACL_EQUIV_MODE */
+
+/* Define to 1 if you have the `richacl_free' function. */
+/* #undef HAVE_RICHACL_FREE */
+
+/* Define to 1 if you have the `richacl_get_fd' function. */
+/* #undef HAVE_RICHACL_GET_FD */
+
+/* Define to 1 if you have the `richacl_get_file' function. */
+/* #undef HAVE_RICHACL_GET_FILE */
+
+/* Define to 1 if you have the `richacl_set_fd' function. */
+/* #undef HAVE_RICHACL_SET_FD */
+
+/* Define to 1 if you have the `richacl_set_file' function. */
+/* #undef HAVE_RICHACL_SET_FILE */
+
+/* Define to 1 if you have the <ripemd.h> header file. */
+
+/* Define to 1 if you have the `select' function. */
+#define HAVE_SELECT 1
+
+/* Define to 1 if you have the `setenv' function. */
+#define HAVE_SETENV 1
+
+/* Define to 1 if you have the `setlocale' function. */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have the `setxattr' function. */
+/* #undef HAVE_SETXATTR */
+
+/* Define to 1 if you have the <sha256.h> header file. */
+
+/* Define to 1 if you have the <sha512.h> header file. */
+
+/* Define to 1 if you have the <sha.h> header file. */
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the <spawn.h> header file. */
+#define HAVE_SPAWN_H 1
+
+/* Define to 1 if you have the `statfs' function. */
+#define HAVE_STATFS 1
+
+/* Define to 1 if you have the `statvfs' function. */
+#define HAVE_STATVFS 1
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+/* #undef HAVE_STAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the `strerror_r' function. */
+#define HAVE_STRERROR_R 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strncpy_s' function. */
+/* #undef HAVE_STRNCPY_S */
+
+/* Define to 1 if you have the `strrchr' function. */
+#define HAVE_STRRCHR 1
+
+/* Define to 1 if the system has the type `struct richace'. */
+/* #undef HAVE_STRUCT_RICHACE */
+
+/* Define to 1 if the system has the type `struct richacl'. */
+/* #undef HAVE_STRUCT_RICHACL */
+
+/* Define to 1 if `f_namemax' is a member of `struct statfs'. */
+#define HAVE_STRUCT_STATFS_F_NAMEMAX 1
+
+/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */
+/* #undef HAVE_STRUCT_STATVFS_F_IOSIZE */
+
+/* Define to 1 if `st_birthtime' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
+
+/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
+
+/* Define to 1 if `st_blksize' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+
+/* Define to 1 if `st_flags' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+
+/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
+
+/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIME_N */
+
+/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIME_USEC */
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+
+/* Define to 1 if `st_umtime' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_UMTIME */
+
+/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */
+#define HAVE_STRUCT_TM_TM_GMTOFF 1
+
+/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */
+/* #undef HAVE_STRUCT_TM___TM_GMTOFF */
+
+/* Define to 1 if the system has the type `struct vfsconf'. */
+#define HAVE_STRUCT_VFSCONF 1
+
+/* Define to 1 if the system has the type `struct xvfsconf'. */
+#define HAVE_STRUCT_XVFSCONF 1
+
+/* Define to 1 if you have the `symlink' function. */
+#define HAVE_SYMLINK 1
+
+/* Define to 1 if you have the <sys/acl.h> header file. */
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#define HAVE_SYS_CDEFS_H 1
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/ea.h> header file. */
+/* #undef HAVE_SYS_EA_H */
+
+/* Define to 1 if you have the <sys/extattr.h> header file. */
+#define HAVE_SYS_EXTATTR_H 1
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/mkdev.h> header file. */
+/* #undef HAVE_SYS_MKDEV_H */
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#define HAVE_SYS_MOUNT_H 1
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#define HAVE_SYS_POLL_H 1
+
+/* Define to 1 if you have the <sys/richacl.h> header file. */
+/* #undef HAVE_SYS_RICHACL_H */
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/statfs.h> header file. */
+/* #undef HAVE_SYS_STATFS_H */
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+#define HAVE_SYS_STATVFS_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/sysmacros.h> header file. */
+/* #undef HAVE_SYS_SYSMACROS_H */
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+/* #undef HAVE_SYS_UTIME_H */
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#define HAVE_SYS_UTSNAME_H 1
+
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+/* #undef HAVE_SYS_VFS_H */
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+/* #undef HAVE_SYS_XATTR_H */
+
+/* Define to 1 if you have the `timegm' function. */
+#define HAVE_TIMEGM 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the `tzset' function. */
+#define HAVE_TZSET 1
+
+/* Define to 1 if the system has the type `uintmax_t'. */
+#define HAVE_UINTMAX_T 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `unlinkat' function. */
+#define HAVE_UNLINKAT 1
+
+/* Define to 1 if you have the `unsetenv' function. */
+#define HAVE_UNSETENV 1
+
+/* Define to 1 if the system has the type `unsigned long long'. */
+#define HAVE_UNSIGNED_LONG_LONG 1
+
+/* Define to 1 if the system has the type `unsigned long long int'. */
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+
+/* Define to 1 if you have the `utime' function. */
+#define HAVE_UTIME 1
+
+/* Define to 1 if you have the `utimensat' function. */
+#define HAVE_UTIMENSAT 1
+
+/* Define to 1 if you have the `utimes' function. */
+#define HAVE_UTIMES 1
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if the system has the type `wchar_t'. */
+#define HAVE_WCHAR_T 1
+
+/* Define to 1 if you have the `wcrtomb' function. */
+#define HAVE_WCRTOMB 1
+
+/* Define to 1 if you have the `wcscmp' function. */
+#define HAVE_WCSCMP 1
+
+/* Define to 1 if you have the `wcscpy' function. */
+#define HAVE_WCSCPY 1
+
+/* Define to 1 if you have the `wcslen' function. */
+#define HAVE_WCSLEN 1
+
+/* Define to 1 if you have the `wctomb' function. */
+#define HAVE_WCTOMB 1
+
+/* Define to 1 if you have the <wctype.h> header file. */
+#define HAVE_WCTYPE_H 1
+
+/* Define to 1 if you have the <wincrypt.h> header file. */
+/* #undef HAVE_WINCRYPT_H */
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if you have the <winioctl.h> header file. */
+/* #undef HAVE_WINIOCTL_H */
+
+/* Define to 1 if you have the `wmemcmp' function. */
+#define HAVE_WMEMCMP 1
+
+/* Define to 1 if you have the `wmemcpy' function. */
+#define HAVE_WMEMCPY 1
+
+/* Define to 1 if you have the `wmemmove' function. */
+#define HAVE_WMEMMOVE 1
+
+/* Define to 1 if you have a working EXT2_IOC_GETFLAGS */
+/* #undef HAVE_WORKING_EXT2_IOC_GETFLAGS */
+
+/* Define to 1 if you have a working FS_IOC_GETFLAGS */
+/* #undef HAVE_WORKING_FS_IOC_GETFLAGS */
+
+/* Define to 1 if you have the <zlib.h> header file. */
+/* #undef HAVE_ZLIB_H */
+
+/* Define to 1 if you have the <zstd.h> header file. */
+/* #undef HAVE_ZSTD_H */
+
+/* Define to 1 if you have the `_ctime64_s' function. */
+/* #undef HAVE__CTIME64_S */
+
+/* Define to 1 if you have the `_fseeki64' function. */
+/* #undef HAVE__FSEEKI64 */
+
+/* Define to 1 if you have the `_get_timezone' function. */
+/* #undef HAVE__GET_TIMEZONE */
+
+/* Define to 1 if you have the `_gmtime64_s' function. */
+/* #undef HAVE__GMTIME64_S */
+
+/* Define to 1 if you have the `_localtime64_s' function. */
+/* #undef HAVE__LOCALTIME64_S */
+
+/* Define to 1 if you have the `_mkgmtime64' function. */
+/* #undef HAVE__MKGMTIME64 */
+
+/* Define as const if the declaration of iconv() needs const. */
+/* #undef ICONV_CONST */
+
+/* Version number of libarchive as a single integer */
+#define LIBARCHIVE_VERSION_NUMBER "3004003"
+
+/* Version number of libarchive */
+#define LIBARCHIVE_VERSION_STRING "3.4.3"
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+   slash. */
+#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+   */
+/* #undef MAJOR_IN_MKDEV */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+   <sysmacros.h>. */
+/* #undef MAJOR_IN_SYSMACROS */
+
+/* Define to '0x05020000' for Windows Server 2003 APIs. */
+/* #undef NTDDI_VERSION */
+
+/* Name of package */
+#define PACKAGE "libarchive"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "libarchive-discuss@googlegroups.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libarchive"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libarchive 3.4.3"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libarchive"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "3.4.3"
+
+/* Define to 1 if PCRE_STATIC needs to be defined. */
+/* #undef PCRE_STATIC */
+
+/* The size of `wchar_t', as computed by sizeof. */
+#define SIZEOF_WCHAR_T 4
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if strerror_r returns char *. */
+/* #undef STRERROR_R_CHAR_P */
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Version number of package */
+#define VERSION "3.4.3"
+
+/* Define to '0x0502' for Windows Server 2003 APIs. */
+/* #undef WINVER */
+
+/* Enable large inode numbers on Mac OS X 10.5.  */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to '0x0502' for Windows Server 2003 APIs. */
+/* #undef _WIN32_WINNT */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to match typeof st_gid field of struct stat if <sys/types.h> doesn't
+   define. */
+/* #undef gid_t */
+
+/* Define to `unsigned long' if <sys/types.h> does not define. */
+/* #undef id_t */
+
+/* Define to the type of a signed integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int16_t */
+
+/* Define to the type of a signed integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int32_t */
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int64_t */
+
+/* Define to the widest signed integer type if <stdint.h> and <inttypes.h> do
+   not define. */
+/* #undef intmax_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `long long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to match typeof st_uid field of struct stat if <sys/types.h> doesn't
+   define. */
+/* #undef uid_t */
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint16_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
+
+/* Define to the widest unsigned integer type if <stdint.h> and <inttypes.h>
+   do not define. */
+/* #undef uintmax_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef uintptr_t */
diff --git a/c/autoconf-linux/config.h b/c/autoconf-linux/config.h
new file mode 100644
--- /dev/null
+++ b/c/autoconf-linux/config.h
@@ -0,0 +1,1427 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Darwin ACL support */
+/* #undef ARCHIVE_ACL_DARWIN */
+
+/* FreeBSD ACL support */
+/* #undef ARCHIVE_ACL_FREEBSD */
+
+/* FreeBSD NFSv4 ACL support */
+/* #undef ARCHIVE_ACL_FREEBSD_NFS4 */
+
+/* Linux POSIX.1e ACL support via libacl */
+/* #undef ARCHIVE_ACL_LIBACL */
+
+/* Linux NFSv4 ACL support via librichacl */
+/* #undef ARCHIVE_ACL_LIBRICHACL */
+
+/* Solaris ACL support */
+/* #undef ARCHIVE_ACL_SUNOS */
+
+/* Solaris NFSv4 ACL support */
+/* #undef ARCHIVE_ACL_SUNOS_NFS4 */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBC */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBMD supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBMD */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_LIBSYSTEM */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_MBEDTLS */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_NETTLE */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_OPENSSL */
+
+/* MD5 via ARCHIVE_CRYPTO_MD5_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_MD5_WIN */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_LIBC */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_LIBMD supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_LIBMD */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_MBEDTLS */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_NETTLE */
+
+/* RMD160 via ARCHIVE_CRYPTO_RMD160_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_RMD160_OPENSSL */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBC */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBMD supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBMD */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_LIBSYSTEM */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_MBEDTLS */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_NETTLE */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_OPENSSL */
+
+/* SHA1 via ARCHIVE_CRYPTO_SHA1_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA1_WIN */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC2 */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBC3 */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBMD supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBMD */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_LIBSYSTEM */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_MBEDTLS */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_NETTLE */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_OPENSSL */
+
+/* SHA256 via ARCHIVE_CRYPTO_SHA256_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA256_WIN */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC2 */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBC3 */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_LIBSYSTEM */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_MBEDTLS */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_NETTLE */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_OPENSSL */
+
+/* SHA384 via ARCHIVE_CRYPTO_SHA384_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA384_WIN */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC2 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC2 */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBC3 supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBC3 */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBMD supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBMD */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_LIBSYSTEM supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_LIBSYSTEM */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_MBEDTLS supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_MBEDTLS */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_NETTLE supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_NETTLE */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_OPENSSL supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_OPENSSL */
+
+/* SHA512 via ARCHIVE_CRYPTO_SHA512_WIN supported. */
+/* #undef ARCHIVE_CRYPTO_SHA512_WIN */
+
+/* AIX xattr support */
+/* #undef ARCHIVE_XATTR_AIX */
+
+/* Darwin xattr support */
+/* #undef ARCHIVE_XATTR_DARWIN */
+
+/* FreeBSD xattr support */
+/* #undef ARCHIVE_XATTR_FREEBSD */
+
+/* Linux xattr support */
+/* #undef ARCHIVE_XATTR_LINUX */
+
+/* Version number of bsdcat */
+#define BSDCAT_VERSION_STRING "3.4.3"
+
+/* Version number of bsdcpio */
+#define BSDCPIO_VERSION_STRING "3.4.3"
+
+/* Version number of bsdtar */
+#define BSDTAR_VERSION_STRING "3.4.3"
+
+/* Define to 1 if the system has the type `ace_t'. */
+/* #undef HAVE_ACE_T */
+
+/* Define to 1 if you have the `acl' function. */
+/* #undef HAVE_ACL */
+
+/* Define to 1 if the system has the type `aclent_t'. */
+/* #undef HAVE_ACLENT_T */
+
+/* Define to 1 if you have the `acl_add_flag_np' function. */
+/* #undef HAVE_ACL_ADD_FLAG_NP */
+
+/* Define to 1 if you have the `acl_add_perm' function. */
+/* #undef HAVE_ACL_ADD_PERM */
+
+/* Define to 1 if you have the `acl_clear_flags_np' function. */
+/* #undef HAVE_ACL_CLEAR_FLAGS_NP */
+
+/* Define to 1 if you have the `acl_clear_perms' function. */
+/* #undef HAVE_ACL_CLEAR_PERMS */
+
+/* Define to 1 if you have the `acl_create_entry' function. */
+/* #undef HAVE_ACL_CREATE_ENTRY */
+
+/* Define to 1 if you have the `acl_delete_def_file' function. */
+/* #undef HAVE_ACL_DELETE_DEF_FILE */
+
+/* Define to 1 if the system has the type `acl_entry_t'. */
+/* #undef HAVE_ACL_ENTRY_T */
+
+/* Define to 1 if you have the `acl_free' function. */
+/* #undef HAVE_ACL_FREE */
+
+/* Define to 1 if you have the `acl_get_brand_np' function. */
+/* #undef HAVE_ACL_GET_BRAND_NP */
+
+/* Define to 1 if you have the `acl_get_entry' function. */
+/* #undef HAVE_ACL_GET_ENTRY */
+
+/* Define to 1 if you have the `acl_get_entry_type_np' function. */
+/* #undef HAVE_ACL_GET_ENTRY_TYPE_NP */
+
+/* Define to 1 if you have the `acl_get_fd' function. */
+/* #undef HAVE_ACL_GET_FD */
+
+/* Define to 1 if you have the `acl_get_fd_np' function. */
+/* #undef HAVE_ACL_GET_FD_NP */
+
+/* Define to 1 if you have the `acl_get_file' function. */
+/* #undef HAVE_ACL_GET_FILE */
+
+/* Define to 1 if you have the `acl_get_flagset_np' function. */
+/* #undef HAVE_ACL_GET_FLAGSET_NP */
+
+/* Define to 1 if you have the `acl_get_flag_np' function. */
+/* #undef HAVE_ACL_GET_FLAG_NP */
+
+/* Define to 1 if you have the `acl_get_link_np' function. */
+/* #undef HAVE_ACL_GET_LINK_NP */
+
+/* Define to 1 if you have the `acl_get_perm' function. */
+/* #undef HAVE_ACL_GET_PERM */
+
+/* Define to 1 if you have the `acl_get_permset' function. */
+/* #undef HAVE_ACL_GET_PERMSET */
+
+/* Define to 1 if you have the `acl_get_perm_np' function. */
+/* #undef HAVE_ACL_GET_PERM_NP */
+
+/* Define to 1 if you have the `acl_get_qualifier' function. */
+/* #undef HAVE_ACL_GET_QUALIFIER */
+
+/* Define to 1 if you have the `acl_get_tag_type' function. */
+/* #undef HAVE_ACL_GET_TAG_TYPE */
+
+/* Define to 1 if you have the `acl_init' function. */
+/* #undef HAVE_ACL_INIT */
+
+/* Define to 1 if you have the `acl_is_trivial_np' function. */
+/* #undef HAVE_ACL_IS_TRIVIAL_NP */
+
+/* Define to 1 if you have the <acl/libacl.h> header file. */
+#define HAVE_ACL_LIBACL_H 1
+
+/* Define to 1 if the system has the type `acl_permset_t'. */
+/* #undef HAVE_ACL_PERMSET_T */
+
+/* Define to 1 if you have the `acl_set_entry_type_np' function. */
+/* #undef HAVE_ACL_SET_ENTRY_TYPE_NP */
+
+/* Define to 1 if you have the `acl_set_fd' function. */
+/* #undef HAVE_ACL_SET_FD */
+
+/* Define to 1 if you have the `acl_set_fd_np' function. */
+/* #undef HAVE_ACL_SET_FD_NP */
+
+/* Define to 1 if you have the `acl_set_file' function. */
+/* #undef HAVE_ACL_SET_FILE */
+
+/* Define to 1 if you have the `acl_set_link_np' function. */
+/* #undef HAVE_ACL_SET_LINK_NP */
+
+/* Define to 1 if you have the `acl_set_qualifier' function. */
+/* #undef HAVE_ACL_SET_QUALIFIER */
+
+/* Define to 1 if you have the `acl_set_tag_type' function. */
+/* #undef HAVE_ACL_SET_TAG_TYPE */
+
+/* Define to 1 if the system has the type `acl_t'. */
+/* #undef HAVE_ACL_T */
+
+/* Define to 1 if the system has the type `acl_tag_t'. */
+/* #undef HAVE_ACL_TAG_T */
+
+/* Define to 1 if you have the `arc4random_buf' function. */
+/* #undef HAVE_ARC4RANDOM_BUF */
+
+/* Define to 1 if you have the <attr/xattr.h> header file. */
+/* #undef HAVE_ATTR_XATTR_H */
+
+/* Define to 1 if you have the <bcrypt.h> header file. */
+/* #undef HAVE_BCRYPT_H */
+
+/* Define to 1 if you have the <blake2.h> header file. */
+/* #undef HAVE_BLAKE2_H */
+
+/* Define to 1 if you have the <bzlib.h> header file. */
+/* #undef HAVE_BZLIB_H */
+
+/* Define to 1 if you have the `chflags' function. */
+/* #undef HAVE_CHFLAGS */
+
+/* Define to 1 if you have the `chown' function. */
+#define HAVE_CHOWN 1
+
+/* Define to 1 if you have the `chroot' function. */
+#define HAVE_CHROOT 1
+
+/* Define to 1 if you have the <copyfile.h> header file. */
+/* #undef HAVE_COPYFILE_H */
+
+/* Define to 1 if you have the `ctime_r' function. */
+#define HAVE_CTIME_R 1
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the `cygwin_conv_path' function. */
+/* #undef HAVE_CYGWIN_CONV_PATH */
+
+/* Define to 1 if you have the declaration of `ACE_GETACL', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACE_GETACL */
+
+/* Define to 1 if you have the declaration of `ACE_GETACLCNT', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACE_GETACLCNT */
+
+/* Define to 1 if you have the declaration of `ACE_SETACL', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACE_SETACL */
+
+/* Define to 1 if you have the declaration of `ACL_SYNCHRONIZE', and to 0 if
+   you don't. */
+/* #undef HAVE_DECL_ACL_SYNCHRONIZE */
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_EXTENDED', and to 0 if
+   you don't. */
+/* #undef HAVE_DECL_ACL_TYPE_EXTENDED */
+
+/* Define to 1 if you have the declaration of `ACL_TYPE_NFS4', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACL_TYPE_NFS4 */
+
+/* Define to 1 if you have the declaration of `ACL_USER', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_ACL_USER */
+
+/* Define to 1 if you have the declaration of `EXTATTR_NAMESPACE_USER', and to
+   0 if you don't. */
+/* #undef HAVE_DECL_EXTATTR_NAMESPACE_USER */
+
+/* Define to 1 if you have the declaration of `GETACL', and to 0 if you don't.
+   */
+/* #undef HAVE_DECL_GETACL */
+
+/* Define to 1 if you have the declaration of `GETACLCNT', and to 0 if you
+   don't. */
+/* #undef HAVE_DECL_GETACLCNT */
+
+/* Define to 1 if you have the declaration of `INT32_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT32_MAX 1
+
+/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT32_MIN 1
+
+/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT64_MAX 1
+
+/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you
+   don't. */
+#define HAVE_DECL_INT64_MIN 1
+
+/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_INTMAX_MAX 1
+
+/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you
+   don't. */
+#define HAVE_DECL_INTMAX_MIN 1
+
+/* Define to 1 if you have the declaration of `SETACL', and to 0 if you don't.
+   */
+/* #undef HAVE_DECL_SETACL */
+
+/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_SIZE_MAX 1
+
+/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_SSIZE_MAX 1
+
+/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
+   don't. */
+#define HAVE_DECL_STRERROR_R 1
+
+/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_UINT32_MAX 1
+
+/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_UINT64_MAX 1
+
+/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you
+   don't. */
+#define HAVE_DECL_UINTMAX_MAX 1
+
+/* Define to 1 if you have the declaration of `XATTR_NOFOLLOW', and to 0 if
+   you don't. */
+/* #undef HAVE_DECL_XATTR_NOFOLLOW */
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+   */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have a dirfd function or macro */
+#define HAVE_DIRFD 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Define to 1 if nl_langinfo supports D_MD_ORDER */
+/* #undef HAVE_D_MD_ORDER */
+
+/* A possible errno value for invalid file format errors */
+/* #undef HAVE_EFTYPE */
+
+/* A possible errno value for invalid file format errors */
+#define HAVE_EILSEQ 1
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the <expat.h> header file. */
+/* #undef HAVE_EXPAT_H */
+
+/* Define to 1 if you have the <ext2fs/ext2_fs.h> header file. */
+
+/* Define to 1 if you have the `extattr_get_fd' function. */
+/* #undef HAVE_EXTATTR_GET_FD */
+
+/* Define to 1 if you have the `extattr_get_file' function. */
+/* #undef HAVE_EXTATTR_GET_FILE */
+
+/* Define to 1 if you have the `extattr_get_link' function. */
+/* #undef HAVE_EXTATTR_GET_LINK */
+
+/* Define to 1 if you have the `extattr_list_fd' function. */
+/* #undef HAVE_EXTATTR_LIST_FD */
+
+/* Define to 1 if you have the `extattr_list_file' function. */
+/* #undef HAVE_EXTATTR_LIST_FILE */
+
+/* Define to 1 if you have the `extattr_list_link' function. */
+/* #undef HAVE_EXTATTR_LIST_LINK */
+
+/* Define to 1 if you have the `extattr_set_fd' function. */
+/* #undef HAVE_EXTATTR_SET_FD */
+
+/* Define to 1 if you have the `extattr_set_link' function. */
+/* #undef HAVE_EXTATTR_SET_LINK */
+
+/* Define to 1 if you have the `facl' function. */
+/* #undef HAVE_FACL */
+
+/* Define to 1 if you have the `fchdir' function. */
+#define HAVE_FCHDIR 1
+
+/* Define to 1 if you have the `fchflags' function. */
+/* #undef HAVE_FCHFLAGS */
+
+/* Define to 1 if you have the `fchmod' function. */
+#define HAVE_FCHMOD 1
+
+/* Define to 1 if you have the `fchown' function. */
+#define HAVE_FCHOWN 1
+
+/* Define to 1 if you have the `fcntl' function. */
+#define HAVE_FCNTL 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `fdopendir' function. */
+#define HAVE_FDOPENDIR 1
+
+/* Define to 1 if you have the `fgetea' function. */
+/* #undef HAVE_FGETEA */
+
+/* Define to 1 if you have the `fgetxattr' function. */
+/* #undef HAVE_FGETXATTR */
+
+/* Define to 1 if you have the `flistea' function. */
+/* #undef HAVE_FLISTEA */
+
+/* Define to 1 if you have the `flistxattr' function. */
+/* #undef HAVE_FLISTXATTR */
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `fsetea' function. */
+/* #undef HAVE_FSETEA */
+
+/* Define to 1 if you have the `fsetxattr' function. */
+/* #undef HAVE_FSETXATTR */
+
+/* Define to 1 if you have the `fstat' function. */
+#define HAVE_FSTAT 1
+
+/* Define to 1 if you have the `fstatat' function. */
+#define HAVE_FSTATAT 1
+
+/* Define to 1 if you have the `fstatfs' function. */
+#define HAVE_FSTATFS 1
+
+/* Define to 1 if you have the `fstatvfs' function. */
+#define HAVE_FSTATVFS 1
+
+/* Define to 1 if you have the `ftruncate' function. */
+#define HAVE_FTRUNCATE 1
+
+/* Define to 1 if you have the `futimens' function. */
+#define HAVE_FUTIMENS 1
+
+/* Define to 1 if you have the `futimes' function. */
+#define HAVE_FUTIMES 1
+
+/* Define to 1 if you have the `futimesat' function. */
+#define HAVE_FUTIMESAT 1
+
+/* Define to 1 if you have the `getea' function. */
+/* #undef HAVE_GETEA */
+
+/* Define to 1 if you have the `geteuid' function. */
+#define HAVE_GETEUID 1
+
+/* Define to 1 if you have the `getgrgid_r' function. */
+#define HAVE_GETGRGID_R 1
+
+/* Define to 1 if you have the `getgrnam_r' function. */
+#define HAVE_GETGRNAM_R 1
+
+/* Define to 1 if you have the `getpid' function. */
+#define HAVE_GETPID 1
+
+/* Define to 1 if you have the `getpwnam_r' function. */
+#define HAVE_GETPWNAM_R 1
+
+/* Define to 1 if you have the `getpwuid_r' function. */
+#define HAVE_GETPWUID_R 1
+
+/* Define to 1 if you have the `getvfsbyname' function. */
+/* #undef HAVE_GETVFSBYNAME */
+
+/* Define to 1 if you have the `getxattr' function. */
+/* #undef HAVE_GETXATTR */
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#define HAVE_GMTIME_R 1
+
+/* Define to 1 if you have the <grp.h> header file. */
+#define HAVE_GRP_H 1
+
+/* Define if you have the iconv() function and it works. */
+/* #undef HAVE_ICONV */
+
+/* Define to 1 if you have the <iconv.h> header file. */
+/* #undef HAVE_ICONV_H */
+
+/* Define to 1 if the system has the type `intmax_t'. */
+#define HAVE_INTMAX_T 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <io.h> header file. */
+/* #undef HAVE_IO_H */
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define to 1 if you have the `lchflags' function. */
+/* #undef HAVE_LCHFLAGS */
+
+/* Define to 1 if you have the `lchmod' function. */
+/* #undef HAVE_LCHMOD */
+
+/* Define to 1 if you have the `lchown' function. */
+#define HAVE_LCHOWN 1
+
+/* Define to 1 if you have the `lgetea' function. */
+/* #undef HAVE_LGETEA */
+
+/* Define to 1 if you have the `lgetxattr' function. */
+/* #undef HAVE_LGETXATTR */
+
+/* Define to 1 if you have the `acl' library (-lacl). */
+/* #undef HAVE_LIBACL */
+
+/* Define to 1 if you have the `b2' library (-lb2). */
+/* #undef HAVE_LIBB2 */
+
+/* Define to 1 if you have the `bz2' library (-lbz2). */
+/* #undef HAVE_LIBBZ2 */
+
+/* Define to 1 if you have the `charset' library (-lcharset). */
+/* #undef HAVE_LIBCHARSET */
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+/* #undef HAVE_LIBCRYPTO */
+
+/* Define to 1 if you have the `eay32' library (-leay32). */
+/* #undef HAVE_LIBEAY32 */
+
+/* Define to 1 if you have the `eay64' library (-leay64). */
+/* #undef HAVE_LIBEAY64 */
+
+/* Define to 1 if you have the `expat' library (-lexpat). */
+/* #undef HAVE_LIBEXPAT */
+
+/* Define to 1 if you have the `lz4' library (-llz4). */
+/* #undef HAVE_LIBLZ4 */
+
+/* Define to 1 if you have the `lzma' library (-llzma). */
+/* #undef HAVE_LIBLZMA */
+
+/* Define to 1 if you have the `lzo2' library (-llzo2). */
+/* #undef HAVE_LIBLZO2 */
+
+/* Define to 1 if you have the `mbedcrypto' library (-lmbedcrypto). */
+/* #undef HAVE_LIBMBEDCRYPTO */
+
+/* Define to 1 if you have the `md' library (-lmd). */
+/* #undef HAVE_LIBMD */
+
+/* Define to 1 if you have the `nettle' library (-lnettle). */
+/* #undef HAVE_LIBNETTLE */
+
+/* Define to 1 if you have the `pcre' library (-lpcre). */
+/* #undef HAVE_LIBPCRE */
+
+/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */
+/* #undef HAVE_LIBPCREPOSIX */
+
+/* Define to 1 if you have the `regex' library (-lregex). */
+/* #undef HAVE_LIBREGEX */
+
+/* Define to 1 if you have the `richacl' library (-lrichacl). */
+/* #undef HAVE_LIBRICHACL */
+
+/* Define to 1 if you have the `xml2' library (-lxml2). */
+/* #undef HAVE_LIBXML2 */
+
+/* Define to 1 if you have the <libxml/xmlreader.h> header file. */
+/* #undef HAVE_LIBXML_XMLREADER_H */
+
+/* Define to 1 if you have the <libxml/xmlwriter.h> header file. */
+/* #undef HAVE_LIBXML_XMLWRITER_H */
+
+/* Define to 1 if you have the `z' library (-lz). */
+/* #undef HAVE_LIBZ */
+
+/* Define to 1 if you have the `zstd' library (-lzstd). */
+/* #undef HAVE_LIBZSTD */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the `link' function. */
+#define HAVE_LINK 1
+
+/* Define to 1 if you have the <linux/fiemap.h> header file. */
+#define HAVE_LINUX_FIEMAP_H 1
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+#define HAVE_LINUX_FS_H 1
+
+/* Define to 1 if you have the <linux/magic.h> header file. */
+#define HAVE_LINUX_MAGIC_H 1
+
+/* Define to 1 if you have the <linux/types.h> header file. */
+#define HAVE_LINUX_TYPES_H 1
+
+/* Define to 1 if you have the `listea' function. */
+/* #undef HAVE_LISTEA */
+
+/* Define to 1 if you have the `listxattr' function. */
+/* #undef HAVE_LISTXATTR */
+
+/* Define to 1 if you have the `llistea' function. */
+/* #undef HAVE_LLISTEA */
+
+/* Define to 1 if you have the `llistxattr' function. */
+/* #undef HAVE_LLISTXATTR */
+
+/* Define to 1 if you have the <localcharset.h> header file. */
+/* #undef HAVE_LOCALCHARSET_H */
+
+/* Define to 1 if you have the `locale_charset' function. */
+/* #undef HAVE_LOCALE_CHARSET */
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have the `localtime_r' function. */
+#define HAVE_LOCALTIME_R 1
+
+/* Define to 1 if the system has the type `long long int'. */
+#define HAVE_LONG_LONG_INT 1
+
+/* Define to 1 if you have the `lsetea' function. */
+/* #undef HAVE_LSETEA */
+
+/* Define to 1 if you have the `lsetxattr' function. */
+/* #undef HAVE_LSETXATTR */
+
+/* Define to 1 if you have the `lstat' function. */
+#define HAVE_LSTAT 1
+
+/* Define to 1 if `lstat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+/* #undef HAVE_LSTAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the `lutimes' function. */
+#define HAVE_LUTIMES 1
+
+/* Define to 1 if you have the <lz4hc.h> header file. */
+/* #undef HAVE_LZ4HC_H */
+
+/* Define to 1 if you have the <lz4.h> header file. */
+/* #undef HAVE_LZ4_H */
+
+/* Define to 1 if you have the <lzma.h> header file. */
+/* #undef HAVE_LZMA_H */
+
+/* Define to 1 if you have the `lzma_stream_encoder_mt' function. */
+/* #undef HAVE_LZMA_STREAM_ENCODER_MT */
+
+/* Define to 1 if you have the <lzo/lzo1x.h> header file. */
+/* #undef HAVE_LZO_LZO1X_H */
+
+/* Define to 1 if you have the <lzo/lzoconf.h> header file. */
+/* #undef HAVE_LZO_LZOCONF_H */
+
+/* Define to 1 if you have the <mbedtls/aes.h> header file. */
+/* #undef HAVE_MBEDTLS_AES_H */
+
+/* Define to 1 if you have the <mbedtls/md.h> header file. */
+/* #undef HAVE_MBEDTLS_MD_H */
+
+/* Define to 1 if you have the <mbedtls/pkcs5.h> header file. */
+/* #undef HAVE_MBEDTLS_PKCS5_H */
+
+/* Define to 1 if you have the `mbrtowc' function. */
+#define HAVE_MBRTOWC 1
+
+/* Define to 1 if you have the `mbr_gid_to_uuid' function. */
+/* #undef HAVE_MBR_GID_TO_UUID */
+
+/* Define to 1 if you have the `mbr_uid_to_uuid' function. */
+/* #undef HAVE_MBR_UID_TO_UUID */
+
+/* Define to 1 if you have the `mbr_uuid_to_id' function. */
+/* #undef HAVE_MBR_UUID_TO_ID */
+
+/* Define to 1 if you have the <md5.h> header file. */
+/* #undef HAVE_MD5_H */
+
+/* Define to 1 if you have the <membership.h> header file. */
+/* #undef HAVE_MEMBERSHIP_H */
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the `mkdir' function. */
+#define HAVE_MKDIR 1
+
+/* Define to 1 if you have the `mkfifo' function. */
+#define HAVE_MKFIFO 1
+
+/* Define to 1 if you have the `mknod' function. */
+#define HAVE_MKNOD 1
+
+/* Define to 1 if you have the `mkstemp' function. */
+#define HAVE_MKSTEMP 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the <nettle/aes.h> header file. */
+/* #undef HAVE_NETTLE_AES_H */
+
+/* Define to 1 if you have the <nettle/hmac.h> header file. */
+/* #undef HAVE_NETTLE_HMAC_H */
+
+/* Define to 1 if you have the <nettle/md5.h> header file. */
+/* #undef HAVE_NETTLE_MD5_H */
+
+/* Define to 1 if you have the <nettle/pbkdf2.h> header file. */
+/* #undef HAVE_NETTLE_PBKDF2_H */
+
+/* Define to 1 if you have the <nettle/ripemd160.h> header file. */
+/* #undef HAVE_NETTLE_RIPEMD160_H */
+
+/* Define to 1 if you have the <nettle/sha.h> header file. */
+/* #undef HAVE_NETTLE_SHA_H */
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#define HAVE_NL_LANGINFO 1
+
+/* Define to 1 if you have the `openat' function. */
+#define HAVE_OPENAT 1
+
+/* Define to 1 if you have the <openssl/evp.h> header file. */
+/* #undef HAVE_OPENSSL_EVP_H */
+
+/* Define to 1 if you have the <paths.h> header file. */
+#define HAVE_PATHS_H 1
+
+/* Define to 1 if you have the <pcreposix.h> header file. */
+/* #undef HAVE_PCREPOSIX_H */
+
+/* Define to 1 if you have the `pipe' function. */
+#define HAVE_PIPE 1
+
+/* Define to 1 if you have the `PKCS5_PBKDF2_HMAC_SHA1' function. */
+/* #undef HAVE_PKCS5_PBKDF2_HMAC_SHA1 */
+
+/* Define to 1 if you have the `poll' function. */
+#define HAVE_POLL 1
+
+/* Define to 1 if you have the <poll.h> header file. */
+#define HAVE_POLL_H 1
+
+/* Define to 1 if you have the `posix_spawnp' function. */
+#define HAVE_POSIX_SPAWNP 1
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#define HAVE_PTHREAD_H 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have a POSIX compatible readdir_r */
+#define HAVE_READDIR_R 1
+
+/* Define to 1 if you have the `readlink' function. */
+#define HAVE_READLINK 1
+
+/* Define to 1 if you have the `readlinkat' function. */
+#define HAVE_READLINKAT 1
+
+/* Define to 1 if you have the `readpassphrase' function. */
+/* #undef HAVE_READPASSPHRASE */
+
+/* Define to 1 if you have the <readpassphrase.h> header file. */
+/* #undef HAVE_READPASSPHRASE_H */
+
+/* Define to 1 if you have the <regex.h> header file. */
+/* #undef HAVE_REGEX_H */
+
+/* Define to 1 if you have the `richacl_alloc' function. */
+/* #undef HAVE_RICHACL_ALLOC */
+
+/* Define to 1 if you have the `richacl_equiv_mode' function. */
+/* #undef HAVE_RICHACL_EQUIV_MODE */
+
+/* Define to 1 if you have the `richacl_free' function. */
+/* #undef HAVE_RICHACL_FREE */
+
+/* Define to 1 if you have the `richacl_get_fd' function. */
+/* #undef HAVE_RICHACL_GET_FD */
+
+/* Define to 1 if you have the `richacl_get_file' function. */
+/* #undef HAVE_RICHACL_GET_FILE */
+
+/* Define to 1 if you have the `richacl_set_fd' function. */
+/* #undef HAVE_RICHACL_SET_FD */
+
+/* Define to 1 if you have the `richacl_set_file' function. */
+/* #undef HAVE_RICHACL_SET_FILE */
+
+/* Define to 1 if you have the <ripemd.h> header file. */
+/* #undef HAVE_RIPEMD_H */
+
+/* Define to 1 if you have the `select' function. */
+#define HAVE_SELECT 1
+
+/* Define to 1 if you have the `setenv' function. */
+#define HAVE_SETENV 1
+
+/* Define to 1 if you have the `setlocale' function. */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have the `setxattr' function. */
+/* #undef HAVE_SETXATTR */
+
+/* Define to 1 if you have the <sha256.h> header file. */
+/* #undef HAVE_SHA256_H */
+
+/* Define to 1 if you have the <sha512.h> header file. */
+/* #undef HAVE_SHA512_H */
+
+/* Define to 1 if you have the <sha.h> header file. */
+/* #undef HAVE_SHA_H */
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the <spawn.h> header file. */
+#define HAVE_SPAWN_H 1
+
+/* Define to 1 if you have the `statfs' function. */
+#define HAVE_STATFS 1
+
+/* Define to 1 if you have the `statvfs' function. */
+#define HAVE_STATVFS 1
+
+/* Define to 1 if `stat' has the bug that it succeeds when given the
+   zero-length file name argument. */
+/* #undef HAVE_STAT_EMPTY_STRING_BUG */
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the `strerror_r' function. */
+#define HAVE_STRERROR_R 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strncpy_s' function. */
+/* #undef HAVE_STRNCPY_S */
+
+/* Define to 1 if you have the `strrchr' function. */
+#define HAVE_STRRCHR 1
+
+/* Define to 1 if the system has the type `struct richace'. */
+/* #undef HAVE_STRUCT_RICHACE */
+
+/* Define to 1 if the system has the type `struct richacl'. */
+/* #undef HAVE_STRUCT_RICHACL */
+
+/* Define to 1 if `f_namemax' is a member of `struct statfs'. */
+/* #undef HAVE_STRUCT_STATFS_F_NAMEMAX */
+
+/* Define to 1 if `f_iosize' is a member of `struct statvfs'. */
+/* #undef HAVE_STRUCT_STATVFS_F_IOSIZE */
+
+/* Define to 1 if `st_birthtime' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_BIRTHTIME */
+
+/* Define to 1 if `st_birthtimespec.tv_nsec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC */
+
+/* Define to 1 if `st_blksize' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+
+/* Define to 1 if `st_flags' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_FLAGS */
+
+/* Define to 1 if `st_mtimespec.tv_nsec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC */
+
+/* Define to 1 if `st_mtime_n' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIME_N */
+
+/* Define to 1 if `st_mtime_usec' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_MTIME_USEC */
+
+/* Define to 1 if `st_mtim.tv_nsec' is a member of `struct stat'. */
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+
+/* Define to 1 if `st_umtime' is a member of `struct stat'. */
+/* #undef HAVE_STRUCT_STAT_ST_UMTIME */
+
+/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */
+#define HAVE_STRUCT_TM_TM_GMTOFF 1
+
+/* Define to 1 if `__tm_gmtoff' is a member of `struct tm'. */
+/* #undef HAVE_STRUCT_TM___TM_GMTOFF */
+
+/* Define to 1 if the system has the type `struct vfsconf'. */
+/* #undef HAVE_STRUCT_VFSCONF */
+
+/* Define to 1 if the system has the type `struct xvfsconf'. */
+/* #undef HAVE_STRUCT_XVFSCONF */
+
+/* Define to 1 if you have the `symlink' function. */
+#define HAVE_SYMLINK 1
+
+/* Define to 1 if you have the <sys/acl.h> header file. */
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#define HAVE_SYS_CDEFS_H 1
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/ea.h> header file. */
+/* #undef HAVE_SYS_EA_H */
+
+/* Define to 1 if you have the <sys/extattr.h> header file. */
+/* #undef HAVE_SYS_EXTATTR_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/mkdev.h> header file. */
+/* #undef HAVE_SYS_MKDEV_H */
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#define HAVE_SYS_MOUNT_H 1
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+   */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#define HAVE_SYS_POLL_H 1
+
+/* Define to 1 if you have the <sys/richacl.h> header file. */
+/* #undef HAVE_SYS_RICHACL_H */
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/statfs.h> header file. */
+#define HAVE_SYS_STATFS_H 1
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+#define HAVE_SYS_STATVFS_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/sysmacros.h> header file. */
+#define HAVE_SYS_SYSMACROS_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/utime.h> header file. */
+/* #undef HAVE_SYS_UTIME_H */
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#define HAVE_SYS_UTSNAME_H 1
+
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+#define HAVE_SYS_VFS_H 1
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <sys/xattr.h> header file. */
+#define HAVE_SYS_XATTR_H 1
+
+/* Define to 1 if you have the `timegm' function. */
+#define HAVE_TIMEGM 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the `tzset' function. */
+#define HAVE_TZSET 1
+
+/* Define to 1 if the system has the type `uintmax_t'. */
+#define HAVE_UINTMAX_T 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `unlinkat' function. */
+#define HAVE_UNLINKAT 1
+
+/* Define to 1 if you have the `unsetenv' function. */
+#define HAVE_UNSETENV 1
+
+/* Define to 1 if the system has the type `unsigned long long'. */
+#define HAVE_UNSIGNED_LONG_LONG 1
+
+/* Define to 1 if the system has the type `unsigned long long int'. */
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+
+/* Define to 1 if you have the `utime' function. */
+#define HAVE_UTIME 1
+
+/* Define to 1 if you have the `utimensat' function. */
+#define HAVE_UTIMENSAT 1
+
+/* Define to 1 if you have the `utimes' function. */
+#define HAVE_UTIMES 1
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if the system has the type `wchar_t'. */
+#define HAVE_WCHAR_T 1
+
+/* Define to 1 if you have the `wcrtomb' function. */
+#define HAVE_WCRTOMB 1
+
+/* Define to 1 if you have the `wcscmp' function. */
+#define HAVE_WCSCMP 1
+
+/* Define to 1 if you have the `wcscpy' function. */
+#define HAVE_WCSCPY 1
+
+/* Define to 1 if you have the `wcslen' function. */
+#define HAVE_WCSLEN 1
+
+/* Define to 1 if you have the `wctomb' function. */
+#define HAVE_WCTOMB 1
+
+/* Define to 1 if you have the <wctype.h> header file. */
+#define HAVE_WCTYPE_H 1
+
+/* Define to 1 if you have the <wincrypt.h> header file. */
+/* #undef HAVE_WINCRYPT_H */
+
+/* Define to 1 if you have the <windows.h> header file. */
+/* #undef HAVE_WINDOWS_H */
+
+/* Define to 1 if you have the <winioctl.h> header file. */
+/* #undef HAVE_WINIOCTL_H */
+
+/* Define to 1 if you have the `wmemcmp' function. */
+#define HAVE_WMEMCMP 1
+
+/* Define to 1 if you have the `wmemcpy' function. */
+#define HAVE_WMEMCPY 1
+
+/* Define to 1 if you have the `wmemmove' function. */
+#define HAVE_WMEMMOVE 1
+
+/* Define to 1 if you have a working EXT2_IOC_GETFLAGS */
+#define HAVE_WORKING_EXT2_IOC_GETFLAGS 1
+
+/* Define to 1 if you have a working FS_IOC_GETFLAGS */
+#define HAVE_WORKING_FS_IOC_GETFLAGS 1
+
+/* Define to 1 if you have the <zlib.h> header file. */
+/* #undef HAVE_ZLIB_H */
+
+/* Define to 1 if you have the <zstd.h> header file. */
+/* #undef HAVE_ZSTD_H */
+
+/* Define to 1 if you have the `_ctime64_s' function. */
+/* #undef HAVE__CTIME64_S */
+
+/* Define to 1 if you have the `_fseeki64' function. */
+/* #undef HAVE__FSEEKI64 */
+
+/* Define to 1 if you have the `_get_timezone' function. */
+/* #undef HAVE__GET_TIMEZONE */
+
+/* Define to 1 if you have the `_gmtime64_s' function. */
+/* #undef HAVE__GMTIME64_S */
+
+/* Define to 1 if you have the `_localtime64_s' function. */
+/* #undef HAVE__LOCALTIME64_S */
+
+/* Define to 1 if you have the `_mkgmtime64' function. */
+/* #undef HAVE__MKGMTIME64 */
+
+/* Define as const if the declaration of iconv() needs const. */
+/* #undef ICONV_CONST */
+
+/* Version number of libarchive as a single integer */
+#define LIBARCHIVE_VERSION_NUMBER "3004003"
+
+/* Version number of libarchive */
+#define LIBARCHIVE_VERSION_STRING "3.4.3"
+
+/* Define to 1 if `lstat' dereferences a symlink specified with a trailing
+   slash. */
+#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in <mkdev.h>.
+   */
+/* #undef MAJOR_IN_MKDEV */
+
+/* Define to 1 if `major', `minor', and `makedev' are declared in
+   <sysmacros.h>. */
+#define MAJOR_IN_SYSMACROS 1
+
+/* Define to '0x05020000' for Windows Server 2003 APIs. */
+/* #undef NTDDI_VERSION */
+
+/* Name of package */
+#define PACKAGE "libarchive"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "libarchive-discuss@googlegroups.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libarchive"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libarchive 3.4.3"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libarchive"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "3.4.3"
+
+/* Define to 1 if PCRE_STATIC needs to be defined. */
+/* #undef PCRE_STATIC */
+
+/* The size of `wchar_t', as computed by sizeof. */
+#define SIZEOF_WCHAR_T 4
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if strerror_r returns char *. */
+#define STRERROR_R_CHAR_P 1
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Version number of package */
+#define VERSION "3.4.3"
+
+/* Define to '0x0502' for Windows Server 2003 APIs. */
+/* #undef WINVER */
+
+/* Enable large inode numbers on Mac OS X 10.5.  */
+#ifndef _DARWIN_USE_64_BIT_INODE
+# define _DARWIN_USE_64_BIT_INODE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT32_T */
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT64_T */
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+/* #undef _UINT8_T */
+
+/* Define to '0x0502' for Windows Server 2003 APIs. */
+/* #undef _WIN32_WINNT */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to match typeof st_gid field of struct stat if <sys/types.h> doesn't
+   define. */
+/* #undef gid_t */
+
+/* Define to `unsigned long' if <sys/types.h> does not define. */
+/* #undef id_t */
+
+/* Define to the type of a signed integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int16_t */
+
+/* Define to the type of a signed integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int32_t */
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef int64_t */
+
+/* Define to the widest signed integer type if <stdint.h> and <inttypes.h> do
+   not define. */
+/* #undef intmax_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `long long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to match typeof st_uid field of struct stat if <sys/types.h> doesn't
+   define. */
+/* #undef uid_t */
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint16_t */
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint32_t */
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint64_t */
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+   such a type exists and the standard includes do not define it. */
+/* #undef uint8_t */
+
+/* Define to the widest unsigned integer type if <stdint.h> and <inttypes.h>
+   do not define. */
+/* #undef uintmax_t */
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef uintptr_t */
diff --git a/c/config_freebsd.h b/c/config_freebsd.h
new file mode 100644
--- /dev/null
+++ b/c/config_freebsd.h
@@ -0,0 +1,260 @@
+/*-
+ * 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$
+ */
+
+#include <osreldate.h>
+
+/* FreeBSD 5.0 and later has ACL and extattr support. */
+#if __FreeBSD__ > 4
+#define ARCHIVE_ACL_FREEBSD 1
+#define HAVE_ACL_GET_PERM_NP 1
+#define HAVE_ARC4RANDOM_BUF 1
+#define HAVE_EXTATTR_GET_FILE 1
+#define HAVE_EXTATTR_LIST_FILE 1
+#define HAVE_EXTATTR_SET_FD 1
+#define HAVE_EXTATTR_SET_FILE 1
+#define HAVE_STRUCT_XVFSCONF 1
+#define HAVE_SYS_ACL_H 1
+#define HAVE_SYS_EXTATTR_H 1
+#if __FreeBSD__ > 7
+/* FreeBSD 8.0 and later has NFSv4 ACL support */
+#define ARCHIVE_ACL_FREEBSD_NFS4 1
+#define HAVE_ACL_GET_LINK_NP 1
+#define HAVE_ACL_IS_TRIVIAL_NP 1
+#define HAVE_ACL_SET_LINK_NP 1
+#endif /* __FreeBSD__ > 7 */
+#endif /* __FreeBSD__ > 4 */
+
+#ifdef WITH_OPENSSL
+#define HAVE_LIBCRYPTO 1
+#define HAVE_OPENSSL_EVP_H 1
+#define HAVE_OPENSSL_MD5_H 1
+#define HAVE_OPENSSL_RIPEMD_H 1
+#define HAVE_OPENSSL_SHA_H 1
+#define HAVE_OPENSSL_SHA256_INIT 1
+#define HAVE_OPENSSL_SHA384_INIT 1
+#define HAVE_OPENSSL_SHA512_INIT 1
+#define HAVE_PKCS5_PBKDF2_HMAC_SHA1 1
+#define HAVE_SHA256 1
+#define HAVE_SHA384 1
+#define HAVE_SHA512 1
+#else
+#define HAVE_LIBMD 1
+#define HAVE_MD5_H 1
+#define HAVE_MD5INIT 1
+#define HAVE_RIPEMD_H 1
+#define HAVE_SHA_H 1
+#define HAVE_SHA1 1
+#define HAVE_SHA1_INIT 1
+#define HAVE_SHA256 1
+#define HAVE_SHA256_H 1
+#define HAVE_SHA256_INIT 1
+#define HAVE_SHA512 1
+#define HAVE_SHA512_H 1
+#define HAVE_SHA512_INIT 1
+#endif
+
+#define HAVE_BSDXML_H 1
+#define HAVE_BZLIB_H 1
+#define HAVE_CHFLAGS 1
+#define HAVE_CHOWN 1
+#define HAVE_CHROOT 1
+#define HAVE_CTIME_R 1
+#define HAVE_CTYPE_H 1
+#define HAVE_DECL_EXTATTR_NAMESPACE_USER 1
+#define HAVE_DECL_INT32_MAX 1
+#define HAVE_DECL_INT32_MIN 1
+#define HAVE_DECL_INT64_MAX 1
+#define HAVE_DECL_INT64_MIN 1
+#define HAVE_DECL_INTMAX_MAX 1
+#define HAVE_DECL_INTMAX_MIN 1
+#define HAVE_DECL_SIZE_MAX 1
+#define HAVE_DECL_SSIZE_MAX 1
+#define HAVE_DECL_STRERROR_R 1
+#define HAVE_DECL_UINT32_MAX 1
+#define HAVE_DECL_UINT64_MAX 1
+#define HAVE_DECL_UINTMAX_MAX 1
+#define HAVE_DIRENT_H 1
+#define HAVE_DLFCN_H 1
+#define HAVE_D_MD_ORDER 1
+#define HAVE_EFTYPE 1
+#define HAVE_EILSEQ 1
+#define HAVE_ERRNO_H 1
+#define HAVE_FCHDIR 1
+#define HAVE_FCHFLAGS 1
+#define HAVE_FCHMOD 1
+#define HAVE_FCHOWN 1
+#define HAVE_FCNTL 1
+#define HAVE_FCNTL_H 1
+#define HAVE_FDOPENDIR 1
+#define HAVE_FORK 1
+#define HAVE_FSEEKO 1
+#define HAVE_FSTAT 1
+#define HAVE_FSTATAT 1
+#define HAVE_FSTATFS 1
+#define HAVE_FSTATVFS 1
+#define HAVE_FTRUNCATE 1
+#define HAVE_FUTIMES 1
+#define HAVE_FUTIMESAT 1
+#define HAVE_GETEUID 1
+#define HAVE_GETGRGID_R 1
+#define HAVE_GETGRNAM_R 1
+#define HAVE_GETPID 1
+#define HAVE_GETPWNAM_R 1
+#define HAVE_GETPWUID_R 1
+#define HAVE_GETVFSBYNAME 1
+#define HAVE_GMTIME_R 1
+#define HAVE_GRP_H 1
+#define HAVE_INTMAX_T 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_LANGINFO_H 1
+#define HAVE_LCHFLAGS 1
+#define HAVE_LCHMOD 1
+#define HAVE_LCHOWN 1
+#define HAVE_LIBZ 1
+#define HAVE_LIMITS_H 1
+#define HAVE_LINK 1
+#define HAVE_LOCALE_H 1
+#define HAVE_LOCALTIME_R 1
+#define HAVE_LONG_LONG_INT 1
+#define HAVE_LSTAT 1
+#define HAVE_LUTIMES 1
+#define HAVE_MBRTOWC 1
+#define HAVE_MEMMOVE 1
+#define HAVE_MEMORY_H 1
+#define HAVE_MEMSET 1
+#define HAVE_MKDIR 1
+#define HAVE_MKFIFO 1
+#define HAVE_MKNOD 1
+#define HAVE_MKSTEMP 1
+#define HAVE_NL_LANGINFO 1
+#define HAVE_OPENAT 1
+#define HAVE_PATHS_H 1
+#define HAVE_PIPE 1
+#define HAVE_POLL 1
+#define HAVE_POLL_H 1
+#define HAVE_POSIX_SPAWNP 1
+#define HAVE_PTHREAD_H 1
+#define HAVE_PWD_H 1
+#define HAVE_READDIR_R 1
+#define HAVE_READLINK 1
+#define HAVE_READLINKAT 1
+#define HAVE_READPASSPHRASE 1
+#define HAVE_READPASSPHRASE_H 1
+#define HAVE_REGEX_H 1
+#define HAVE_SELECT 1
+#define HAVE_SETENV 1
+#define HAVE_SETLOCALE 1
+#define HAVE_SIGACTION 1
+#define HAVE_SIGNAL_H 1
+#define HAVE_SPAWN_H 1
+#define HAVE_STATFS 1
+#define HAVE_STATVFS 1
+#define HAVE_STDARG_H 1
+#define HAVE_STDINT_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRCHR 1
+#define HAVE_STRDUP 1
+#define HAVE_STRERROR 1
+#define HAVE_STRERROR_R 1
+#define HAVE_STRFTIME 1
+#define HAVE_STRINGS_H 1
+#define HAVE_STRING_H 1
+#define HAVE_STRRCHR 1
+#define HAVE_STRUCT_STATFS_F_NAMEMAX 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1
+#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1
+#define HAVE_STRUCT_STAT_ST_BLKSIZE 1
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1
+#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1
+#define HAVE_STRUCT_TM_TM_GMTOFF 1
+#define HAVE_SYMLINK 1
+#define HAVE_SYS_CDEFS_H 1
+#define HAVE_SYS_IOCTL_H 1
+#define HAVE_SYS_MOUNT_H 1
+#define HAVE_SYS_PARAM_H 1
+#define HAVE_SYS_POLL_H 1
+#define HAVE_SYS_SELECT_H 1
+#define HAVE_SYS_STATVFS_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TIME_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_SYS_UTSNAME_H 1
+#define HAVE_SYS_WAIT_H 1
+#define HAVE_TIMEGM 1
+#define HAVE_TIME_H 1
+#define HAVE_TZSET 1
+#define HAVE_UINTMAX_T 1
+#define HAVE_UNISTD_H 1
+#define HAVE_UNLINKAT 1
+#define HAVE_UNSETENV 1
+#define HAVE_UNSIGNED_LONG_LONG 1
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+#define HAVE_UTIME 1
+#define HAVE_UTIMES 1
+#define HAVE_UTIME_H 1
+#define HAVE_VFORK 1
+#define HAVE_VPRINTF 1
+#define HAVE_WCHAR_H 1
+#define HAVE_WCHAR_T 1
+#define HAVE_WCRTOMB 1
+#define HAVE_WCSCMP 1
+#define HAVE_WCSCPY 1
+#define HAVE_WCSLEN 1
+#define HAVE_WCTOMB 1
+#define HAVE_WCTYPE_H 1
+#define HAVE_WMEMCMP 1
+#define HAVE_WMEMCPY 1
+#define HAVE_WMEMMOVE 1
+#define HAVE_ZLIB_H 1
+#define TIME_WITH_SYS_TIME 1
+
+#if __FreeBSD_version >= 1100056
+#define HAVE_FUTIMENS 1
+#define HAVE_UTIMENSAT 1
+#endif
+
+/* FreeBSD 4 and earlier lack intmax_t/uintmax_t */
+#if __FreeBSD__ < 5
+#define intmax_t int64_t
+#define uintmax_t uint64_t
+#endif
+
+/* FreeBSD defines for archive_hash.h */
+#ifdef WITH_OPENSSL
+#define ARCHIVE_CRYPTO_MD5_OPENSSL 1
+#define ARCHIVE_CRYPTO_RMD160_OPENSSL 1
+#define ARCHIVE_CRYPTO_SHA1_OPENSSL
+#define ARCHIVE_CRYPTO_SHA256_OPENSSL 1
+#define ARCHIVE_CRYPTO_SHA384_OPENSSL 1
+#define ARCHIVE_CRYPTO_SHA512_OPENSSL 1
+#else
+#define ARCHIVE_CRYPTO_MD5_LIBMD 1
+#define ARCHIVE_CRYPTO_SHA1_LIBMD 1
+#define ARCHIVE_CRYPTO_SHA256_LIBMD 1
+#define ARCHIVE_CRYPTO_SHA512_LIBMD 1
+#endif
diff --git a/c/filter_fork.h b/c/filter_fork.h
new file mode 100644
--- /dev/null
+++ b/c/filter_fork.h
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * 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: head/lib/libarchive/filter_fork.h 201087 2009-12-28 02:18:26Z kientzle $
+ */
+
+#ifndef FILTER_FORK_H
+#define FILTER_FORK_H
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	HANDLE *out_child);
+#else
+	pid_t *out_child);
+#endif
+
+void
+__archive_check_child(int in, int out);
+
+#endif
diff --git a/c/filter_fork_posix.c b/c/filter_fork_posix.c
new file mode 100644
--- /dev/null
+++ b/c/filter_fork_posix.c
@@ -0,0 +1,240 @@
+/*-
+ * Copyright (c) 2007 Joerg Sonnenberger
+ * Copyright (c) 2012 Michihiro NAKAJIMA 
+ * 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"
+
+/* This capability is only available on POSIX systems. */
+#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \
+    (defined(HAVE_FORK) || defined(HAVE_VFORK) || defined(HAVE_POSIX_SPAWNP))
+
+__FBSDID("$FreeBSD: head/lib/libarchive/filter_fork.c 182958 2008-09-12 05:33:00Z kientzle $");
+
+#if defined(HAVE_SYS_TYPES_H)
+#  include <sys/types.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#  include <errno.h>
+#endif
+#ifdef HAVE_STRING_H
+#  include <string.h>
+#endif
+#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
+#  if defined(HAVE_POLL_H)
+#    include <poll.h>
+#  elif defined(HAVE_SYS_POLL_H)
+#    include <sys/poll.h>
+#  endif
+#elif defined(HAVE_SELECT)
+#  if defined(HAVE_SYS_SELECT_H)
+#    include <sys/select.h>
+#  elif defined(HAVE_UNISTD_H)
+#    include <unistd.h>
+#  endif
+#endif
+#ifdef HAVE_FCNTL_H
+#  include <fcntl.h>
+#endif
+#ifdef HAVE_SPAWN_H
+#  include <spawn.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#  include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+#endif
+
+#include "archive.h"
+#include "archive_cmdline_private.h"
+
+#include "filter_fork.h"
+
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+		pid_t *out_child)
+{
+	pid_t child;
+	int stdin_pipe[2], stdout_pipe[2], tmp;
+#if HAVE_POSIX_SPAWNP
+	posix_spawn_file_actions_t actions;
+	int r;
+#endif
+	struct archive_cmdline *cmdline;
+
+	cmdline = __archive_cmdline_allocate();
+	if (cmdline == NULL)
+		goto state_allocated;
+	if (__archive_cmdline_parse(cmdline, cmd) != ARCHIVE_OK)
+		goto state_allocated;
+
+	if (pipe(stdin_pipe) == -1)
+		goto state_allocated;
+	if (stdin_pipe[0] == 1 /* stdout */) {
+		if ((tmp = dup(stdin_pipe[0])) == -1)
+			goto stdin_opened;
+		close(stdin_pipe[0]);
+		stdin_pipe[0] = tmp;
+	}
+	if (pipe(stdout_pipe) == -1)
+		goto stdin_opened;
+	if (stdout_pipe[1] == 0 /* stdin */) {
+		if ((tmp = dup(stdout_pipe[1])) == -1)
+			goto stdout_opened;
+		close(stdout_pipe[1]);
+		stdout_pipe[1] = tmp;
+	}
+
+#if HAVE_POSIX_SPAWNP
+
+	r = posix_spawn_file_actions_init(&actions);
+	if (r != 0) {
+		errno = r;
+		goto stdout_opened;
+	}
+	r = posix_spawn_file_actions_addclose(&actions, stdin_pipe[1]);
+	if (r != 0)
+		goto actions_inited;
+	r = posix_spawn_file_actions_addclose(&actions, stdout_pipe[0]);
+	if (r != 0)
+		goto actions_inited;
+	/* Setup for stdin. */
+	r = posix_spawn_file_actions_adddup2(&actions, stdin_pipe[0], 0);
+	if (r != 0)
+		goto actions_inited;
+	if (stdin_pipe[0] != 0 /* stdin */) {
+		r = posix_spawn_file_actions_addclose(&actions, stdin_pipe[0]);
+		if (r != 0)
+			goto actions_inited;
+	}
+	/* Setup for stdout. */
+	r = posix_spawn_file_actions_adddup2(&actions, stdout_pipe[1], 1);
+	if (r != 0)
+		goto actions_inited;
+	if (stdout_pipe[1] != 1 /* stdout */) {
+		r = posix_spawn_file_actions_addclose(&actions, stdout_pipe[1]);
+		if (r != 0)
+			goto actions_inited;
+	}
+	r = posix_spawnp(&child, cmdline->path, &actions, NULL,
+		cmdline->argv, NULL);
+	if (r != 0)
+		goto actions_inited;
+	posix_spawn_file_actions_destroy(&actions);
+
+#else /* HAVE_POSIX_SPAWNP */
+
+#if HAVE_VFORK
+	child = vfork();
+#else
+	child = fork();
+#endif
+	if (child == -1)
+		goto stdout_opened;
+	if (child == 0) {
+		close(stdin_pipe[1]);
+		close(stdout_pipe[0]);
+		if (dup2(stdin_pipe[0], 0 /* stdin */) == -1)
+			_exit(254);
+		if (stdin_pipe[0] != 0 /* stdin */)
+			close(stdin_pipe[0]);
+		if (dup2(stdout_pipe[1], 1 /* stdout */) == -1)
+			_exit(254);
+		if (stdout_pipe[1] != 1 /* stdout */)
+			close(stdout_pipe[1]);
+		execvp(cmdline->path, cmdline->argv);
+		_exit(254);
+	}
+#endif /* HAVE_POSIX_SPAWNP */
+
+	close(stdin_pipe[0]);
+	close(stdout_pipe[1]);
+
+	*child_stdin = stdin_pipe[1];
+	fcntl(*child_stdin, F_SETFL, O_NONBLOCK);
+	*child_stdout = stdout_pipe[0];
+	fcntl(*child_stdout, F_SETFL, O_NONBLOCK);
+	__archive_cmdline_free(cmdline);
+
+	*out_child = child;
+	return ARCHIVE_OK;
+
+#if HAVE_POSIX_SPAWNP
+actions_inited:
+	errno = r;
+	posix_spawn_file_actions_destroy(&actions);
+#endif
+stdout_opened:
+	close(stdout_pipe[0]);
+	close(stdout_pipe[1]);
+stdin_opened:
+	close(stdin_pipe[0]);
+	close(stdin_pipe[1]);
+state_allocated:
+	__archive_cmdline_free(cmdline);
+	return ARCHIVE_FAILED;
+}
+
+void
+__archive_check_child(int in, int out)
+{
+#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
+	struct pollfd fds[2];
+	int idx;
+
+	idx = 0;
+	if (in != -1) {
+		fds[idx].fd = in;
+		fds[idx].events = POLLOUT;
+		++idx;
+	}
+	if (out != -1) {
+		fds[idx].fd = out;
+		fds[idx].events = POLLIN;
+		++idx;
+	}
+
+	poll(fds, idx, -1); /* -1 == INFTIM, wait forever */
+#elif defined(HAVE_SELECT)
+	fd_set fds_in, fds_out, fds_error;
+
+	FD_ZERO(&fds_in);
+	FD_ZERO(&fds_out);
+	FD_ZERO(&fds_error);
+	if (out != -1) {
+		FD_SET(out, &fds_in);
+		FD_SET(out, &fds_error);
+	}
+	if (in != -1) {
+		FD_SET(in, &fds_out);
+		FD_SET(in, &fds_error);
+	}
+	select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL);
+#else
+	sleep(1);
+#endif
+}
+
+#endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */
diff --git a/c/filter_fork_windows.c b/c/filter_fork_windows.c
new file mode 100644
--- /dev/null
+++ b/c/filter_fork_windows.c
@@ -0,0 +1,199 @@
+/*-
+ * Copyright (c) 2009-2012 Michihiro NAKAJIMA
+ * 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"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include "archive_cmdline_private.h"
+#include "archive_string.h"
+
+#include "filter_fork.h"
+
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout,
+		HANDLE *out_child)
+{
+	HANDLE childStdout[2], childStdin[2],childStderr;
+	SECURITY_ATTRIBUTES secAtts;
+	STARTUPINFOA staInfo;
+	PROCESS_INFORMATION childInfo;
+	struct archive_string cmdline;
+	struct archive_string fullpath;
+	struct archive_cmdline *acmd;
+	char *arg0, *ext;
+	int i, l;
+	DWORD fl, fl_old;
+	HANDLE child;
+
+	childStdout[0] = childStdout[1] = INVALID_HANDLE_VALUE;
+	childStdin[0] = childStdin[1] = INVALID_HANDLE_VALUE;
+	childStderr = INVALID_HANDLE_VALUE;
+	archive_string_init(&cmdline);
+	archive_string_init(&fullpath);
+
+	acmd = __archive_cmdline_allocate();
+	if (acmd == NULL)
+		goto fail;
+	if (__archive_cmdline_parse(acmd, cmd) != ARCHIVE_OK)
+		goto fail;
+
+	/*
+	 * Search the full path of 'path'.
+	 * NOTE: This does not need if we give CreateProcessA 'path' as
+	 * a part of the cmdline and give CreateProcessA NULL as first
+	 * parameter, but I do not like that way.
+	 */
+	ext = strrchr(acmd->path, '.');
+	if (ext == NULL || strlen(ext) > 4)
+		/* 'path' does not have a proper extension, so we have to
+		 * give SearchPath() ".exe" as the extension. */
+		ext = ".exe";
+	else
+		ext = NULL;/* 'path' has an extension. */
+
+	fl = MAX_PATH;
+	do {
+		if (archive_string_ensure(&fullpath, fl) == NULL)
+			goto fail;
+		fl_old = fl;
+		fl = SearchPathA(NULL, acmd->path, ext, fl, fullpath.s,
+			&arg0);
+	} while (fl != 0 && fl > fl_old);
+	if (fl == 0)
+		goto fail;
+
+	/*
+	 * Make a command line.
+	 */
+	for (l = 0, i = 0;  acmd->argv[i] != NULL; i++) {
+		if (i == 0)
+			continue;
+		l += (int)strlen(acmd->argv[i]) + 1;
+	}
+	if (archive_string_ensure(&cmdline, l + 1) == NULL)
+		goto fail;
+	for (i = 0;  acmd->argv[i] != NULL; i++) {
+		if (i == 0) {
+			const char *p, *sp;
+
+			if ((p = strchr(acmd->argv[i], '/')) != NULL ||
+			    (p = strchr(acmd->argv[i], '\\')) != NULL)
+				p++;
+			else
+				p = acmd->argv[i];
+			if ((sp = strchr(p, ' ')) != NULL)
+				archive_strappend_char(&cmdline, '"');
+			archive_strcat(&cmdline, p);
+			if (sp != NULL)
+				archive_strappend_char(&cmdline, '"');
+		} else {
+			archive_strappend_char(&cmdline, ' ');
+			archive_strcat(&cmdline, acmd->argv[i]);
+		}
+	}
+	if (i <= 1) {
+		const char *sp;
+
+		if ((sp = strchr(arg0, ' ')) != NULL)
+			archive_strappend_char(&cmdline, '"');
+		archive_strcat(&cmdline, arg0);
+		if (sp != NULL)
+			archive_strappend_char(&cmdline, '"');
+	}
+
+	secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
+	secAtts.bInheritHandle = TRUE;
+	secAtts.lpSecurityDescriptor = NULL;
+	if (CreatePipe(&childStdout[0], &childStdout[1], &secAtts, 0) == 0)
+		goto fail;
+	if (!SetHandleInformation(childStdout[0], HANDLE_FLAG_INHERIT, 0))
+		goto fail;
+	if (CreatePipe(&childStdin[0], &childStdin[1], &secAtts, 0) == 0)
+		goto fail;
+	if (!SetHandleInformation(childStdin[1], HANDLE_FLAG_INHERIT, 0))
+		goto fail;
+	if (DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE),
+	    GetCurrentProcess(), &childStderr, 0, TRUE,
+	    DUPLICATE_SAME_ACCESS) == 0)
+		goto fail;
+
+	memset(&staInfo, 0, sizeof(staInfo));
+	staInfo.cb = sizeof(staInfo);
+	staInfo.hStdError = childStderr;
+	staInfo.hStdOutput = childStdout[1];
+	staInfo.hStdInput = childStdin[0];
+	staInfo.wShowWindow = SW_HIDE;
+	staInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+	if (CreateProcessA(fullpath.s, cmdline.s, NULL, NULL, TRUE, 0,
+	      NULL, NULL, &staInfo, &childInfo) == 0)
+		goto fail;
+	WaitForInputIdle(childInfo.hProcess, INFINITE);
+	CloseHandle(childInfo.hProcess);
+	CloseHandle(childInfo.hThread);
+
+	*child_stdout = _open_osfhandle((intptr_t)childStdout[0], _O_RDONLY);
+	*child_stdin = _open_osfhandle((intptr_t)childStdin[1], _O_WRONLY);
+	
+	child = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
+		childInfo.dwProcessId);
+	if (child == NULL) // INVALID_HANDLE_VALUE ?
+		goto fail;
+
+	*out_child = child;
+
+	CloseHandle(childStdout[1]);
+	CloseHandle(childStdin[0]);
+
+	archive_string_free(&cmdline);
+	archive_string_free(&fullpath);
+	__archive_cmdline_free(acmd);
+	return ARCHIVE_OK;
+
+fail:
+	if (childStdout[0] != INVALID_HANDLE_VALUE)
+		CloseHandle(childStdout[0]);
+	if (childStdout[1] != INVALID_HANDLE_VALUE)
+		CloseHandle(childStdout[1]);
+	if (childStdin[0] != INVALID_HANDLE_VALUE)
+		CloseHandle(childStdin[0]);
+	if (childStdin[1] != INVALID_HANDLE_VALUE)
+		CloseHandle(childStdin[1]);
+	if (childStderr != INVALID_HANDLE_VALUE)
+		CloseHandle(childStderr);
+	archive_string_free(&cmdline);
+	archive_string_free(&fullpath);
+	__archive_cmdline_free(acmd);
+	return ARCHIVE_FAILED;
+}
+
+void
+__archive_check_child(int in, int out)
+{
+	(void)in; /* UNUSED */
+	(void)out; /* UNUSED */
+	Sleep(100);
+}
+
+#endif /* _WIN32 && !__CYGWIN__ */
diff --git a/c/xxhash.c b/c/xxhash.c
new file mode 100644
--- /dev/null
+++ b/c/xxhash.c
@@ -0,0 +1,521 @@
+/*
+xxHash - Fast Hash algorithm
+Copyright (C) 2012-2014, Yann Collet.
+BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+* 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+OWNER 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.
+
+You can contact the author at :
+- xxHash source repository : http://code.google.com/p/xxhash/
+*/
+#include "archive_platform.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "archive_xxhash.h"
+
+#ifdef HAVE_LIBLZ4
+
+/***************************************
+** Tuning parameters
+****************************************/
+/* Unaligned memory access is automatically enabled for "common" CPU, such as x86.
+** For others CPU, the compiler will be more cautious, and insert extra code to ensure aligned access is respected.
+** If you know your target CPU supports unaligned memory access, you want to force this option manually to improve performance.
+** You can also enable this parameter if you know your input data will always be aligned (boundaries of 4, for U32).
+*/
+#if defined(__ARM_FEATURE_UNALIGNED) || defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+#  define XXH_USE_UNALIGNED_ACCESS 1
+#endif
+
+/* XXH_ACCEPT_NULL_INPUT_POINTER :
+** If the input pointer is a null pointer, xxHash default behavior is to trigger a memory access error, since it is a bad pointer.
+** When this option is enabled, xxHash output for null input pointers will be the same as a null-length input.
+** This option has a very small performance cost (only measurable on small inputs).
+** By default, this option is disabled. To enable it, uncomment below define :
+** #define XXH_ACCEPT_NULL_INPUT_POINTER 1
+
+** XXH_FORCE_NATIVE_FORMAT :
+** By default, xxHash library provides endian-independent Hash values, based on little-endian convention.
+** Results are therefore identical for little-endian and big-endian CPU.
+** This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format.
+** Should endian-independence be of no importance for your application, you may set the #define below to 1.
+** It will improve speed for Big-endian CPU.
+** This option has no impact on Little_Endian CPU.
+*/
+#define XXH_FORCE_NATIVE_FORMAT 0
+
+/***************************************
+** Compiler Specific Options
+****************************************/
+/* Disable some Visual warning messages */
+#ifdef _MSC_VER  /* Visual Studio */
+#  pragma warning(disable : 4127)      /* disable: C4127: conditional expression is constant */
+#endif
+
+#ifdef _MSC_VER    /* Visual Studio */
+#  define FORCE_INLINE __forceinline
+#else
+#  ifdef __GNUC__
+#    define FORCE_INLINE inline __attribute__((always_inline))
+#  else
+#    define FORCE_INLINE inline
+#  endif
+#endif
+
+/***************************************
+** Includes & Memory related functions
+****************************************/
+#define XXH_malloc malloc
+#define XXH_free free
+#define XXH_memcpy memcpy
+
+
+static unsigned int	  XXH32 (const void*, unsigned int, unsigned int);
+static void*		  XXH32_init   (unsigned int);
+static XXH_errorcode	  XXH32_update (void*, const void*, unsigned int);
+static unsigned int	  XXH32_digest (void*);
+/*static int		  XXH32_sizeofState(void);*/
+static XXH_errorcode	  XXH32_resetState(void*, unsigned int);
+#define       XXH32_SIZEOFSTATE 48
+typedef struct { long long ll[(XXH32_SIZEOFSTATE+(sizeof(long long)-1))/sizeof(long long)]; } XXH32_stateSpace_t;
+static unsigned int	  XXH32_intermediateDigest (void*);
+
+/***************************************
+** Basic Types
+****************************************/
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L   /* C99 */
+# include <stdint.h>
+  typedef uint8_t  BYTE;
+  typedef uint16_t U16;
+  typedef uint32_t U32;
+  typedef  int32_t S32;
+  typedef uint64_t U64;
+#else
+  typedef unsigned char      BYTE;
+  typedef unsigned short     U16;
+  typedef unsigned int       U32;
+  typedef   signed int       S32;
+  typedef unsigned long long U64;
+#endif
+
+#if defined(__GNUC__)  && !defined(XXH_USE_UNALIGNED_ACCESS)
+#  define _PACKED __attribute__ ((packed))
+#else
+#  define _PACKED
+#endif
+
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)
+#  ifdef __IBMC__
+#    pragma pack(1)
+#  else
+#    pragma pack(push, 1)
+#  endif
+#endif
+
+typedef struct _U32_S { U32 v; } _PACKED U32_S;
+
+#if !defined(XXH_USE_UNALIGNED_ACCESS) && !defined(__GNUC__)
+#  pragma pack(pop)
+#endif
+
+
+/****************************************
+** Compiler-specific Functions and Macros
+*****************************************/
+#define GCC_VERSION ((__GNUC__-0) * 100 + (__GNUC_MINOR__ - 0))
+
+#if GCC_VERSION >= 409
+__attribute__((__no_sanitize_undefined__))
+#endif
+static inline U32 A32(const void * x)
+{
+    return (((const U32_S *)(x))->v);
+}
+
+/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
+#if defined(_MSC_VER)
+#  define XXH_rotl32(x,r) _rotl(x,r)
+#else
+#  define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r)))
+#endif
+
+#if defined(_MSC_VER)     /* Visual Studio */
+#  define XXH_swap32 _byteswap_ulong
+#elif GCC_VERSION >= 403
+#  define XXH_swap32 __builtin_bswap32
+#else
+static inline U32 XXH_swap32 (U32 x) {
+    return  ((x << 24) & 0xff000000 ) |
+			((x <<  8) & 0x00ff0000 ) |
+			((x >>  8) & 0x0000ff00 ) |
+			((x >> 24) & 0x000000ff );}
+#endif
+
+
+/***************************************
+** Constants
+****************************************/
+#define PRIME32_1   2654435761U
+#define PRIME32_2   2246822519U
+#define PRIME32_3   3266489917U
+#define PRIME32_4    668265263U
+#define PRIME32_5    374761393U
+
+
+/***************************************
+** Architecture Macros
+****************************************/
+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
+#ifndef XXH_CPU_LITTLE_ENDIAN   /* It is possible to define XXH_CPU_LITTLE_ENDIAN externally, for example using a compiler switch */
+    static const int one = 1;
+#   define XXH_CPU_LITTLE_ENDIAN   (*(const char*)(&one))
+#endif
+
+
+/***************************************
+** Macros
+****************************************/
+#define XXH_STATIC_ASSERT(c)   { enum { XXH_static_assert = 1/(!!(c)) }; }    /* use only *after* variable declarations */
+
+
+/*****************************
+** Memory reads
+******************************/
+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
+
+static
+FORCE_INLINE U32 XXH_readLE32_align(const U32* ptr, XXH_endianess endian, XXH_alignment align)
+{
+    if (align==XXH_unaligned)
+        return endian==XXH_littleEndian ? A32(ptr) : XXH_swap32(A32(ptr));
+    else
+        return endian==XXH_littleEndian ? *ptr : XXH_swap32(*ptr);
+}
+
+static
+FORCE_INLINE U32 XXH_readLE32(const U32* ptr, XXH_endianess endian) { return XXH_readLE32_align(ptr, endian, XXH_unaligned); }
+
+
+/*****************************
+** Simple Hash Functions
+******************************/
+static
+FORCE_INLINE U32 XXH32_endian_align(const void* input, unsigned int len, U32 seed, XXH_endianess endian, XXH_alignment align)
+{
+    const BYTE* p = (const BYTE*)input;
+    const BYTE* bEnd = p + len;
+    U32 h32;
+#define XXH_get32bits(p) XXH_readLE32_align((const U32*)p, endian, align)
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+    if (p==NULL) { len=0; bEnd=p=(const BYTE*)(size_t)16; }
+#endif
+
+    if (len>=16)
+    {
+        const BYTE* const limit = bEnd - 16;
+        U32 v1 = seed + PRIME32_1 + PRIME32_2;
+        U32 v2 = seed + PRIME32_2;
+        U32 v3 = seed + 0;
+        U32 v4 = seed - PRIME32_1;
+
+        do
+        {
+            v1 += XXH_get32bits(p) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;
+            v2 += XXH_get32bits(p) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;
+            v3 += XXH_get32bits(p) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;
+            v4 += XXH_get32bits(p) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;
+        } while (p<=limit);
+
+        h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
+    }
+    else
+    {
+        h32  = seed + PRIME32_5;
+    }
+
+    h32 += (U32) len;
+
+    while (p<=bEnd-4)
+    {
+        h32 += XXH_get32bits(p) * PRIME32_3;
+        h32  = XXH_rotl32(h32, 17) * PRIME32_4 ;
+        p+=4;
+    }
+
+    while (p<bEnd)
+    {
+        h32 += (*p) * PRIME32_5;
+        h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
+        p++;
+    }
+
+    h32 ^= h32 >> 15;
+    h32 *= PRIME32_2;
+    h32 ^= h32 >> 13;
+    h32 *= PRIME32_3;
+    h32 ^= h32 >> 16;
+
+    return h32;
+}
+
+
+U32 XXH32(const void* input, unsigned int len, U32 seed)
+{
+#if 0
+    // Simple version, good for code maintenance, but unfortunately slow for small inputs
+    void* state = XXH32_init(seed);
+    XXH32_update(state, input, len);
+    return XXH32_digest(state);
+#else
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+#  if !defined(XXH_USE_UNALIGNED_ACCESS)
+    if ((((size_t)input) & 3) == 0)   /* Input is aligned, let's leverage the speed advantage */
+    {
+        if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+            return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned);
+        else
+            return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned);
+    }
+#  endif
+
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned);
+    else
+        return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned);
+#endif
+}
+
+/*****************************
+** Advanced Hash Functions
+******************************/
+
+struct XXH_state32_t
+{
+    U64 total_len;
+    U32 seed;
+    U32 v1;
+    U32 v2;
+    U32 v3;
+    U32 v4;
+    int memsize;
+    char memory[16];
+};
+
+#if 0
+static
+int XXH32_sizeofState(void)
+{
+    XXH_STATIC_ASSERT(XXH32_SIZEOFSTATE >= sizeof(struct XXH_state32_t));   /* A compilation error here means XXH32_SIZEOFSTATE is not large enough */
+    return sizeof(struct XXH_state32_t);
+}
+#endif
+
+static
+XXH_errorcode XXH32_resetState(void* state_in, U32 seed)
+{
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+    state->seed = seed;
+    state->v1 = seed + PRIME32_1 + PRIME32_2;
+    state->v2 = seed + PRIME32_2;
+    state->v3 = seed + 0;
+    state->v4 = seed - PRIME32_1;
+    state->total_len = 0;
+    state->memsize = 0;
+    return XXH_OK;
+}
+
+static
+void* XXH32_init (U32 seed)
+{
+    void* state = XXH_malloc (sizeof(struct XXH_state32_t));
+    XXH32_resetState(state, seed);
+    return state;
+}
+
+static
+FORCE_INLINE XXH_errorcode XXH32_update_endian (void* state_in, const void* input, int len, XXH_endianess endian)
+{
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+    const BYTE* p = (const BYTE*)input;
+    const BYTE* const bEnd = p + len;
+
+#ifdef XXH_ACCEPT_NULL_INPUT_POINTER
+    if (input==NULL) return XXH_ERROR;
+#endif
+
+    state->total_len += len;
+
+    if (state->memsize + len < 16)   /* fill in tmp buffer */
+    {
+        XXH_memcpy(state->memory + state->memsize, input, len);
+        state->memsize +=  len;
+        return XXH_OK;
+    }
+
+    if (state->memsize)   /* some data left from previous update */
+    {
+        XXH_memcpy(state->memory + state->memsize, input, 16-state->memsize);
+        {
+            const U32* p32 = (const U32*)state->memory;
+            state->v1 += XXH_readLE32(p32, endian) * PRIME32_2; state->v1 = XXH_rotl32(state->v1, 13); state->v1 *= PRIME32_1; p32++;
+            state->v2 += XXH_readLE32(p32, endian) * PRIME32_2; state->v2 = XXH_rotl32(state->v2, 13); state->v2 *= PRIME32_1; p32++;
+            state->v3 += XXH_readLE32(p32, endian) * PRIME32_2; state->v3 = XXH_rotl32(state->v3, 13); state->v3 *= PRIME32_1; p32++;
+            state->v4 += XXH_readLE32(p32, endian) * PRIME32_2; state->v4 = XXH_rotl32(state->v4, 13); state->v4 *= PRIME32_1; p32++;
+        }
+        p += 16-state->memsize;
+        state->memsize = 0;
+    }
+
+    if (p <= bEnd-16)
+    {
+        const BYTE* const limit = bEnd - 16;
+        U32 v1 = state->v1;
+        U32 v2 = state->v2;
+        U32 v3 = state->v3;
+        U32 v4 = state->v4;
+
+        do
+        {
+            v1 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v1 = XXH_rotl32(v1, 13); v1 *= PRIME32_1; p+=4;
+            v2 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v2 = XXH_rotl32(v2, 13); v2 *= PRIME32_1; p+=4;
+            v3 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v3 = XXH_rotl32(v3, 13); v3 *= PRIME32_1; p+=4;
+            v4 += XXH_readLE32((const U32*)p, endian) * PRIME32_2; v4 = XXH_rotl32(v4, 13); v4 *= PRIME32_1; p+=4;
+        } while (p<=limit);
+
+        state->v1 = v1;
+        state->v2 = v2;
+        state->v3 = v3;
+        state->v4 = v4;
+    }
+
+    if (p < bEnd)
+    {
+        XXH_memcpy(state->memory, p, bEnd-p);
+        state->memsize = (int)(bEnd-p);
+    }
+
+    return XXH_OK;
+}
+
+static
+XXH_errorcode XXH32_update (void* state_in, const void* input, unsigned int len)
+{
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH32_update_endian(state_in, input, len, XXH_littleEndian);
+    else
+        return XXH32_update_endian(state_in, input, len, XXH_bigEndian);
+}
+
+
+
+static
+FORCE_INLINE U32 XXH32_intermediateDigest_endian (void* state_in, XXH_endianess endian)
+{
+    struct XXH_state32_t * state = (struct XXH_state32_t *) state_in;
+    const BYTE * p = (const BYTE*)state->memory;
+    BYTE* bEnd = (BYTE*)state->memory + state->memsize;
+    U32 h32;
+
+    if (state->total_len >= 16)
+    {
+        h32 = XXH_rotl32(state->v1, 1) + XXH_rotl32(state->v2, 7) + XXH_rotl32(state->v3, 12) + XXH_rotl32(state->v4, 18);
+    }
+    else
+    {
+        h32  = state->seed + PRIME32_5;
+    }
+
+    h32 += (U32) state->total_len;
+
+    while (p<=bEnd-4)
+    {
+        h32 += XXH_readLE32((const U32*)p, endian) * PRIME32_3;
+        h32  = XXH_rotl32(h32, 17) * PRIME32_4;
+        p+=4;
+    }
+
+    while (p<bEnd)
+    {
+        h32 += (*p) * PRIME32_5;
+        h32 = XXH_rotl32(h32, 11) * PRIME32_1;
+        p++;
+    }
+
+    h32 ^= h32 >> 15;
+    h32 *= PRIME32_2;
+    h32 ^= h32 >> 13;
+    h32 *= PRIME32_3;
+    h32 ^= h32 >> 16;
+
+    return h32;
+}
+
+static
+U32 XXH32_intermediateDigest (void* state_in)
+{
+    XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN;
+
+    if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT)
+        return XXH32_intermediateDigest_endian(state_in, XXH_littleEndian);
+    else
+        return XXH32_intermediateDigest_endian(state_in, XXH_bigEndian);
+}
+
+static
+U32 XXH32_digest (void* state_in)
+{
+    U32 h32 = XXH32_intermediateDigest(state_in);
+
+    XXH_free(state_in);
+
+    return h32;
+}
+
+const
+struct archive_xxhash __archive_xxhash = {
+	XXH32,
+	XXH32_init,
+	XXH32_update,
+	XXH32_digest
+};
+#else
+
+/*
+ * Define an empty version of the struct if we aren't using the LZ4 library.
+ */
+const
+struct archive_xxhash __archive_xxhash = {
+	NULL,
+	NULL,
+	NULL,
+	NULL
+};
+
+#endif /* HAVE_LIBLZ4 */
diff --git a/libarchive.cabal b/libarchive.cabal
--- a/libarchive.cabal
+++ b/libarchive.cabal
@@ -1,19 +1,26 @@
-cabal-version:   1.18
-name:            libarchive
-version:         2.2.5.0
-license:         BSD3
-license-file:    LICENSE
-copyright:       Copyright: (c) 2018-2020 Vanessa McHale
-maintainer:      vamchale@gmail.com
-author:          Vanessa McHale
-tested-with:     ghc ==8.4.4 ghc ==8.6.5 ghc ==8.8.3 ghc ==8.10.1
-bug-reports:     https://github.com/vmchale/libarchive/issues
-synopsis:        Haskell interface to libarchive
+cabal-version:      3.0
+name:               libarchive
+version:            2.2.5.1
+license:            BSD-3-Clause
+license-file:       LICENSE
+copyright:          Copyright: (c) 2018-2020 Vanessa McHale
+maintainer:         vamchale@gmail.com
+author:             Vanessa McHale
+tested-with:        ghc ==8.4.4 ghc ==8.6.5 ghc ==8.8.3 ghc ==8.10.1
+bug-reports:        https://github.com/vmchale/libarchive/issues
+synopsis:           Haskell interface to libarchive
 description:
     Haskell bindings for [libarchive](https://www.libarchive.org/). Provides the ability to unpack archives, including the ability to unpack archives lazily.
 
-category:        Codec
-build-type:      Custom
+category:           Codec
+build-type:         Custom
+extra-source-files:
+    c/autoconf-darwin/config.h
+    c/autoconf-linux/config.h
+    c/autoconf-freebsd/config.h
+    c/*.c
+    c/*.h
+
 extra-doc-files:
     README.md
     CHANGELOG.md
@@ -37,6 +44,13 @@
     description: Run low-memory version of test suite
     default:     False
 
+flag static
+    description:
+        Use the bundled sources (this is default for windows and darwin)
+
+    default:     False
+    manual:      True
+
 library
     exposed-modules:
         Codec.Archive
@@ -44,8 +58,7 @@
         Codec.Archive.Foreign.Archive
         Codec.Archive.Foreign.ArchiveEntry
 
-    pkgconfig-depends: libarchive (==3.4.0 || >3.4.0) && <4.0
-    hs-source-dirs:    src
+    hs-source-dirs:   src
     other-modules:
         Codec.Archive.Foreign.Archive.Macros
         Codec.Archive.Foreign.ArchiveEntry.Macros
@@ -60,8 +73,8 @@
         Codec.Archive.Common
         Codec.Archive.Monad
 
-    default-language:  Haskell2010
-    other-extensions:  DeriveGeneric DeriveAnyClass
+    default-language: Haskell2010
+    other-extensions: DeriveGeneric DeriveAnyClass
     ghc-options:
         -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates
         -Wredundant-constraints
@@ -84,6 +97,158 @@
 
     if impl(ghc >=8.10)
         ghc-options: -Wunused-packages
+
+    if flag(static)
+        cc-options:   -std=gnu99 -DHAVE_CONFIG_H
+        c-sources:
+            c/archive_acl.c
+            c/archive_blake2sp_ref.c
+            c/archive_blake2s_ref.c
+            c/archive_check_magic.c
+            c/archive_cmdline.c
+            c/archive_cryptor.c
+            c/archive_digest.c
+            c/archive_disk_acl_darwin.c
+            c/archive_disk_acl_freebsd.c
+            c/archive_disk_acl_linux.c
+            c/archive_disk_acl_sunos.c
+            c/archive_entry.c
+            c/archive_entry_copy_bhfi.c
+            c/archive_entry_copy_stat.c
+            c/archive_entry_link_resolver.c
+            c/archive_entry_sparse.c
+            c/archive_entry_stat.c
+            c/archive_entry_strmode.c
+            c/archive_entry_xattr.c
+            c/archive_getdate.c
+            c/archive_hmac.c
+            c/archive_match.c
+            c/archive_options.c
+            c/archive_pack_dev.c
+            c/archive_pathmatch.c
+            c/archive_ppmd7.c
+            c/archive_ppmd8.c
+            c/archive_random.c
+            c/archive_rb.c
+            c/archive_read_add_passphrase.c
+            c/archive_read_append_filter.c
+            c/archive_read.c
+            c/archive_read_data_into_fd.c
+            c/archive_read_disk_entry_from_file.c
+            c/archive_read_disk_posix.c
+            c/archive_read_disk_set_standard_lookup.c
+            c/archive_read_disk_windows.c
+            c/archive_read_extract2.c
+            c/archive_read_extract.c
+            c/archive_read_open_fd.c
+            c/archive_read_open_file.c
+            c/archive_read_open_filename.c
+            c/archive_read_open_memory.c
+            c/archive_read_set_format.c
+            c/archive_read_set_options.c
+            c/archive_read_support_filter_all.c
+            c/archive_read_support_filter_bzip2.c
+            c/archive_read_support_filter_compress.c
+            c/archive_read_support_filter_grzip.c
+            c/archive_read_support_filter_gzip.c
+            c/archive_read_support_filter_lrzip.c
+            c/archive_read_support_filter_lz4.c
+            c/archive_read_support_filter_lzop.c
+            c/archive_read_support_filter_none.c
+            c/archive_read_support_filter_program.c
+            c/archive_read_support_filter_rpm.c
+            c/archive_read_support_filter_uu.c
+            c/archive_read_support_filter_xz.c
+            c/archive_read_support_filter_zstd.c
+            c/archive_read_support_format_7zip.c
+            c/archive_read_support_format_all.c
+            c/archive_read_support_format_ar.c
+            c/archive_read_support_format_by_code.c
+            c/archive_read_support_format_cab.c
+            c/archive_read_support_format_cpio.c
+            c/archive_read_support_format_empty.c
+            c/archive_read_support_format_iso9660.c
+            c/archive_read_support_format_lha.c
+            c/archive_read_support_format_mtree.c
+            c/archive_read_support_format_rar5.c
+            c/archive_read_support_format_rar.c
+            c/archive_read_support_format_raw.c
+            c/archive_read_support_format_tar.c
+            c/archive_read_support_format_warc.c
+            c/archive_read_support_format_xar.c
+            c/archive_read_support_format_zip.c
+            c/archive_string.c
+            c/archive_string_sprintf.c
+            c/archive_util.c
+            c/archive_version_details.c
+            c/archive_virtual.c
+            c/archive_windows.c
+            c/archive_write_add_filter_b64encode.c
+            c/archive_write_add_filter_by_name.c
+            c/archive_write_add_filter_bzip2.c
+            c/archive_write_add_filter.c
+            c/archive_write_add_filter_compress.c
+            c/archive_write_add_filter_grzip.c
+            c/archive_write_add_filter_gzip.c
+            c/archive_write_add_filter_lrzip.c
+            c/archive_write_add_filter_lz4.c
+            c/archive_write_add_filter_lzop.c
+            c/archive_write_add_filter_none.c
+            c/archive_write_add_filter_program.c
+            c/archive_write_add_filter_uuencode.c
+            c/archive_write_add_filter_xz.c
+            c/archive_write_add_filter_zstd.c
+            c/archive_write.c
+            c/archive_write_disk_posix.c
+            c/archive_write_disk_set_standard_lookup.c
+            c/archive_write_disk_windows.c
+            c/archive_write_open_fd.c
+            c/archive_write_open_file.c
+            c/archive_write_open_filename.c
+            c/archive_write_open_memory.c
+            c/archive_write_set_format_7zip.c
+            c/archive_write_set_format_ar.c
+            c/archive_write_set_format_by_name.c
+            c/archive_write_set_format.c
+            c/archive_write_set_format_cpio.c
+            c/archive_write_set_format_cpio_newc.c
+            c/archive_write_set_format_filter_by_ext.c
+            c/archive_write_set_format_gnutar.c
+            c/archive_write_set_format_iso9660.c
+            c/archive_write_set_format_mtree.c
+            c/archive_write_set_format_pax.c
+            c/archive_write_set_format_raw.c
+            c/archive_write_set_format_shar.c
+            c/archive_write_set_format_ustar.c
+            c/archive_write_set_format_v7tar.c
+            c/archive_write_set_format_warc.c
+            c/archive_write_set_format_xar.c
+            c/archive_write_set_format_zip.c
+            c/archive_write_set_options.c
+            c/archive_write_set_passphrase.c
+            c/filter_fork_posix.c
+            c/filter_fork_windows.c
+            c/xxhash.c
+
+        include-dirs: c/
+
+        if os(osx)
+            include-dirs: c/autoconf-darwin
+
+        else
+            if os(linux)
+                include-dirs: c/autoconf-linux
+
+            else
+                if os(freebsd)
+                    include-dirs: c/autoconf-freebsd
+
+                else
+                    buildable:     False
+                    build-depends: unbuildable <0
+
+    else
+        pkgconfig-depends: libarchive (==3.4.0 || >3.4.0) && <4.0
 
 test-suite libarchive-test
     type:               exitcode-stdio-1.0
diff --git a/mem/Mem.hs b/mem/Mem.hs
--- a/mem/Mem.hs
+++ b/mem/Mem.hs
@@ -10,5 +10,5 @@
 main = readArc
 
 readArc :: IO ()
-readArc = either throw forceList =<< runArchiveM
+readArc = forceList =<< throwArchiveM
     (readArchiveFile "test/data/llvm-9.0.0.src.tar")
diff --git a/src/Codec/Archive/Pack/Lazy.hs b/src/Codec/Archive/Pack/Lazy.hs
--- a/src/Codec/Archive/Pack/Lazy.hs
+++ b/src/Codec/Archive/Pack/Lazy.hs
@@ -101,3 +101,4 @@
             modifyIORef' bsRef (`DL.snoc` bsl)
             pure bytesRead
           doNothing _ _ = pure ArchiveOk
+          -- FIXME: this part isn't sufficiently lazy
diff --git a/src/Codec/Archive/Types/Foreign.chs b/src/Codec/Archive/Types/Foreign.chs
--- a/src/Codec/Archive/Types/Foreign.chs
+++ b/src/Codec/Archive/Types/Foreign.chs
@@ -35,6 +35,7 @@
 import           Control.DeepSeq    (NFData)
 import           Control.Exception  (Exception)
 import           Data.Bits          (Bits (..))
+import           Data.Semigroup     (Semigroup (..))
 import           Foreign.C.String   (CString)
 import           Foreign.C.Types    (CInt, CSize)
 import           Foreign.Ptr        (Ptr)
