diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,65 @@
+The libarchive distribution as a whole is Copyright by Tim Kientzle
+and is subject to the copyright notice reproduced at the bottom of
+this file.
+
+Each individual file in this distribution should have a clear
+copyright/licensing statement at the beginning of the file.  If any do
+not, please let me know and I will rectify it.  The following is
+intended to summarize the copyright status of the individual files;
+the actual statements in the files are controlling.
+
+* Except as listed below, all C sources (including .c and .h files)
+  and documentation files are subject to the copyright notice reproduced
+  at the bottom of this file.
+
+* The following source files are also subject in whole or in part to
+  a 3-clause UC Regents copyright; please read the individual source
+  files for details:
+   libarchive/archive_read_support_filter_compress.c
+   libarchive/archive_write_add_filter_compress.c
+   libarchive/mtree.5
+
+* The following source files are in the public domain:
+   libarchive/archive_getdate.c
+
+* The following source files are triple-licensed with the ability to choose
+  from CC0 1.0 Universal, OpenSSL or Apache 2.0 licenses:
+   libarchive/archive_blake2.h
+   libarchive/archive_blake2_impl.h
+   libarchive/archive_blake2s_ref.c
+   libarchive/archive_blake2sp_ref.c
+
+* The build files---including Makefiles, configure scripts,
+  and auxiliary scripts used as part of the compile process---have
+  widely varying licensing terms.  Please check individual files before
+  distributing them to see if those restrictions apply to you.
+
+I intend for all new source code to use the license below and hope over
+time to replace code with other licenses with new implementations that
+do use the license below.  The varying licensing of the build scripts
+seems to be an unavoidable mess.
+
+
+Copyright (c) 2003-2018 <author(s)>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer
+   in this position and unchanged.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMainWithHooks autoconfUserHooks
diff --git a/build/autoconf/config.guess b/build/autoconf/config.guess
new file mode 100644
--- /dev/null
+++ b/build/autoconf/config.guess
@@ -0,0 +1,1818 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright 1992-2025 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2025-07-10'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system '$me' is run on.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2025 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try '$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+# Just in case it came from the environment.
+GUESS=
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still
+# use 'HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+    # prevent multiple calls if $tmp is already set
+    test "$tmp" && return 0
+    : "${TMPDIR=/tmp}"
+    # shellcheck disable=SC2039,SC3028
+    { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+	{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+	{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+	{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+    dummy=$tmp/dummy
+    case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+	,,)    echo "int x;" > "$dummy.c"
+	       for driver in cc gcc c17 c99 c89 ; do
+		   if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+		       CC_FOR_BUILD=$driver
+		       break
+		   fi
+	       done
+	       if test x"$CC_FOR_BUILD" = x ; then
+		   CC_FOR_BUILD=no_compiler_found
+	       fi
+	       ;;
+	,,*)   CC_FOR_BUILD=$CC ;;
+	,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+    esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case $UNAME_SYSTEM in
+Linux|GNU|GNU/*)
+	LIBC=unknown
+
+	set_cc_for_build
+	cat <<-EOF > "$dummy.c"
+	#if defined(__ANDROID__)
+	LIBC=android
+	#else
+	#include <features.h>
+	#if defined(__UCLIBC__)
+	LIBC=uclibc
+	#elif defined(__dietlibc__)
+	LIBC=dietlibc
+	#elif defined(__GLIBC__)
+	LIBC=gnu
+	#elif defined(__LLVM_LIBC__)
+	LIBC=llvm
+	#else
+	#include <stdarg.h>
+	/* First heuristic to detect musl libc.  */
+	#ifdef __DEFINED_va_list
+	LIBC=musl
+	#endif
+	#endif
+	#endif
+	EOF
+	cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+	eval "$cc_set_libc"
+
+	# Second heuristic to detect musl libc.
+	if [ "$LIBC" = unknown ] &&
+	   command -v ldd >/dev/null &&
+	   ldd --version 2>&1 | grep -q ^musl; then
+		LIBC=musl
+	fi
+
+	# If the system lacks a compiler, then just pick glibc.
+	# We could probably try harder.
+	if [ "$LIBC" = unknown ]; then
+		LIBC=gnu
+	fi
+	;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
+    *:NetBSD:*:*)
+	# NetBSD (nbsd) targets should (where applicable) match one or
+	# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	#
+	# Note: NetBSD doesn't particularly care about the vendor
+	# portion of the name.  We always set it to "unknown".
+	UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+	    /sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+	    /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+	    echo unknown)`
+	case $UNAME_MACHINE_ARCH in
+	    aarch64eb) machine=aarch64_be-unknown ;;
+	    armeb) machine=armeb-unknown ;;
+	    arm*) machine=arm-unknown ;;
+	    sh3el) machine=shl-unknown ;;
+	    sh3eb) machine=sh-unknown ;;
+	    sh5el) machine=sh5le-unknown ;;
+	    earmv*)
+		arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+		endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+		machine=${arch}${endian}-unknown
+		;;
+	    *) machine=$UNAME_MACHINE_ARCH-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently (or will in the future) and ABI.
+	case $UNAME_MACHINE_ARCH in
+	    earm*)
+		os=netbsdelf
+		;;
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+		set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep -q __ELF__
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+		os=netbsd
+		;;
+	esac
+	# Determine ABI tags.
+	case $UNAME_MACHINE_ARCH in
+	    earm*)
+		expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+		abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+		;;
+	esac
+	# The OS release
+	# Debian GNU/NetBSD machines have a different userland, and
+	# thus, need a distinct triplet. However, they do not need
+	# kernel version information, so it can be replaced with a
+	# suitable tag, in the style of linux-gnu.
+	case $UNAME_VERSION in
+	    Debian*)
+		release='-gnu'
+		;;
+	    *)
+		release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+		;;
+	esac
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	GUESS=$machine-${os}${release}${abi-}
+	;;
+    *:Bitrig:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE
+	;;
+    *:OpenBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE
+	;;
+    *:SecBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE
+	;;
+    *:LibertyBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE
+	;;
+    *:MidnightBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE
+	;;
+    *:ekkoBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE
+	;;
+    *:SolidBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE
+	;;
+    *:OS108:*:*)
+	GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE
+	;;
+    macppc:MirBSD:*:*)
+	GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE
+	;;
+    *:MirBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE
+	;;
+    *:Sortix:*:*)
+	GUESS=$UNAME_MACHINE-unknown-sortix
+	;;
+    *:Twizzler:*:*)
+	GUESS=$UNAME_MACHINE-unknown-twizzler
+	;;
+    *:Redox:*:*)
+	GUESS=$UNAME_MACHINE-unknown-redox
+	;;
+    mips:OSF1:*.*)
+	GUESS=mips-dec-osf1
+	;;
+    alpha:OSF1:*:*)
+	# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+	trap '' 0
+	case $UNAME_RELEASE in
+	*4.0)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+		;;
+	*5.*)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+		;;
+	esac
+	# According to Compaq, /usr/sbin/psrinfo has been available on
+	# OSF/1 and Tru64 systems produced since 1995.  I hope that
+	# covers most systems running today.  This code pipes the CPU
+	# types through head -n 1, so we only detect the type of CPU 0.
+	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+	case $ALPHA_CPU_TYPE in
+	    "EV4 (21064)")
+		UNAME_MACHINE=alpha ;;
+	    "EV4.5 (21064)")
+		UNAME_MACHINE=alpha ;;
+	    "LCA4 (21066/21068)")
+		UNAME_MACHINE=alpha ;;
+	    "EV5 (21164)")
+		UNAME_MACHINE=alphaev5 ;;
+	    "EV5.6 (21164A)")
+		UNAME_MACHINE=alphaev56 ;;
+	    "EV5.6 (21164PC)")
+		UNAME_MACHINE=alphapca56 ;;
+	    "EV5.7 (21164PC)")
+		UNAME_MACHINE=alphapca57 ;;
+	    "EV6 (21264)")
+		UNAME_MACHINE=alphaev6 ;;
+	    "EV6.7 (21264A)")
+		UNAME_MACHINE=alphaev67 ;;
+	    "EV6.8CB (21264C)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.8AL (21264B)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.8CX (21264D)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.9A (21264/EV69A)")
+		UNAME_MACHINE=alphaev69 ;;
+	    "EV7 (21364)")
+		UNAME_MACHINE=alphaev7 ;;
+	    "EV7.9 (21364A)")
+		UNAME_MACHINE=alphaev79 ;;
+	esac
+	# A Pn.n version is a patched version.
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+	GUESS=$UNAME_MACHINE-dec-osf$OSF_REL
+	;;
+    Amiga*:UNIX_System_V:4.0:*)
+	GUESS=m68k-unknown-sysv4
+	;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	GUESS=$UNAME_MACHINE-unknown-amigaos
+	;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+	GUESS=$UNAME_MACHINE-unknown-morphos
+	;;
+    *:OS/390:*:*)
+	GUESS=i370-ibm-openedition
+	;;
+    *:z/VM:*:*)
+	GUESS=s390-ibm-zvmoe
+	;;
+    *:OS400:*:*)
+	GUESS=powerpc-ibm-os400
+	;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	GUESS=arm-acorn-riscix$UNAME_RELEASE
+	;;
+    arm*:riscos:*:*|arm*:RISCOS:*:*)
+	GUESS=arm-unknown-riscos
+	;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	GUESS=hppa1.1-hitachi-hiuxmpp
+	;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	case `(/bin/universe) 2>/dev/null` in
+	    att) GUESS=pyramid-pyramid-sysv3 ;;
+	    *)   GUESS=pyramid-pyramid-bsd   ;;
+	esac
+	;;
+    NILE*:*:*:dcosx)
+	GUESS=pyramid-pyramid-svr4
+	;;
+    DRS?6000:unix:4.0:6*)
+	GUESS=sparc-icl-nx6
+	;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+	case `/usr/bin/uname -p` in
+	    sparc) GUESS=sparc-icl-nx7 ;;
+	esac
+	;;
+    s390x:SunOS:*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL
+	;;
+    sun4H:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-hal-solaris2$SUN_REL
+	;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-sun-solaris2$SUN_REL
+	;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+	GUESS=i386-pc-auroraux$UNAME_RELEASE
+	;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+	set_cc_for_build
+	SUN_ARCH=i386
+	# If there is a compiler, see if it is configured for 64-bit objects.
+	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+	# This test works for both compilers.
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+		(CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \
+		grep IS_64BIT_ARCH >/dev/null
+	    then
+		SUN_ARCH=x86_64
+	    fi
+	fi
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=$SUN_ARCH-pc-solaris2$SUN_REL
+	;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-sun-solaris3$SUN_REL
+	;;
+    sun4*:SunOS:*:*)
+	case `/usr/bin/arch -k` in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like '4.1.3-JL'.
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
+	GUESS=sparc-sun-sunos$SUN_REL
+	;;
+    sun3*:SunOS:*:*)
+	GUESS=m68k-sun-sunos$UNAME_RELEASE
+	;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+	case `/bin/arch` in
+	    sun3)
+		GUESS=m68k-sun-sunos$UNAME_RELEASE
+		;;
+	    sun4)
+		GUESS=sparc-sun-sunos$UNAME_RELEASE
+		;;
+	esac
+	;;
+    aushp:SunOS:*:*)
+	GUESS=sparc-auspex-sunos$UNAME_RELEASE
+	;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+	GUESS=m68k-milan-mint$UNAME_RELEASE
+	;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+	GUESS=m68k-hades-mint$UNAME_RELEASE
+	;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+	GUESS=m68k-unknown-mint$UNAME_RELEASE
+	;;
+    m68k:machten:*:*)
+	GUESS=m68k-apple-machten$UNAME_RELEASE
+	;;
+    powerpc:machten:*:*)
+	GUESS=powerpc-apple-machten$UNAME_RELEASE
+	;;
+    RISC*:Mach:*:*)
+	GUESS=mips-dec-mach_bsd4.3
+	;;
+    RISC*:ULTRIX:*:*)
+	GUESS=mips-dec-ultrix$UNAME_RELEASE
+	;;
+    VAX*:ULTRIX*:*:*)
+	GUESS=vax-dec-ultrix$UNAME_RELEASE
+	;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	GUESS=clipper-intergraph-clix$UNAME_RELEASE
+	;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	set_cc_for_build
+	sed 's/^	//' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+	  dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+	  SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+	    { echo "$SYSTEM_NAME"; exit; }
+	GUESS=mips-mips-riscos$UNAME_RELEASE
+	;;
+    Motorola:PowerMAX_OS:*:*)
+	GUESS=powerpc-motorola-powermax
+	;;
+    Motorola:*:4.3:PL8-*)
+	GUESS=powerpc-harris-powermax
+	;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+	GUESS=powerpc-harris-powermax
+	;;
+    Night_Hawk:Power_UNIX:*:*)
+	GUESS=powerpc-harris-powerunix
+	;;
+    m88k:CX/UX:7*:*)
+	GUESS=m88k-harris-cxux7
+	;;
+    m88k:*:4*:R4*)
+	GUESS=m88k-motorola-sysv4
+	;;
+    m88k:*:3*:R3*)
+	GUESS=m88k-motorola-sysv3
+	;;
+    AViiON:dgux:*:*)
+	# DG/UX returns AViiON for all architectures
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
+	then
+	    if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
+	       test "$TARGET_BINARY_INTERFACE"x = x
+	    then
+		GUESS=m88k-dg-dgux$UNAME_RELEASE
+	    else
+		GUESS=m88k-dg-dguxbcs$UNAME_RELEASE
+	    fi
+	else
+	    GUESS=i586-dg-dgux$UNAME_RELEASE
+	fi
+	;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	GUESS=m88k-dolphin-sysv3
+	;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	GUESS=m88k-motorola-sysv3
+	;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	GUESS=m88k-tektronix-sysv3
+	;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	GUESS=m68k-tektronix-bsd
+	;;
+    *:IRIX*:*:*)
+	IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'`
+	GUESS=mips-sgi-irix$IRIX_REL
+	;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	GUESS=romp-ibm-aix    # uname -m gives an 8 hex-code CPU id
+	;;                    # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	GUESS=i386-ibm-aix
+	;;
+    ia64:AIX:*:*)
+	if test -x /usr/bin/oslevel ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+	fi
+	GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV
+	;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		set_cc_for_build
+		sed 's/^		//' << EOF > "$dummy.c"
+		#include <sys/systemcfg.h>
+
+		int
+		main ()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+		then
+			GUESS=$SYSTEM_NAME
+		else
+			GUESS=rs6000-ibm-aix3.2.5
+		fi
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		GUESS=rs6000-ibm-aix3.2.4
+	else
+		GUESS=rs6000-ibm-aix3.2
+	fi
+	;;
+    *:AIX:*:[4567])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if test -x /usr/bin/lslpp ; then
+		IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \
+			   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+	else
+		IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+	fi
+	GUESS=$IBM_ARCH-ibm-aix$IBM_REV
+	;;
+    *:AIX:*:*)
+	GUESS=rs6000-ibm-aix
+	;;
+    ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+	GUESS=romp-ibm-bsd4.4
+	;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	GUESS=romp-ibm-bsd$UNAME_RELEASE    # 4.3 with uname added to
+	;;                                  # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	GUESS=rs6000-bull-bosx
+	;;
+    DPX/2?00:B.O.S.:*:*)
+	GUESS=m68k-bull-sysv3
+	;;
+    9000/[34]??:4.3bsd:1.*:*)
+	GUESS=m68k-hp-bsd
+	;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	GUESS=m68k-hp-bsd4.4
+	;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+	case $UNAME_MACHINE in
+	    9000/31?)            HP_ARCH=m68000 ;;
+	    9000/[34]??)         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+		if test -x /usr/bin/getconf; then
+		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+		    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+		    case $sc_cpu_version in
+		      523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+		      528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+		      532)                      # CPU_PA_RISC2_0
+			case $sc_kernel_bits in
+			  32) HP_ARCH=hppa2.0n ;;
+			  64) HP_ARCH=hppa2.0w ;;
+			  '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
+			esac ;;
+		    esac
+		fi
+		if test "$HP_ARCH" = ""; then
+		    set_cc_for_build
+		    sed 's/^		//' << EOF > "$dummy.c"
+
+		#define _HPUX_SOURCE
+		#include <stdlib.h>
+		#include <unistd.h>
+
+		int
+		main ()
+		{
+		#if defined(_SC_KERNEL_BITS)
+		    long bits = sysconf(_SC_KERNEL_BITS);
+		#endif
+		    long cpu  = sysconf (_SC_CPU_VERSION);
+
+		    switch (cpu)
+			{
+			case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+			case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+			case CPU_PA_RISC2_0:
+		#if defined(_SC_KERNEL_BITS)
+			    switch (bits)
+				{
+				case 64: puts ("hppa2.0w"); break;
+				case 32: puts ("hppa2.0n"); break;
+				default: puts ("hppa2.0"); break;
+				} break;
+		#else  /* !defined(_SC_KERNEL_BITS) */
+			    puts ("hppa2.0"); break;
+		#endif
+			default: puts ("hppa1.0"); break;
+			}
+		    exit (0);
+		}
+EOF
+		    (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+		    test -z "$HP_ARCH" && HP_ARCH=hppa
+		fi ;;
+	esac
+	if test "$HP_ARCH" = hppa2.0w
+	then
+	    set_cc_for_build
+
+	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+	    # generating 64-bit code.  GNU and HP use different nomenclature:
+	    #
+	    # $ CC_FOR_BUILD=cc ./config.guess
+	    # => hppa2.0w-hp-hpux11.23
+	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+	    # => hppa64-hp-hpux11.23
+
+	    if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+		grep -q __LP64__
+	    then
+		HP_ARCH=hppa2.0w
+	    else
+		HP_ARCH=hppa64
+	    fi
+	fi
+	GUESS=$HP_ARCH-hp-hpux$HPUX_REV
+	;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+	GUESS=ia64-hp-hpux$HPUX_REV
+	;;
+    3050*:HI-UX:*:*)
+	set_cc_for_build
+	sed 's/^	//' << EOF > "$dummy.c"
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+		{ echo "$SYSTEM_NAME"; exit; }
+	GUESS=unknown-hitachi-hiuxwe2
+	;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+	GUESS=hppa1.1-hp-bsd
+	;;
+    9000/8??:4.3bsd:*:*)
+	GUESS=hppa1.0-hp-bsd
+	;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	GUESS=hppa1.0-hp-mpeix
+	;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+	GUESS=hppa1.1-hp-osf
+	;;
+    hp8??:OSF1:*:*)
+	GUESS=hppa1.0-hp-osf
+	;;
+    i*86:OSF1:*:*)
+	if test -x /usr/sbin/sysversion ; then
+	    GUESS=$UNAME_MACHINE-unknown-osf1mk
+	else
+	    GUESS=$UNAME_MACHINE-unknown-osf1
+	fi
+	;;
+    parisc*:Lites*:*:*)
+	GUESS=hppa1.1-hp-lites
+	;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	GUESS=c1-convex-bsd
+	;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	GUESS=c34-convex-bsd
+	;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	GUESS=c38-convex-bsd
+	;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	GUESS=c4-convex-bsd
+	;;
+    CRAY*Y-MP:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=ymp-cray-unicos$CRAY_REL
+	;;
+    CRAY*[A-Z]90:*:*:*)
+	echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*TS:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=t90-cray-unicos$CRAY_REL
+	;;
+    CRAY*T3E:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=alphaev5-cray-unicosmk$CRAY_REL
+	;;
+    CRAY*SV1:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=sv1-cray-unicos$CRAY_REL
+	;;
+    *:UNICOS/mp:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=craynv-cray-unicosmp$CRAY_REL
+	;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+	GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+	;;
+    5000:UNIX_System_V:4.*:*)
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+	GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+	;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE
+	;;
+    sparc*:BSD/OS:*:*)
+	GUESS=sparc-unknown-bsdi$UNAME_RELEASE
+	;;
+    *:BSD/OS:*:*)
+	GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE
+	;;
+    arm:FreeBSD:*:*)
+	UNAME_PROCESSOR=`uname -p`
+	set_cc_for_build
+	if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_PCS_VFP
+	then
+	    FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	    GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
+	else
+	    FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	    GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
+	fi
+	;;
+    *:FreeBSD:*:*)
+	UNAME_PROCESSOR=`uname -p`
+	case $UNAME_PROCESSOR in
+	    amd64)
+		UNAME_PROCESSOR=x86_64 ;;
+	    i386)
+		UNAME_PROCESSOR=i586 ;;
+	esac
+	FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL
+	;;
+    i*:CYGWIN*:*)
+	GUESS=$UNAME_MACHINE-pc-cygwin
+	;;
+    *:MINGW64*:*)
+	GUESS=$UNAME_MACHINE-pc-mingw64
+	;;
+    *:MINGW*:*)
+	GUESS=$UNAME_MACHINE-pc-mingw32
+	;;
+    *:MSYS*:*)
+	GUESS=$UNAME_MACHINE-pc-msys
+	;;
+    i*:PW*:*)
+	GUESS=$UNAME_MACHINE-pc-pw32
+	;;
+    *:SerenityOS:*:*)
+        GUESS=$UNAME_MACHINE-pc-serenity
+        ;;
+    *:Interix*:*)
+	case $UNAME_MACHINE in
+	    x86)
+		GUESS=i586-pc-interix$UNAME_RELEASE
+		;;
+	    authenticamd | genuineintel | EM64T)
+		GUESS=x86_64-unknown-interix$UNAME_RELEASE
+		;;
+	    IA64)
+		GUESS=ia64-unknown-interix$UNAME_RELEASE
+		;;
+	esac ;;
+    i*:UWIN*:*)
+	GUESS=$UNAME_MACHINE-pc-uwin
+	;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+	GUESS=x86_64-pc-cygwin
+	;;
+    prep*:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=powerpcle-unknown-solaris2$SUN_REL
+	;;
+    *:GNU:*:*)
+	# the GNU system
+	GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'`
+	GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'`
+	GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL
+	;;
+    *:GNU/*:*:*)
+	# other systems with GNU libc and userland
+	GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"`
+	GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
+	;;
+    x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*)
+	GUESS="$UNAME_MACHINE-pc-managarm-mlibc"
+	;;
+    *:[Mm]anagarm:*:*)
+	GUESS="$UNAME_MACHINE-unknown-managarm-mlibc"
+	;;
+    *:Minix:*:*)
+	GUESS=$UNAME_MACHINE-unknown-minix
+	;;
+    aarch64:Linux:*:*)
+	set_cc_for_build
+	CPU=$UNAME_MACHINE
+	LIBCABI=$LIBC
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    ABI=64
+	    sed 's/^	    //' << EOF > "$dummy.c"
+	    #ifdef __ARM_EABI__
+	    #ifdef __ARM_PCS_VFP
+	    ABI=eabihf
+	    #else
+	    ABI=eabi
+	    #endif
+	    #endif
+EOF
+	    cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
+	    eval "$cc_set_abi"
+	    case $ABI in
+		eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;;
+	    esac
+	fi
+	GUESS=$CPU-unknown-linux-$LIBCABI
+	;;
+    aarch64_be:Linux:*:*)
+	UNAME_MACHINE=aarch64_be
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+	esac
+	objdump --private-headers /bin/sh | grep -q ld.so.1
+	if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    arm*:Linux:*:*)
+	set_cc_for_build
+	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_EABI__
+	then
+	    GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	else
+	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+		| grep -q __ARM_PCS_VFP
+	    then
+		GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi
+	    else
+		GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf
+	    fi
+	fi
+	;;
+    avr32*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    cris:Linux:*:*)
+	GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+	;;
+    crisv32:Linux:*:*)
+	GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+	;;
+    e2k:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    frv:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    hexagon:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    i*86:Linux:*:*)
+	GUESS=$UNAME_MACHINE-pc-linux-$LIBC
+	;;
+    ia64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    k1om:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    kvx:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    kvx:cos:*:*)
+	GUESS=$UNAME_MACHINE-unknown-cos
+	;;
+    kvx:mbr:*:*)
+	GUESS=$UNAME_MACHINE-unknown-mbr
+	;;
+    loongarch32:Linux:*:* | loongarch64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    m32r*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    m68*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+	set_cc_for_build
+	IS_GLIBC=0
+	test x"${LIBC}" = xgnu && IS_GLIBC=1
+	sed 's/^	//' << EOF > "$dummy.c"
+	#undef CPU
+	#undef mips
+	#undef mipsel
+	#undef mips64
+	#undef mips64el
+	#if ${IS_GLIBC} && defined(_ABI64)
+	LIBCABI=gnuabi64
+	#else
+	#if ${IS_GLIBC} && defined(_ABIN32)
+	LIBCABI=gnuabin32
+	#else
+	LIBCABI=${LIBC}
+	#endif
+	#endif
+
+	#if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+	CPU=mipsisa64r6
+	#else
+	#if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+	CPU=mipsisa32r6
+	#else
+	#if defined(__mips64)
+	CPU=mips64
+	#else
+	CPU=mips
+	#endif
+	#endif
+	#endif
+
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	MIPS_ENDIAN=el
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	MIPS_ENDIAN=
+	#else
+	MIPS_ENDIAN=
+	#endif
+	#endif
+EOF
+	cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`
+	eval "$cc_set_vars"
+	test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+	;;
+    mips64el:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    openrisc*:Linux:*:*)
+	GUESS=or1k-unknown-linux-$LIBC
+	;;
+    or32:Linux:*:* | or1k*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    padre:Linux:*:*)
+	GUESS=sparc-unknown-linux-$LIBC
+	;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	GUESS=hppa64-unknown-linux-$LIBC
+	;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;;
+	  PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;;
+	  *)    GUESS=hppa-unknown-linux-$LIBC ;;
+	esac
+	;;
+    ppc64:Linux:*:*)
+	GUESS=powerpc64-unknown-linux-$LIBC
+	;;
+    ppc:Linux:*:*)
+	GUESS=powerpc-unknown-linux-$LIBC
+	;;
+    ppc64le:Linux:*:*)
+	GUESS=powerpc64le-unknown-linux-$LIBC
+	;;
+    ppcle:Linux:*:*)
+	GUESS=powerpcle-unknown-linux-$LIBC
+	;;
+    riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	GUESS=$UNAME_MACHINE-ibm-linux-$LIBC
+	;;
+    sh64*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    sh*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    tile*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    vax:Linux:*:*)
+	GUESS=$UNAME_MACHINE-dec-linux-$LIBC
+	;;
+    x86_64:Linux:*:*)
+	set_cc_for_build
+	CPU=$UNAME_MACHINE
+	LIBCABI=$LIBC
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    ABI=64
+	    sed 's/^	    //' << EOF > "$dummy.c"
+	    #ifdef __i386__
+	    ABI=x86
+	    #else
+	    #ifdef __ILP32__
+	    ABI=x32
+	    #endif
+	    #endif
+EOF
+	    cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
+	    eval "$cc_set_abi"
+	    case $ABI in
+		x86) CPU=i686 ;;
+		x32) LIBCABI=${LIBC}x32 ;;
+	    esac
+	fi
+	GUESS=$CPU-pc-linux-$LIBCABI
+	;;
+    xtensa*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	GUESS=i386-sequent-sysv4
+	;;
+    i*86:UNIX_SV:4.2MP:2.*)
+	# Unixware is an offshoot of SVR4, but it has its own version
+	# number series starting with 2...
+	# I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+	# Use sysv4.2uw... so that sysv4* matches it.
+	GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
+	;;
+    i*86:OS/2:*:*)
+	# If we were able to find 'uname', then EMX Unix compatibility
+	# is probably installed.
+	GUESS=$UNAME_MACHINE-pc-os2-emx
+	;;
+    i*86:XTS-300:*:STOP)
+	GUESS=$UNAME_MACHINE-unknown-stop
+	;;
+    i*86:atheos:*:*)
+	GUESS=$UNAME_MACHINE-unknown-atheos
+	;;
+    i*86:syllable:*:*)
+	GUESS=$UNAME_MACHINE-pc-syllable
+	;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+	GUESS=i386-unknown-lynxos$UNAME_RELEASE
+	;;
+    i*86:*DOS:*:*)
+	GUESS=$UNAME_MACHINE-pc-msdosdjgpp
+	;;
+    i*86:*:4.*:*)
+	UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL
+	else
+		GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL
+	fi
+	;;
+    i*86:*:5:[678]*)
+	# UnixWare 7.x, OpenUNIX and OpenServer 6.
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		GUESS=$UNAME_MACHINE-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL
+	else
+		GUESS=$UNAME_MACHINE-pc-sysv32
+	fi
+	;;
+    pc:*:*:*)
+	# Left here for compatibility:
+	# uname -m prints for DJGPP always 'pc', but it prints nothing about
+	# the processor, so we play safe by assuming i586.
+	# Note: whatever this is, it MUST be the same as what config.sub
+	# prints for the "djgpp" host, or else GDB configure will decide that
+	# this is a cross-build.
+	GUESS=i586-pc-msdosdjgpp
+	;;
+    Intel:Mach:3*:*)
+	GUESS=i386-pc-mach3
+	;;
+    paragon:*:*:*)
+	GUESS=i860-intel-osf1
+	;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  GUESS=i860-stardent-sysv$UNAME_RELEASE    # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  GUESS=i860-unknown-sysv$UNAME_RELEASE     # Unknown i860-SVR4
+	fi
+	;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	GUESS=m68010-convergent-sysv
+	;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+	GUESS=m68k-convergent-sysv
+	;;
+    M680?0:D-NIX:5.3:*)
+	GUESS=m68k-diab-dnix
+	;;
+    M68*:*:R3V[5678]*:*)
+	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+	OS_REL='.3'
+	test -r /etc/.relid \
+	    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	    && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	    && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+	    && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	GUESS=m68k-unknown-lynxos$UNAME_RELEASE
+	;;
+    mc68030:UNIX_System_V:4.*:*)
+	GUESS=m68k-atari-sysv4
+	;;
+    TSUNAMI:LynxOS:2.*:*)
+	GUESS=sparc-unknown-lynxos$UNAME_RELEASE
+	;;
+    rs6000:LynxOS:2.*:*)
+	GUESS=rs6000-unknown-lynxos$UNAME_RELEASE
+	;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+	GUESS=powerpc-unknown-lynxos$UNAME_RELEASE
+	;;
+    SM[BE]S:UNIX_SV:*:*)
+	GUESS=mips-dde-sysv$UNAME_RELEASE
+	;;
+    RM*:ReliantUNIX-*:*:*)
+	GUESS=mips-sni-sysv4
+	;;
+    RM*:SINIX-*:*:*)
+	GUESS=mips-sni-sysv4
+	;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		GUESS=$UNAME_MACHINE-sni-sysv4
+	else
+		GUESS=ns32k-sni-sysv
+	fi
+	;;
+    PENTIUM:*:4.0*:*)	# Unisys 'ClearPath HMP IX 4000' SVR4/MP effort
+			# says <Richard.M.Bartel@ccMail.Census.GOV>
+	GUESS=i586-unisys-sysv4
+	;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	GUESS=hppa1.1-stratus-sysv4
+	;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	GUESS=i860-stratus-sysv4
+	;;
+    i*86:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	GUESS=$UNAME_MACHINE-stratus-vos
+	;;
+    *:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	GUESS=hppa1.1-stratus-vos
+	;;
+    mc68*:A/UX:*:*)
+	GUESS=m68k-apple-aux$UNAME_RELEASE
+	;;
+    news*:NEWS-OS:6*:*)
+	GUESS=mips-sony-newsos6
+	;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if test -d /usr/nec; then
+		GUESS=mips-nec-sysv$UNAME_RELEASE
+	else
+		GUESS=mips-unknown-sysv$UNAME_RELEASE
+	fi
+	;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	GUESS=powerpc-be-beos
+	;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	GUESS=powerpc-apple-beos
+	;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	GUESS=i586-pc-beos
+	;;
+    BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
+	GUESS=i586-pc-haiku
+	;;
+    ppc:Haiku:*:*)	# Haiku running on Apple PowerPC
+	GUESS=powerpc-apple-haiku
+	;;
+    *:Haiku:*:*)	# Haiku modern gcc (not bound by BeOS compat)
+	GUESS=$UNAME_MACHINE-unknown-haiku
+	;;
+    SX-4:SUPER-UX:*:*)
+	GUESS=sx4-nec-superux$UNAME_RELEASE
+	;;
+    SX-5:SUPER-UX:*:*)
+	GUESS=sx5-nec-superux$UNAME_RELEASE
+	;;
+    SX-6:SUPER-UX:*:*)
+	GUESS=sx6-nec-superux$UNAME_RELEASE
+	;;
+    SX-7:SUPER-UX:*:*)
+	GUESS=sx7-nec-superux$UNAME_RELEASE
+	;;
+    SX-8:SUPER-UX:*:*)
+	GUESS=sx8-nec-superux$UNAME_RELEASE
+	;;
+    SX-8R:SUPER-UX:*:*)
+	GUESS=sx8r-nec-superux$UNAME_RELEASE
+	;;
+    SX-ACE:SUPER-UX:*:*)
+	GUESS=sxace-nec-superux$UNAME_RELEASE
+	;;
+    Power*:Rhapsody:*:*)
+	GUESS=powerpc-apple-rhapsody$UNAME_RELEASE
+	;;
+    *:Rhapsody:*:*)
+	GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE
+	;;
+    arm64:Darwin:*:*)
+	GUESS=aarch64-apple-darwin$UNAME_RELEASE
+	;;
+    *:Darwin:*:*)
+	UNAME_PROCESSOR=`uname -p`
+	case $UNAME_PROCESSOR in
+	    unknown) UNAME_PROCESSOR=powerpc ;;
+	esac
+	if command -v xcode-select > /dev/null 2> /dev/null && \
+		! xcode-select --print-path > /dev/null 2> /dev/null ; then
+	    # Avoid executing cc if there is no toolchain installed as
+	    # cc will be a stub that puts up a graphical alert
+	    # prompting the user to install developer tools.
+	    CC_FOR_BUILD=no_compiler_found
+	else
+	    set_cc_for_build
+	fi
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		   (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		   grep IS_64BIT_ARCH >/dev/null
+	    then
+		case $UNAME_PROCESSOR in
+		    i386) UNAME_PROCESSOR=x86_64 ;;
+		    powerpc) UNAME_PROCESSOR=powerpc64 ;;
+		esac
+	    fi
+	    # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+	    if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+		   (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		   grep IS_PPC >/dev/null
+	    then
+		UNAME_PROCESSOR=powerpc
+	    fi
+	elif test "$UNAME_PROCESSOR" = i386 ; then
+	    # uname -m returns i386 or x86_64
+	    UNAME_PROCESSOR=$UNAME_MACHINE
+	fi
+	GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE
+	;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	UNAME_PROCESSOR=`uname -p`
+	if test "$UNAME_PROCESSOR" = x86; then
+		UNAME_PROCESSOR=i386
+		UNAME_MACHINE=pc
+	fi
+	GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE
+	;;
+    *:QNX:*:4*)
+	GUESS=i386-pc-qnx
+	;;
+    NEO-*:NONSTOP_KERNEL:*:*)
+	GUESS=neo-tandem-nsk$UNAME_RELEASE
+	;;
+    NSE-*:NONSTOP_KERNEL:*:*)
+	GUESS=nse-tandem-nsk$UNAME_RELEASE
+	;;
+    NSR-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsr-tandem-nsk$UNAME_RELEASE
+	;;
+    NSV-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsv-tandem-nsk$UNAME_RELEASE
+	;;
+    NSX-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsx-tandem-nsk$UNAME_RELEASE
+	;;
+    *:NonStop-UX:*:*)
+	GUESS=mips-compaq-nonstopux
+	;;
+    BS2000:POSIX*:*:*)
+	GUESS=bs2000-siemens-sysv
+	;;
+    DS/*:UNIX_System_V:*:*)
+	GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE
+	;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "${cputype-}" = 386; then
+	    UNAME_MACHINE=i386
+	elif test "x${cputype-}" != x; then
+	    UNAME_MACHINE=$cputype
+	fi
+	GUESS=$UNAME_MACHINE-unknown-plan9
+	;;
+    *:TOPS-10:*:*)
+	GUESS=pdp10-unknown-tops10
+	;;
+    *:TENEX:*:*)
+	GUESS=pdp10-unknown-tenex
+	;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	GUESS=pdp10-dec-tops20
+	;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	GUESS=pdp10-xkl-tops20
+	;;
+    *:TOPS-20:*:*)
+	GUESS=pdp10-unknown-tops20
+	;;
+    *:ITS:*:*)
+	GUESS=pdp10-unknown-its
+	;;
+    SEI:*:*:SEIUX)
+	GUESS=mips-sei-seiux$UNAME_RELEASE
+	;;
+    *:DragonFly:*:*)
+	DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL
+	;;
+    *:*VMS:*:*)
+	UNAME_MACHINE=`(uname -p) 2>/dev/null`
+	case $UNAME_MACHINE in
+	    A*) GUESS=alpha-dec-vms ;;
+	    I*) GUESS=ia64-dec-vms ;;
+	    V*) GUESS=vax-dec-vms ;;
+	esac ;;
+    *:XENIX:*:SysV)
+	GUESS=i386-pc-xenix
+	;;
+    i*86:skyos:*:*)
+	SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
+	GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
+	;;
+    i*86:rdos:*:*)
+	GUESS=$UNAME_MACHINE-pc-rdos
+	;;
+    i*86:Fiwix:*:*)
+	GUESS=$UNAME_MACHINE-pc-fiwix
+	;;
+    *:AROS:*:*)
+	GUESS=$UNAME_MACHINE-unknown-aros
+	;;
+    x86_64:VMkernel:*:*)
+	GUESS=$UNAME_MACHINE-unknown-esx
+	;;
+    amd64:Isilon\ OneFS:*:*)
+	GUESS=x86_64-unknown-onefs
+	;;
+    *:Unleashed:*:*)
+	GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
+	;;
+    x86_64:[Ii]ronclad:*:*|i?86:[Ii]ronclad:*:*)
+	GUESS=$UNAME_MACHINE-pc-ironclad-mlibc
+	;;
+    *:[Ii]ronclad:*:*)
+	GUESS=$UNAME_MACHINE-unknown-ironclad-mlibc
+	;;
+esac
+
+# Do we have a guess based on uname results?
+if test "x$GUESS" != x; then
+    echo "$GUESS"
+    exit
+fi
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+int
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+  "4"
+#else
+  ""
+#endif
+  ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+  struct utsname un;
+
+  uname(&un);
+  if (strncmp(un.version, "V2", 2) == 0) {
+    printf ("i386-sequent-ptx2\n"); exit (0);
+  }
+  if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+    printf ("i386-sequent-ptx1\n"); exit (0);
+  }
+  printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+  printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+  printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname un;
+  uname (&un);
+  printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname *un;
+  uname (&un);
+  printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` &&
+	{ echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case $UNAME_MACHINE:$UNAME_SYSTEM in
+    mips:Linux | mips64:Linux)
+	# If we got here on MIPS GNU/Linux, output extra information.
+	cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+	;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+and
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+EOF
+
+our_year=`echo $timestamp | sed 's,-.*,,'`
+thisyear=`date +%Y`
+# shellcheck disable=SC2003
+script_age=`expr "$thisyear" - "$our_year"`
+if test "$script_age" -lt 3 ; then
+   cat >&2 <<EOF
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM  = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+fi
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp nil t)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%Y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/build/autoconf/config.sub b/build/autoconf/config.sub
new file mode 100644
--- /dev/null
+++ b/build/autoconf/config.sub
@@ -0,0 +1,2364 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright 1992-2025 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale
+
+timestamp='2025-07-10'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2025 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try '$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo "$1"
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Split fields of configuration type
+saved_IFS=$IFS
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+IFS=$saved_IFS
+
+# Separate into logical components for further validation
+case $1 in
+	*-*-*-*-*)
+		echo "Invalid configuration '$1': more than four components" >&2
+		exit 1
+		;;
+	*-*-*-*)
+		basic_machine=$field1-$field2
+		basic_os=$field3-$field4
+		;;
+	*-*-*)
+		# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+		# parts
+		maybe_os=$field2-$field3
+		case $maybe_os in
+			  cloudabi*-eabi* \
+			| kfreebsd*-gnu* \
+			| knetbsd*-gnu* \
+			| kopensolaris*-gnu* \
+			| ironclad-* \
+			| linux-* \
+			| managarm-* \
+			| netbsd*-eabi* \
+			| netbsd*-gnu* \
+			| nto-qnx* \
+			| os2-emx* \
+			| rtmk-nova* \
+			| storm-chaos* \
+			| uclinux-gnu* \
+			| uclinux-uclibc* \
+			| windows-* )
+				basic_machine=$field1
+				basic_os=$maybe_os
+				;;
+			android-linux)
+				basic_machine=$field1-unknown
+				basic_os=linux-android
+				;;
+			*)
+				basic_machine=$field1-$field2
+				basic_os=$field3
+				;;
+		esac
+		;;
+	*-*)
+		case $field1-$field2 in
+			# Shorthands that happen to contain a single dash
+			convex-c[12] | convex-c3[248])
+				basic_machine=$field2-convex
+				basic_os=
+				;;
+			decstation-3100)
+				basic_machine=mips-dec
+				basic_os=
+				;;
+			*-*)
+				# Second component is usually, but not always the OS
+				case $field2 in
+					# Do not treat sunos as a manufacturer
+					sun*os*)
+						basic_machine=$field1
+						basic_os=$field2
+						;;
+					# Manufacturers
+					  3100* \
+					| 32* \
+					| 3300* \
+					| 3600* \
+					| 7300* \
+					| acorn \
+					| altos* \
+					| apollo \
+					| apple \
+					| atari \
+					| att* \
+					| axis \
+					| be \
+					| bull \
+					| cbm \
+					| ccur \
+					| cisco \
+					| commodore \
+					| convergent* \
+					| convex* \
+					| cray \
+					| crds \
+					| dec* \
+					| delta* \
+					| dg \
+					| digital \
+					| dolphin \
+					| encore* \
+					| gould \
+					| harris \
+					| highlevel \
+					| hitachi* \
+					| hp \
+					| ibm* \
+					| intergraph \
+					| isi* \
+					| knuth \
+					| masscomp \
+					| microblaze* \
+					| mips* \
+					| motorola* \
+					| ncr* \
+					| news \
+					| next \
+					| ns \
+					| oki \
+					| omron* \
+					| pc533* \
+					| rebel \
+					| rom68k \
+					| rombug \
+					| semi \
+					| sequent* \
+					| sgi* \
+					| siemens \
+					| sim \
+					| sni \
+					| sony* \
+					| stratus \
+					| sun \
+					| sun[234]* \
+					| tektronix \
+					| tti* \
+					| ultra \
+					| unicom* \
+					| wec \
+					| winbond \
+					| wrs)
+						basic_machine=$field1-$field2
+						basic_os=
+						;;
+					tock* | zephyr*)
+						basic_machine=$field1-unknown
+						basic_os=$field2
+						;;
+					*)
+						basic_machine=$field1
+						basic_os=$field2
+						;;
+				esac
+			;;
+		esac
+		;;
+	*)
+		# Convert single-component short-hands not valid as part of
+		# multi-component configurations.
+		case $field1 in
+			386bsd)
+				basic_machine=i386-pc
+				basic_os=bsd
+				;;
+			a29khif)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			adobe68k)
+				basic_machine=m68010-adobe
+				basic_os=scout
+				;;
+			alliant)
+				basic_machine=fx80-alliant
+				basic_os=
+				;;
+			altos | altos3068)
+				basic_machine=m68k-altos
+				basic_os=
+				;;
+			am29k)
+				basic_machine=a29k-none
+				basic_os=bsd
+				;;
+			amdahl)
+				basic_machine=580-amdahl
+				basic_os=sysv
+				;;
+			amiga)
+				basic_machine=m68k-unknown
+				basic_os=
+				;;
+			amigaos | amigados)
+				basic_machine=m68k-unknown
+				basic_os=amigaos
+				;;
+			amigaunix | amix)
+				basic_machine=m68k-unknown
+				basic_os=sysv4
+				;;
+			apollo68)
+				basic_machine=m68k-apollo
+				basic_os=sysv
+				;;
+			apollo68bsd)
+				basic_machine=m68k-apollo
+				basic_os=bsd
+				;;
+			aros)
+				basic_machine=i386-pc
+				basic_os=aros
+				;;
+			aux)
+				basic_machine=m68k-apple
+				basic_os=aux
+				;;
+			balance)
+				basic_machine=ns32k-sequent
+				basic_os=dynix
+				;;
+			blackfin)
+				basic_machine=bfin-unknown
+				basic_os=linux
+				;;
+			cegcc)
+				basic_machine=arm-unknown
+				basic_os=cegcc
+				;;
+			cray)
+				basic_machine=j90-cray
+				basic_os=unicos
+				;;
+			crds | unos)
+				basic_machine=m68k-crds
+				basic_os=
+				;;
+			da30)
+				basic_machine=m68k-da30
+				basic_os=
+				;;
+			decstation | pmax | pmin | dec3100 | decstatn)
+				basic_machine=mips-dec
+				basic_os=
+				;;
+			delta88)
+				basic_machine=m88k-motorola
+				basic_os=sysv3
+				;;
+			dicos)
+				basic_machine=i686-pc
+				basic_os=dicos
+				;;
+			djgpp)
+				basic_machine=i586-pc
+				basic_os=msdosdjgpp
+				;;
+			ebmon29k)
+				basic_machine=a29k-amd
+				basic_os=ebmon
+				;;
+			es1800 | OSE68k | ose68k | ose | OSE)
+				basic_machine=m68k-ericsson
+				basic_os=ose
+				;;
+			gmicro)
+				basic_machine=tron-gmicro
+				basic_os=sysv
+				;;
+			go32)
+				basic_machine=i386-pc
+				basic_os=go32
+				;;
+			h8300hms)
+				basic_machine=h8300-hitachi
+				basic_os=hms
+				;;
+			h8300xray)
+				basic_machine=h8300-hitachi
+				basic_os=xray
+				;;
+			h8500hms)
+				basic_machine=h8500-hitachi
+				basic_os=hms
+				;;
+			harris)
+				basic_machine=m88k-harris
+				basic_os=sysv3
+				;;
+			hp300 | hp300hpux)
+				basic_machine=m68k-hp
+				basic_os=hpux
+				;;
+			hp300bsd)
+				basic_machine=m68k-hp
+				basic_os=bsd
+				;;
+			hppaosf)
+				basic_machine=hppa1.1-hp
+				basic_os=osf
+				;;
+			hppro)
+				basic_machine=hppa1.1-hp
+				basic_os=proelf
+				;;
+			i386mach)
+				basic_machine=i386-mach
+				basic_os=mach
+				;;
+			isi68 | isi)
+				basic_machine=m68k-isi
+				basic_os=sysv
+				;;
+			m68knommu)
+				basic_machine=m68k-unknown
+				basic_os=linux
+				;;
+			magnum | m3230)
+				basic_machine=mips-mips
+				basic_os=sysv
+				;;
+			merlin)
+				basic_machine=ns32k-utek
+				basic_os=sysv
+				;;
+			mingw64)
+				basic_machine=x86_64-pc
+				basic_os=mingw64
+				;;
+			mingw32)
+				basic_machine=i686-pc
+				basic_os=mingw32
+				;;
+			mingw32ce)
+				basic_machine=arm-unknown
+				basic_os=mingw32ce
+				;;
+			monitor)
+				basic_machine=m68k-rom68k
+				basic_os=coff
+				;;
+			morphos)
+				basic_machine=powerpc-unknown
+				basic_os=morphos
+				;;
+			moxiebox)
+				basic_machine=moxie-unknown
+				basic_os=moxiebox
+				;;
+			msdos)
+				basic_machine=i386-pc
+				basic_os=msdos
+				;;
+			msys)
+				basic_machine=i686-pc
+				basic_os=msys
+				;;
+			mvs)
+				basic_machine=i370-ibm
+				basic_os=mvs
+				;;
+			nacl)
+				basic_machine=le32-unknown
+				basic_os=nacl
+				;;
+			ncr3000)
+				basic_machine=i486-ncr
+				basic_os=sysv4
+				;;
+			netbsd386)
+				basic_machine=i386-pc
+				basic_os=netbsd
+				;;
+			netwinder)
+				basic_machine=armv4l-rebel
+				basic_os=linux
+				;;
+			news | news700 | news800 | news900)
+				basic_machine=m68k-sony
+				basic_os=newsos
+				;;
+			news1000)
+				basic_machine=m68030-sony
+				basic_os=newsos
+				;;
+			necv70)
+				basic_machine=v70-nec
+				basic_os=sysv
+				;;
+			nh3000)
+				basic_machine=m68k-harris
+				basic_os=cxux
+				;;
+			nh[45]000)
+				basic_machine=m88k-harris
+				basic_os=cxux
+				;;
+			nindy960)
+				basic_machine=i960-intel
+				basic_os=nindy
+				;;
+			mon960)
+				basic_machine=i960-intel
+				basic_os=mon960
+				;;
+			nonstopux)
+				basic_machine=mips-compaq
+				basic_os=nonstopux
+				;;
+			os400)
+				basic_machine=powerpc-ibm
+				basic_os=os400
+				;;
+			OSE68000 | ose68000)
+				basic_machine=m68000-ericsson
+				basic_os=ose
+				;;
+			os68k)
+				basic_machine=m68k-none
+				basic_os=os68k
+				;;
+			paragon)
+				basic_machine=i860-intel
+				basic_os=osf
+				;;
+			parisc)
+				basic_machine=hppa-unknown
+				basic_os=linux
+				;;
+			psp)
+				basic_machine=mipsallegrexel-sony
+				basic_os=psp
+				;;
+			pw32)
+				basic_machine=i586-unknown
+				basic_os=pw32
+				;;
+			rdos | rdos64)
+				basic_machine=x86_64-pc
+				basic_os=rdos
+				;;
+			rdos32)
+				basic_machine=i386-pc
+				basic_os=rdos
+				;;
+			rom68k)
+				basic_machine=m68k-rom68k
+				basic_os=coff
+				;;
+			sa29200)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			sei)
+				basic_machine=mips-sei
+				basic_os=seiux
+				;;
+			sequent)
+				basic_machine=i386-sequent
+				basic_os=
+				;;
+			sps7)
+				basic_machine=m68k-bull
+				basic_os=sysv2
+				;;
+			st2000)
+				basic_machine=m68k-tandem
+				basic_os=
+				;;
+			stratus)
+				basic_machine=i860-stratus
+				basic_os=sysv4
+				;;
+			sun2)
+				basic_machine=m68000-sun
+				basic_os=
+				;;
+			sun2os3)
+				basic_machine=m68000-sun
+				basic_os=sunos3
+				;;
+			sun2os4)
+				basic_machine=m68000-sun
+				basic_os=sunos4
+				;;
+			sun3)
+				basic_machine=m68k-sun
+				basic_os=
+				;;
+			sun3os3)
+				basic_machine=m68k-sun
+				basic_os=sunos3
+				;;
+			sun3os4)
+				basic_machine=m68k-sun
+				basic_os=sunos4
+				;;
+			sun4)
+				basic_machine=sparc-sun
+				basic_os=
+				;;
+			sun4os3)
+				basic_machine=sparc-sun
+				basic_os=sunos3
+				;;
+			sun4os4)
+				basic_machine=sparc-sun
+				basic_os=sunos4
+				;;
+			sun4sol2)
+				basic_machine=sparc-sun
+				basic_os=solaris2
+				;;
+			sun386 | sun386i | roadrunner)
+				basic_machine=i386-sun
+				basic_os=
+				;;
+			sv1)
+				basic_machine=sv1-cray
+				basic_os=unicos
+				;;
+			symmetry)
+				basic_machine=i386-sequent
+				basic_os=dynix
+				;;
+			t3e)
+				basic_machine=alphaev5-cray
+				basic_os=unicos
+				;;
+			t90)
+				basic_machine=t90-cray
+				basic_os=unicos
+				;;
+			toad1)
+				basic_machine=pdp10-xkl
+				basic_os=tops20
+				;;
+			tpf)
+				basic_machine=s390x-ibm
+				basic_os=tpf
+				;;
+			udi29k)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			ultra3)
+				basic_machine=a29k-nyu
+				basic_os=sym1
+				;;
+			v810 | necv810)
+				basic_machine=v810-nec
+				basic_os=none
+				;;
+			vaxv)
+				basic_machine=vax-dec
+				basic_os=sysv
+				;;
+			vms)
+				basic_machine=vax-dec
+				basic_os=vms
+				;;
+			vsta)
+				basic_machine=i386-pc
+				basic_os=vsta
+				;;
+			vxworks960)
+				basic_machine=i960-wrs
+				basic_os=vxworks
+				;;
+			vxworks68)
+				basic_machine=m68k-wrs
+				basic_os=vxworks
+				;;
+			vxworks29k)
+				basic_machine=a29k-wrs
+				basic_os=vxworks
+				;;
+			xbox)
+				basic_machine=i686-pc
+				basic_os=mingw32
+				;;
+			ymp)
+				basic_machine=ymp-cray
+				basic_os=unicos
+				;;
+			*)
+				basic_machine=$1
+				basic_os=
+				;;
+		esac
+		;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+	# Here we handle the default manufacturer of certain CPU types.  It is in
+	# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		cpu=hppa1.1
+		vendor=winbond
+		;;
+	op50n)
+		cpu=hppa1.1
+		vendor=oki
+		;;
+	op60c)
+		cpu=hppa1.1
+		vendor=oki
+		;;
+	ibm*)
+		cpu=i370
+		vendor=ibm
+		;;
+	orion105)
+		cpu=clipper
+		vendor=highlevel
+		;;
+	mac | mpw | mac-mpw)
+		cpu=m68k
+		vendor=apple
+		;;
+	pmac | pmac-mpw)
+		cpu=powerpc
+		vendor=apple
+		;;
+
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		cpu=m68000
+		vendor=att
+		;;
+	3b*)
+		cpu=we32k
+		vendor=att
+		;;
+	bluegene*)
+		cpu=powerpc
+		vendor=ibm
+		basic_os=cnk
+		;;
+	decsystem10* | dec10*)
+		cpu=pdp10
+		vendor=dec
+		basic_os=tops10
+		;;
+	decsystem20* | dec20*)
+		cpu=pdp10
+		vendor=dec
+		basic_os=tops20
+		;;
+	delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300)
+		cpu=m68k
+		vendor=motorola
+		;;
+	# This used to be dpx2*, but that gets the RS6000-based
+	# DPX/20 and the x86-based DPX/2-100 wrong.  See
+	# https://oldskool.silicium.org/stations/bull_dpx20.htm
+	# https://www.feb-patrimoine.com/english/bull_dpx2.htm
+	# https://www.feb-patrimoine.com/english/unix_and_bull.htm
+	dpx2 | dpx2[23]00 | dpx2[23]xx)
+		cpu=m68k
+		vendor=bull
+		;;
+	dpx2100 | dpx21xx)
+		cpu=i386
+		vendor=bull
+		;;
+	dpx20)
+		cpu=rs6000
+		vendor=bull
+		;;
+	encore | umax | mmax)
+		cpu=ns32k
+		vendor=encore
+		;;
+	elxsi)
+		cpu=elxsi
+		vendor=elxsi
+		basic_os=${basic_os:-bsd}
+		;;
+	fx2800)
+		cpu=i860
+		vendor=alliant
+		;;
+	genix)
+		cpu=ns32k
+		vendor=ns
+		;;
+	h3050r* | hiux*)
+		cpu=hppa1.1
+		vendor=hitachi
+		basic_os=hiuxwe2
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		cpu=m68000
+		vendor=hp
+		;;
+	hp9k3[2-9][0-9])
+		cpu=m68k
+		vendor=hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	i*86v32)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv32
+		;;
+	i*86v4*)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv4
+		;;
+	i*86v)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv
+		;;
+	i*86sol2)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=solaris2
+		;;
+	j90 | j90-cray)
+		cpu=j90
+		vendor=cray
+		basic_os=${basic_os:-unicos}
+		;;
+	iris | iris4d)
+		cpu=mips
+		vendor=sgi
+		case $basic_os in
+		    irix*)
+			;;
+		    *)
+			basic_os=irix4
+			;;
+		esac
+		;;
+	miniframe)
+		cpu=m68000
+		vendor=convergent
+		;;
+	*mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		cpu=m68k
+		vendor=atari
+		basic_os=mint
+		;;
+	news-3600 | risc-news)
+		cpu=mips
+		vendor=sony
+		basic_os=newsos
+		;;
+	next | m*-next)
+		cpu=m68k
+		vendor=next
+		;;
+	np1)
+		cpu=np1
+		vendor=gould
+		;;
+	op50n-* | op60c-*)
+		cpu=hppa1.1
+		vendor=oki
+		basic_os=proelf
+		;;
+	pa-hitachi)
+		cpu=hppa1.1
+		vendor=hitachi
+		basic_os=hiuxwe2
+		;;
+	pbd)
+		cpu=sparc
+		vendor=tti
+		;;
+	pbb)
+		cpu=m68k
+		vendor=tti
+		;;
+	pc532)
+		cpu=ns32k
+		vendor=pc532
+		;;
+	pn)
+		cpu=pn
+		vendor=gould
+		;;
+	power)
+		cpu=power
+		vendor=ibm
+		;;
+	ps2)
+		cpu=i386
+		vendor=ibm
+		;;
+	rm[46]00)
+		cpu=mips
+		vendor=siemens
+		;;
+	rtpc | rtpc-*)
+		cpu=romp
+		vendor=ibm
+		;;
+	sde)
+		cpu=mipsisa32
+		vendor=sde
+		basic_os=${basic_os:-elf}
+		;;
+	simso-wrs)
+		cpu=sparclite
+		vendor=wrs
+		basic_os=vxworks
+		;;
+	tower | tower-32)
+		cpu=m68k
+		vendor=ncr
+		;;
+	vpp*|vx|vx-*)
+		cpu=f301
+		vendor=fujitsu
+		;;
+	w65)
+		cpu=w65
+		vendor=wdc
+		;;
+	w89k-*)
+		cpu=hppa1.1
+		vendor=winbond
+		basic_os=proelf
+		;;
+	none)
+		cpu=none
+		vendor=none
+		;;
+	leon|leon[3-9])
+		cpu=sparc
+		vendor=$basic_machine
+		;;
+	leon-*|leon[3-9]-*)
+		cpu=sparc
+		vendor=`echo "$basic_machine" | sed 's/-.*//'`
+		;;
+
+	*-*)
+		saved_IFS=$IFS
+		IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+		IFS=$saved_IFS
+		;;
+	# We use 'pc' rather than 'unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+		cpu=$basic_machine
+		vendor=pc
+		;;
+	# These rules are duplicated from below for sake of the special case above;
+	# i.e. things that normalized to x86 arches should also default to "pc"
+	pc98)
+		cpu=i386
+		vendor=pc
+		;;
+	x64 | amd64)
+		cpu=x86_64
+		vendor=pc
+		;;
+	# Recognize the basic CPU types without company name.
+	*)
+		cpu=$basic_machine
+		vendor=unknown
+		;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+	# Here we handle the default manufacturer of certain CPU types in canonical form.
+	# It is in some cases the only manufacturer, in others, it is the most popular.
+	c[12]-convex | c[12]-unknown | c3[248]-convex | c3[248]-unknown)
+		vendor=convex
+		basic_os=${basic_os:-bsd}
+		;;
+	craynv-unknown)
+		vendor=cray
+		basic_os=${basic_os:-unicosmp}
+		;;
+	c90-unknown | c90-cray)
+		vendor=cray
+		basic_os=${basic_os:-unicos}
+		;;
+	fx80-unknown)
+		vendor=alliant
+		;;
+	romp-unknown)
+		vendor=ibm
+		;;
+	mmix-unknown)
+		vendor=knuth
+		;;
+	microblaze-unknown | microblazeel-unknown)
+		vendor=xilinx
+		;;
+	rs6000-unknown)
+		vendor=ibm
+		;;
+	vax-unknown)
+		vendor=dec
+		;;
+	pdp11-unknown)
+		vendor=dec
+		;;
+	we32k-unknown)
+		vendor=att
+		;;
+	cydra-unknown)
+		vendor=cydrome
+		;;
+	i370-ibm*)
+		vendor=ibm
+		;;
+	orion-unknown)
+		vendor=highlevel
+		;;
+	xps-unknown | xps100-unknown)
+		cpu=xps100
+		vendor=honeywell
+		;;
+
+	# Here we normalize CPU types with a missing or matching vendor
+	armh-unknown | armh-alt)
+		cpu=armv7l
+		vendor=alt
+		basic_os=${basic_os:-linux-gnueabihf}
+		;;
+
+	# Normalized CPU+vendor pairs that imply an OS, if not otherwise specified
+	m68k-isi)
+		basic_os=${basic_os:-sysv}
+		;;
+	m68k-sony)
+		basic_os=${basic_os:-newsos}
+		;;
+	m68k-tektronix)
+		basic_os=${basic_os:-bsd}
+		;;
+	m88k-harris)
+		basic_os=${basic_os:-sysv3}
+		;;
+	i386-bull | m68k-bull)
+		basic_os=${basic_os:-sysv3}
+		;;
+	rs6000-bull)
+		basic_os=${basic_os:-bosx}
+		;;
+	mips-sni)
+		basic_os=${basic_os:-sysv4}
+		;;
+
+	# Here we normalize CPU types irrespective of the vendor
+	amd64-*)
+		cpu=x86_64
+		;;
+	blackfin-*)
+		cpu=bfin
+		basic_os=${basic_os:-linux}
+		;;
+	c54x-*)
+		cpu=tic54x
+		;;
+	c55x-*)
+		cpu=tic55x
+		;;
+	c6x-*)
+		cpu=tic6x
+		;;
+	e500v[12]-*)
+		cpu=powerpc
+		basic_os=${basic_os}"spe"
+		;;
+	mips3*-*)
+		cpu=mips64
+		;;
+	ms1-*)
+		cpu=mt
+		;;
+	m68knommu-*)
+		cpu=m68k
+		basic_os=${basic_os:-linux}
+		;;
+	m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+		cpu=s12z
+		;;
+	openrisc-*)
+		cpu=or32
+		;;
+	parisc-*)
+		cpu=hppa
+		basic_os=${basic_os:-linux}
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+		cpu=i586
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-* | athlon_*-*)
+		cpu=i686
+		;;
+	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+		cpu=i686
+		;;
+	pentium4-*)
+		cpu=i786
+		;;
+	ppc-* | ppcbe-*)
+		cpu=powerpc
+		;;
+	ppcle-* | powerpclittle-*)
+		cpu=powerpcle
+		;;
+	ppc64-*)
+		cpu=powerpc64
+		;;
+	ppc64le-* | powerpc64little-*)
+		cpu=powerpc64le
+		;;
+	sb1-*)
+		cpu=mipsisa64sb1
+		;;
+	sb1el-*)
+		cpu=mipsisa64sb1el
+		;;
+	sh5e[lb]-*)
+		cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+		;;
+	spur-*)
+		cpu=spur
+		;;
+	strongarm-* | thumb-*)
+		cpu=arm
+		;;
+	tx39-*)
+		cpu=mipstx39
+		;;
+	tx39el-*)
+		cpu=mipstx39el
+		;;
+	xscale-* | xscalee[bl]-*)
+		cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+		;;
+	arm64-* | aarch64le-* | arm64_32-*)
+		cpu=aarch64
+		;;
+
+	# Recognize the canonical CPU Types that limit and/or modify the
+	# company names they are paired with.
+	cr16-*)
+		basic_os=${basic_os:-elf}
+		;;
+	crisv32-* | etraxfs*-*)
+		cpu=crisv32
+		vendor=axis
+		;;
+	cris-* | etrax*-*)
+		cpu=cris
+		vendor=axis
+		;;
+	crx-*)
+		basic_os=${basic_os:-elf}
+		;;
+	neo-tandem)
+		cpu=neo
+		vendor=tandem
+		;;
+	nse-tandem)
+		cpu=nse
+		vendor=tandem
+		;;
+	nsr-tandem)
+		cpu=nsr
+		vendor=tandem
+		;;
+	nsv-tandem)
+		cpu=nsv
+		vendor=tandem
+		;;
+	nsx-tandem)
+		cpu=nsx
+		vendor=tandem
+		;;
+	mipsallegrexel-sony)
+		cpu=mipsallegrexel
+		vendor=sony
+		;;
+	tile*-*)
+		basic_os=${basic_os:-linux-gnu}
+		;;
+
+	*)
+		# Recognize the canonical CPU types that are allowed with any
+		# company name.
+		case $cpu in
+			  1750a \
+			| 580 \
+			| [cjt]90 \
+			| a29k \
+			| aarch64 \
+			| aarch64_be \
+			| aarch64c \
+			| abacus \
+			| alpha \
+			| alpha64 \
+			| alpha64ev56 \
+			| alpha64ev6[78] \
+			| alpha64ev[4-8] \
+			| alpha64pca5[67] \
+			| alphaev56 \
+			| alphaev6[78] \
+			| alphaev[4-8] \
+			| alphapca5[67] \
+			| am33_2.0 \
+			| amdgcn \
+			| arc \
+			| arc32 \
+			| arc64 \
+			| arceb \
+			| arm \
+			| arm64e \
+			| arm64ec \
+			| arm[lb]e \
+			| arme[lb] \
+			| armv* \
+			| asmjs \
+			| avr \
+			| avr32 \
+			| ba \
+			| be32 \
+			| be64 \
+			| bfin \
+			| bpf \
+			| bs2000 \
+			| c30 \
+			| c4x \
+			| c8051 \
+			| c[123]* \
+			| clipper \
+			| craynv \
+			| csky \
+			| cydra \
+			| d10v \
+			| d30v \
+			| dlx \
+			| dsp16xx \
+			| e2k \
+			| elxsi \
+			| epiphany \
+			| f30[01] \
+			| f700 \
+			| fido \
+			| fr30 \
+			| frv \
+			| ft32 \
+			| fx80 \
+			| h8300 \
+			| h8500 \
+			| hexagon \
+			| hppa \
+			| hppa1.[01] \
+			| hppa2.0 \
+			| hppa2.0[nw] \
+			| hppa64 \
+			| i*86 \
+			| i370 \
+			| i860 \
+			| i960 \
+			| ia16 \
+			| ia64 \
+			| intelgt \
+			| ip2k \
+			| iq2000 \
+			| javascript \
+			| k1om \
+			| kvx \
+			| le32 \
+			| le64 \
+			| lm32 \
+			| loongarch32 \
+			| loongarch64 \
+			| m32c \
+			| m32r \
+			| m32rle \
+			| m5200 \
+			| m68000 \
+			| m680[012346]0 \
+			| m6811 \
+			| m6812 \
+			| m68360 \
+			| m683?2 \
+			| m68hc11 \
+			| m68hc12 \
+			| m68hcs12x \
+			| m68k \
+			| m88110 \
+			| m88k \
+			| maxq \
+			| mb \
+			| mcore \
+			| mep \
+			| metag \
+			| microblaze \
+			| microblazeel \
+			| mips* \
+			| mmix \
+			| mn10200 \
+			| mn10300 \
+			| moxie \
+			| msp430 \
+			| mt \
+			| nanomips* \
+			| nds32 \
+			| nds32be \
+			| nds32le \
+			| nfp \
+			| nios \
+			| nios2 \
+			| nios2eb \
+			| nios2el \
+			| none \
+			| np1 \
+			| ns16k \
+			| ns32k \
+			| nvptx \
+			| open8 \
+			| or1k* \
+			| or32 \
+			| orion \
+			| pdp10 \
+			| pdp11 \
+			| picochip \
+			| pj \
+			| pjl \
+			| pn \
+			| power \
+			| powerpc \
+			| powerpc64 \
+			| powerpc64le \
+			| powerpcle \
+			| powerpcspe \
+			| pru \
+			| pyramid \
+			| riscv \
+			| riscv32 \
+			| riscv32be \
+			| riscv64 \
+			| riscv64be \
+			| rl78 \
+			| romp \
+			| rs6000 \
+			| rx \
+			| s390 \
+			| s390x \
+			| score \
+			| sh \
+			| sh64 \
+			| sh64le \
+			| sh[12345][lb]e \
+			| sh[1234] \
+			| sh[1234]e[lb] \
+			| sh[23]e \
+			| sh[23]ele \
+			| sh[24]a \
+			| sh[24]ae[lb] \
+			| sh[lb]e \
+			| she[lb] \
+			| shl \
+			| sparc \
+			| sparc64 \
+			| sparc64b \
+			| sparc64v \
+			| sparc86x \
+			| sparclet \
+			| sparclite \
+			| sparcv8 \
+			| sparcv9 \
+			| sparcv9b \
+			| sparcv9v \
+			| spu \
+			| sv1 \
+			| sx* \
+			| tahoe \
+			| thumbv7* \
+			| tic30 \
+			| tic4x \
+			| tic54x \
+			| tic55x \
+			| tic6x \
+			| tic80 \
+			| tron \
+			| ubicom32 \
+			| v70 \
+			| v810 \
+			| v850 \
+			| v850e \
+			| v850e1 \
+			| v850e2 \
+			| v850e2v3 \
+			| v850es \
+			| vax \
+			| vc4 \
+			| visium \
+			| w65 \
+			| wasm32 \
+			| wasm64 \
+			| we32k \
+			| x86 \
+			| x86_64 \
+			| xc16x \
+			| xgate \
+			| xps100 \
+			| xstormy16 \
+			| xtensa* \
+			| ymp \
+			| z80 \
+			| z8k)
+				;;
+
+			*)
+				echo "Invalid configuration '$1': machine '$cpu-$vendor' not recognized" 1>&2
+				exit 1
+				;;
+		esac
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+	digital*)
+		vendor=dec
+		;;
+	commodore*)
+		vendor=cbm
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if test x"$basic_os" != x
+then
+
+# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just
+# set os.
+obj=
+case $basic_os in
+	gnu/linux*)
+		kernel=linux
+		os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'`
+		;;
+	os2-emx)
+		kernel=os2
+		os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'`
+		;;
+	nto-qnx*)
+		kernel=nto
+		os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'`
+		;;
+	*-*)
+		saved_IFS=$IFS
+		IFS="-" read kernel os <<EOF
+$basic_os
+EOF
+		IFS=$saved_IFS
+		;;
+	# Default OS when just kernel was specified
+	nto*)
+		kernel=nto
+		os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
+		;;
+	ironclad*)
+		kernel=ironclad
+		os=`echo "$basic_os" | sed -e 's|ironclad|mlibc|'`
+		;;
+	linux*)
+		kernel=linux
+		os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
+		;;
+	managarm*)
+		kernel=managarm
+		os=`echo "$basic_os" | sed -e 's|managarm|mlibc|'`
+		;;
+	*)
+		kernel=
+		os=$basic_os
+		;;
+esac
+
+# Now, normalize the OS (knowing we just have one component, it's not a kernel,
+# etc.)
+case $os in
+	# First match some system type aliases that might get confused
+	# with valid system types.
+	# solaris* is a basic system type, with this one exception.
+	auroraux)
+		os=auroraux
+		;;
+	bluegene*)
+		os=cnk
+		;;
+	solaris1 | solaris1.*)
+		os=`echo "$os" | sed -e 's|solaris1|sunos4|'`
+		;;
+	solaris)
+		os=solaris2
+		;;
+	unixware*)
+		os=sysv4.2uw
+		;;
+	# The marketing names for NeXT's operating systems were
+	# NeXTSTEP, NeXTSTEP 2, OpenSTEP 3, OpenSTEP 4.  'openstep' is
+	# mapped to 'openstep3', but 'openstep1' and 'openstep2' are
+	# mapped to 'nextstep' and 'nextstep2', consistent with the
+	# treatment of SunOS/Solaris.
+	ns | ns1 | nextstep | nextstep1 | openstep1)
+		os=nextstep
+		;;
+	ns2 | nextstep2 | openstep2)
+		os=nextstep2
+		;;
+	ns3 | nextstep3 | openstep | openstep3)
+		os=openstep3
+		;;
+	ns4 | nextstep4 | openstep4)
+		os=openstep4
+		;;
+	# es1800 is here to avoid being matched by es* (a different OS)
+	es1800*)
+		os=ose
+		;;
+	# Some version numbers need modification
+	chorusos*)
+		os=chorusos
+		;;
+	isc)
+		os=isc2.2
+		;;
+	sco6)
+		os=sco5v6
+		;;
+	sco5)
+		os=sco3.2v5
+		;;
+	sco4)
+		os=sco3.2v4
+		;;
+	sco3.2.[4-9]*)
+		os=`echo "$os" | sed -e 's/sco3.2./sco3.2v/'`
+		;;
+	sco*v* | scout)
+		# Don't match below
+		;;
+	sco*)
+		os=sco3.2v2
+		;;
+	psos*)
+		os=psos
+		;;
+	qnx*)
+		os=qnx
+		;;
+	hiux*)
+		os=hiuxwe2
+		;;
+	lynx*178)
+		os=lynxos178
+		;;
+	lynx*5)
+		os=lynxos5
+		;;
+	lynxos*)
+		# don't get caught up in next wildcard
+		;;
+	lynx*)
+		os=lynxos
+		;;
+	mac[0-9]*)
+		os=`echo "$os" | sed -e 's|mac|macos|'`
+		;;
+	opened*)
+		os=openedition
+		;;
+	os400*)
+		os=os400
+		;;
+	sunos5*)
+		os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+		;;
+	sunos6*)
+		os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+		;;
+	wince*)
+		os=wince
+		;;
+	utek*)
+		os=bsd
+		vendor=`echo "$vendor" | sed -e 's|^unknown$|tektronix|'`
+		;;
+	dynix*)
+		os=bsd
+		;;
+	acis*)
+		os=aos
+		;;
+	atheos*)
+		os=atheos
+		;;
+	syllable*)
+		os=syllable
+		;;
+	386bsd)
+		os=bsd
+		;;
+	ctix*)
+		os=sysv
+		vendor=`echo "$vendor" | sed -e 's|^unknown$|convergent|'`
+		;;
+	uts*)
+		os=sysv
+		;;
+	nova*)
+		kernel=rtmk
+		os=nova
+		;;
+	# Preserve the version number of sinix5.
+	sinix5.*)
+		os=`echo "$os" | sed -e 's|sinix|sysv|'`
+		vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'`
+		;;
+	sinix*)
+		os=sysv4
+		vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'`
+		;;
+	tpf*)
+		os=tpf
+		;;
+	triton*)
+		os=sysv3
+		;;
+	oss*)
+		os=sysv3
+		;;
+	svr4*)
+		os=sysv4
+		;;
+	svr3)
+		os=sysv3
+		;;
+	sysvr4)
+		os=sysv4
+		;;
+	ose*)
+		os=ose
+		;;
+	*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+		os=mint
+		;;
+	dicos*)
+		os=dicos
+		;;
+	pikeos*)
+		# Until real need of OS specific support for
+		# particular features comes up, bare metal
+		# configurations are quite functional.
+		case $cpu in
+		    arm*)
+			os=eabi
+			;;
+		    *)
+			os=
+			obj=elf
+			;;
+		esac
+		;;
+	aout* | coff* | elf* | pe*)
+		# These are machine code file formats, not OSes
+		obj=$os
+		os=
+		;;
+	*)
+		# No normalization, but not necessarily accepted, that comes below.
+		;;
+esac
+
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+kernel=
+obj=
+case $cpu-$vendor in
+	score-*)
+		os=
+		obj=elf
+		;;
+	spu-*)
+		os=
+		obj=elf
+		;;
+	*-acorn)
+		os=riscix1.2
+		;;
+	arm*-rebel)
+		kernel=linux
+		os=gnu
+		;;
+	arm*-semi)
+		os=
+		obj=aout
+		;;
+	c4x-* | tic4x-*)
+		os=
+		obj=coff
+		;;
+	c8051-*)
+		os=
+		obj=elf
+		;;
+	clipper-intergraph)
+		os=clix
+		;;
+	hexagon-*)
+		os=
+		obj=elf
+		;;
+	tic54x-*)
+		os=
+		obj=coff
+		;;
+	tic55x-*)
+		os=
+		obj=coff
+		;;
+	tic6x-*)
+		os=
+		obj=coff
+		;;
+	# This must come before the *-dec entry.
+	pdp10-*)
+		os=tops20
+		;;
+	pdp11-*)
+		os=none
+		;;
+	*-dec | vax-*)
+		os=ultrix4.2
+		;;
+	m68*-apollo)
+		os=domain
+		;;
+	i386-sun)
+		os=sunos4.0.2
+		;;
+	m68000-sun)
+		os=sunos3
+		;;
+	m68*-cisco)
+		os=
+		obj=aout
+		;;
+	mep-*)
+		os=
+		obj=elf
+		;;
+	# The -sgi and -siemens entries must be before the mips- entry
+	# or we get the wrong os.
+	*-sgi)
+		os=irix
+		;;
+	*-siemens)
+		os=sysv4
+		;;
+	mips*-cisco)
+		os=
+		obj=elf
+		;;
+	mips*-*|nanomips*-*)
+		os=
+		obj=elf
+		;;
+	or32-*)
+		os=
+		obj=coff
+		;;
+	# This must be before the sparc-* entry or we get the wrong os.
+	*-tti)
+		os=sysv3
+		;;
+	sparc-* | *-sun)
+		os=sunos4.1.1
+		;;
+	pru-*)
+		os=
+		obj=elf
+		;;
+	*-be)
+		os=beos
+		;;
+	*-ibm)
+		os=aix
+		;;
+	*-knuth)
+		os=mmixware
+		;;
+	*-wec)
+		os=proelf
+		;;
+	*-winbond)
+		os=proelf
+		;;
+	*-oki)
+		os=proelf
+		;;
+	*-hp)
+		os=hpux
+		;;
+	*-hitachi)
+		os=hiuxwe2
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=sysv
+		;;
+	*-cbm)
+		os=amigaos
+		;;
+	*-dg)
+		os=dgux
+		;;
+	*-dolphin)
+		os=sysv3
+		;;
+	m68k-ccur)
+		os=rtu
+		;;
+	m88k-omron*)
+		os=luna
+		;;
+	*-next)
+		os=nextstep
+		;;
+	*-sequent)
+		os=ptx
+		;;
+	*-crds)
+		os=unos
+		;;
+	*-ns)
+		os=genix
+		;;
+	i370-*)
+		os=mvs
+		;;
+	*-gould)
+		os=sysv
+		;;
+	*-highlevel)
+		os=bsd
+		;;
+	*-encore)
+		os=bsd
+		;;
+	*-masscomp)
+		os=rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=uxpv
+		;;
+	*-rom68k)
+		os=
+		obj=coff
+		;;
+	*-*bug)
+		os=
+		obj=coff
+		;;
+	*-apple)
+		os=macos
+		;;
+	*-atari*)
+		os=mint
+		;;
+	*-wrs)
+		os=vxworks
+		;;
+	*)
+		os=none
+		;;
+esac
+
+fi
+
+# Now, validate our (potentially fixed-up) individual pieces (OS, OBJ).
+
+case $os in
+	# Sometimes we do "kernel-libc", so those need to count as OSes.
+	llvm* | musl* | newlib* | relibc* | uclibc*)
+		;;
+	# Likewise for "kernel-abi"
+	eabi* | gnueabi*)
+		;;
+	# VxWorks passes extra cpu info in the 4th filed.
+	simlinux | simwindows | spe)
+		;;
+	# See `case $cpu-$os` validation below
+	ghcjs)
+		;;
+	# Now accept the basic system types.
+	# Each alternative MUST end in a * to match a version number.
+	  abug \
+	| aix* \
+	| amdhsa* \
+	| amigados* \
+	| amigaos* \
+	| android* \
+	| aof* \
+	| aos* \
+	| aros* \
+	| atheos* \
+	| auroraux* \
+	| aux* \
+	| banan_os* \
+	| beos* \
+	| bitrig* \
+	| bme* \
+	| bosx* \
+	| bsd* \
+	| cegcc* \
+	| chorusos* \
+	| chorusrdb* \
+	| clix* \
+	| cloudabi* \
+	| cnk* \
+	| conix* \
+	| cos* \
+	| cxux* \
+	| cygwin* \
+	| darwin* \
+	| dgux* \
+	| dicos* \
+	| dnix* \
+	| domain* \
+	| dragonfly* \
+	| drops* \
+	| ebmon* \
+	| ecoff* \
+	| ekkobsd* \
+	| emscripten* \
+	| emx* \
+	| es* \
+	| fiwix* \
+	| freebsd* \
+	| fuchsia* \
+	| genix* \
+	| genode* \
+	| glidix* \
+	| gnu* \
+	| go32* \
+	| haiku* \
+	| hcos* \
+	| hiux* \
+	| hms* \
+	| hpux* \
+	| ieee* \
+	| interix* \
+	| ios* \
+	| iris* \
+	| irix* \
+	| isc* \
+	| its* \
+	| l4re* \
+	| libertybsd* \
+	| lites* \
+	| lnews* \
+	| luna* \
+	| lynxos* \
+	| mach* \
+	| macos* \
+	| magic* \
+	| mbr* \
+	| midipix* \
+	| midnightbsd* \
+	| mingw32* \
+	| mingw64* \
+	| minix* \
+	| mint* \
+	| mirbsd* \
+	| mks* \
+	| mlibc* \
+	| mmixware* \
+	| mon960* \
+	| morphos* \
+	| moss* \
+	| moxiebox* \
+	| mpeix* \
+	| mpw* \
+	| msdos* \
+	| msys* \
+	| mvs* \
+	| nacl* \
+	| netbsd* \
+	| netware* \
+	| newsos* \
+	| nextstep* \
+	| nindy* \
+	| nonstopux* \
+	| nova* \
+	| nsk* \
+	| nucleus* \
+	| nx6 \
+	| nx7 \
+	| oabi* \
+	| ohos* \
+	| onefs* \
+	| openbsd* \
+	| openedition* \
+	| openstep* \
+	| os108* \
+	| os2* \
+	| os400* \
+	| os68k* \
+	| os9* \
+	| ose* \
+	| osf* \
+	| oskit* \
+	| osx* \
+	| palmos* \
+	| phoenix* \
+	| plan9* \
+	| powermax* \
+	| powerunix* \
+	| proelf* \
+	| psos* \
+	| psp* \
+	| ptx* \
+	| pw32* \
+	| qnx* \
+	| rdos* \
+	| redox* \
+	| rhapsody* \
+	| riscix* \
+	| riscos* \
+	| rtems* \
+	| rtmk* \
+	| rtu* \
+	| scout* \
+	| secbsd* \
+	| sei* \
+	| serenity* \
+	| sim* \
+	| skyos* \
+	| solaris* \
+	| solidbsd* \
+	| sortix* \
+	| storm-chaos* \
+	| sunos \
+	| sunos[34]* \
+	| superux* \
+	| syllable* \
+	| sym* \
+	| sysv* \
+	| tenex* \
+	| tirtos* \
+	| tock* \
+	| toppers* \
+	| tops10* \
+	| tops20* \
+	| tpf* \
+	| tvos* \
+	| twizzler* \
+	| uclinux* \
+	| udi* \
+	| udk* \
+	| ultrix* \
+	| unicos* \
+	| uniplus* \
+	| unleashed* \
+	| unos* \
+	| uwin* \
+	| uxpv* \
+	| v88r* \
+	|*vms* \
+	| vos* \
+	| vsta* \
+	| vxsim* \
+	| vxworks* \
+	| wasi* \
+	| watchos* \
+	| wince* \
+	| windiss* \
+	| windows* \
+	| winnt* \
+	| xenix* \
+	| xray* \
+	| zephyr* \
+	| zvmoe* )
+		;;
+	# This one is extra strict with allowed versions
+	sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
+		# Don't forget version if it is 3.2v4 or newer.
+		;;
+	# This refers to builds using the UEFI calling convention
+	# (which depends on the architecture) and PE file format.
+	# Note that this is both a different calling convention and
+	# different file format than that of GNU-EFI
+	# (x86_64-w64-mingw32).
+	uefi)
+		;;
+	none)
+		;;
+	kernel* | msvc* )
+		# Restricted further below
+		;;
+	'')
+		if test x"$obj" = x
+		then
+			echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2
+		fi
+		;;
+	*)
+		echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2
+		exit 1
+		;;
+esac
+
+case $obj in
+	aout* | coff* | elf* | pe*)
+		;;
+	'')
+		# empty is fine
+		;;
+	*)
+		echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2
+		exit 1
+		;;
+esac
+
+# Here we handle the constraint that a (synthetic) cpu and os are
+# valid only in combination with each other and nowhere else.
+case $cpu-$os in
+	# The "javascript-unknown-ghcjs" triple is used by GHC; we
+	# accept it here in order to tolerate that, but reject any
+	# variations.
+	javascript-ghcjs)
+		;;
+	javascript-* | *-ghcjs)
+		echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2
+		exit 1
+		;;
+esac
+
+# As a final step for OS-related things, validate the OS-kernel combination
+# (given a valid OS), if there is a kernel.
+case $kernel-$os-$obj in
+	linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \
+		    | linux-mlibc*- | linux-musl*- | linux-newlib*- \
+		    | linux-relibc*- | linux-uclibc*- | linux-ohos*- )
+		;;
+	uclinux-uclibc*- | uclinux-gnu*- )
+		;;
+	ironclad-mlibc*-)
+		;;
+	managarm-mlibc*- | managarm-kernel*- )
+		;;
+	windows*-msvc*-)
+		;;
+	-dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \
+		    | -uclibc*- )
+		# These are just libc implementations, not actual OSes, and thus
+		# require a kernel.
+		echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2
+		exit 1
+		;;
+	-kernel*- )
+		echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2
+		exit 1
+		;;
+	*-kernel*- )
+		echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2
+		exit 1
+		;;
+	*-msvc*- )
+		echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2
+		exit 1
+		;;
+	kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-)
+		;;
+	vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-)
+		;;
+	nto-qnx*-)
+		;;
+	os2-emx-)
+		;;
+	rtmk-nova-)
+		;;
+	*-eabi*- | *-gnueabi*-)
+		;;
+	ios*-simulator- | tvos*-simulator- | watchos*-simulator- )
+		;;
+	none--*)
+		# None (no kernel, i.e. freestanding / bare metal),
+		# can be paired with an machine code file format
+		;;
+	-*-)
+		# Blank kernel with real OS is always fine.
+		;;
+	--*)
+		# Blank kernel and OS with real machine code file format is always fine.
+		;;
+	*-*-*)
+		echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2
+		exit 1
+		;;
+esac
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+case $vendor in
+	unknown)
+		case $cpu-$os in
+			*-riscix*)
+				vendor=acorn
+				;;
+			*-sunos* | *-solaris*)
+				vendor=sun
+				;;
+			*-cnk* | *-aix*)
+				vendor=ibm
+				;;
+			*-beos*)
+				vendor=be
+				;;
+			*-hpux*)
+				vendor=hp
+				;;
+			*-mpeix*)
+				vendor=hp
+				;;
+			*-hiux*)
+				vendor=hitachi
+				;;
+			*-unos*)
+				vendor=crds
+				;;
+			*-dgux*)
+				vendor=dg
+				;;
+			*-luna*)
+				vendor=omron
+				;;
+			*-genix*)
+				vendor=ns
+				;;
+			*-clix*)
+				vendor=intergraph
+				;;
+			*-mvs* | *-opened*)
+				vendor=ibm
+				;;
+			*-os400*)
+				vendor=ibm
+				;;
+			s390-* | s390x-*)
+				vendor=ibm
+				;;
+			*-ptx*)
+				vendor=sequent
+				;;
+			*-tpf*)
+				vendor=ibm
+				;;
+			*-vxsim* | *-vxworks* | *-windiss*)
+				vendor=wrs
+				;;
+			*-aux*)
+				vendor=apple
+				;;
+			*-hms*)
+				vendor=hitachi
+				;;
+			*-mpw* | *-macos*)
+				vendor=apple
+				;;
+			*-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
+				vendor=atari
+				;;
+			*-vos*)
+				vendor=stratus
+				;;
+		esac
+		;;
+esac
+
+echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp nil t)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%Y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/build/autoconf/install-sh b/build/autoconf/install-sh
new file mode 100644
--- /dev/null
+++ b/build/autoconf/install-sh
@@ -0,0 +1,541 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2025-06-18.21; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# 'make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+tab='	'
+nl='
+'
+IFS=" $tab$nl"
+
+# Set DOITPROG to "echo" to test this script.
+
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+# Create dirs (including intermediate dirs) using mode 755.
+# This is like GNU 'install' as of coreutils 8.32 (2020).
+mkdir_umask=22
+
+backupsuffix=
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+is_target_a_directory=possibly
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -p            pass -p to $cpprog.
+  -s            $stripprog installed files.
+  -S SUFFIX     attempt to back up existing files, with suffix SUFFIX.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+
+By default, rm is invoked with -f; when overridden with RMPROG,
+it's up to you to specify -f if you want it.
+
+If -S is not specified, no backups are attempted.
+
+Report bugs to <bug-automake@gnu.org>.
+GNU Automake home page: <https://www.gnu.org/software/automake/>.
+General help using GNU software: <https://www.gnu.org/gethelp/>."
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
+
+    -o) chowncmd="$chownprog $2"
+        shift;;
+
+    -p) cpprog="$cpprog -p";;
+
+    -s) stripcmd=$stripprog;;
+
+    -S) backupsuffix="$2"
+        shift;;
+
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
+
+    -T) is_target_a_directory=never;;
+
+    --version) echo "$0 (GNU Automake) $scriptversion"; exit $?;;
+
+    --) shift
+        break;;
+
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call 'install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names problematic for 'test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+    # Don't chown directories that already exist.
+    if test $dstdir_status = 0; then
+      chowncmd=""
+    fi
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename.
+    if test -d "$dst"; then
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
+      fi
+      dstdir=$dst
+      dstbase=`basename "$src"`
+      case $dst in
+	*/) dst=$dst$dstbase;;
+	*)  dst=$dst/$dstbase;;
+      esac
+      dstdir_status=0
+    else
+      dstdir=`dirname "$dst"`
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  case $dstdir in
+    */) dstdirslash=$dstdir;;
+    *)  dstdirslash=$dstdir/;;
+  esac
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+	# The $RANDOM variable is not portable (e.g., dash).  Use it
+	# here however when possible just to lower collision chance.
+	tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
+	trap '
+	  ret=$?
+	  rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
+	  exit $ret
+	' 0
+
+	# Because "mkdir -p" follows existing symlinks and we likely work
+	# directly in world-writable /tmp, make sure that the '$tmpdir'
+	# directory is successfully created first before we actually test
+	# 'mkdir -p'.
+	if (umask $mkdir_umask &&
+	    $mkdirprog $mkdir_mode "$tmpdir" &&
+	    exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+	then
+	  if test -z "$dir_arg" || {
+	       # Check for POSIX incompatibility with -m.
+	       # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+	       # other-writable bit of parent directory when it shouldn't.
+	       # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+	       test_tmpdir="$tmpdir/a"
+	       ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+	       case $ls_ld_tmpdir in
+		 d????-?r-*) different_mode=700;;
+		 d????-?--*) different_mode=755;;
+		 *) false;;
+	       esac &&
+	       $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+		 ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+		 test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+	       }
+	     }
+	  then posix_mkdir=:
+	  fi
+	  rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+	else
+	  # Remove any dirs left behind by ancient mkdir implementations.
+	  rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+	fi
+	trap '' 0;;
+    esac
+
+    if
+      $posix_mkdir && (
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
+      esac
+
+      oIFS=$IFS
+      IFS=/
+      set -f
+      set fnord $dstdir
+      shift
+      set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask $mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=${dstdirslash}_inst.$$_
+    rmtmp=${dstdirslash}_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask &&
+     { test -z "$stripcmd" || {
+	 # Create $dsttmp read-write so that cp doesn't create it read-only,
+	 # which would cause strip to fail.
+	 if test -z "$doit"; then
+	   : >"$dsttmp" # No need to fork-exec 'touch'.
+	 else
+	   $doit touch "$dsttmp"
+	 fi
+       }
+     } &&
+     $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       set +f &&
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # If $backupsuffix is set, and the file being installed
+      # already exists, attempt a backup.  Don't worry if it fails,
+      # e.g., if mv doesn't support -f.
+      if test -n "$backupsuffix" && test -f "$dst"; then
+        $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
+      fi
+
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp nil t)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%Y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/build/autoconf/ltmain.sh b/build/autoconf/ltmain.sh
new file mode 100644
--- /dev/null
+++ b/build/autoconf/ltmain.sh
@@ -0,0 +1,11429 @@
+#! /usr/bin/env sh
+## DO NOT EDIT - This file generated from ./build-aux/ltmain.in
+##               by inline-source v2019-02-19.15
+
+# libtool (GNU libtool) 2.4.7
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+
+# Copyright (C) 1996-2019, 2021-2022 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions.  There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+PROGRAM=libtool
+PACKAGE=libtool
+VERSION=2.4.7
+package_revision=2.4.7
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# Run './libtool --help' for help with using this script from the
+# command line.
+
+
+## ------------------------------- ##
+## User overridable command paths. ##
+## ------------------------------- ##
+
+# After configure completes, it has a better idea of some of the
+# shell tools we need than the defaults used by the functions shared
+# with bootstrap, so set those here where they can still be over-
+# ridden by the user, but otherwise take precedence.
+
+: ${AUTOCONF="autoconf"}
+: ${AUTOMAKE="automake"}
+
+
+## -------------------------- ##
+## Source external libraries. ##
+## -------------------------- ##
+
+# Much of our low-level functionality needs to be sourced from external
+# libraries, which are installed to $pkgauxdir.
+
+# Set a version string for this script.
+scriptversion=2019-02-19.15; # UTC
+
+# General shell script boiler plate, and helper functions.
+# Written by Gary V. Vaughan, 2004
+
+# This is free software.  There is NO warranty; not even for
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Copyright (C) 2004-2019, 2021 Bootstrap Authors
+#
+# This file is dual licensed under the terms of the MIT license
+# <https://opensource.org/license/MIT>, and GPL version 2 or later
+# <http://www.gnu.org/licenses/gpl-2.0.html>.  You must apply one of
+# these licenses when using or redistributing this software or any of
+# the files within it.  See the URLs above, or the file `LICENSE`
+# included in the Bootstrap distribution for the full license texts.
+
+# Please report bugs or propose patches to:
+# <https://github.com/gnulib-modules/bootstrap/issues>
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# Evaluate this file near the top of your script to gain access to
+# the functions and variables defined here:
+#
+#   . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh
+#
+# If you need to override any of the default environment variable
+# settings, do that before evaluating this file.
+
+
+## -------------------- ##
+## Shell normalisation. ##
+## -------------------- ##
+
+# Some shells need a little help to be as Bourne compatible as possible.
+# Before doing anything else, make sure all that help has been provided!
+
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac
+fi
+
+# NLS nuisances: We save the old values in case they are required later.
+_G_user_locale=
+_G_safe_locale=
+for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+do
+  eval "if test set = \"\${$_G_var+set}\"; then
+          save_$_G_var=\$$_G_var
+          $_G_var=C
+	  export $_G_var
+	  _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\"
+	  _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\"
+	fi"
+done
+# These NLS vars are set unconditionally (bootstrap issue #24).  Unset those
+# in case the environment reset is needed later and the $save_* variant is not
+# defined (see the code above).
+LC_ALL=C
+LANGUAGE=C
+export LANGUAGE LC_ALL
+
+# Make sure IFS has a sensible default
+sp=' '
+nl='
+'
+IFS="$sp	$nl"
+
+# There are apparently some retarded systems that use ';' as a PATH separator!
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# func_unset VAR
+# --------------
+# Portably unset VAR.
+# In some shells, an 'unset VAR' statement leaves a non-zero return
+# status if VAR is already unset, which might be problematic if the
+# statement is used at the end of a function (thus poisoning its return
+# value) or when 'set -e' is active (causing even a spurious abort of
+# the script in this case).
+func_unset ()
+{
+    { eval $1=; (eval unset $1) >/dev/null 2>&1 && eval unset $1 || : ; }
+}
+
+
+# Make sure CDPATH doesn't cause `cd` commands to output the target dir.
+func_unset CDPATH
+
+# Make sure ${,E,F}GREP behave sanely.
+func_unset GREP_OPTIONS
+
+
+## ------------------------- ##
+## Locate command utilities. ##
+## ------------------------- ##
+
+
+# func_executable_p FILE
+# ----------------------
+# Check that FILE is an executable regular file.
+func_executable_p ()
+{
+    test -f "$1" && test -x "$1"
+}
+
+
+# func_path_progs PROGS_LIST CHECK_FUNC [PATH]
+# --------------------------------------------
+# Search for either a program that responds to --version with output
+# containing "GNU", or else returned by CHECK_FUNC otherwise, by
+# trying all the directories in PATH with each of the elements of
+# PROGS_LIST.
+#
+# CHECK_FUNC should accept the path to a candidate program, and
+# set $func_check_prog_result if it truncates its output less than
+# $_G_path_prog_max characters.
+func_path_progs ()
+{
+    _G_progs_list=$1
+    _G_check_func=$2
+    _G_PATH=${3-"$PATH"}
+
+    _G_path_prog_max=0
+    _G_path_prog_found=false
+    _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:}
+    for _G_dir in $_G_PATH; do
+      IFS=$_G_save_IFS
+      test -z "$_G_dir" && _G_dir=.
+      for _G_prog_name in $_G_progs_list; do
+        for _exeext in '' .EXE; do
+          _G_path_prog=$_G_dir/$_G_prog_name$_exeext
+          func_executable_p "$_G_path_prog" || continue
+          case `"$_G_path_prog" --version 2>&1` in
+            *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;;
+            *)     $_G_check_func $_G_path_prog
+		   func_path_progs_result=$func_check_prog_result
+		   ;;
+          esac
+          $_G_path_prog_found && break 3
+        done
+      done
+    done
+    IFS=$_G_save_IFS
+    test -z "$func_path_progs_result" && {
+      echo "no acceptable sed could be found in \$PATH" >&2
+      exit 1
+    }
+}
+
+
+# We want to be able to use the functions in this file before configure
+# has figured out where the best binaries are kept, which means we have
+# to search for them ourselves - except when the results are already set
+# where we skip the searches.
+
+# Unless the user overrides by setting SED, search the path for either GNU
+# sed, or the sed that truncates its output the least.
+test -z "$SED" && {
+  _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+  for _G_i in 1 2 3 4 5 6 7; do
+    _G_sed_script=$_G_sed_script$nl$_G_sed_script
+  done
+  echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed
+  _G_sed_script=
+
+  func_check_prog_sed ()
+  {
+    _G_path_prog=$1
+
+    _G_count=0
+    printf 0123456789 >conftest.in
+    while :
+    do
+      cat conftest.in conftest.in >conftest.tmp
+      mv conftest.tmp conftest.in
+      cp conftest.in conftest.nl
+      echo '' >> conftest.nl
+      "$_G_path_prog" -f conftest.sed <conftest.nl >conftest.out 2>/dev/null || break
+      diff conftest.out conftest.nl >/dev/null 2>&1 || break
+      _G_count=`expr $_G_count + 1`
+      if test "$_G_count" -gt "$_G_path_prog_max"; then
+        # Best one so far, save it but keep looking for a better one
+        func_check_prog_result=$_G_path_prog
+        _G_path_prog_max=$_G_count
+      fi
+      # 10*(2^10) chars as input seems more than enough
+      test 10 -lt "$_G_count" && break
+    done
+    rm -f conftest.in conftest.tmp conftest.nl conftest.out
+  }
+
+  func_path_progs "sed gsed" func_check_prog_sed "$PATH:/usr/xpg4/bin"
+  rm -f conftest.sed
+  SED=$func_path_progs_result
+}
+
+
+# Unless the user overrides by setting GREP, search the path for either GNU
+# grep, or the grep that truncates its output the least.
+test -z "$GREP" && {
+  func_check_prog_grep ()
+  {
+    _G_path_prog=$1
+
+    _G_count=0
+    _G_path_prog_max=0
+    printf 0123456789 >conftest.in
+    while :
+    do
+      cat conftest.in conftest.in >conftest.tmp
+      mv conftest.tmp conftest.in
+      cp conftest.in conftest.nl
+      echo 'GREP' >> conftest.nl
+      "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' <conftest.nl >conftest.out 2>/dev/null || break
+      diff conftest.out conftest.nl >/dev/null 2>&1 || break
+      _G_count=`expr $_G_count + 1`
+      if test "$_G_count" -gt "$_G_path_prog_max"; then
+        # Best one so far, save it but keep looking for a better one
+        func_check_prog_result=$_G_path_prog
+        _G_path_prog_max=$_G_count
+      fi
+      # 10*(2^10) chars as input seems more than enough
+      test 10 -lt "$_G_count" && break
+    done
+    rm -f conftest.in conftest.tmp conftest.nl conftest.out
+  }
+
+  func_path_progs "grep ggrep" func_check_prog_grep "$PATH:/usr/xpg4/bin"
+  GREP=$func_path_progs_result
+}
+
+
+## ------------------------------- ##
+## User overridable command paths. ##
+## ------------------------------- ##
+
+# All uppercase variable names are used for environment variables.  These
+# variables can be overridden by the user before calling a script that
+# uses them if a suitable command of that name is not already available
+# in the command search PATH.
+
+: ${CP="cp -f"}
+: ${ECHO="printf %s\n"}
+: ${EGREP="$GREP -E"}
+: ${FGREP="$GREP -F"}
+: ${LN_S="ln -s"}
+: ${MAKE="make"}
+: ${MKDIR="mkdir"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
+
+
+## -------------------- ##
+## Useful sed snippets. ##
+## -------------------- ##
+
+sed_dirname='s|/[^/]*$||'
+sed_basename='s|^.*/||'
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+
+# Same as above, but do not quote variable references.
+sed_double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution that turns a string into a regex matching for the
+# string literally.
+sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g'
+
+# Sed substitution that converts a w32 file name or path
+# that contains forward slashes, into one that contains
+# (escaped) backslashes.  A very naive implementation.
+sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+
+# Re-'\' parameter expansions in output of sed_double_quote_subst that
+# were '\'-ed in input to the same.  If an odd number of '\' preceded a
+# '$' in input to sed_double_quote_subst, that '$' was protected from
+# expansion.  Since each input '\' is now two '\'s, look for any number
+# of runs of four '\'s followed by two '\'s and then a '$'.  '\' that '$'.
+_G_bs='\\'
+_G_bs2='\\\\'
+_G_bs4='\\\\\\\\'
+_G_dollar='\$'
+sed_double_backslash="\
+  s/$_G_bs4/&\\
+/g
+  s/^$_G_bs2$_G_dollar/$_G_bs&/
+  s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g
+  s/\n//g"
+
+# require_check_ifs_backslash
+# ---------------------------
+# Check if we can use backslash as IFS='\' separator, and set
+# $check_ifs_backshlash_broken to ':' or 'false'.
+require_check_ifs_backslash=func_require_check_ifs_backslash
+func_require_check_ifs_backslash ()
+{
+  _G_save_IFS=$IFS
+  IFS='\'
+  _G_check_ifs_backshlash='a\\b'
+  for _G_i in $_G_check_ifs_backshlash
+  do
+  case $_G_i in
+  a)
+    check_ifs_backshlash_broken=false
+    ;;
+  '')
+    break
+    ;;
+  *)
+    check_ifs_backshlash_broken=:
+    break
+    ;;
+  esac
+  done
+  IFS=$_G_save_IFS
+  require_check_ifs_backslash=:
+}
+
+
+## ----------------- ##
+## Global variables. ##
+## ----------------- ##
+
+# Except for the global variables explicitly listed below, the following
+# functions in the '^func_' namespace, and the '^require_' namespace
+# variables initialised in the 'Resource management' section, sourcing
+# this file will not pollute your global namespace with anything
+# else. There's no portable way to scope variables in Bourne shell
+# though, so actually running these functions will sometimes place
+# results into a variable named after the function, and often use
+# temporary variables in the '^_G_' namespace. If you are careful to
+# avoid using those namespaces casually in your sourcing script, things
+# should continue to work as you expect. And, of course, you can freely
+# overwrite any of the functions or variables defined here before
+# calling anything to customize them.
+
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63  # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77	  # $? = 77 is used to indicate a skipped test to automake.
+
+# Allow overriding, eg assuming that you follow the convention of
+# putting '$debug_cmd' at the start of all your functions, you can get
+# bash to show function call trace with:
+#
+#    debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name
+debug_cmd=${debug_cmd-":"}
+exit_cmd=:
+
+# By convention, finish your script with:
+#
+#    exit $exit_status
+#
+# so that you can set exit_status to non-zero if you want to indicate
+# something went wrong during execution without actually bailing out at
+# the point of failure.
+exit_status=$EXIT_SUCCESS
+
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath=$0
+
+# The name of this program.
+progname=`$ECHO "$progpath" |$SED "$sed_basename"`
+
+# Make sure we have an absolute progpath for reexecution:
+case $progpath in
+  [\\/]*|[A-Za-z]:\\*) ;;
+  *[\\/]*)
+     progdir=`$ECHO "$progpath" |$SED "$sed_dirname"`
+     progdir=`cd "$progdir" && pwd`
+     progpath=$progdir/$progname
+     ;;
+  *)
+     _G_IFS=$IFS
+     IFS=${PATH_SEPARATOR-:}
+     for progdir in $PATH; do
+       IFS=$_G_IFS
+       test -x "$progdir/$progname" && break
+     done
+     IFS=$_G_IFS
+     test -n "$progdir" || progdir=`pwd`
+     progpath=$progdir/$progname
+     ;;
+esac
+
+
+## ----------------- ##
+## Standard options. ##
+## ----------------- ##
+
+# The following options affect the operation of the functions defined
+# below, and should be set appropriately depending on run-time para-
+# meters passed on the command line.
+
+opt_dry_run=false
+opt_quiet=false
+opt_verbose=false
+
+# Categories 'all' and 'none' are always available.  Append any others
+# you will pass as the first argument to func_warning from your own
+# code.
+warning_categories=
+
+# By default, display warnings according to 'opt_warning_types'.  Set
+# 'warning_func'  to ':' to elide all warnings, or func_fatal_error to
+# treat the next displayed warning as a fatal error.
+warning_func=func_warn_and_continue
+
+# Set to 'all' to display all warnings, 'none' to suppress all
+# warnings, or a space delimited list of some subset of
+# 'warning_categories' to display only the listed warnings.
+opt_warning_types=all
+
+
+## -------------------- ##
+## Resource management. ##
+## -------------------- ##
+
+# This section contains definitions for functions that each ensure a
+# particular resource (a file, or a non-empty configuration variable for
+# example) is available, and if appropriate to extract default values
+# from pertinent package files. Call them using their associated
+# 'require_*' variable to ensure that they are executed, at most, once.
+#
+# It's entirely deliberate that calling these functions can set
+# variables that don't obey the namespace limitations obeyed by the rest
+# of this file, in order that that they be as useful as possible to
+# callers.
+
+
+# require_term_colors
+# -------------------
+# Allow display of bold text on terminals that support it.
+require_term_colors=func_require_term_colors
+func_require_term_colors ()
+{
+    $debug_cmd
+
+    test -t 1 && {
+      # COLORTERM and USE_ANSI_COLORS environment variables take
+      # precedence, because most terminfo databases neglect to describe
+      # whether color sequences are supported.
+      test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"}
+
+      if test 1 = "$USE_ANSI_COLORS"; then
+        # Standard ANSI escape sequences
+        tc_reset='[0m'
+        tc_bold='[1m';   tc_standout='[7m'
+        tc_red='[31m';   tc_green='[32m'
+        tc_blue='[34m';  tc_cyan='[36m'
+      else
+        # Otherwise trust the terminfo database after all.
+        test -n "`tput sgr0 2>/dev/null`" && {
+          tc_reset=`tput sgr0`
+          test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold`
+          tc_standout=$tc_bold
+          test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso`
+          test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1`
+          test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2`
+          test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4`
+          test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5`
+        }
+      fi
+    }
+
+    require_term_colors=:
+}
+
+
+## ----------------- ##
+## Function library. ##
+## ----------------- ##
+
+# This section contains a variety of useful functions to call in your
+# scripts. Take note of the portable wrappers for features provided by
+# some modern shells, which will fall back to slower equivalents on
+# less featureful shells.
+
+
+# func_append VAR VALUE
+# ---------------------
+# Append VALUE onto the existing contents of VAR.
+
+  # We should try to minimise forks, especially on Windows where they are
+  # unreasonably slow, so skip the feature probes when bash or zsh are
+  # being used:
+  if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then
+    : ${_G_HAVE_ARITH_OP="yes"}
+    : ${_G_HAVE_XSI_OPS="yes"}
+    # The += operator was introduced in bash 3.1
+    case $BASH_VERSION in
+      [12].* | 3.0 | 3.0*) ;;
+      *)
+        : ${_G_HAVE_PLUSEQ_OP="yes"}
+        ;;
+    esac
+  fi
+
+  # _G_HAVE_PLUSEQ_OP
+  # Can be empty, in which case the shell is probed, "yes" if += is
+  # useable or anything else if it does not work.
+  test -z "$_G_HAVE_PLUSEQ_OP" \
+    && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \
+    && _G_HAVE_PLUSEQ_OP=yes
+
+if test yes = "$_G_HAVE_PLUSEQ_OP"
+then
+  # This is an XSI compatible shell, allowing a faster implementation...
+  eval 'func_append ()
+  {
+    $debug_cmd
+
+    eval "$1+=\$2"
+  }'
+else
+  # ...otherwise fall back to using expr, which is often a shell builtin.
+  func_append ()
+  {
+    $debug_cmd
+
+    eval "$1=\$$1\$2"
+  }
+fi
+
+
+# func_append_quoted VAR VALUE
+# ----------------------------
+# Quote VALUE and append to the end of shell variable VAR, separated
+# by a space.
+if test yes = "$_G_HAVE_PLUSEQ_OP"; then
+  eval 'func_append_quoted ()
+  {
+    $debug_cmd
+
+    func_quote_arg pretty "$2"
+    eval "$1+=\\ \$func_quote_arg_result"
+  }'
+else
+  func_append_quoted ()
+  {
+    $debug_cmd
+
+    func_quote_arg pretty "$2"
+    eval "$1=\$$1\\ \$func_quote_arg_result"
+  }
+fi
+
+
+# func_append_uniq VAR VALUE
+# --------------------------
+# Append unique VALUE onto the existing contents of VAR, assuming
+# entries are delimited by the first character of VALUE.  For example:
+#
+#   func_append_uniq options " --another-option option-argument"
+#
+# will only append to $options if " --another-option option-argument "
+# is not already present somewhere in $options already (note spaces at
+# each end implied by leading space in second argument).
+func_append_uniq ()
+{
+    $debug_cmd
+
+    eval _G_current_value='`$ECHO $'$1'`'
+    _G_delim=`expr "$2" : '\(.\)'`
+
+    case $_G_delim$_G_current_value$_G_delim in
+      *"$2$_G_delim"*) ;;
+      *) func_append "$@" ;;
+    esac
+}
+
+
+# func_arith TERM...
+# ------------------
+# Set func_arith_result to the result of evaluating TERMs.
+  test -z "$_G_HAVE_ARITH_OP" \
+    && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \
+    && _G_HAVE_ARITH_OP=yes
+
+if test yes = "$_G_HAVE_ARITH_OP"; then
+  eval 'func_arith ()
+  {
+    $debug_cmd
+
+    func_arith_result=$(( $* ))
+  }'
+else
+  func_arith ()
+  {
+    $debug_cmd
+
+    func_arith_result=`expr "$@"`
+  }
+fi
+
+
+# func_basename FILE
+# ------------------
+# Set func_basename_result to FILE with everything up to and including
+# the last / stripped.
+if test yes = "$_G_HAVE_XSI_OPS"; then
+  # If this shell supports suffix pattern removal, then use it to avoid
+  # forking. Hide the definitions single quotes in case the shell chokes
+  # on unsupported syntax...
+  _b='func_basename_result=${1##*/}'
+  _d='case $1 in
+        */*) func_dirname_result=${1%/*}$2 ;;
+        *  ) func_dirname_result=$3        ;;
+      esac'
+
+else
+  # ...otherwise fall back to using sed.
+  _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`'
+  _d='func_dirname_result=`$ECHO "$1"  |$SED "$sed_dirname"`
+      if test "X$func_dirname_result" = "X$1"; then
+        func_dirname_result=$3
+      else
+        func_append func_dirname_result "$2"
+      fi'
+fi
+
+eval 'func_basename ()
+{
+    $debug_cmd
+
+    '"$_b"'
+}'
+
+
+# func_dirname FILE APPEND NONDIR_REPLACEMENT
+# -------------------------------------------
+# Compute the dirname of FILE.  If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+eval 'func_dirname ()
+{
+    $debug_cmd
+
+    '"$_d"'
+}'
+
+
+# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT
+# --------------------------------------------------------
+# Perform func_basename and func_dirname in a single function
+# call:
+#   dirname:  Compute the dirname of FILE.  If nonempty,
+#             add APPEND to the result, otherwise set result
+#             to NONDIR_REPLACEMENT.
+#             value returned in "$func_dirname_result"
+#   basename: Compute filename of FILE.
+#             value retuned in "$func_basename_result"
+# For efficiency, we do not delegate to the functions above but instead
+# duplicate the functionality here.
+eval 'func_dirname_and_basename ()
+{
+    $debug_cmd
+
+    '"$_b"'
+    '"$_d"'
+}'
+
+
+# func_echo ARG...
+# ----------------
+# Echo program name prefixed message.
+func_echo ()
+{
+    $debug_cmd
+
+    _G_message=$*
+
+    func_echo_IFS=$IFS
+    IFS=$nl
+    for _G_line in $_G_message; do
+      IFS=$func_echo_IFS
+      $ECHO "$progname: $_G_line"
+    done
+    IFS=$func_echo_IFS
+}
+
+
+# func_echo_all ARG...
+# --------------------
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO "$*"
+}
+
+
+# func_echo_infix_1 INFIX ARG...
+# ------------------------------
+# Echo program name, followed by INFIX on the first line, with any
+# additional lines not showing INFIX.
+func_echo_infix_1 ()
+{
+    $debug_cmd
+
+    $require_term_colors
+
+    _G_infix=$1; shift
+    _G_indent=$_G_infix
+    _G_prefix="$progname: $_G_infix: "
+    _G_message=$*
+
+    # Strip color escape sequences before counting printable length
+    for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan"
+    do
+      test -n "$_G_tc" && {
+        _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"`
+        _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"`
+      }
+    done
+    _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`"  " ## exclude from sc_prohibit_nested_quotes
+
+    func_echo_infix_1_IFS=$IFS
+    IFS=$nl
+    for _G_line in $_G_message; do
+      IFS=$func_echo_infix_1_IFS
+      $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2
+      _G_prefix=$_G_indent
+    done
+    IFS=$func_echo_infix_1_IFS
+}
+
+
+# func_error ARG...
+# -----------------
+# Echo program name prefixed message to standard error.
+func_error ()
+{
+    $debug_cmd
+
+    $require_term_colors
+
+    func_echo_infix_1 "  $tc_standout${tc_red}error$tc_reset" "$*" >&2
+}
+
+
+# func_fatal_error ARG...
+# -----------------------
+# Echo program name prefixed message to standard error, and exit.
+func_fatal_error ()
+{
+    $debug_cmd
+
+    func_error "$*"
+    exit $EXIT_FAILURE
+}
+
+
+# func_grep EXPRESSION FILENAME
+# -----------------------------
+# Check whether EXPRESSION matches any line of FILENAME, without output.
+func_grep ()
+{
+    $debug_cmd
+
+    $GREP "$1" "$2" >/dev/null 2>&1
+}
+
+
+# func_len STRING
+# ---------------
+# Set func_len_result to the length of STRING. STRING may not
+# start with a hyphen.
+  test -z "$_G_HAVE_XSI_OPS" \
+    && (eval 'x=a/b/c;
+      test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
+    && _G_HAVE_XSI_OPS=yes
+
+if test yes = "$_G_HAVE_XSI_OPS"; then
+  eval 'func_len ()
+  {
+    $debug_cmd
+
+    func_len_result=${#1}
+  }'
+else
+  func_len ()
+  {
+    $debug_cmd
+
+    func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+  }
+fi
+
+
+# func_mkdir_p DIRECTORY-PATH
+# ---------------------------
+# Make sure the entire path to DIRECTORY-PATH is available.
+func_mkdir_p ()
+{
+    $debug_cmd
+
+    _G_directory_path=$1
+    _G_dir_list=
+
+    if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then
+
+      # Protect directory names starting with '-'
+      case $_G_directory_path in
+        -*) _G_directory_path=./$_G_directory_path ;;
+      esac
+
+      # While some portion of DIR does not yet exist...
+      while test ! -d "$_G_directory_path"; do
+        # ...make a list in topmost first order.  Use a colon delimited
+	# list incase some portion of path contains whitespace.
+        _G_dir_list=$_G_directory_path:$_G_dir_list
+
+        # If the last portion added has no slash in it, the list is done
+        case $_G_directory_path in */*) ;; *) break ;; esac
+
+        # ...otherwise throw away the child directory and loop
+        _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"`
+      done
+      _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'`
+
+      func_mkdir_p_IFS=$IFS; IFS=:
+      for _G_dir in $_G_dir_list; do
+	IFS=$func_mkdir_p_IFS
+        # mkdir can fail with a 'File exist' error if two processes
+        # try to create one of the directories concurrently.  Don't
+        # stop in that case!
+        $MKDIR "$_G_dir" 2>/dev/null || :
+      done
+      IFS=$func_mkdir_p_IFS
+
+      # Bail out if we (or some other process) failed to create a directory.
+      test -d "$_G_directory_path" || \
+        func_fatal_error "Failed to create '$1'"
+    fi
+}
+
+
+# func_mktempdir [BASENAME]
+# -------------------------
+# Make a temporary directory that won't clash with other running
+# libtool processes, and avoids race conditions if possible.  If
+# given, BASENAME is the basename for that directory.
+func_mktempdir ()
+{
+    $debug_cmd
+
+    _G_template=${TMPDIR-/tmp}/${1-$progname}
+
+    if test : = "$opt_dry_run"; then
+      # Return a directory name, but don't create it in dry-run mode
+      _G_tmpdir=$_G_template-$$
+    else
+
+      # If mktemp works, use that first and foremost
+      _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null`
+
+      if test ! -d "$_G_tmpdir"; then
+        # Failing that, at least try and use $RANDOM to avoid a race
+        _G_tmpdir=$_G_template-${RANDOM-0}$$
+
+        func_mktempdir_umask=`umask`
+        umask 0077
+        $MKDIR "$_G_tmpdir"
+        umask $func_mktempdir_umask
+      fi
+
+      # If we're not in dry-run mode, bomb out on failure
+      test -d "$_G_tmpdir" || \
+        func_fatal_error "cannot create temporary directory '$_G_tmpdir'"
+    fi
+
+    $ECHO "$_G_tmpdir"
+}
+
+
+# func_normal_abspath PATH
+# ------------------------
+# Remove doubled-up and trailing slashes, "." path components,
+# and cancel out any ".." path components in PATH after making
+# it an absolute path.
+func_normal_abspath ()
+{
+    $debug_cmd
+
+    # These SED scripts presuppose an absolute path with a trailing slash.
+    _G_pathcar='s|^/\([^/]*\).*$|\1|'
+    _G_pathcdr='s|^/[^/]*||'
+    _G_removedotparts=':dotsl
+		s|/\./|/|g
+		t dotsl
+		s|/\.$|/|'
+    _G_collapseslashes='s|/\{1,\}|/|g'
+    _G_finalslash='s|/*$|/|'
+
+    # Start from root dir and reassemble the path.
+    func_normal_abspath_result=
+    func_normal_abspath_tpath=$1
+    func_normal_abspath_altnamespace=
+    case $func_normal_abspath_tpath in
+      "")
+        # Empty path, that just means $cwd.
+        func_stripname '' '/' "`pwd`"
+        func_normal_abspath_result=$func_stripname_result
+        return
+        ;;
+      # The next three entries are used to spot a run of precisely
+      # two leading slashes without using negated character classes;
+      # we take advantage of case's first-match behaviour.
+      ///*)
+        # Unusual form of absolute path, do nothing.
+        ;;
+      //*)
+        # Not necessarily an ordinary path; POSIX reserves leading '//'
+        # and for example Cygwin uses it to access remote file shares
+        # over CIFS/SMB, so we conserve a leading double slash if found.
+        func_normal_abspath_altnamespace=/
+        ;;
+      /*)
+        # Absolute path, do nothing.
+        ;;
+      *)
+        # Relative path, prepend $cwd.
+        func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
+        ;;
+    esac
+
+    # Cancel out all the simple stuff to save iterations.  We also want
+    # the path to end with a slash for ease of parsing, so make sure
+    # there is one (and only one) here.
+    func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+          -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"`
+    while :; do
+      # Processed it all yet?
+      if test / = "$func_normal_abspath_tpath"; then
+        # If we ascended to the root using ".." the result may be empty now.
+        if test -z "$func_normal_abspath_result"; then
+          func_normal_abspath_result=/
+        fi
+        break
+      fi
+      func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
+          -e "$_G_pathcar"`
+      func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+          -e "$_G_pathcdr"`
+      # Figure out what to do with it
+      case $func_normal_abspath_tcomponent in
+        "")
+          # Trailing empty path component, ignore it.
+          ;;
+        ..)
+          # Parent dir; strip last assembled component from result.
+          func_dirname "$func_normal_abspath_result"
+          func_normal_abspath_result=$func_dirname_result
+          ;;
+        *)
+          # Actual path component, append it.
+          func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent"
+          ;;
+      esac
+    done
+    # Restore leading double-slash if one was found on entry.
+    func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
+}
+
+
+# func_notquiet ARG...
+# --------------------
+# Echo program name prefixed message only when not in quiet mode.
+func_notquiet ()
+{
+    $debug_cmd
+
+    $opt_quiet || func_echo ${1+"$@"}
+
+    # A bug in bash halts the script if the last line of a function
+    # fails when set -e is in force, so we need another command to
+    # work around that:
+    :
+}
+
+
+# func_relative_path SRCDIR DSTDIR
+# --------------------------------
+# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR.
+func_relative_path ()
+{
+    $debug_cmd
+
+    func_relative_path_result=
+    func_normal_abspath "$1"
+    func_relative_path_tlibdir=$func_normal_abspath_result
+    func_normal_abspath "$2"
+    func_relative_path_tbindir=$func_normal_abspath_result
+
+    # Ascend the tree starting from libdir
+    while :; do
+      # check if we have found a prefix of bindir
+      case $func_relative_path_tbindir in
+        $func_relative_path_tlibdir)
+          # found an exact match
+          func_relative_path_tcancelled=
+          break
+          ;;
+        $func_relative_path_tlibdir*)
+          # found a matching prefix
+          func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
+          func_relative_path_tcancelled=$func_stripname_result
+          if test -z "$func_relative_path_result"; then
+            func_relative_path_result=.
+          fi
+          break
+          ;;
+        *)
+          func_dirname $func_relative_path_tlibdir
+          func_relative_path_tlibdir=$func_dirname_result
+          if test -z "$func_relative_path_tlibdir"; then
+            # Have to descend all the way to the root!
+            func_relative_path_result=../$func_relative_path_result
+            func_relative_path_tcancelled=$func_relative_path_tbindir
+            break
+          fi
+          func_relative_path_result=../$func_relative_path_result
+          ;;
+      esac
+    done
+
+    # Now calculate path; take care to avoid doubling-up slashes.
+    func_stripname '' '/' "$func_relative_path_result"
+    func_relative_path_result=$func_stripname_result
+    func_stripname '/' '/' "$func_relative_path_tcancelled"
+    if test -n "$func_stripname_result"; then
+      func_append func_relative_path_result "/$func_stripname_result"
+    fi
+
+    # Normalisation. If bindir is libdir, return '.' else relative path.
+    if test -n "$func_relative_path_result"; then
+      func_stripname './' '' "$func_relative_path_result"
+      func_relative_path_result=$func_stripname_result
+    fi
+
+    test -n "$func_relative_path_result" || func_relative_path_result=.
+
+    :
+}
+
+
+# func_quote_portable EVAL ARG
+# ----------------------------
+# Internal function to portably implement func_quote_arg.  Note that we still
+# keep attention to performance here so we as much as possible try to avoid
+# calling sed binary (so far O(N) complexity as long as func_append is O(1)).
+func_quote_portable ()
+{
+    $debug_cmd
+
+    $require_check_ifs_backslash
+
+    func_quote_portable_result=$2
+
+    # one-time-loop (easy break)
+    while true
+    do
+      if $1; then
+        func_quote_portable_result=`$ECHO "$2" | $SED \
+          -e "$sed_double_quote_subst" -e "$sed_double_backslash"`
+        break
+      fi
+
+      # Quote for eval.
+      case $func_quote_portable_result in
+        *[\\\`\"\$]*)
+          # Fallback to sed for $func_check_bs_ifs_broken=:, or when the string
+          # contains the shell wildcard characters.
+          case $check_ifs_backshlash_broken$func_quote_portable_result in
+            :*|*[\[\*\?]*)
+              func_quote_portable_result=`$ECHO "$func_quote_portable_result" \
+                  | $SED "$sed_quote_subst"`
+              break
+              ;;
+          esac
+
+          func_quote_portable_old_IFS=$IFS
+          for _G_char in '\' '`' '"' '$'
+          do
+            # STATE($1) PREV($2) SEPARATOR($3)
+            set start "" ""
+            func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy
+            IFS=$_G_char
+            for _G_part in $func_quote_portable_result
+            do
+              case $1 in
+              quote)
+                func_append func_quote_portable_result "$3$2"
+                set quote "$_G_part" "\\$_G_char"
+                ;;
+              start)
+                set first "" ""
+                func_quote_portable_result=
+                ;;
+              first)
+                set quote "$_G_part" ""
+                ;;
+              esac
+            done
+          done
+          IFS=$func_quote_portable_old_IFS
+          ;;
+        *) ;;
+      esac
+      break
+    done
+
+    func_quote_portable_unquoted_result=$func_quote_portable_result
+    case $func_quote_portable_result in
+      # double-quote args containing shell metacharacters to delay
+      # word splitting, command substitution and variable expansion
+      # for a subsequent eval.
+      # many bourne shells cannot handle close brackets correctly
+      # in scan sets, so we specify it separately.
+      *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \	]*|*]*|"")
+        func_quote_portable_result=\"$func_quote_portable_result\"
+        ;;
+    esac
+}
+
+
+# func_quotefast_eval ARG
+# -----------------------
+# Quote one ARG (internal).  This is equivalent to 'func_quote_arg eval ARG',
+# but optimized for speed.  Result is stored in $func_quotefast_eval.
+if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then
+  printf -v _GL_test_printf_tilde %q '~'
+  if test '\~' = "$_GL_test_printf_tilde"; then
+    func_quotefast_eval ()
+    {
+      printf -v func_quotefast_eval_result %q "$1"
+    }
+  else
+    # Broken older Bash implementations.  Make those faster too if possible.
+    func_quotefast_eval ()
+    {
+      case $1 in
+        '~'*)
+          func_quote_portable false "$1"
+          func_quotefast_eval_result=$func_quote_portable_result
+          ;;
+        *)
+          printf -v func_quotefast_eval_result %q "$1"
+          ;;
+      esac
+    }
+  fi
+else
+  func_quotefast_eval ()
+  {
+    func_quote_portable false "$1"
+    func_quotefast_eval_result=$func_quote_portable_result
+  }
+fi
+
+
+# func_quote_arg MODEs ARG
+# ------------------------
+# Quote one ARG to be evaled later.  MODEs argument may contain zero or more
+# specifiers listed below separated by ',' character.  This function returns two
+# values:
+#   i) func_quote_arg_result
+#      double-quoted (when needed), suitable for a subsequent eval
+#  ii) func_quote_arg_unquoted_result
+#      has all characters that are still active within double
+#      quotes backslashified.  Available only if 'unquoted' is specified.
+#
+# Available modes:
+# ----------------
+# 'eval' (default)
+#       - escape shell special characters
+# 'expand'
+#       - the same as 'eval';  but do not quote variable references
+# 'pretty'
+#       - request aesthetic output, i.e. '"a b"' instead of 'a\ b'.  This might
+#         be used later in func_quote to get output like: 'echo "a b"' instead
+#         of 'echo a\ b'.  This is slower than default on some shells.
+# 'unquoted'
+#       - produce also $func_quote_arg_unquoted_result which does not contain
+#         wrapping double-quotes.
+#
+# Examples for 'func_quote_arg pretty,unquoted string':
+#
+#   string      | *_result              | *_unquoted_result
+#   ------------+-----------------------+-------------------
+#   "           | \"                    | \"
+#   a b         | "a b"                 | a b
+#   "a b"       | "\"a b\""             | \"a b\"
+#   *           | "*"                   | *
+#   z="${x-$y}" | "z=\"\${x-\$y}\""     | z=\"\${x-\$y}\"
+#
+# Examples for 'func_quote_arg pretty,unquoted,expand string':
+#
+#   string        |   *_result          |  *_unquoted_result
+#   --------------+---------------------+--------------------
+#   z="${x-$y}"   | "z=\"${x-$y}\""     | z=\"${x-$y}\"
+func_quote_arg ()
+{
+    _G_quote_expand=false
+    case ,$1, in
+      *,expand,*)
+        _G_quote_expand=:
+        ;;
+    esac
+
+    case ,$1, in
+      *,pretty,*|*,expand,*|*,unquoted,*)
+        func_quote_portable $_G_quote_expand "$2"
+        func_quote_arg_result=$func_quote_portable_result
+        func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result
+        ;;
+      *)
+        # Faster quote-for-eval for some shells.
+        func_quotefast_eval "$2"
+        func_quote_arg_result=$func_quotefast_eval_result
+        ;;
+    esac
+}
+
+
+# func_quote MODEs ARGs...
+# ------------------------
+# Quote all ARGs to be evaled later and join them into single command.  See
+# func_quote_arg's description for more info.
+func_quote ()
+{
+    $debug_cmd
+    _G_func_quote_mode=$1 ; shift
+    func_quote_result=
+    while test 0 -lt $#; do
+      func_quote_arg "$_G_func_quote_mode" "$1"
+      if test -n "$func_quote_result"; then
+        func_append func_quote_result " $func_quote_arg_result"
+      else
+        func_append func_quote_result "$func_quote_arg_result"
+      fi
+      shift
+    done
+}
+
+
+# func_stripname PREFIX SUFFIX NAME
+# ---------------------------------
+# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+if test yes = "$_G_HAVE_XSI_OPS"; then
+  eval 'func_stripname ()
+  {
+    $debug_cmd
+
+    # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+    # positional parameters, so assign one to ordinary variable first.
+    func_stripname_result=$3
+    func_stripname_result=${func_stripname_result#"$1"}
+    func_stripname_result=${func_stripname_result%"$2"}
+  }'
+else
+  func_stripname ()
+  {
+    $debug_cmd
+
+    case $2 in
+      .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;;
+      *)  func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;;
+    esac
+  }
+fi
+
+
+# func_show_eval CMD [FAIL_EXP]
+# -----------------------------
+# Unless opt_quiet is true, then output CMD.  Then, if opt_dryrun is
+# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.
+func_show_eval ()
+{
+    $debug_cmd
+
+    _G_cmd=$1
+    _G_fail_exp=${2-':'}
+
+    func_quote_arg pretty,expand "$_G_cmd"
+    eval "func_notquiet $func_quote_arg_result"
+
+    $opt_dry_run || {
+      eval "$_G_cmd"
+      _G_status=$?
+      if test 0 -ne "$_G_status"; then
+	eval "(exit $_G_status); $_G_fail_exp"
+      fi
+    }
+}
+
+
+# func_show_eval_locale CMD [FAIL_EXP]
+# ------------------------------------
+# Unless opt_quiet is true, then output CMD.  Then, if opt_dryrun is
+# not true, evaluate CMD.  If the evaluation of CMD fails, and FAIL_EXP
+# is given, then evaluate it.  Use the saved locale for evaluation.
+func_show_eval_locale ()
+{
+    $debug_cmd
+
+    _G_cmd=$1
+    _G_fail_exp=${2-':'}
+
+    $opt_quiet || {
+      func_quote_arg expand,pretty "$_G_cmd"
+      eval "func_echo $func_quote_arg_result"
+    }
+
+    $opt_dry_run || {
+      eval "$_G_user_locale
+	    $_G_cmd"
+      _G_status=$?
+      eval "$_G_safe_locale"
+      if test 0 -ne "$_G_status"; then
+	eval "(exit $_G_status); $_G_fail_exp"
+      fi
+    }
+}
+
+
+# func_tr_sh
+# ----------
+# Turn $1 into a string suitable for a shell variable name.
+# Result is stored in $func_tr_sh_result.  All characters
+# not in the set a-zA-Z0-9_ are replaced with '_'. Further,
+# if $1 begins with a digit, a '_' is prepended as well.
+func_tr_sh ()
+{
+    $debug_cmd
+
+    case $1 in
+    [0-9]* | *[!a-zA-Z0-9_]*)
+      func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'`
+      ;;
+    * )
+      func_tr_sh_result=$1
+      ;;
+    esac
+}
+
+
+# func_verbose ARG...
+# -------------------
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
+{
+    $debug_cmd
+
+    $opt_verbose && func_echo "$*"
+
+    :
+}
+
+
+# func_warn_and_continue ARG...
+# -----------------------------
+# Echo program name prefixed warning message to standard error.
+func_warn_and_continue ()
+{
+    $debug_cmd
+
+    $require_term_colors
+
+    func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2
+}
+
+
+# func_warning CATEGORY ARG...
+# ----------------------------
+# Echo program name prefixed warning message to standard error. Warning
+# messages can be filtered according to CATEGORY, where this function
+# elides messages where CATEGORY is not listed in the global variable
+# 'opt_warning_types'.
+func_warning ()
+{
+    $debug_cmd
+
+    # CATEGORY must be in the warning_categories list!
+    case " $warning_categories " in
+      *" $1 "*) ;;
+      *) func_internal_error "invalid warning category '$1'" ;;
+    esac
+
+    _G_category=$1
+    shift
+
+    case " $opt_warning_types " in
+      *" $_G_category "*) $warning_func ${1+"$@"} ;;
+    esac
+}
+
+
+# func_sort_ver VER1 VER2
+# -----------------------
+# 'sort -V' is not generally available.
+# Note this deviates from the version comparison in automake
+# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a
+# but this should suffice as we won't be specifying old
+# version formats or redundant trailing .0 in bootstrap.conf.
+# If we did want full compatibility then we should probably
+# use m4_version_compare from autoconf.
+func_sort_ver ()
+{
+    $debug_cmd
+
+    printf '%s\n%s\n' "$1" "$2" \
+      | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n
+}
+
+# func_lt_ver PREV CURR
+# ---------------------
+# Return true if PREV and CURR are in the correct order according to
+# func_sort_ver, otherwise false.  Use it like this:
+#
+#  func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..."
+func_lt_ver ()
+{
+    $debug_cmd
+
+    test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q`
+}
+
+
+# Local variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-time-zone: "UTC"
+# End:
+#! /bin/sh
+
+# A portable, pluggable option parser for Bourne shell.
+# Written by Gary V. Vaughan, 2010
+
+# This is free software.  There is NO warranty; not even for
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Copyright (C) 2010-2019, 2021 Bootstrap Authors
+#
+# This file is dual licensed under the terms of the MIT license
+# <https://opensource.org/license/MIT>, and GPL version 2 or later
+# <http://www.gnu.org/licenses/gpl-2.0.html>.  You must apply one of
+# these licenses when using or redistributing this software or any of
+# the files within it.  See the URLs above, or the file `LICENSE`
+# included in the Bootstrap distribution for the full license texts.
+
+# Please report bugs or propose patches to:
+# <https://github.com/gnulib-modules/bootstrap/issues>
+
+# Set a version string for this script.
+scriptversion=2019-02-19.15; # UTC
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# This file is a library for parsing options in your shell scripts along
+# with assorted other useful supporting features that you can make use
+# of too.
+#
+# For the simplest scripts you might need only:
+#
+#   #!/bin/sh
+#   . relative/path/to/funclib.sh
+#   . relative/path/to/options-parser
+#   scriptversion=1.0
+#   func_options ${1+"$@"}
+#   eval set dummy "$func_options_result"; shift
+#   ...rest of your script...
+#
+# In order for the '--version' option to work, you will need to have a
+# suitably formatted comment like the one at the top of this file
+# starting with '# Written by ' and ending with '# Copyright'.
+#
+# For '-h' and '--help' to work, you will also need a one line
+# description of your script's purpose in a comment directly above the
+# '# Written by ' line, like the one at the top of this file.
+#
+# The default options also support '--debug', which will turn on shell
+# execution tracing (see the comment above debug_cmd below for another
+# use), and '--verbose' and the func_verbose function to allow your script
+# to display verbose messages only when your user has specified
+# '--verbose'.
+#
+# After sourcing this file, you can plug in processing for additional
+# options by amending the variables from the 'Configuration' section
+# below, and following the instructions in the 'Option parsing'
+# section further down.
+
+## -------------- ##
+## Configuration. ##
+## -------------- ##
+
+# You should override these variables in your script after sourcing this
+# file so that they reflect the customisations you have added to the
+# option parser.
+
+# The usage line for option parsing errors and the start of '-h' and
+# '--help' output messages. You can embed shell variables for delayed
+# expansion at the time the message is displayed, but you will need to
+# quote other shell meta-characters carefully to prevent them being
+# expanded when the contents are evaled.
+usage='$progpath [OPTION]...'
+
+# Short help message in response to '-h' and '--help'.  Add to this or
+# override it after sourcing this library to reflect the full set of
+# options your script accepts.
+usage_message="\
+       --debug        enable verbose shell tracing
+   -W, --warnings=CATEGORY
+                      report the warnings falling in CATEGORY [all]
+   -v, --verbose      verbosely report processing
+       --version      print version information and exit
+   -h, --help         print short or long help message and exit
+"
+
+# Additional text appended to 'usage_message' in response to '--help'.
+long_help_message="
+Warning categories include:
+       'all'          show all warnings
+       'none'         turn off all the warnings
+       'error'        warnings are treated as fatal errors"
+
+# Help message printed before fatal option parsing errors.
+fatal_help="Try '\$progname --help' for more information."
+
+
+
+## ------------------------- ##
+## Hook function management. ##
+## ------------------------- ##
+
+# This section contains functions for adding, removing, and running hooks
+# in the main code.  A hook is just a list of function names that can be
+# run in order later on.
+
+# func_hookable FUNC_NAME
+# -----------------------
+# Declare that FUNC_NAME will run hooks added with
+# 'func_add_hook FUNC_NAME ...'.
+func_hookable ()
+{
+    $debug_cmd
+
+    func_append hookable_fns " $1"
+}
+
+
+# func_add_hook FUNC_NAME HOOK_FUNC
+# ---------------------------------
+# Request that FUNC_NAME call HOOK_FUNC before it returns.  FUNC_NAME must
+# first have been declared "hookable" by a call to 'func_hookable'.
+func_add_hook ()
+{
+    $debug_cmd
+
+    case " $hookable_fns " in
+      *" $1 "*) ;;
+      *) func_fatal_error "'$1' does not accept hook functions." ;;
+    esac
+
+    eval func_append ${1}_hooks '" $2"'
+}
+
+
+# func_remove_hook FUNC_NAME HOOK_FUNC
+# ------------------------------------
+# Remove HOOK_FUNC from the list of hook functions to be called by
+# FUNC_NAME.
+func_remove_hook ()
+{
+    $debug_cmd
+
+    eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`'
+}
+
+
+# func_propagate_result FUNC_NAME_A FUNC_NAME_B
+# ---------------------------------------------
+# If the *_result variable of FUNC_NAME_A _is set_, assign its value to
+# *_result variable of FUNC_NAME_B.
+func_propagate_result ()
+{
+    $debug_cmd
+
+    func_propagate_result_result=:
+    if eval "test \"\${${1}_result+set}\" = set"
+    then
+      eval "${2}_result=\$${1}_result"
+    else
+      func_propagate_result_result=false
+    fi
+}
+
+
+# func_run_hooks FUNC_NAME [ARG]...
+# ---------------------------------
+# Run all hook functions registered to FUNC_NAME.
+# It's assumed that the list of hook functions contains nothing more
+# than a whitespace-delimited list of legal shell function names, and
+# no effort is wasted trying to catch shell meta-characters or preserve
+# whitespace.
+func_run_hooks ()
+{
+    $debug_cmd
+
+    case " $hookable_fns " in
+      *" $1 "*) ;;
+      *) func_fatal_error "'$1' does not support hook functions." ;;
+    esac
+
+    eval _G_hook_fns=\$$1_hooks; shift
+
+    for _G_hook in $_G_hook_fns; do
+      func_unset "${_G_hook}_result"
+      eval $_G_hook '${1+"$@"}'
+      func_propagate_result $_G_hook func_run_hooks
+      if $func_propagate_result_result; then
+        eval set dummy "$func_run_hooks_result"; shift
+      fi
+    done
+}
+
+
+
+## --------------- ##
+## Option parsing. ##
+## --------------- ##
+
+# In order to add your own option parsing hooks, you must accept the
+# full positional parameter list from your hook function.  You may remove
+# or edit any options that you action, and then pass back the remaining
+# unprocessed options in '<hooked_function_name>_result', escaped
+# suitably for 'eval'.
+#
+# The '<hooked_function_name>_result' variable is automatically unset
+# before your hook gets called; for best performance, only set the
+# *_result variable when necessary (i.e. don't call the 'func_quote'
+# function unnecessarily because it can be an expensive operation on some
+# machines).
+#
+# Like this:
+#
+#    my_options_prep ()
+#    {
+#        $debug_cmd
+#
+#        # Extend the existing usage message.
+#        usage_message=$usage_message'
+#      -s, --silent       don'\''t print informational messages
+#    '
+#        # No change in '$@' (ignored completely by this hook).  Leave
+#        # my_options_prep_result variable intact.
+#    }
+#    func_add_hook func_options_prep my_options_prep
+#
+#
+#    my_silent_option ()
+#    {
+#        $debug_cmd
+#
+#        args_changed=false
+#
+#        # Note that, for efficiency, we parse as many options as we can
+#        # recognise in a loop before passing the remainder back to the
+#        # caller on the first unrecognised argument we encounter.
+#        while test $# -gt 0; do
+#          opt=$1; shift
+#          case $opt in
+#            --silent|-s) opt_silent=:
+#                         args_changed=:
+#                         ;;
+#            # Separate non-argument short options:
+#            -s*)         func_split_short_opt "$_G_opt"
+#                         set dummy "$func_split_short_opt_name" \
+#                             "-$func_split_short_opt_arg" ${1+"$@"}
+#                         shift
+#                         args_changed=:
+#                         ;;
+#            *)           # Make sure the first unrecognised option "$_G_opt"
+#                         # is added back to "$@" in case we need it later,
+#                         # if $args_changed was set to 'true'.
+#                         set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+#          esac
+#        done
+#
+#        # Only call 'func_quote' here if we processed at least one argument.
+#        if $args_changed; then
+#          func_quote eval ${1+"$@"}
+#          my_silent_option_result=$func_quote_result
+#        fi
+#    }
+#    func_add_hook func_parse_options my_silent_option
+#
+#
+#    my_option_validation ()
+#    {
+#        $debug_cmd
+#
+#        $opt_silent && $opt_verbose && func_fatal_help "\
+#    '--silent' and '--verbose' options are mutually exclusive."
+#    }
+#    func_add_hook func_validate_options my_option_validation
+#
+# You'll also need to manually amend $usage_message to reflect the extra
+# options you parse.  It's preferable to append if you can, so that
+# multiple option parsing hooks can be added safely.
+
+
+# func_options_finish [ARG]...
+# ----------------------------
+# Finishing the option parse loop (call 'func_options' hooks ATM).
+func_options_finish ()
+{
+    $debug_cmd
+
+    func_run_hooks func_options ${1+"$@"}
+    func_propagate_result func_run_hooks func_options_finish
+}
+
+
+# func_options [ARG]...
+# ---------------------
+# All the functions called inside func_options are hookable. See the
+# individual implementations for details.
+func_hookable func_options
+func_options ()
+{
+    $debug_cmd
+
+    _G_options_quoted=false
+
+    for my_func in options_prep parse_options validate_options options_finish
+    do
+      func_unset func_${my_func}_result
+      func_unset func_run_hooks_result
+      eval func_$my_func '${1+"$@"}'
+      func_propagate_result func_$my_func func_options
+      if $func_propagate_result_result; then
+        eval set dummy "$func_options_result"; shift
+        _G_options_quoted=:
+      fi
+    done
+
+    $_G_options_quoted || {
+      # As we (func_options) are top-level options-parser function and
+      # nobody quoted "$@" for us yet, we need to do it explicitly for
+      # caller.
+      func_quote eval ${1+"$@"}
+      func_options_result=$func_quote_result
+    }
+}
+
+
+# func_options_prep [ARG]...
+# --------------------------
+# All initialisations required before starting the option parse loop.
+# Note that when calling hook functions, we pass through the list of
+# positional parameters.  If a hook function modifies that list, and
+# needs to propagate that back to rest of this script, then the complete
+# modified list must be put in 'func_run_hooks_result' before returning.
+func_hookable func_options_prep
+func_options_prep ()
+{
+    $debug_cmd
+
+    # Option defaults:
+    opt_verbose=false
+    opt_warning_types=
+
+    func_run_hooks func_options_prep ${1+"$@"}
+    func_propagate_result func_run_hooks func_options_prep
+}
+
+
+# func_parse_options [ARG]...
+# ---------------------------
+# The main option parsing loop.
+func_hookable func_parse_options
+func_parse_options ()
+{
+    $debug_cmd
+
+    _G_parse_options_requote=false
+    # this just eases exit handling
+    while test $# -gt 0; do
+      # Defer to hook functions for initial option parsing, so they
+      # get priority in the event of reusing an option name.
+      func_run_hooks func_parse_options ${1+"$@"}
+      func_propagate_result func_run_hooks func_parse_options
+      if $func_propagate_result_result; then
+        eval set dummy "$func_parse_options_result"; shift
+        # Even though we may have changed "$@", we passed the "$@" array
+        # down into the hook and it quoted it for us (because we are in
+        # this if-branch).  No need to quote it again.
+        _G_parse_options_requote=false
+      fi
+
+      # Break out of the loop if we already parsed every option.
+      test $# -gt 0 || break
+
+      # We expect that one of the options parsed in this function matches
+      # and thus we remove _G_opt from "$@" and need to re-quote.
+      _G_match_parse_options=:
+      _G_opt=$1
+      shift
+      case $_G_opt in
+        --debug|-x)   debug_cmd='set -x'
+                      func_echo "enabling shell trace mode" >&2
+                      $debug_cmd
+                      ;;
+
+        --no-warnings|--no-warning|--no-warn)
+                      set dummy --warnings none ${1+"$@"}
+                      shift
+		      ;;
+
+        --warnings|--warning|-W)
+                      if test $# = 0 && func_missing_arg $_G_opt; then
+                        _G_parse_options_requote=:
+                        break
+                      fi
+                      case " $warning_categories $1" in
+                        *" $1 "*)
+                          # trailing space prevents matching last $1 above
+                          func_append_uniq opt_warning_types " $1"
+                          ;;
+                        *all)
+                          opt_warning_types=$warning_categories
+                          ;;
+                        *none)
+                          opt_warning_types=none
+                          warning_func=:
+                          ;;
+                        *error)
+                          opt_warning_types=$warning_categories
+                          warning_func=func_fatal_error
+                          ;;
+                        *)
+                          func_fatal_error \
+                             "unsupported warning category: '$1'"
+                          ;;
+                      esac
+                      shift
+                      ;;
+
+        --verbose|-v) opt_verbose=: ;;
+        --version)    func_version ;;
+        -\?|-h)       func_usage ;;
+        --help)       func_help ;;
+
+	# Separate optargs to long options (plugins may need this):
+	--*=*)        func_split_equals "$_G_opt"
+	              set dummy "$func_split_equals_lhs" \
+                          "$func_split_equals_rhs" ${1+"$@"}
+                      shift
+                      ;;
+
+       # Separate optargs to short options:
+        -W*)
+                      func_split_short_opt "$_G_opt"
+                      set dummy "$func_split_short_opt_name" \
+                          "$func_split_short_opt_arg" ${1+"$@"}
+                      shift
+                      ;;
+
+        # Separate non-argument short options:
+        -\?*|-h*|-v*|-x*)
+                      func_split_short_opt "$_G_opt"
+                      set dummy "$func_split_short_opt_name" \
+                          "-$func_split_short_opt_arg" ${1+"$@"}
+                      shift
+                      ;;
+
+        --)           _G_parse_options_requote=: ; break ;;
+        -*)           func_fatal_help "unrecognised option: '$_G_opt'" ;;
+        *)            set dummy "$_G_opt" ${1+"$@"}; shift
+                      _G_match_parse_options=false
+                      break
+                      ;;
+      esac
+
+      if $_G_match_parse_options; then
+        _G_parse_options_requote=:
+      fi
+    done
+
+    if $_G_parse_options_requote; then
+      # save modified positional parameters for caller
+      func_quote eval ${1+"$@"}
+      func_parse_options_result=$func_quote_result
+    fi
+}
+
+
+# func_validate_options [ARG]...
+# ------------------------------
+# Perform any sanity checks on option settings and/or unconsumed
+# arguments.
+func_hookable func_validate_options
+func_validate_options ()
+{
+    $debug_cmd
+
+    # Display all warnings if -W was not given.
+    test -n "$opt_warning_types" || opt_warning_types=" $warning_categories"
+
+    func_run_hooks func_validate_options ${1+"$@"}
+    func_propagate_result func_run_hooks func_validate_options
+
+    # Bail if the options were screwed!
+    $exit_cmd $EXIT_FAILURE
+}
+
+
+
+## ----------------- ##
+## Helper functions. ##
+## ----------------- ##
+
+# This section contains the helper functions used by the rest of the
+# hookable option parser framework in ascii-betical order.
+
+
+# func_fatal_help ARG...
+# ----------------------
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+    $debug_cmd
+
+    eval \$ECHO \""Usage: $usage"\"
+    eval \$ECHO \""$fatal_help"\"
+    func_error ${1+"$@"}
+    exit $EXIT_FAILURE
+}
+
+
+# func_help
+# ---------
+# Echo long help message to standard output and exit.
+func_help ()
+{
+    $debug_cmd
+
+    func_usage_message
+    $ECHO "$long_help_message"
+    exit 0
+}
+
+
+# func_missing_arg ARGNAME
+# ------------------------
+# Echo program name prefixed message to standard error and set global
+# exit_cmd.
+func_missing_arg ()
+{
+    $debug_cmd
+
+    func_error "Missing argument for '$1'."
+    exit_cmd=exit
+}
+
+
+# func_split_equals STRING
+# ------------------------
+# Set func_split_equals_lhs and func_split_equals_rhs shell variables
+# after splitting STRING at the '=' sign.
+test -z "$_G_HAVE_XSI_OPS" \
+    && (eval 'x=a/b/c;
+      test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
+    && _G_HAVE_XSI_OPS=yes
+
+if test yes = "$_G_HAVE_XSI_OPS"
+then
+  # This is an XSI compatible shell, allowing a faster implementation...
+  eval 'func_split_equals ()
+  {
+      $debug_cmd
+
+      func_split_equals_lhs=${1%%=*}
+      func_split_equals_rhs=${1#*=}
+      if test "x$func_split_equals_lhs" = "x$1"; then
+        func_split_equals_rhs=
+      fi
+  }'
+else
+  # ...otherwise fall back to using expr, which is often a shell builtin.
+  func_split_equals ()
+  {
+      $debug_cmd
+
+      func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'`
+      func_split_equals_rhs=
+      test "x$func_split_equals_lhs=" = "x$1" \
+        || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'`
+  }
+fi #func_split_equals
+
+
+# func_split_short_opt SHORTOPT
+# -----------------------------
+# Set func_split_short_opt_name and func_split_short_opt_arg shell
+# variables after splitting SHORTOPT after the 2nd character.
+if test yes = "$_G_HAVE_XSI_OPS"
+then
+  # This is an XSI compatible shell, allowing a faster implementation...
+  eval 'func_split_short_opt ()
+  {
+      $debug_cmd
+
+      func_split_short_opt_arg=${1#??}
+      func_split_short_opt_name=${1%"$func_split_short_opt_arg"}
+  }'
+else
+  # ...otherwise fall back to using expr, which is often a shell builtin.
+  func_split_short_opt ()
+  {
+      $debug_cmd
+
+      func_split_short_opt_name=`expr "x$1" : 'x\(-.\)'`
+      func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'`
+  }
+fi #func_split_short_opt
+
+
+# func_usage
+# ----------
+# Echo short help message to standard output and exit.
+func_usage ()
+{
+    $debug_cmd
+
+    func_usage_message
+    $ECHO "Run '$progname --help |${PAGER-more}' for full usage"
+    exit 0
+}
+
+
+# func_usage_message
+# ------------------
+# Echo short help message to standard output.
+func_usage_message ()
+{
+    $debug_cmd
+
+    eval \$ECHO \""Usage: $usage"\"
+    echo
+    $SED -n 's|^# ||
+        /^Written by/{
+          x;p;x
+        }
+	h
+	/^Written by/q' < "$progpath"
+    echo
+    eval \$ECHO \""$usage_message"\"
+}
+
+
+# func_version
+# ------------
+# Echo version message to standard output and exit.
+# The version message is extracted from the calling file's header
+# comments, with leading '# ' stripped:
+#   1. First display the progname and version
+#   2. Followed by the header comment line matching  /^# Written by /
+#   3. Then a blank line followed by the first following line matching
+#      /^# Copyright /
+#   4. Immediately followed by any lines between the previous matches,
+#      except lines preceding the intervening completely blank line.
+# For example, see the header comments of this file.
+func_version ()
+{
+    $debug_cmd
+
+    printf '%s\n' "$progname $scriptversion"
+    $SED -n '
+        /^# Written by /!b
+        s|^# ||; p; n
+
+        :fwd2blnk
+        /./ {
+          n
+          b fwd2blnk
+        }
+        p; n
+
+        :holdwrnt
+        s|^# ||
+        s|^# *$||
+        /^Copyright /!{
+          /./H
+          n
+          b holdwrnt
+        }
+
+        s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2|
+        G
+        s|\(\n\)\n*|\1|g
+        p; q' < "$progpath"
+
+    exit $?
+}
+
+
+# Local variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-pattern: "30/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-time-zone: "UTC"
+# End:
+
+# Set a version string.
+scriptversion='(GNU libtool) 2.4.7'
+
+
+# func_echo ARG...
+# ----------------
+# Libtool also displays the current mode in messages, so override
+# funclib.sh func_echo with this custom definition.
+func_echo ()
+{
+    $debug_cmd
+
+    _G_message=$*
+
+    func_echo_IFS=$IFS
+    IFS=$nl
+    for _G_line in $_G_message; do
+      IFS=$func_echo_IFS
+      $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line"
+    done
+    IFS=$func_echo_IFS
+}
+
+
+# func_warning ARG...
+# -------------------
+# Libtool warnings are not categorized, so override funclib.sh
+# func_warning with this simpler definition.
+func_warning ()
+{
+    $debug_cmd
+
+    $warning_func ${1+"$@"}
+}
+
+
+## ---------------- ##
+## Options parsing. ##
+## ---------------- ##
+
+# Hook in the functions to make sure our own options are parsed during
+# the option parsing loop.
+
+usage='$progpath [OPTION]... [MODE-ARG]...'
+
+# Short help message in response to '-h'.
+usage_message="Options:
+       --config             show all configuration variables
+       --debug              enable verbose shell tracing
+   -n, --dry-run            display commands without modifying any files
+       --features           display basic configuration information and exit
+       --mode=MODE          use operation mode MODE
+       --no-warnings        equivalent to '-Wnone'
+       --preserve-dup-deps  don't remove duplicate dependency libraries
+       --quiet, --silent    don't print informational messages
+       --tag=TAG            use configuration variables from tag TAG
+   -v, --verbose            print more informational messages than default
+       --version            print version information
+   -W, --warnings=CATEGORY  report the warnings falling in CATEGORY [all]
+   -h, --help, --help-all   print short, long, or detailed help message
+"
+
+# Additional text appended to 'usage_message' in response to '--help'.
+func_help ()
+{
+    $debug_cmd
+
+    func_usage_message
+    $ECHO "$long_help_message
+
+MODE must be one of the following:
+
+       clean           remove files from the build directory
+       compile         compile a source file into a libtool object
+       execute         automatically set library path, then run a program
+       finish          complete the installation of libtool libraries
+       install         install libraries or executables
+       link            create a library or an executable
+       uninstall       remove libraries from an installed directory
+
+MODE-ARGS vary depending on the MODE.  When passed as first option,
+'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that.
+Try '$progname --help --mode=MODE' for a more detailed description of MODE.
+
+When reporting a bug, please describe a test case to reproduce it and
+include the following information:
+
+       host-triplet:   $host
+       shell:          $SHELL
+       compiler:       $LTCC
+       compiler flags: $LTCFLAGS
+       linker:         $LD (gnu? $with_gnu_ld)
+       version:        $progname (GNU libtool) 2.4.7
+       automake:       `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
+       autoconf:       `($AUTOCONF --version) 2>/dev/null |$SED 1q`
+
+Report bugs to <bug-libtool@gnu.org>.
+GNU libtool home page: <http://www.gnu.org/software/libtool/>.
+General help using GNU software: <http://www.gnu.org/gethelp/>."
+    exit 0
+}
+
+
+# func_lo2o OBJECT-NAME
+# ---------------------
+# Transform OBJECT-NAME from a '.lo' suffix to the platform specific
+# object suffix.
+
+lo2o=s/\\.lo\$/.$objext/
+o2lo=s/\\.$objext\$/.lo/
+
+if test yes = "$_G_HAVE_XSI_OPS"; then
+  eval 'func_lo2o ()
+  {
+    case $1 in
+      *.lo) func_lo2o_result=${1%.lo}.$objext ;;
+      *   ) func_lo2o_result=$1               ;;
+    esac
+  }'
+
+  # func_xform LIBOBJ-OR-SOURCE
+  # ---------------------------
+  # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise)
+  # suffix to a '.lo' libtool-object suffix.
+  eval 'func_xform ()
+  {
+    func_xform_result=${1%.*}.lo
+  }'
+else
+  # ...otherwise fall back to using sed.
+  func_lo2o ()
+  {
+    func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"`
+  }
+
+  func_xform ()
+  {
+    func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'`
+  }
+fi
+
+
+# func_fatal_configuration ARG...
+# -------------------------------
+# Echo program name prefixed message to standard error, followed by
+# a configuration failure hint, and exit.
+func_fatal_configuration ()
+{
+    func_fatal_error ${1+"$@"} \
+      "See the $PACKAGE documentation for more information." \
+      "Fatal configuration error."
+}
+
+
+# func_config
+# -----------
+# Display the configuration for all the tags in this script.
+func_config ()
+{
+    re_begincf='^# ### BEGIN LIBTOOL'
+    re_endcf='^# ### END LIBTOOL'
+
+    # Default configuration.
+    $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath"
+
+    # Now print the configurations for the tags.
+    for tagname in $taglist; do
+      $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath"
+    done
+
+    exit $?
+}
+
+
+# func_features
+# -------------
+# Display the features supported by this script.
+func_features ()
+{
+    echo "host: $host"
+    if test yes = "$build_libtool_libs"; then
+      echo "enable shared libraries"
+    else
+      echo "disable shared libraries"
+    fi
+    if test yes = "$build_old_libs"; then
+      echo "enable static libraries"
+    else
+      echo "disable static libraries"
+    fi
+
+    exit $?
+}
+
+
+# func_enable_tag TAGNAME
+# -----------------------
+# Verify that TAGNAME is valid, and either flag an error and exit, or
+# enable the TAGNAME tag.  We also add TAGNAME to the global $taglist
+# variable here.
+func_enable_tag ()
+{
+    # Global variable:
+    tagname=$1
+
+    re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+    re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+    sed_extractcf=/$re_begincf/,/$re_endcf/p
+
+    # Validate tagname.
+    case $tagname in
+      *[!-_A-Za-z0-9,/]*)
+        func_fatal_error "invalid tag name: $tagname"
+        ;;
+    esac
+
+    # Don't test for the "default" C tag, as we know it's
+    # there but not specially marked.
+    case $tagname in
+        CC) ;;
+    *)
+        if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+	  taglist="$taglist $tagname"
+
+	  # Evaluate the configuration.  Be careful to quote the path
+	  # and the sed script, to avoid splitting on whitespace, but
+	  # also don't use non-portable quotes within backquotes within
+	  # quotes we have to do it in 2 steps:
+	  extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+	  eval "$extractedcf"
+        else
+	  func_error "ignoring unknown tag $tagname"
+        fi
+        ;;
+    esac
+}
+
+
+# func_check_version_match
+# ------------------------
+# Ensure that we are using m4 macros, and libtool script from the same
+# release of libtool.
+func_check_version_match ()
+{
+    if test "$package_revision" != "$macro_revision"; then
+      if test "$VERSION" != "$macro_version"; then
+        if test -z "$macro_version"; then
+          cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from an older release.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+        else
+          cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, but the
+$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
+$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
+$progname: and run autoconf again.
+_LT_EOF
+        fi
+      else
+        cat >&2 <<_LT_EOF
+$progname: Version mismatch error.  This is $PACKAGE $VERSION, revision $package_revision,
+$progname: but the definition of this LT_INIT comes from revision $macro_revision.
+$progname: You should recreate aclocal.m4 with macros from revision $package_revision
+$progname: of $PACKAGE $VERSION and run autoconf again.
+_LT_EOF
+      fi
+
+      exit $EXIT_MISMATCH
+    fi
+}
+
+
+# libtool_options_prep [ARG]...
+# -----------------------------
+# Preparation for options parsed by libtool.
+libtool_options_prep ()
+{
+    $debug_mode
+
+    # Option defaults:
+    opt_config=false
+    opt_dlopen=
+    opt_dry_run=false
+    opt_help=false
+    opt_mode=
+    opt_preserve_dup_deps=false
+    opt_quiet=false
+
+    nonopt=
+    preserve_args=
+
+    _G_rc_lt_options_prep=:
+
+    # Shorthand for --mode=foo, only valid as the first argument
+    case $1 in
+    clean|clea|cle|cl)
+      shift; set dummy --mode clean ${1+"$@"}; shift
+      ;;
+    compile|compil|compi|comp|com|co|c)
+      shift; set dummy --mode compile ${1+"$@"}; shift
+      ;;
+    execute|execut|execu|exec|exe|ex|e)
+      shift; set dummy --mode execute ${1+"$@"}; shift
+      ;;
+    finish|finis|fini|fin|fi|f)
+      shift; set dummy --mode finish ${1+"$@"}; shift
+      ;;
+    install|instal|insta|inst|ins|in|i)
+      shift; set dummy --mode install ${1+"$@"}; shift
+      ;;
+    link|lin|li|l)
+      shift; set dummy --mode link ${1+"$@"}; shift
+      ;;
+    uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+      shift; set dummy --mode uninstall ${1+"$@"}; shift
+      ;;
+    *)
+      _G_rc_lt_options_prep=false
+      ;;
+    esac
+
+    if $_G_rc_lt_options_prep; then
+      # Pass back the list of options.
+      func_quote eval ${1+"$@"}
+      libtool_options_prep_result=$func_quote_result
+    fi
+}
+func_add_hook func_options_prep libtool_options_prep
+
+
+# libtool_parse_options [ARG]...
+# ---------------------------------
+# Provide handling for libtool specific options.
+libtool_parse_options ()
+{
+    $debug_cmd
+
+    _G_rc_lt_parse_options=false
+
+    # Perform our own loop to consume as many options as possible in
+    # each iteration.
+    while test $# -gt 0; do
+      _G_match_lt_parse_options=:
+      _G_opt=$1
+      shift
+      case $_G_opt in
+        --dry-run|--dryrun|-n)
+                        opt_dry_run=:
+                        ;;
+
+        --config)       func_config ;;
+
+        --dlopen|-dlopen)
+                        opt_dlopen="${opt_dlopen+$opt_dlopen
+}$1"
+                        shift
+                        ;;
+
+        --preserve-dup-deps)
+                        opt_preserve_dup_deps=: ;;
+
+        --features)     func_features ;;
+
+        --finish)       set dummy --mode finish ${1+"$@"}; shift ;;
+
+        --help)         opt_help=: ;;
+
+        --help-all)     opt_help=': help-all' ;;
+
+        --mode)         test $# = 0 && func_missing_arg $_G_opt && break
+                        opt_mode=$1
+                        case $1 in
+                          # Valid mode arguments:
+                          clean|compile|execute|finish|install|link|relink|uninstall) ;;
+
+                          # Catch anything else as an error
+                          *) func_error "invalid argument for $_G_opt"
+                             exit_cmd=exit
+                             break
+                             ;;
+                        esac
+                        shift
+                        ;;
+
+        --no-silent|--no-quiet)
+                        opt_quiet=false
+                        func_append preserve_args " $_G_opt"
+                        ;;
+
+        --no-warnings|--no-warning|--no-warn)
+                        opt_warning=false
+                        func_append preserve_args " $_G_opt"
+                        ;;
+
+        --no-verbose)
+                        opt_verbose=false
+                        func_append preserve_args " $_G_opt"
+                        ;;
+
+        --silent|--quiet)
+                        opt_quiet=:
+                        opt_verbose=false
+                        func_append preserve_args " $_G_opt"
+                        ;;
+
+        --tag)          test $# = 0 && func_missing_arg $_G_opt && break
+                        opt_tag=$1
+                        func_append preserve_args " $_G_opt $1"
+                        func_enable_tag "$1"
+                        shift
+                        ;;
+
+        --verbose|-v)   opt_quiet=false
+                        opt_verbose=:
+                        func_append preserve_args " $_G_opt"
+                        ;;
+
+        # An option not handled by this hook function:
+        *)              set dummy "$_G_opt" ${1+"$@"} ; shift
+                        _G_match_lt_parse_options=false
+                        break
+                        ;;
+      esac
+      $_G_match_lt_parse_options && _G_rc_lt_parse_options=:
+    done
+
+    if $_G_rc_lt_parse_options; then
+      # save modified positional parameters for caller
+      func_quote eval ${1+"$@"}
+      libtool_parse_options_result=$func_quote_result
+    fi
+}
+func_add_hook func_parse_options libtool_parse_options
+
+
+
+# libtool_validate_options [ARG]...
+# ---------------------------------
+# Perform any sanity checks on option settings and/or unconsumed
+# arguments.
+libtool_validate_options ()
+{
+    # save first non-option argument
+    if test 0 -lt $#; then
+      nonopt=$1
+      shift
+    fi
+
+    # preserve --debug
+    test : = "$debug_cmd" || func_append preserve_args " --debug"
+
+    # Keeping compiler generated duplicates in $postdeps and $predeps is not
+    # harmful, and is necessary in a majority of systems that use it to satisfy
+    # symbol dependencies.
+    opt_duplicate_compiler_generated_deps=:
+
+    $opt_help || {
+      # Sanity checks first:
+      func_check_version_match
+
+      test yes != "$build_libtool_libs" \
+        && test yes != "$build_old_libs" \
+        && func_fatal_configuration "not configured to build any kind of library"
+
+      # Darwin sucks
+      eval std_shrext=\"$shrext_cmds\"
+
+      # Only execute mode is allowed to have -dlopen flags.
+      if test -n "$opt_dlopen" && test execute != "$opt_mode"; then
+        func_error "unrecognized option '-dlopen'"
+        $ECHO "$help" 1>&2
+        exit $EXIT_FAILURE
+      fi
+
+      # Change the help message to a mode-specific one.
+      generic_help=$help
+      help="Try '$progname --help --mode=$opt_mode' for more information."
+    }
+
+    # Pass back the unparsed argument list
+    func_quote eval ${1+"$@"}
+    libtool_validate_options_result=$func_quote_result
+}
+func_add_hook func_validate_options libtool_validate_options
+
+
+# Process options as early as possible so that --help and --version
+# can return quickly.
+func_options ${1+"$@"}
+eval set dummy "$func_options_result"; shift
+
+
+
+## ----------- ##
+##    Main.    ##
+## ----------- ##
+
+magic='%%%MAGIC variable%%%'
+magic_exe='%%%MAGIC EXE variable%%%'
+
+# Global variables.
+extracted_archives=
+extracted_serial=0
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end.  This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+
+# func_generated_by_libtool
+# True iff stdin has been generated by Libtool. This function is only
+# a basic sanity check; it will hardly flush out determined imposters.
+func_generated_by_libtool_p ()
+{
+  $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
+# func_lalib_p file
+# True iff FILE is a libtool '.la' library or '.lo' object file.
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_lalib_p ()
+{
+    test -f "$1" &&
+      $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p
+}
+
+# func_lalib_unsafe_p file
+# True iff FILE is a libtool '.la' library or '.lo' object file.
+# This function implements the same check as func_lalib_p without
+# resorting to external programs.  To this end, it redirects stdin and
+# closes it afterwards, without saving the original file descriptor.
+# As a safety measure, use it only where a negative result would be
+# fatal anyway.  Works if 'file' does not exist.
+func_lalib_unsafe_p ()
+{
+    lalib_p=no
+    if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then
+	for lalib_p_l in 1 2 3 4
+	do
+	    read lalib_p_line
+	    case $lalib_p_line in
+		\#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
+	    esac
+	done
+	exec 0<&5 5<&-
+    fi
+    test yes = "$lalib_p"
+}
+
+# func_ltwrapper_script_p file
+# True iff FILE is a libtool wrapper script
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_script_p ()
+{
+    test -f "$1" &&
+      $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p
+}
+
+# func_ltwrapper_executable_p file
+# True iff FILE is a libtool wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_executable_p ()
+{
+    func_ltwrapper_exec_suffix=
+    case $1 in
+    *.exe) ;;
+    *) func_ltwrapper_exec_suffix=.exe ;;
+    esac
+    $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1
+}
+
+# func_ltwrapper_scriptname file
+# Assumes file is an ltwrapper_executable
+# uses $file to determine the appropriate filename for a
+# temporary ltwrapper_script.
+func_ltwrapper_scriptname ()
+{
+    func_dirname_and_basename "$1" "" "."
+    func_stripname '' '.exe' "$func_basename_result"
+    func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper
+}
+
+# func_ltwrapper_p file
+# True iff FILE is a libtool wrapper script or wrapper executable
+# This function is only a basic sanity check; it will hardly flush out
+# determined imposters.
+func_ltwrapper_p ()
+{
+    func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1"
+}
+
+
+# func_execute_cmds commands fail_cmd
+# Execute tilde-delimited COMMANDS.
+# If FAIL_CMD is given, eval that upon failure.
+# FAIL_CMD may read-access the current command in variable CMD!
+func_execute_cmds ()
+{
+    $debug_cmd
+
+    save_ifs=$IFS; IFS='~'
+    for cmd in $1; do
+      IFS=$sp$nl
+      eval cmd=\"$cmd\"
+      IFS=$save_ifs
+      func_show_eval "$cmd" "${2-:}"
+    done
+    IFS=$save_ifs
+}
+
+
+# func_source file
+# Source FILE, adding directory component if necessary.
+# Note that it is not necessary on cygwin/mingw to append a dot to
+# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
+# behavior happens only for exec(3), not for open(2)!  Also, sourcing
+# 'FILE.' does not work on cygwin managed mounts.
+func_source ()
+{
+    $debug_cmd
+
+    case $1 in
+    */* | *\\*)	. "$1" ;;
+    *)		. "./$1" ;;
+    esac
+}
+
+
+# func_resolve_sysroot PATH
+# Replace a leading = in PATH with a sysroot.  Store the result into
+# func_resolve_sysroot_result
+func_resolve_sysroot ()
+{
+  func_resolve_sysroot_result=$1
+  case $func_resolve_sysroot_result in
+  =*)
+    func_stripname '=' '' "$func_resolve_sysroot_result"
+    func_resolve_sysroot_result=$lt_sysroot$func_stripname_result
+    ;;
+  esac
+}
+
+# func_replace_sysroot PATH
+# If PATH begins with the sysroot, replace it with = and
+# store the result into func_replace_sysroot_result.
+func_replace_sysroot ()
+{
+  case $lt_sysroot:$1 in
+  ?*:"$lt_sysroot"*)
+    func_stripname "$lt_sysroot" '' "$1"
+    func_replace_sysroot_result='='$func_stripname_result
+    ;;
+  *)
+    # Including no sysroot.
+    func_replace_sysroot_result=$1
+    ;;
+  esac
+}
+
+# func_infer_tag arg
+# Infer tagged configuration to use if any are available and
+# if one wasn't chosen via the "--tag" command line option.
+# Only attempt this if the compiler in the base compile
+# command doesn't match the default compiler.
+# arg is usually of the form 'gcc ...'
+func_infer_tag ()
+{
+    $debug_cmd
+
+    if test -n "$available_tags" && test -z "$tagname"; then
+      CC_quoted=
+      for arg in $CC; do
+	func_append_quoted CC_quoted "$arg"
+      done
+      CC_expanded=`func_echo_all $CC`
+      CC_quoted_expanded=`func_echo_all $CC_quoted`
+      case $@ in
+      # Blanks in the command may have been stripped by the calling shell,
+      # but not from the CC environment variable when configure was run.
+      " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+      " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*) ;;
+      # Blanks at the start of $base_compile will cause this to fail
+      # if we don't check for them as well.
+      *)
+	for z in $available_tags; do
+	  if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
+	    # Evaluate the configuration.
+	    eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+	    CC_quoted=
+	    for arg in $CC; do
+	      # Double-quote args containing other shell metacharacters.
+	      func_append_quoted CC_quoted "$arg"
+	    done
+	    CC_expanded=`func_echo_all $CC`
+	    CC_quoted_expanded=`func_echo_all $CC_quoted`
+	    case "$@ " in
+	    " $CC "* | "$CC "* | " $CC_expanded "* | "$CC_expanded "* | \
+	    " $CC_quoted"* | "$CC_quoted "* | " $CC_quoted_expanded "* | "$CC_quoted_expanded "*)
+	      # The compiler in the base compile command matches
+	      # the one in the tagged configuration.
+	      # Assume this is the tagged configuration we want.
+	      tagname=$z
+	      break
+	      ;;
+	    esac
+	  fi
+	done
+	# If $tagname still isn't set, then no tagged configuration
+	# was found and let the user know that the "--tag" command
+	# line option must be used.
+	if test -z "$tagname"; then
+	  func_echo "unable to infer tagged configuration"
+	  func_fatal_error "specify a tag with '--tag'"
+#	else
+#	  func_verbose "using $tagname tagged configuration"
+	fi
+	;;
+      esac
+    fi
+}
+
+
+
+# func_write_libtool_object output_name pic_name nonpic_name
+# Create a libtool object file (analogous to a ".la" file),
+# but don't create it if we're doing a dry run.
+func_write_libtool_object ()
+{
+    write_libobj=$1
+    if test yes = "$build_libtool_libs"; then
+      write_lobj=\'$2\'
+    else
+      write_lobj=none
+    fi
+
+    if test yes = "$build_old_libs"; then
+      write_oldobj=\'$3\'
+    else
+      write_oldobj=none
+    fi
+
+    $opt_dry_run || {
+      cat >${write_libobj}T <<EOF
+# $write_libobj - a libtool object file
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# Name of the PIC object.
+pic_object=$write_lobj
+
+# Name of the non-PIC object
+non_pic_object=$write_oldobj
+
+EOF
+      $MV "${write_libobj}T" "$write_libobj"
+    }
+}
+
+
+##################################################
+# FILE NAME AND PATH CONVERSION HELPER FUNCTIONS #
+##################################################
+
+# func_convert_core_file_wine_to_w32 ARG
+# Helper function used by file name conversion functions when $build is *nix,
+# and $host is mingw, cygwin, or some other w32 environment. Relies on a
+# correctly configured wine environment available, with the winepath program
+# in $build's $PATH.
+#
+# ARG is the $build file name to be converted to w32 format.
+# Result is available in $func_convert_core_file_wine_to_w32_result, and will
+# be empty on error (or when ARG is empty)
+func_convert_core_file_wine_to_w32 ()
+{
+  $debug_cmd
+
+  func_convert_core_file_wine_to_w32_result=$1
+  if test -n "$1"; then
+    # Unfortunately, winepath does not exit with a non-zero error code, so we
+    # are forced to check the contents of stdout. On the other hand, if the
+    # command is not found, the shell will set an exit code of 127 and print
+    # *an error message* to stdout. So we must check for both error code of
+    # zero AND non-empty stdout, which explains the odd construction:
+    func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null`
+    if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then
+      func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" |
+        $SED -e "$sed_naive_backslashify"`
+    else
+      func_convert_core_file_wine_to_w32_result=
+    fi
+  fi
+}
+# end: func_convert_core_file_wine_to_w32
+
+
+# func_convert_core_path_wine_to_w32 ARG
+# Helper function used by path conversion functions when $build is *nix, and
+# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly
+# configured wine environment available, with the winepath program in $build's
+# $PATH. Assumes ARG has no leading or trailing path separator characters.
+#
+# ARG is path to be converted from $build format to win32.
+# Result is available in $func_convert_core_path_wine_to_w32_result.
+# Unconvertible file (directory) names in ARG are skipped; if no directory names
+# are convertible, then the result may be empty.
+func_convert_core_path_wine_to_w32 ()
+{
+  $debug_cmd
+
+  # unfortunately, winepath doesn't convert paths, only file names
+  func_convert_core_path_wine_to_w32_result=
+  if test -n "$1"; then
+    oldIFS=$IFS
+    IFS=:
+    for func_convert_core_path_wine_to_w32_f in $1; do
+      IFS=$oldIFS
+      func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f"
+      if test -n "$func_convert_core_file_wine_to_w32_result"; then
+        if test -z "$func_convert_core_path_wine_to_w32_result"; then
+          func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result
+        else
+          func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result"
+        fi
+      fi
+    done
+    IFS=$oldIFS
+  fi
+}
+# end: func_convert_core_path_wine_to_w32
+
+
+# func_cygpath ARGS...
+# Wrapper around calling the cygpath program via LT_CYGPATH. This is used when
+# when (1) $build is *nix and Cygwin is hosted via a wine environment; or (2)
+# $build is MSYS and $host is Cygwin, or (3) $build is Cygwin. In case (1) or
+# (2), returns the Cygwin file name or path in func_cygpath_result (input
+# file name or path is assumed to be in w32 format, as previously converted
+# from $build's *nix or MSYS format). In case (3), returns the w32 file name
+# or path in func_cygpath_result (input file name or path is assumed to be in
+# Cygwin format). Returns an empty string on error.
+#
+# ARGS are passed to cygpath, with the last one being the file name or path to
+# be converted.
+#
+# Specify the absolute *nix (or w32) name to cygpath in the LT_CYGPATH
+# environment variable; do not put it in $PATH.
+func_cygpath ()
+{
+  $debug_cmd
+
+  if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then
+    func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null`
+    if test "$?" -ne 0; then
+      # on failure, ensure result is empty
+      func_cygpath_result=
+    fi
+  else
+    func_cygpath_result=
+    func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'"
+  fi
+}
+#end: func_cygpath
+
+
+# func_convert_core_msys_to_w32 ARG
+# Convert file name or path ARG from MSYS format to w32 format.  Return
+# result in func_convert_core_msys_to_w32_result.
+func_convert_core_msys_to_w32 ()
+{
+  $debug_cmd
+
+  # awkward: cmd appends spaces to result
+  func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null |
+    $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"`
+}
+#end: func_convert_core_msys_to_w32
+
+
+# func_convert_file_check ARG1 ARG2
+# Verify that ARG1 (a file name in $build format) was converted to $host
+# format in ARG2. Otherwise, emit an error message, but continue (resetting
+# func_to_host_file_result to ARG1).
+func_convert_file_check ()
+{
+  $debug_cmd
+
+  if test -z "$2" && test -n "$1"; then
+    func_error "Could not determine host file name corresponding to"
+    func_error "  '$1'"
+    func_error "Continuing, but uninstalled executables may not work."
+    # Fallback:
+    func_to_host_file_result=$1
+  fi
+}
+# end func_convert_file_check
+
+
+# func_convert_path_check FROM_PATHSEP TO_PATHSEP FROM_PATH TO_PATH
+# Verify that FROM_PATH (a path in $build format) was converted to $host
+# format in TO_PATH. Otherwise, emit an error message, but continue, resetting
+# func_to_host_file_result to a simplistic fallback value (see below).
+func_convert_path_check ()
+{
+  $debug_cmd
+
+  if test -z "$4" && test -n "$3"; then
+    func_error "Could not determine the host path corresponding to"
+    func_error "  '$3'"
+    func_error "Continuing, but uninstalled executables may not work."
+    # Fallback.  This is a deliberately simplistic "conversion" and
+    # should not be "improved".  See libtool.info.
+    if test "x$1" != "x$2"; then
+      lt_replace_pathsep_chars="s|$1|$2|g"
+      func_to_host_path_result=`echo "$3" |
+        $SED -e "$lt_replace_pathsep_chars"`
+    else
+      func_to_host_path_result=$3
+    fi
+  fi
+}
+# end func_convert_path_check
+
+
+# func_convert_path_front_back_pathsep FRONTPAT BACKPAT REPL ORIG
+# Modifies func_to_host_path_result by prepending REPL if ORIG matches FRONTPAT
+# and appending REPL if ORIG matches BACKPAT.
+func_convert_path_front_back_pathsep ()
+{
+  $debug_cmd
+
+  case $4 in
+  $1 ) func_to_host_path_result=$3$func_to_host_path_result
+    ;;
+  esac
+  case $4 in
+  $2 ) func_append func_to_host_path_result "$3"
+    ;;
+  esac
+}
+# end func_convert_path_front_back_pathsep
+
+
+##################################################
+# $build to $host FILE NAME CONVERSION FUNCTIONS #
+##################################################
+# invoked via '$to_host_file_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# Result will be available in $func_to_host_file_result.
+
+
+# func_to_host_file ARG
+# Converts the file name ARG from $build format to $host format. Return result
+# in func_to_host_file_result.
+func_to_host_file ()
+{
+  $debug_cmd
+
+  $to_host_file_cmd "$1"
+}
+# end func_to_host_file
+
+
+# func_to_tool_file ARG LAZY
+# converts the file name ARG from $build format to toolchain format. Return
+# result in func_to_tool_file_result.  If the conversion in use is listed
+# in (the comma separated) LAZY, no conversion takes place.
+func_to_tool_file ()
+{
+  $debug_cmd
+
+  case ,$2, in
+    *,"$to_tool_file_cmd",*)
+      func_to_tool_file_result=$1
+      ;;
+    *)
+      $to_tool_file_cmd "$1"
+      func_to_tool_file_result=$func_to_host_file_result
+      ;;
+  esac
+}
+# end func_to_tool_file
+
+
+# func_convert_file_noop ARG
+# Copy ARG to func_to_host_file_result.
+func_convert_file_noop ()
+{
+  func_to_host_file_result=$1
+}
+# end func_convert_file_noop
+
+
+# func_convert_file_msys_to_w32 ARG
+# Convert file name ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper.  Returns result in
+# func_to_host_file_result.
+func_convert_file_msys_to_w32 ()
+{
+  $debug_cmd
+
+  func_to_host_file_result=$1
+  if test -n "$1"; then
+    func_convert_core_msys_to_w32 "$1"
+    func_to_host_file_result=$func_convert_core_msys_to_w32_result
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_w32
+
+
+# func_convert_file_cygwin_to_w32 ARG
+# Convert file name ARG from Cygwin to w32 format.  Returns result in
+# func_to_host_file_result.
+func_convert_file_cygwin_to_w32 ()
+{
+  $debug_cmd
+
+  func_to_host_file_result=$1
+  if test -n "$1"; then
+    # because $build is cygwin, we call "the" cygpath in $PATH; no need to use
+    # LT_CYGPATH in this case.
+    func_to_host_file_result=`cygpath -m "$1"`
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_cygwin_to_w32
+
+
+# func_convert_file_nix_to_w32 ARG
+# Convert file name ARG from *nix to w32 format.  Requires a wine environment
+# and a working winepath. Returns result in func_to_host_file_result.
+func_convert_file_nix_to_w32 ()
+{
+  $debug_cmd
+
+  func_to_host_file_result=$1
+  if test -n "$1"; then
+    func_convert_core_file_wine_to_w32 "$1"
+    func_to_host_file_result=$func_convert_core_file_wine_to_w32_result
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_w32
+
+
+# func_convert_file_msys_to_cygwin ARG
+# Convert file name ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_file_msys_to_cygwin ()
+{
+  $debug_cmd
+
+  func_to_host_file_result=$1
+  if test -n "$1"; then
+    func_convert_core_msys_to_w32 "$1"
+    func_cygpath -u "$func_convert_core_msys_to_w32_result"
+    func_to_host_file_result=$func_cygpath_result
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_msys_to_cygwin
+
+
+# func_convert_file_nix_to_cygwin ARG
+# Convert file name ARG from *nix to Cygwin format.  Requires Cygwin installed
+# in a wine environment, working winepath, and LT_CYGPATH set.  Returns result
+# in func_to_host_file_result.
+func_convert_file_nix_to_cygwin ()
+{
+  $debug_cmd
+
+  func_to_host_file_result=$1
+  if test -n "$1"; then
+    # convert from *nix to w32, then use cygpath to convert from w32 to cygwin.
+    func_convert_core_file_wine_to_w32 "$1"
+    func_cygpath -u "$func_convert_core_file_wine_to_w32_result"
+    func_to_host_file_result=$func_cygpath_result
+  fi
+  func_convert_file_check "$1" "$func_to_host_file_result"
+}
+# end func_convert_file_nix_to_cygwin
+
+
+#############################################
+# $build to $host PATH CONVERSION FUNCTIONS #
+#############################################
+# invoked via '$to_host_path_cmd ARG'
+#
+# In each case, ARG is the path to be converted from $build to $host format.
+# The result will be available in $func_to_host_path_result.
+#
+# Path separators are also converted from $build format to $host format.  If
+# ARG begins or ends with a path separator character, it is preserved (but
+# converted to $host format) on output.
+#
+# All path conversion functions are named using the following convention:
+#   file name conversion function    : func_convert_file_X_to_Y ()
+#   path conversion function         : func_convert_path_X_to_Y ()
+# where, for any given $build/$host combination the 'X_to_Y' value is the
+# same.  If conversion functions are added for new $build/$host combinations,
+# the two new functions must follow this pattern, or func_init_to_host_path_cmd
+# will break.
+
+
+# func_init_to_host_path_cmd
+# Ensures that function "pointer" variable $to_host_path_cmd is set to the
+# appropriate value, based on the value of $to_host_file_cmd.
+to_host_path_cmd=
+func_init_to_host_path_cmd ()
+{
+  $debug_cmd
+
+  if test -z "$to_host_path_cmd"; then
+    func_stripname 'func_convert_file_' '' "$to_host_file_cmd"
+    to_host_path_cmd=func_convert_path_$func_stripname_result
+  fi
+}
+
+
+# func_to_host_path ARG
+# Converts the path ARG from $build format to $host format. Return result
+# in func_to_host_path_result.
+func_to_host_path ()
+{
+  $debug_cmd
+
+  func_init_to_host_path_cmd
+  $to_host_path_cmd "$1"
+}
+# end func_to_host_path
+
+
+# func_convert_path_noop ARG
+# Copy ARG to func_to_host_path_result.
+func_convert_path_noop ()
+{
+  func_to_host_path_result=$1
+}
+# end func_convert_path_noop
+
+
+# func_convert_path_msys_to_w32 ARG
+# Convert path ARG from (mingw) MSYS to (mingw) w32 format; automatic
+# conversion to w32 is not available inside the cwrapper.  Returns result in
+# func_to_host_path_result.
+func_convert_path_msys_to_w32 ()
+{
+  $debug_cmd
+
+  func_to_host_path_result=$1
+  if test -n "$1"; then
+    # Remove leading and trailing path separator characters from ARG.  MSYS
+    # behavior is inconsistent here; cygpath turns them into '.;' and ';.';
+    # and winepath ignores them completely.
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+    func_to_host_path_result=$func_convert_core_msys_to_w32_result
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_msys_to_w32
+
+
+# func_convert_path_cygwin_to_w32 ARG
+# Convert path ARG from Cygwin to w32 format.  Returns result in
+# func_to_host_file_result.
+func_convert_path_cygwin_to_w32 ()
+{
+  $debug_cmd
+
+  func_to_host_path_result=$1
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_to_host_path_result=`cygpath -m -p "$func_to_host_path_tmp1"`
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_cygwin_to_w32
+
+
+# func_convert_path_nix_to_w32 ARG
+# Convert path ARG from *nix to w32 format.  Requires a wine environment and
+# a working winepath.  Returns result in func_to_host_file_result.
+func_convert_path_nix_to_w32 ()
+{
+  $debug_cmd
+
+  func_to_host_path_result=$1
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+    func_to_host_path_result=$func_convert_core_path_wine_to_w32_result
+    func_convert_path_check : ";" \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
+  fi
+}
+# end func_convert_path_nix_to_w32
+
+
+# func_convert_path_msys_to_cygwin ARG
+# Convert path ARG from MSYS to Cygwin format.  Requires LT_CYGPATH set.
+# Returns result in func_to_host_file_result.
+func_convert_path_msys_to_cygwin ()
+{
+  $debug_cmd
+
+  func_to_host_path_result=$1
+  if test -n "$1"; then
+    # See func_convert_path_msys_to_w32:
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
+    func_cygpath -u -p "$func_convert_core_msys_to_w32_result"
+    func_to_host_path_result=$func_cygpath_result
+    func_convert_path_check : : \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+  fi
+}
+# end func_convert_path_msys_to_cygwin
+
+
+# func_convert_path_nix_to_cygwin ARG
+# Convert path ARG from *nix to Cygwin format.  Requires Cygwin installed in a
+# a wine environment, working winepath, and LT_CYGPATH set.  Returns result in
+# func_to_host_file_result.
+func_convert_path_nix_to_cygwin ()
+{
+  $debug_cmd
+
+  func_to_host_path_result=$1
+  if test -n "$1"; then
+    # Remove leading and trailing path separator characters from
+    # ARG. msys behavior is inconsistent here, cygpath turns them
+    # into '.;' and ';.', and winepath ignores them completely.
+    func_stripname : : "$1"
+    func_to_host_path_tmp1=$func_stripname_result
+    func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
+    func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result"
+    func_to_host_path_result=$func_cygpath_result
+    func_convert_path_check : : \
+      "$func_to_host_path_tmp1" "$func_to_host_path_result"
+    func_convert_path_front_back_pathsep ":*" "*:" : "$1"
+  fi
+}
+# end func_convert_path_nix_to_cygwin
+
+
+# func_dll_def_p FILE
+# True iff FILE is a Windows DLL '.def' file.
+# Keep in sync with _LT_DLL_DEF_P in libtool.m4
+func_dll_def_p ()
+{
+  $debug_cmd
+
+  func_dll_def_p_tmp=`$SED -n \
+    -e 's/^[	 ]*//' \
+    -e '/^\(;.*\)*$/d' \
+    -e 's/^\(EXPORTS\|LIBRARY\)\([	 ].*\)*$/DEF/p' \
+    -e q \
+    "$1"`
+  test DEF = "$func_dll_def_p_tmp"
+}
+
+
+# func_mode_compile arg...
+func_mode_compile ()
+{
+    $debug_cmd
+
+    # Get the compilation command and the source file.
+    base_compile=
+    srcfile=$nonopt  #  always keep a non-empty value in "srcfile"
+    suppress_opt=yes
+    suppress_output=
+    arg_mode=normal
+    libobj=
+    later=
+    pie_flag=
+
+    for arg
+    do
+      case $arg_mode in
+      arg  )
+	# do not "continue".  Instead, add this to base_compile
+	lastarg=$arg
+	arg_mode=normal
+	;;
+
+      target )
+	libobj=$arg
+	arg_mode=normal
+	continue
+	;;
+
+      normal )
+	# Accept any command-line options.
+	case $arg in
+	-o)
+	  test -n "$libobj" && \
+	    func_fatal_error "you cannot specify '-o' more than once"
+	  arg_mode=target
+	  continue
+	  ;;
+
+	-pie | -fpie | -fPIE)
+          func_append pie_flag " $arg"
+	  continue
+	  ;;
+
+	-shared | -static | -prefer-pic | -prefer-non-pic)
+	  func_append later " $arg"
+	  continue
+	  ;;
+
+	-no-suppress)
+	  suppress_opt=no
+	  continue
+	  ;;
+
+	-Xcompiler)
+	  arg_mode=arg  #  the next one goes into the "base_compile" arg list
+	  continue      #  The current "srcfile" will either be retained or
+	  ;;            #  replaced later.  I would guess that would be a bug.
+
+	-Wc,*)
+	  func_stripname '-Wc,' '' "$arg"
+	  args=$func_stripname_result
+	  lastarg=
+	  save_ifs=$IFS; IFS=,
+	  for arg in $args; do
+	    IFS=$save_ifs
+	    func_append_quoted lastarg "$arg"
+	  done
+	  IFS=$save_ifs
+	  func_stripname ' ' '' "$lastarg"
+	  lastarg=$func_stripname_result
+
+	  # Add the arguments to base_compile.
+	  func_append base_compile " $lastarg"
+	  continue
+	  ;;
+
+	*)
+	  # Accept the current argument as the source file.
+	  # The previous "srcfile" becomes the current argument.
+	  #
+	  lastarg=$srcfile
+	  srcfile=$arg
+	  ;;
+	esac  #  case $arg
+	;;
+      esac    #  case $arg_mode
+
+      # Aesthetically quote the previous argument.
+      func_append_quoted base_compile "$lastarg"
+    done # for arg
+
+    case $arg_mode in
+    arg)
+      func_fatal_error "you must specify an argument for -Xcompile"
+      ;;
+    target)
+      func_fatal_error "you must specify a target with '-o'"
+      ;;
+    *)
+      # Get the name of the library object.
+      test -z "$libobj" && {
+	func_basename "$srcfile"
+	libobj=$func_basename_result
+      }
+      ;;
+    esac
+
+    # Recognize several different file suffixes.
+    # If the user specifies -o file.o, it is replaced with file.lo
+    case $libobj in
+    *.[cCFSifmso] | \
+    *.ada | *.adb | *.ads | *.asm | \
+    *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
+    *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup)
+      func_xform "$libobj"
+      libobj=$func_xform_result
+      ;;
+    esac
+
+    case $libobj in
+    *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
+    *)
+      func_fatal_error "cannot determine name of library object from '$libobj'"
+      ;;
+    esac
+
+    func_infer_tag $base_compile
+
+    for arg in $later; do
+      case $arg in
+      -shared)
+	test yes = "$build_libtool_libs" \
+	  || func_fatal_configuration "cannot build a shared library"
+	build_old_libs=no
+	continue
+	;;
+
+      -static)
+	build_libtool_libs=no
+	build_old_libs=yes
+	continue
+	;;
+
+      -prefer-pic)
+	pic_mode=yes
+	continue
+	;;
+
+      -prefer-non-pic)
+	pic_mode=no
+	continue
+	;;
+      esac
+    done
+
+    func_quote_arg pretty "$libobj"
+    test "X$libobj" != "X$func_quote_arg_result" \
+      && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"'	 &()|`$[]' \
+      && func_warning "libobj name '$libobj' may not contain shell special characters."
+    func_dirname_and_basename "$obj" "/" ""
+    objname=$func_basename_result
+    xdir=$func_dirname_result
+    lobj=$xdir$objdir/$objname
+
+    test -z "$base_compile" && \
+      func_fatal_help "you must specify a compilation command"
+
+    # Delete any leftover library objects.
+    if test yes = "$build_old_libs"; then
+      removelist="$obj $lobj $libobj ${libobj}T"
+    else
+      removelist="$lobj $libobj ${libobj}T"
+    fi
+
+    # On Cygwin there's no "real" PIC flag so we must build both object types
+    case $host_os in
+    cygwin* | mingw* | pw32* | os2* | cegcc*)
+      pic_mode=default
+      ;;
+    esac
+    if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then
+      # non-PIC code in shared libraries is not supported
+      pic_mode=default
+    fi
+
+    # Calculate the filename of the output object if compiler does
+    # not support -o with -c
+    if test no = "$compiler_c_o"; then
+      output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext
+      lockfile=$output_obj.lock
+    else
+      output_obj=
+      need_locks=no
+      lockfile=
+    fi
+
+    # Lock this critical section if it is needed
+    # We use this script file to make the link, it avoids creating a new file
+    if test yes = "$need_locks"; then
+      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+	func_echo "Waiting for $lockfile to be removed"
+	sleep 2
+      done
+    elif test warn = "$need_locks"; then
+      if test -f "$lockfile"; then
+	$ECHO "\
+*** ERROR, $lockfile exists and contains:
+`cat $lockfile 2>/dev/null`
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$opt_dry_run || $RM $removelist
+	exit $EXIT_FAILURE
+      fi
+      func_append removelist " $output_obj"
+      $ECHO "$srcfile" > "$lockfile"
+    fi
+
+    $opt_dry_run || $RM $removelist
+    func_append removelist " $lockfile"
+    trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15
+
+    func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
+    srcfile=$func_to_tool_file_result
+    func_quote_arg pretty "$srcfile"
+    qsrcfile=$func_quote_arg_result
+
+    # Only build a PIC object if we are building libtool libraries.
+    if test yes = "$build_libtool_libs"; then
+      # Without this assignment, base_compile gets emptied.
+      fbsd_hideous_sh_bug=$base_compile
+
+      if test no != "$pic_mode"; then
+	command="$base_compile $qsrcfile $pic_flag"
+      else
+	# Don't build PIC code
+	command="$base_compile $qsrcfile"
+      fi
+
+      func_mkdir_p "$xdir$objdir"
+
+      if test -z "$output_obj"; then
+	# Place PIC objects in $objdir
+	func_append command " -o $lobj"
+      fi
+
+      func_show_eval_locale "$command"	\
+          'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
+
+      if test warn = "$need_locks" &&
+	 test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+	$ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$opt_dry_run || $RM $removelist
+	exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed, then go on to compile the next one
+      if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then
+	func_show_eval '$MV "$output_obj" "$lobj"' \
+	  'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+      fi
+
+      # Allow error messages only from the first compilation.
+      if test yes = "$suppress_opt"; then
+	suppress_output=' >/dev/null 2>&1'
+      fi
+    fi
+
+    # Only build a position-dependent object if we build old libraries.
+    if test yes = "$build_old_libs"; then
+      if test yes != "$pic_mode"; then
+	# Don't build PIC code
+	command="$base_compile $qsrcfile$pie_flag"
+      else
+	command="$base_compile $qsrcfile $pic_flag"
+      fi
+      if test yes = "$compiler_c_o"; then
+	func_append command " -o $obj"
+      fi
+
+      # Suppress compiler output if we already did a PIC compilation.
+      func_append command "$suppress_output"
+      func_show_eval_locale "$command" \
+        '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
+
+      if test warn = "$need_locks" &&
+	 test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
+	$ECHO "\
+*** ERROR, $lockfile contains:
+`cat $lockfile 2>/dev/null`
+
+but it should contain:
+$srcfile
+
+This indicates that another process is trying to use the same
+temporary object file, and libtool could not work around it because
+your compiler does not support '-c' and '-o' together.  If you
+repeat this compilation, it may succeed, by chance, but you had better
+avoid parallel builds (make -j) in this platform, or get a better
+compiler."
+
+	$opt_dry_run || $RM $removelist
+	exit $EXIT_FAILURE
+      fi
+
+      # Just move the object if needed
+      if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then
+	func_show_eval '$MV "$output_obj" "$obj"' \
+	  'error=$?; $opt_dry_run || $RM $removelist; exit $error'
+      fi
+    fi
+
+    $opt_dry_run || {
+      func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
+
+      # Unlock the critical section if it was locked
+      if test no != "$need_locks"; then
+	removelist=$lockfile
+        $RM "$lockfile"
+      fi
+    }
+
+    exit $EXIT_SUCCESS
+}
+
+$opt_help || {
+  test compile = "$opt_mode" && func_mode_compile ${1+"$@"}
+}
+
+func_mode_help ()
+{
+    # We need to display help for each of the modes.
+    case $opt_mode in
+      "")
+        # Generic help is extracted from the usage comments
+        # at the start of this file.
+        func_help
+        ;;
+
+      clean)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE...
+
+Remove files from the build directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically '/bin/rm').  RM-OPTIONS are options (such as '-f') to be passed
+to RM.
+
+If FILE is a libtool library, object or program, all the files associated
+with it are deleted. Otherwise, only FILE itself is deleted using RM."
+        ;;
+
+      compile)
+      $ECHO \
+"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE
+
+Compile a source file into a libtool library object.
+
+This mode accepts the following additional options:
+
+  -o OUTPUT-FILE    set the output file name to OUTPUT-FILE
+  -no-suppress      do not suppress compiler output for multiple passes
+  -prefer-pic       try to build PIC objects only
+  -prefer-non-pic   try to build non-PIC objects only
+  -shared           do not build a '.o' file suitable for static linking
+  -static           only build a '.o' file suitable for static linking
+  -Wc,FLAG
+  -Xcompiler FLAG   pass FLAG directly to the compiler
+
+COMPILE-COMMAND is a command to be used in creating a 'standard' object file
+from the given SOURCEFILE.
+
+The output file name is determined by removing the directory component from
+SOURCEFILE, then substituting the C source code suffix '.c' with the
+library object suffix, '.lo'."
+        ;;
+
+      execute)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]...
+
+Automatically set library path, then run a program.
+
+This mode accepts the following additional options:
+
+  -dlopen FILE      add the directory containing FILE to the library path
+
+This mode sets the library path environment variable according to '-dlopen'
+flags.
+
+If any of the ARGS are libtool executable wrappers, then they are translated
+into their corresponding uninstalled binary, and any of their required library
+directories are added to the library path.
+
+Then, COMMAND is executed, with ARGS as arguments."
+        ;;
+
+      finish)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=finish [LIBDIR]...
+
+Complete the installation of libtool libraries.
+
+Each LIBDIR is a directory that contains libtool libraries.
+
+The commands that this mode executes may require superuser privileges.  Use
+the '--dry-run' option if you just want to see what would be executed."
+        ;;
+
+      install)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND...
+
+Install executables or libraries.
+
+INSTALL-COMMAND is the installation command.  The first component should be
+either the 'install' or 'cp' program.
+
+The following components of INSTALL-COMMAND are treated specially:
+
+  -inst-prefix-dir PREFIX-DIR  Use PREFIX-DIR as a staging area for installation
+
+The rest of the components are interpreted as arguments to that command (only
+BSD-compatible install options are recognized)."
+        ;;
+
+      link)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=link LINK-COMMAND...
+
+Link object files or libraries together to form another library, or to
+create an executable program.
+
+LINK-COMMAND is a command using the C compiler that you would use to create
+a program from several object files.
+
+The following components of LINK-COMMAND are treated specially:
+
+  -all-static       do not do any dynamic linking at all
+  -avoid-version    do not add a version suffix if possible
+  -bindir BINDIR    specify path to binaries directory (for systems where
+                    libraries must be found in the PATH setting at runtime)
+  -dlopen FILE      '-dlpreopen' FILE if it cannot be dlopened at runtime
+  -dlpreopen FILE   link in FILE and add its symbols to lt_preloaded_symbols
+  -export-dynamic   allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
+  -export-symbols SYMFILE
+                    try to export only the symbols listed in SYMFILE
+  -export-symbols-regex REGEX
+                    try to export only the symbols matching REGEX
+  -LLIBDIR          search LIBDIR for required installed libraries
+  -lNAME            OUTPUT-FILE requires the installed library libNAME
+  -module           build a library that can dlopened
+  -no-fast-install  disable the fast-install mode
+  -no-install       link a not-installable executable
+  -no-undefined     declare that a library does not refer to external symbols
+  -o OUTPUT-FILE    create OUTPUT-FILE from the specified objects
+  -objectlist FILE  use a list of object files found in FILE to specify objects
+  -os2dllname NAME  force a short DLL name on OS/2 (no effect on other OSes)
+  -precious-files-regex REGEX
+                    don't remove output files matching REGEX
+  -release RELEASE  specify package release information
+  -rpath LIBDIR     the created library will eventually be installed in LIBDIR
+  -R[ ]LIBDIR       add LIBDIR to the runtime path of programs and libraries
+  -shared           only do dynamic linking of libtool libraries
+  -shrext SUFFIX    override the standard shared library file extension
+  -static           do not do any dynamic linking of uninstalled libtool libraries
+  -static-libtool-libs
+                    do not do any dynamic linking of libtool libraries
+  -version-info CURRENT[:REVISION[:AGE]]
+                    specify library version info [each variable defaults to 0]
+  -weak LIBNAME     declare that the target provides the LIBNAME interface
+  -Wc,FLAG
+  -Xcompiler FLAG   pass linker-specific FLAG directly to the compiler
+  -Wa,FLAG
+  -Xassembler FLAG  pass linker-specific FLAG directly to the assembler
+  -Wl,FLAG
+  -Xlinker FLAG     pass linker-specific FLAG directly to the linker
+  -XCClinker FLAG   pass link-specific FLAG to the compiler driver (CC)
+
+All other options (arguments beginning with '-') are ignored.
+
+Every other argument is treated as a filename.  Files ending in '.la' are
+treated as uninstalled libtool libraries, other files are standard or library
+object files.
+
+If the OUTPUT-FILE ends in '.la', then a libtool library is created,
+only library objects ('.lo' files) may be specified, and '-rpath' is
+required, except when creating a convenience library.
+
+If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created
+using 'ar' and 'ranlib', or on Windows using 'lib'.
+
+If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file
+is created, otherwise an executable program is created."
+        ;;
+
+      uninstall)
+        $ECHO \
+"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE...
+
+Remove libraries from an installation directory.
+
+RM is the name of the program to use to delete files associated with each FILE
+(typically '/bin/rm').  RM-OPTIONS are options (such as '-f') to be passed
+to RM.
+
+If FILE is a libtool library, all the files associated with it are deleted.
+Otherwise, only FILE itself is deleted using RM."
+        ;;
+
+      *)
+        func_fatal_help "invalid operation mode '$opt_mode'"
+        ;;
+    esac
+
+    echo
+    $ECHO "Try '$progname --help' for more information about other modes."
+}
+
+# Now that we've collected a possible --mode arg, show help if necessary
+if $opt_help; then
+  if test : = "$opt_help"; then
+    func_mode_help
+  else
+    {
+      func_help noexit
+      for opt_mode in compile link execute install finish uninstall clean; do
+	func_mode_help
+      done
+    } | $SED -n '1p; 2,$s/^Usage:/  or: /p'
+    {
+      func_help noexit
+      for opt_mode in compile link execute install finish uninstall clean; do
+	echo
+	func_mode_help
+      done
+    } |
+    $SED '1d
+      /^When reporting/,/^Report/{
+	H
+	d
+      }
+      $x
+      /information about other modes/d
+      /more detailed .*MODE/d
+      s/^Usage:.*--mode=\([^ ]*\) .*/Description of \1 mode:/'
+  fi
+  exit $?
+fi
+
+
+# func_mode_execute arg...
+func_mode_execute ()
+{
+    $debug_cmd
+
+    # The first argument is the command name.
+    cmd=$nonopt
+    test -z "$cmd" && \
+      func_fatal_help "you must specify a COMMAND"
+
+    # Handle -dlopen flags immediately.
+    for file in $opt_dlopen; do
+      test -f "$file" \
+	|| func_fatal_help "'$file' is not a file"
+
+      dir=
+      case $file in
+      *.la)
+	func_resolve_sysroot "$file"
+	file=$func_resolve_sysroot_result
+
+	# Check to see that this really is a libtool archive.
+	func_lalib_unsafe_p "$file" \
+	  || func_fatal_help "'$lib' is not a valid libtool archive"
+
+	# Read the libtool library.
+	dlname=
+	library_names=
+	func_source "$file"
+
+	# Skip this library if it cannot be dlopened.
+	if test -z "$dlname"; then
+	  # Warn if it was a shared library.
+	  test -n "$library_names" && \
+	    func_warning "'$file' was not linked with '-export-dynamic'"
+	  continue
+	fi
+
+	func_dirname "$file" "" "."
+	dir=$func_dirname_result
+
+	if test -f "$dir/$objdir/$dlname"; then
+	  func_append dir "/$objdir"
+	else
+	  if test ! -f "$dir/$dlname"; then
+	    func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'"
+	  fi
+	fi
+	;;
+
+      *.lo)
+	# Just add the directory containing the .lo file.
+	func_dirname "$file" "" "."
+	dir=$func_dirname_result
+	;;
+
+      *)
+	func_warning "'-dlopen' is ignored for non-libtool libraries and objects"
+	continue
+	;;
+      esac
+
+      # Get the absolute pathname.
+      absdir=`cd "$dir" && pwd`
+      test -n "$absdir" && dir=$absdir
+
+      # Now add the directory to shlibpath_var.
+      if eval "test -z \"\$$shlibpath_var\""; then
+	eval "$shlibpath_var=\"\$dir\""
+      else
+	eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\""
+      fi
+    done
+
+    # This variable tells wrapper scripts just to set shlibpath_var
+    # rather than running their programs.
+    libtool_execute_magic=$magic
+
+    # Check if any of the arguments is a wrapper script.
+    args=
+    for file
+    do
+      case $file in
+      -* | *.la | *.lo ) ;;
+      *)
+	# Do a test to see if this is really a libtool program.
+	if func_ltwrapper_script_p "$file"; then
+	  func_source "$file"
+	  # Transform arg to wrapped name.
+	  file=$progdir/$program
+	elif func_ltwrapper_executable_p "$file"; then
+	  func_ltwrapper_scriptname "$file"
+	  func_source "$func_ltwrapper_scriptname_result"
+	  # Transform arg to wrapped name.
+	  file=$progdir/$program
+	fi
+	;;
+      esac
+      # Quote arguments (to preserve shell metacharacters).
+      func_append_quoted args "$file"
+    done
+
+    if $opt_dry_run; then
+      # Display what would be done.
+      if test -n "$shlibpath_var"; then
+	eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+	echo "export $shlibpath_var"
+      fi
+      $ECHO "$cmd$args"
+      exit $EXIT_SUCCESS
+    else
+      if test -n "$shlibpath_var"; then
+	# Export the shlibpath_var.
+	eval "export $shlibpath_var"
+      fi
+
+      # Restore saved environment variables
+      for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+      do
+	eval "if test \"\${save_$lt_var+set}\" = set; then
+                $lt_var=\$save_$lt_var; export $lt_var
+	      else
+		$lt_unset $lt_var
+	      fi"
+      done
+
+      # Now prepare to actually exec the command.
+      exec_cmd=\$cmd$args
+    fi
+}
+
+test execute = "$opt_mode" && func_mode_execute ${1+"$@"}
+
+
+# func_mode_finish arg...
+func_mode_finish ()
+{
+    $debug_cmd
+
+    libs=
+    libdirs=
+    admincmds=
+
+    for opt in "$nonopt" ${1+"$@"}
+    do
+      if test -d "$opt"; then
+	func_append libdirs " $opt"
+
+      elif test -f "$opt"; then
+	if func_lalib_unsafe_p "$opt"; then
+	  func_append libs " $opt"
+	else
+	  func_warning "'$opt' is not a valid libtool archive"
+	fi
+
+      else
+	func_fatal_error "invalid argument '$opt'"
+      fi
+    done
+
+    if test -n "$libs"; then
+      if test -n "$lt_sysroot"; then
+        sysroot_regex=`$ECHO "$lt_sysroot" | $SED "$sed_make_literal_regex"`
+        sysroot_cmd="s/\([ ']\)$sysroot_regex/\1/g;"
+      else
+        sysroot_cmd=
+      fi
+
+      # Remove sysroot references
+      if $opt_dry_run; then
+        for lib in $libs; do
+          echo "removing references to $lt_sysroot and '=' prefixes from $lib"
+        done
+      else
+        tmpdir=`func_mktempdir`
+        for lib in $libs; do
+	  $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
+	    > $tmpdir/tmp-la
+	  mv -f $tmpdir/tmp-la $lib
+	done
+        ${RM}r "$tmpdir"
+      fi
+    fi
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      for libdir in $libdirs; do
+	if test -n "$finish_cmds"; then
+	  # Do each command in the finish commands.
+	  func_execute_cmds "$finish_cmds" 'admincmds="$admincmds
+'"$cmd"'"'
+	fi
+	if test -n "$finish_eval"; then
+	  # Do the single finish_eval.
+	  eval cmds=\"$finish_eval\"
+	  $opt_dry_run || eval "$cmds" || func_append admincmds "
+       $cmds"
+	fi
+      done
+    fi
+
+    # Exit here if they wanted silent mode.
+    $opt_quiet && exit $EXIT_SUCCESS
+
+    if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+      echo "----------------------------------------------------------------------"
+      echo "Libraries have been installed in:"
+      for libdir in $libdirs; do
+	$ECHO "   $libdir"
+      done
+      echo
+      echo "If you ever happen to want to link against installed libraries"
+      echo "in a given directory, LIBDIR, you must either use libtool, and"
+      echo "specify the full pathname of the library, or use the '-LLIBDIR'"
+      echo "flag during linking and do at least one of the following:"
+      if test -n "$shlibpath_var"; then
+	echo "   - add LIBDIR to the '$shlibpath_var' environment variable"
+	echo "     during execution"
+      fi
+      if test -n "$runpath_var"; then
+	echo "   - add LIBDIR to the '$runpath_var' environment variable"
+	echo "     during linking"
+      fi
+      if test -n "$hardcode_libdir_flag_spec"; then
+	libdir=LIBDIR
+	eval flag=\"$hardcode_libdir_flag_spec\"
+
+	$ECHO "   - use the '$flag' linker flag"
+      fi
+      if test -n "$admincmds"; then
+	$ECHO "   - have your system administrator run these commands:$admincmds"
+      fi
+      if test -f /etc/ld.so.conf; then
+	echo "   - have your system administrator add LIBDIR to '/etc/ld.so.conf'"
+      fi
+      echo
+
+      echo "See any operating system documentation about shared libraries for"
+      case $host in
+	solaris2.[6789]|solaris2.1[0-9])
+	  echo "more information, such as the ld(1), crle(1) and ld.so(8) manual"
+	  echo "pages."
+	  ;;
+	*)
+	  echo "more information, such as the ld(1) and ld.so(8) manual pages."
+	  ;;
+      esac
+      echo "----------------------------------------------------------------------"
+    fi
+    exit $EXIT_SUCCESS
+}
+
+test finish = "$opt_mode" && func_mode_finish ${1+"$@"}
+
+
+# func_mode_install arg...
+func_mode_install ()
+{
+    $debug_cmd
+
+    # There may be an optional sh(1) argument at the beginning of
+    # install_prog (especially on Windows NT).
+    if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" ||
+       # Allow the use of GNU shtool's install command.
+       case $nonopt in *shtool*) :;; *) false;; esac
+    then
+      # Aesthetically quote it.
+      func_quote_arg pretty "$nonopt"
+      install_prog="$func_quote_arg_result "
+      arg=$1
+      shift
+    else
+      install_prog=
+      arg=$nonopt
+    fi
+
+    # The real first argument should be the name of the installation program.
+    # Aesthetically quote it.
+    func_quote_arg pretty "$arg"
+    func_append install_prog "$func_quote_arg_result"
+    install_shared_prog=$install_prog
+    case " $install_prog " in
+      *[\\\ /]cp\ *) install_cp=: ;;
+      *) install_cp=false ;;
+    esac
+
+    # We need to accept at least all the BSD install flags.
+    dest=
+    files=
+    opts=
+    prev=
+    install_type=
+    isdir=false
+    stripme=
+    no_mode=:
+    for arg
+    do
+      arg2=
+      if test -n "$dest"; then
+	func_append files " $dest"
+	dest=$arg
+	continue
+      fi
+
+      case $arg in
+      -d) isdir=: ;;
+      -f)
+	if $install_cp; then :; else
+	  prev=$arg
+	fi
+	;;
+      -g | -m | -o)
+	prev=$arg
+	;;
+      -s)
+	stripme=" -s"
+	continue
+	;;
+      -*)
+	;;
+      *)
+	# If the previous option needed an argument, then skip it.
+	if test -n "$prev"; then
+	  if test X-m = "X$prev" && test -n "$install_override_mode"; then
+	    arg2=$install_override_mode
+	    no_mode=false
+	  fi
+	  prev=
+	else
+	  dest=$arg
+	  continue
+	fi
+	;;
+      esac
+
+      # Aesthetically quote the argument.
+      func_quote_arg pretty "$arg"
+      func_append install_prog " $func_quote_arg_result"
+      if test -n "$arg2"; then
+	func_quote_arg pretty "$arg2"
+      fi
+      func_append install_shared_prog " $func_quote_arg_result"
+    done
+
+    test -z "$install_prog" && \
+      func_fatal_help "you must specify an install program"
+
+    test -n "$prev" && \
+      func_fatal_help "the '$prev' option requires an argument"
+
+    if test -n "$install_override_mode" && $no_mode; then
+      if $install_cp; then :; else
+	func_quote_arg pretty "$install_override_mode"
+	func_append install_shared_prog " -m $func_quote_arg_result"
+      fi
+    fi
+
+    if test -z "$files"; then
+      if test -z "$dest"; then
+	func_fatal_help "no file or destination specified"
+      else
+	func_fatal_help "you must specify a destination"
+      fi
+    fi
+
+    # Strip any trailing slash from the destination.
+    func_stripname '' '/' "$dest"
+    dest=$func_stripname_result
+
+    # Check to see that the destination is a directory.
+    test -d "$dest" && isdir=:
+    if $isdir; then
+      destdir=$dest
+      destname=
+    else
+      func_dirname_and_basename "$dest" "" "."
+      destdir=$func_dirname_result
+      destname=$func_basename_result
+
+      # Not a directory, so check to see that there is only one file specified.
+      set dummy $files; shift
+      test "$#" -gt 1 && \
+	func_fatal_help "'$dest' is not a directory"
+    fi
+    case $destdir in
+    [\\/]* | [A-Za-z]:[\\/]*) ;;
+    *)
+      for file in $files; do
+	case $file in
+	*.lo) ;;
+	*)
+	  func_fatal_help "'$destdir' must be an absolute directory name"
+	  ;;
+	esac
+      done
+      ;;
+    esac
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic=$magic
+
+    staticlibs=
+    future_libdirs=
+    current_libdirs=
+    for file in $files; do
+
+      # Do each installation.
+      case $file in
+      *.$libext)
+	# Do the static libraries later.
+	func_append staticlibs " $file"
+	;;
+
+      *.la)
+	func_resolve_sysroot "$file"
+	file=$func_resolve_sysroot_result
+
+	# Check to see that this really is a libtool archive.
+	func_lalib_unsafe_p "$file" \
+	  || func_fatal_help "'$file' is not a valid libtool archive"
+
+	library_names=
+	old_library=
+	relink_command=
+	func_source "$file"
+
+	# Add the libdir to current_libdirs if it is the destination.
+	if test "X$destdir" = "X$libdir"; then
+	  case "$current_libdirs " in
+	  *" $libdir "*) ;;
+	  *) func_append current_libdirs " $libdir" ;;
+	  esac
+	else
+	  # Note the libdir as a future libdir.
+	  case "$future_libdirs " in
+	  *" $libdir "*) ;;
+	  *) func_append future_libdirs " $libdir" ;;
+	  esac
+	fi
+
+	func_dirname "$file" "/" ""
+	dir=$func_dirname_result
+	func_append dir "$objdir"
+
+	if test -n "$relink_command"; then
+	  # Determine the prefix the user has applied to our future dir.
+	  inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"`
+
+	  # Don't allow the user to place us outside of our expected
+	  # location b/c this prevents finding dependent libraries that
+	  # are installed to the same prefix.
+	  # At present, this check doesn't affect windows .dll's that
+	  # are installed into $libdir/../bin (currently, that works fine)
+	  # but it's something to keep an eye on.
+	  test "$inst_prefix_dir" = "$destdir" && \
+	    func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir"
+
+	  if test -n "$inst_prefix_dir"; then
+	    # Stick the inst_prefix_dir data into the link command.
+	    relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"`
+	  else
+	    relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"`
+	  fi
+
+	  func_warning "relinking '$file'"
+	  func_show_eval "$relink_command" \
+	    'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"'
+	fi
+
+	# See the names of the shared library.
+	set dummy $library_names; shift
+	if test -n "$1"; then
+	  realname=$1
+	  shift
+
+	  srcname=$realname
+	  test -n "$relink_command" && srcname=${realname}T
+
+	  # Install the shared library and build the symlinks.
+	  func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \
+	      'exit $?'
+	  tstripme=$stripme
+	  case $host_os in
+	  cygwin* | mingw* | pw32* | cegcc*)
+	    case $realname in
+	    *.dll.a)
+	      tstripme=
+	      ;;
+	    esac
+	    ;;
+	  os2*)
+	    case $realname in
+	    *_dll.a)
+	      tstripme=
+	      ;;
+	    esac
+	    ;;
+	  esac
+	  if test -n "$tstripme" && test -n "$striplib"; then
+	    func_show_eval "$striplib $destdir/$realname" 'exit $?'
+	  fi
+
+	  if test "$#" -gt 0; then
+	    # Delete the old symlinks, and create new ones.
+	    # Try 'ln -sf' first, because the 'ln' binary might depend on
+	    # the symlink we replace!  Solaris /bin/ln does not understand -f,
+	    # so we also need to try rm && ln -s.
+	    for linkname
+	    do
+	      test "$linkname" != "$realname" \
+		&& func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"
+	    done
+	  fi
+
+	  # Do each command in the postinstall commands.
+	  lib=$destdir/$realname
+	  func_execute_cmds "$postinstall_cmds" 'exit $?'
+	fi
+
+	# Install the pseudo-library for information purposes.
+	func_basename "$file"
+	name=$func_basename_result
+	instname=$dir/${name}i
+	func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
+
+	# Maybe install the static library, too.
+	test -n "$old_library" && func_append staticlibs " $dir/$old_library"
+	;;
+
+      *.lo)
+	# Install (i.e. copy) a libtool object.
+
+	# Figure out destination file name, if it wasn't already specified.
+	if test -n "$destname"; then
+	  destfile=$destdir/$destname
+	else
+	  func_basename "$file"
+	  destfile=$func_basename_result
+	  destfile=$destdir/$destfile
+	fi
+
+	# Deduce the name of the destination old-style object file.
+	case $destfile in
+	*.lo)
+	  func_lo2o "$destfile"
+	  staticdest=$func_lo2o_result
+	  ;;
+	*.$objext)
+	  staticdest=$destfile
+	  destfile=
+	  ;;
+	*)
+	  func_fatal_help "cannot copy a libtool object to '$destfile'"
+	  ;;
+	esac
+
+	# Install the libtool object if requested.
+	test -n "$destfile" && \
+	  func_show_eval "$install_prog $file $destfile" 'exit $?'
+
+	# Install the old object if enabled.
+	if test yes = "$build_old_libs"; then
+	  # Deduce the name of the old-style object file.
+	  func_lo2o "$file"
+	  staticobj=$func_lo2o_result
+	  func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?'
+	fi
+	exit $EXIT_SUCCESS
+	;;
+
+      *)
+	# Figure out destination file name, if it wasn't already specified.
+	if test -n "$destname"; then
+	  destfile=$destdir/$destname
+	else
+	  func_basename "$file"
+	  destfile=$func_basename_result
+	  destfile=$destdir/$destfile
+	fi
+
+	# If the file is missing, and there is a .exe on the end, strip it
+	# because it is most likely a libtool script we actually want to
+	# install
+	stripped_ext=
+	case $file in
+	  *.exe)
+	    if test ! -f "$file"; then
+	      func_stripname '' '.exe' "$file"
+	      file=$func_stripname_result
+	      stripped_ext=.exe
+	    fi
+	    ;;
+	esac
+
+	# Do a test to see if this is really a libtool program.
+	case $host in
+	*cygwin* | *mingw*)
+	    if func_ltwrapper_executable_p "$file"; then
+	      func_ltwrapper_scriptname "$file"
+	      wrapper=$func_ltwrapper_scriptname_result
+	    else
+	      func_stripname '' '.exe' "$file"
+	      wrapper=$func_stripname_result
+	    fi
+	    ;;
+	*)
+	    wrapper=$file
+	    ;;
+	esac
+	if func_ltwrapper_script_p "$wrapper"; then
+	  notinst_deplibs=
+	  relink_command=
+
+	  func_source "$wrapper"
+
+	  # Check the variables that should have been set.
+	  test -z "$generated_by_libtool_version" && \
+	    func_fatal_error "invalid libtool wrapper script '$wrapper'"
+
+	  finalize=:
+	  for lib in $notinst_deplibs; do
+	    # Check to see that each library is installed.
+	    libdir=
+	    if test -f "$lib"; then
+	      func_source "$lib"
+	    fi
+	    libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'`
+	    if test -n "$libdir" && test ! -f "$libfile"; then
+	      func_warning "'$lib' has not been installed in '$libdir'"
+	      finalize=false
+	    fi
+	  done
+
+	  relink_command=
+	  func_source "$wrapper"
+
+	  outputname=
+	  if test no = "$fast_install" && test -n "$relink_command"; then
+	    $opt_dry_run || {
+	      if $finalize; then
+	        tmpdir=`func_mktempdir`
+		func_basename "$file$stripped_ext"
+		file=$func_basename_result
+	        outputname=$tmpdir/$file
+	        # Replace the output file specification.
+	        relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
+
+	        $opt_quiet || {
+	          func_quote_arg expand,pretty "$relink_command"
+		  eval "func_echo $func_quote_arg_result"
+	        }
+	        if eval "$relink_command"; then :
+	          else
+		  func_error "error: relink '$file' with the above command before installing it"
+		  $opt_dry_run || ${RM}r "$tmpdir"
+		  continue
+	        fi
+	        file=$outputname
+	      else
+	        func_warning "cannot relink '$file'"
+	      fi
+	    }
+	  else
+	    # Install the binary that we compiled earlier.
+	    file=`$ECHO "$file$stripped_ext" | $SED "s%\([^/]*\)$%$objdir/\1%"`
+	  fi
+	fi
+
+	# remove .exe since cygwin /usr/bin/install will append another
+	# one anyway
+	case $install_prog,$host in
+	*/usr/bin/install*,*cygwin*)
+	  case $file:$destfile in
+	  *.exe:*.exe)
+	    # this is ok
+	    ;;
+	  *.exe:*)
+	    destfile=$destfile.exe
+	    ;;
+	  *:*.exe)
+	    func_stripname '' '.exe' "$destfile"
+	    destfile=$func_stripname_result
+	    ;;
+	  esac
+	  ;;
+	esac
+	func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?'
+	$opt_dry_run || if test -n "$outputname"; then
+	  ${RM}r "$tmpdir"
+	fi
+	;;
+      esac
+    done
+
+    for file in $staticlibs; do
+      func_basename "$file"
+      name=$func_basename_result
+
+      # Set up the ranlib parameters.
+      oldlib=$destdir/$name
+      func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+      tool_oldlib=$func_to_tool_file_result
+
+      func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
+
+      if test -n "$stripme" && test -n "$old_striplib"; then
+	func_show_eval "$old_striplib $tool_oldlib" 'exit $?'
+      fi
+
+      # Do each command in the postinstall commands.
+      func_execute_cmds "$old_postinstall_cmds" 'exit $?'
+    done
+
+    test -n "$future_libdirs" && \
+      func_warning "remember to run '$progname --finish$future_libdirs'"
+
+    if test -n "$current_libdirs"; then
+      # Maybe just do a dry run.
+      $opt_dry_run && current_libdirs=" -n$current_libdirs"
+      exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs'
+    else
+      exit $EXIT_SUCCESS
+    fi
+}
+
+test install = "$opt_mode" && func_mode_install ${1+"$@"}
+
+
+# func_generate_dlsyms outputname originator pic_p
+# Extract symbols from dlprefiles and create ${outputname}S.o with
+# a dlpreopen symbol table.
+func_generate_dlsyms ()
+{
+    $debug_cmd
+
+    my_outputname=$1
+    my_originator=$2
+    my_pic_p=${3-false}
+    my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'`
+    my_dlsyms=
+
+    if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+      if test -n "$NM" && test -n "$global_symbol_pipe"; then
+	my_dlsyms=${my_outputname}S.c
+      else
+	func_error "not configured to extract global symbols from dlpreopened files"
+      fi
+    fi
+
+    if test -n "$my_dlsyms"; then
+      case $my_dlsyms in
+      "") ;;
+      *.c)
+	# Discover the nlist of each of the dlfiles.
+	nlist=$output_objdir/$my_outputname.nm
+
+	func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
+
+	# Parse the name list into a source file.
+	func_verbose "creating $output_objdir/$my_dlsyms"
+
+	$opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
+/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */
+
+#ifdef __cplusplus
+extern \"C\" {
+#endif
+
+#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
+#endif
+
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data.  */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
+
+/* External symbol declarations for the compiler. */\
+"
+
+	if test yes = "$dlself"; then
+	  func_verbose "generating symbol list for '$output'"
+
+	  $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
+
+	  # Add our own program objects to the symbol list.
+	  progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+	  for progfile in $progfiles; do
+	    func_to_tool_file "$progfile" func_convert_file_msys_to_w32
+	    func_verbose "extracting global C symbols from '$func_to_tool_file_result'"
+	    $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'"
+	  done
+
+	  if test -n "$exclude_expsyms"; then
+	    $opt_dry_run || {
+	      eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T'
+	      eval '$MV "$nlist"T "$nlist"'
+	    }
+	  fi
+
+	  if test -n "$export_symbols_regex"; then
+	    $opt_dry_run || {
+	      eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T'
+	      eval '$MV "$nlist"T "$nlist"'
+	    }
+	  fi
+
+	  # Prepare the list of exported symbols
+	  if test -z "$export_symbols"; then
+	    export_symbols=$output_objdir/$outputname.exp
+	    $opt_dry_run || {
+	      $RM $export_symbols
+	      eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+	      case $host in
+	      *cygwin* | *mingw* | *cegcc* )
+                eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+                eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
+	        ;;
+	      esac
+	    }
+	  else
+	    $opt_dry_run || {
+	      eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+	      eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
+	      eval '$MV "$nlist"T "$nlist"'
+	      case $host in
+	        *cygwin* | *mingw* | *cegcc* )
+	          eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
+	          eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
+	          ;;
+	      esac
+	    }
+	  fi
+	fi
+
+	for dlprefile in $dlprefiles; do
+	  func_verbose "extracting global C symbols from '$dlprefile'"
+	  func_basename "$dlprefile"
+	  name=$func_basename_result
+          case $host in
+	    *cygwin* | *mingw* | *cegcc* )
+	      # if an import library, we need to obtain dlname
+	      if func_win32_import_lib_p "$dlprefile"; then
+	        func_tr_sh "$dlprefile"
+	        eval "curr_lafile=\$libfile_$func_tr_sh_result"
+	        dlprefile_dlbasename=
+	        if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then
+	          # Use subshell, to avoid clobbering current variable values
+	          dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"`
+	          if test -n "$dlprefile_dlname"; then
+	            func_basename "$dlprefile_dlname"
+	            dlprefile_dlbasename=$func_basename_result
+	          else
+	            # no lafile. user explicitly requested -dlpreopen <import library>.
+	            $sharedlib_from_linklib_cmd "$dlprefile"
+	            dlprefile_dlbasename=$sharedlib_from_linklib_result
+	          fi
+	        fi
+	        $opt_dry_run || {
+	          if test -n "$dlprefile_dlbasename"; then
+	            eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"'
+	          else
+	            func_warning "Could not compute DLL name from $name"
+	            eval '$ECHO ": $name " >> "$nlist"'
+	          fi
+	          func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+	          eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
+	            $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
+	        }
+	      else # not an import lib
+	        $opt_dry_run || {
+	          eval '$ECHO ": $name " >> "$nlist"'
+	          func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+	          eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+	        }
+	      fi
+	    ;;
+	    *)
+	      $opt_dry_run || {
+	        eval '$ECHO ": $name " >> "$nlist"'
+	        func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
+	        eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe >> '$nlist'"
+	      }
+	    ;;
+          esac
+	done
+
+	$opt_dry_run || {
+	  # Make sure we have at least an empty file.
+	  test -f "$nlist" || : > "$nlist"
+
+	  if test -n "$exclude_expsyms"; then
+	    $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T
+	    $MV "$nlist"T "$nlist"
+	  fi
+
+	  # Try sorting and uniquifying the output.
+	  if $GREP -v "^: " < "$nlist" |
+	      if sort -k 3 </dev/null >/dev/null 2>&1; then
+		sort -k 3
+	      else
+		sort +2
+	      fi |
+	      uniq > "$nlist"S; then
+	    :
+	  else
+	    $GREP -v "^: " < "$nlist" > "$nlist"S
+	  fi
+
+	  if test -f "$nlist"S; then
+	    eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"'
+	  else
+	    echo '/* NONE */' >> "$output_objdir/$my_dlsyms"
+	  fi
+
+	  func_show_eval '$RM "${nlist}I"'
+	  if test -n "$global_symbol_to_import"; then
+	    eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I'
+	  fi
+
+	  echo >> "$output_objdir/$my_dlsyms" "\
+
+/* The mapping between symbol names and symbols.  */
+typedef struct {
+  const char *name;
+  void *address;
+} lt_dlsymlist;
+extern LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[];\
+"
+
+	  if test -s "$nlist"I; then
+	    echo >> "$output_objdir/$my_dlsyms" "\
+static void lt_syminit(void)
+{
+  LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols;
+  for (; symbol->name; ++symbol)
+    {"
+	    $SED 's/.*/      if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms"
+	    echo >> "$output_objdir/$my_dlsyms" "\
+    }
+}"
+	  fi
+	  echo >> "$output_objdir/$my_dlsyms" "\
+LT_DLSYM_CONST lt_dlsymlist
+lt_${my_prefix}_LTX_preloaded_symbols[] =
+{ {\"$my_originator\", (void *) 0},"
+
+	  if test -s "$nlist"I; then
+	    echo >> "$output_objdir/$my_dlsyms" "\
+  {\"@INIT@\", (void *) &lt_syminit},"
+	  fi
+
+	  case $need_lib_prefix in
+	  no)
+	    eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms"
+	    ;;
+	  *)
+	    eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms"
+	    ;;
+	  esac
+	  echo >> "$output_objdir/$my_dlsyms" "\
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt_${my_prefix}_LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif\
+"
+	} # !$opt_dry_run
+
+	pic_flag_for_symtable=
+	case "$compile_command " in
+	*" -static "*) ;;
+	*)
+	  case $host in
+	  # compiling the symbol table file with pic_flag works around
+	  # a FreeBSD bug that causes programs to crash when -lm is
+	  # linked before any other PIC object.  But we must not use
+	  # pic_flag when linking with -static.  The problem exists in
+	  # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
+	  *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+	    pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
+	  *-*-hpux*)
+	    pic_flag_for_symtable=" $pic_flag"  ;;
+	  *)
+	    $my_pic_p && pic_flag_for_symtable=" $pic_flag"
+	    ;;
+	  esac
+	  ;;
+	esac
+	symtab_cflags=
+	for arg in $LTCFLAGS; do
+	  case $arg in
+	  -pie | -fpie | -fPIE) ;;
+	  *) func_append symtab_cflags " $arg" ;;
+	  esac
+	done
+
+	# Now compile the dynamic symbol file.
+	func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
+
+	# Clean up the generated files.
+	func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"'
+
+	# Transform the symbol file into the correct name.
+	symfileobj=$output_objdir/${my_outputname}S.$objext
+	case $host in
+	*cygwin* | *mingw* | *cegcc* )
+	  if test -f "$output_objdir/$my_outputname.def"; then
+	    compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+	    finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
+	  else
+	    compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	    finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	  fi
+	  ;;
+	*)
+	  compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	  finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$symfileobj%"`
+	  ;;
+	esac
+	;;
+      *)
+	func_fatal_error "unknown suffix for '$my_dlsyms'"
+	;;
+      esac
+    else
+      # We keep going just in case the user didn't refer to
+      # lt_preloaded_symbols.  The linker will fail if global_symbol_pipe
+      # really was required.
+
+      # Nullify the symbol file.
+      compile_command=`$ECHO "$compile_command" | $SED "s% @SYMFILE@%%"`
+      finalize_command=`$ECHO "$finalize_command" | $SED "s% @SYMFILE@%%"`
+    fi
+}
+
+# func_cygming_gnu_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is a GNU/binutils-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_gnu_implib_p ()
+{
+  $debug_cmd
+
+  func_to_tool_file "$1" func_convert_file_msys_to_w32
+  func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
+  test -n "$func_cygming_gnu_implib_tmp"
+}
+
+# func_cygming_ms_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is an MS-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_ms_implib_p ()
+{
+  $debug_cmd
+
+  func_to_tool_file "$1" func_convert_file_msys_to_w32
+  func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
+  test -n "$func_cygming_ms_implib_tmp"
+}
+
+# func_win32_libid arg
+# return the library type of file 'arg'
+#
+# Need a lot of goo to handle *both* DLLs and import libs
+# Has to be a shell function in order to 'eat' the argument
+# that is supplied when $file_magic_command is called.
+# Despite the name, also deal with 64 bit binaries.
+func_win32_libid ()
+{
+  $debug_cmd
+
+  win32_libid_type=unknown
+  win32_fileres=`file -L $1 2>/dev/null`
+  case $win32_fileres in
+  *ar\ archive\ import\ library*) # definitely import
+    win32_libid_type="x86 archive import"
+    ;;
+  *ar\ archive*) # could be an import, or static
+    # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
+    if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
+       $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
+      case $nm_interface in
+      "MS dumpbin")
+	if func_cygming_ms_implib_p "$1" ||
+	   func_cygming_gnu_implib_p "$1"
+	then
+	  win32_nmres=import
+	else
+	  win32_nmres=
+	fi
+	;;
+      *)
+	func_to_tool_file "$1" func_convert_file_msys_to_w32
+	win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
+	  $SED -n -e '
+	    1,100{
+		/ I /{
+		    s|.*|import|
+		    p
+		    q
+		}
+	    }'`
+	;;
+      esac
+      case $win32_nmres in
+      import*)  win32_libid_type="x86 archive import";;
+      *)        win32_libid_type="x86 archive static";;
+      esac
+    fi
+    ;;
+  *DLL*)
+    win32_libid_type="x86 DLL"
+    ;;
+  *executable*) # but shell scripts are "executable" too...
+    case $win32_fileres in
+    *MS\ Windows\ PE\ Intel*)
+      win32_libid_type="x86 DLL"
+      ;;
+    esac
+    ;;
+  esac
+  $ECHO "$win32_libid_type"
+}
+
+# func_cygming_dll_for_implib ARG
+#
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+# Invoked by eval'ing the libtool variable
+#    $sharedlib_from_linklib_cmd
+# Result is available in the variable
+#    $sharedlib_from_linklib_result
+func_cygming_dll_for_implib ()
+{
+  $debug_cmd
+
+  sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"`
+}
+
+# func_cygming_dll_for_implib_fallback_core SECTION_NAME LIBNAMEs
+#
+# The is the core of a fallback implementation of a
+# platform-specific function to extract the name of the
+# DLL associated with the specified import library LIBNAME.
+#
+# SECTION_NAME is either .idata$6 or .idata$7, depending
+# on the platform and compiler that created the implib.
+#
+# Echos the name of the DLL associated with the
+# specified import library.
+func_cygming_dll_for_implib_fallback_core ()
+{
+  $debug_cmd
+
+  match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"`
+  $OBJDUMP -s --section "$1" "$2" 2>/dev/null |
+    $SED '/^Contents of section '"$match_literal"':/{
+      # Place marker at beginning of archive member dllname section
+      s/.*/====MARK====/
+      p
+      d
+    }
+    # These lines can sometimes be longer than 43 characters, but
+    # are always uninteresting
+    /:[	 ]*file format pe[i]\{,1\}-/d
+    /^In archive [^:]*:/d
+    # Ensure marker is printed
+    /^====MARK====/p
+    # Remove all lines with less than 43 characters
+    /^.\{43\}/!d
+    # From remaining lines, remove first 43 characters
+    s/^.\{43\}//' |
+    $SED -n '
+      # Join marker and all lines until next marker into a single line
+      /^====MARK====/ b para
+      H
+      $ b para
+      b
+      :para
+      x
+      s/\n//g
+      # Remove the marker
+      s/^====MARK====//
+      # Remove trailing dots and whitespace
+      s/[\. \t]*$//
+      # Print
+      /./p' |
+    # we now have a list, one entry per line, of the stringified
+    # contents of the appropriate section of all members of the
+    # archive that possess that section. Heuristic: eliminate
+    # all those that have a first or second character that is
+    # a '.' (that is, objdump's representation of an unprintable
+    # character.) This should work for all archives with less than
+    # 0x302f exports -- but will fail for DLLs whose name actually
+    # begins with a literal '.' or a single character followed by
+    # a '.'.
+    #
+    # Of those that remain, print the first one.
+    $SED -e '/^\./d;/^.\./d;q'
+}
+
+# func_cygming_dll_for_implib_fallback ARG
+# Platform-specific function to extract the
+# name of the DLL associated with the specified
+# import library ARG.
+#
+# This fallback implementation is for use when $DLLTOOL
+# does not support the --identify-strict option.
+# Invoked by eval'ing the libtool variable
+#    $sharedlib_from_linklib_cmd
+# Result is available in the variable
+#    $sharedlib_from_linklib_result
+func_cygming_dll_for_implib_fallback ()
+{
+  $debug_cmd
+
+  if func_cygming_gnu_implib_p "$1"; then
+    # binutils import library
+    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"`
+  elif func_cygming_ms_implib_p "$1"; then
+    # ms-generated import library
+    sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"`
+  else
+    # unknown
+    sharedlib_from_linklib_result=
+  fi
+}
+
+
+# func_extract_an_archive dir oldlib
+func_extract_an_archive ()
+{
+    $debug_cmd
+
+    f_ex_an_ar_dir=$1; shift
+    f_ex_an_ar_oldlib=$1
+    if test yes = "$lock_old_archive_extraction"; then
+      lockfile=$f_ex_an_ar_oldlib.lock
+      until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
+	func_echo "Waiting for $lockfile to be removed"
+	sleep 2
+      done
+    fi
+    func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \
+		   'stat=$?; rm -f "$lockfile"; exit $stat'
+    if test yes = "$lock_old_archive_extraction"; then
+      $opt_dry_run || rm -f "$lockfile"
+    fi
+    if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
+     :
+    else
+      func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib"
+    fi
+}
+
+
+# func_extract_archives gentop oldlib ...
+func_extract_archives ()
+{
+    $debug_cmd
+
+    my_gentop=$1; shift
+    my_oldlibs=${1+"$@"}
+    my_oldobjs=
+    my_xlib=
+    my_xabs=
+    my_xdir=
+
+    for my_xlib in $my_oldlibs; do
+      # Extract the objects.
+      case $my_xlib in
+	[\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;;
+	*) my_xabs=`pwd`"/$my_xlib" ;;
+      esac
+      func_basename "$my_xlib"
+      my_xlib=$func_basename_result
+      my_xlib_u=$my_xlib
+      while :; do
+        case " $extracted_archives " in
+	*" $my_xlib_u "*)
+	  func_arith $extracted_serial + 1
+	  extracted_serial=$func_arith_result
+	  my_xlib_u=lt$extracted_serial-$my_xlib ;;
+	*) break ;;
+	esac
+      done
+      extracted_archives="$extracted_archives $my_xlib_u"
+      my_xdir=$my_gentop/$my_xlib_u
+
+      func_mkdir_p "$my_xdir"
+
+      case $host in
+      *-darwin*)
+	func_verbose "Extracting $my_xabs"
+	# Do not bother doing anything if just a dry run
+	$opt_dry_run || {
+	  darwin_orig_dir=`pwd`
+	  cd $my_xdir || exit $?
+	  darwin_archive=$my_xabs
+	  darwin_curdir=`pwd`
+	  func_basename "$darwin_archive"
+	  darwin_base_archive=$func_basename_result
+	  darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
+	  if test -n "$darwin_arches"; then
+	    darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
+	    darwin_arch=
+	    func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
+	    for darwin_arch in  $darwin_arches; do
+	      func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch"
+	      $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive"
+	      cd "unfat-$$/$darwin_base_archive-$darwin_arch"
+	      func_extract_an_archive "`pwd`" "$darwin_base_archive"
+	      cd "$darwin_curdir"
+	      $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive"
+	    done # $darwin_arches
+            ## Okay now we've a bunch of thin objects, gotta fatten them up :)
+	    darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u`
+	    darwin_file=
+	    darwin_files=
+	    for darwin_file in $darwin_filelist; do
+	      darwin_files=`find unfat-$$ -name $darwin_file -print | sort | $NL2SP`
+	      $LIPO -create -output "$darwin_file" $darwin_files
+	    done # $darwin_filelist
+	    $RM -rf unfat-$$
+	    cd "$darwin_orig_dir"
+	  else
+	    cd $darwin_orig_dir
+	    func_extract_an_archive "$my_xdir" "$my_xabs"
+	  fi # $darwin_arches
+	} # !$opt_dry_run
+	;;
+      *)
+        func_extract_an_archive "$my_xdir" "$my_xabs"
+	;;
+      esac
+      my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
+    done
+
+    func_extract_archives_result=$my_oldobjs
+}
+
+
+# func_emit_wrapper [arg=no]
+#
+# Emit a libtool wrapper script on stdout.
+# Don't directly open a file because we may want to
+# incorporate the script contents within a cygwin/mingw
+# wrapper executable.  Must ONLY be called from within
+# func_mode_link because it depends on a number of variables
+# set therein.
+#
+# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
+# variable will take.  If 'yes', then the emitted script
+# will assume that the directory where it is stored is
+# the $objdir directory.  This is a cygwin/mingw-specific
+# behavior.
+func_emit_wrapper ()
+{
+	func_emit_wrapper_arg1=${1-no}
+
+	$ECHO "\
+#! $SHELL
+
+# $output - temporary wrapper script for $objdir/$outputname
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# The $output program cannot be directly executed until all the libtool
+# libraries that it depends on are installed.
+#
+# This wrapper script should never be moved out of the build directory.
+# If it is, it will not operate correctly.
+
+# Sed substitution that helps us do robust quoting.  It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='$sed_quote_subst'
+
+# Be Bourne compatible
+if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac
+fi
+BIN_SH=xpg4; export BIN_SH # for Tru64
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+relink_command=\"$relink_command\"
+
+# This environment variable determines our operation mode.
+if test \"\$libtool_install_magic\" = \"$magic\"; then
+  # install mode needs the following variables:
+  generated_by_libtool_version='$macro_version'
+  notinst_deplibs='$notinst_deplibs'
+else
+  # When we are sourced in execute mode, \$file and \$ECHO are already set.
+  if test \"\$libtool_execute_magic\" != \"$magic\"; then
+    file=\"\$0\""
+
+    func_quote_arg pretty "$ECHO"
+    qECHO=$func_quote_arg_result
+    $ECHO "\
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+    ECHO=$qECHO
+  fi
+
+# Very basic option parsing. These options are (a) specific to
+# the libtool wrapper, (b) are identical between the wrapper
+# /script/ and the wrapper /executable/ that is used only on
+# windows platforms, and (c) all begin with the string "--lt-"
+# (application programs are unlikely to have options that match
+# this pattern).
+#
+# There are only two supported options: --lt-debug and
+# --lt-dump-script. There is, deliberately, no --lt-help.
+#
+# The first argument to this parsing function should be the
+# script's $0 value, followed by "$@".
+lt_option_debug=
+func_parse_lt_options ()
+{
+  lt_script_arg0=\$0
+  shift
+  for lt_opt
+  do
+    case \"\$lt_opt\" in
+    --lt-debug) lt_option_debug=1 ;;
+    --lt-dump-script)
+        lt_dump_D=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%/[^/]*$%%'\`
+        test \"X\$lt_dump_D\" = \"X\$lt_script_arg0\" && lt_dump_D=.
+        lt_dump_F=\`\$ECHO \"X\$lt_script_arg0\" | $SED -e 's/^X//' -e 's%^.*/%%'\`
+        cat \"\$lt_dump_D/\$lt_dump_F\"
+        exit 0
+      ;;
+    --lt-*)
+        \$ECHO \"Unrecognized --lt- option: '\$lt_opt'\" 1>&2
+        exit 1
+      ;;
+    esac
+  done
+
+  # Print the debug banner immediately:
+  if test -n \"\$lt_option_debug\"; then
+    echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2
+  fi
+}
+
+# Used when --lt-debug. Prints its arguments to stdout
+# (redirection is the responsibility of the caller)
+func_lt_dump_args ()
+{
+  lt_dump_args_N=1;
+  for lt_arg
+  do
+    \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\"
+    lt_dump_args_N=\`expr \$lt_dump_args_N + 1\`
+  done
+}
+
+# Core function for launching the target application
+func_exec_program_core ()
+{
+"
+  case $host in
+  # Backslashes separate directories on plain windows
+  *-*-mingw | *-*-os2* | *-cegcc*)
+    $ECHO "\
+      if test -n \"\$lt_option_debug\"; then
+        \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2
+        func_lt_dump_args \${1+\"\$@\"} 1>&2
+      fi
+      exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
+"
+    ;;
+
+  *)
+    $ECHO "\
+      if test -n \"\$lt_option_debug\"; then
+        \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2
+        func_lt_dump_args \${1+\"\$@\"} 1>&2
+      fi
+      exec \"\$progdir/\$program\" \${1+\"\$@\"}
+"
+    ;;
+  esac
+  $ECHO "\
+      \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2
+      exit 1
+}
+
+# A function to encapsulate launching the target application
+# Strips options in the --lt-* namespace from \$@ and
+# launches target application with the remaining arguments.
+func_exec_program ()
+{
+  case \" \$* \" in
+  *\\ --lt-*)
+    for lt_wr_arg
+    do
+      case \$lt_wr_arg in
+      --lt-*) ;;
+      *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
+      esac
+      shift
+    done ;;
+  esac
+  func_exec_program_core \${1+\"\$@\"}
+}
+
+  # Parse options
+  func_parse_lt_options \"\$0\" \${1+\"\$@\"}
+
+  # Find the directory that this script lives in.
+  thisdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*$%%'\`
+  test \"x\$thisdir\" = \"x\$file\" && thisdir=.
+
+  # Follow symbolic links until we get to the real thisdir.
+  file=\`ls -ld \"\$file\" | $SED -n 's/.*-> //p'\`
+  while test -n \"\$file\"; do
+    destdir=\`\$ECHO \"\$file\" | $SED 's%/[^/]*\$%%'\`
+
+    # If there was a directory component, then change thisdir.
+    if test \"x\$destdir\" != \"x\$file\"; then
+      case \"\$destdir\" in
+      [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;;
+      *) thisdir=\"\$thisdir/\$destdir\" ;;
+      esac
+    fi
+
+    file=\`\$ECHO \"\$file\" | $SED 's%^.*/%%'\`
+    file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\`
+  done
+
+  # Usually 'no', except on cygwin/mingw when embedded into
+  # the cwrapper.
+  WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1
+  if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+    # special case for '.'
+    if test \"\$thisdir\" = \".\"; then
+      thisdir=\`pwd\`
+    fi
+    # remove .libs from thisdir
+    case \"\$thisdir\" in
+    *[\\\\/]$objdir ) thisdir=\`\$ECHO \"\$thisdir\" | $SED 's%[\\\\/][^\\\\/]*$%%'\` ;;
+    $objdir )   thisdir=. ;;
+    esac
+  fi
+
+  # Try to get the absolute directory name.
+  absdir=\`cd \"\$thisdir\" && pwd\`
+  test -n \"\$absdir\" && thisdir=\"\$absdir\"
+"
+
+	if test yes = "$fast_install"; then
+	  $ECHO "\
+  program=lt-'$outputname'$exeext
+  progdir=\"\$thisdir/$objdir\"
+
+  if test ! -f \"\$progdir/\$program\" ||
+     { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\
+       test \"X\$file\" != \"X\$progdir/\$program\"; }; then
+
+    file=\"\$\$-\$program\"
+
+    if test ! -d \"\$progdir\"; then
+      $MKDIR \"\$progdir\"
+    else
+      $RM \"\$progdir/\$file\"
+    fi"
+
+	  $ECHO "\
+
+    # relink executable if necessary
+    if test -n \"\$relink_command\"; then
+      if relink_command_output=\`eval \$relink_command 2>&1\`; then :
+      else
+	\$ECHO \"\$relink_command_output\" >&2
+	$RM \"\$progdir/\$file\"
+	exit 1
+      fi
+    fi
+
+    $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null ||
+    { $RM \"\$progdir/\$program\";
+      $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; }
+    $RM \"\$progdir/\$file\"
+  fi"
+	else
+	  $ECHO "\
+  program='$outputname'
+  progdir=\"\$thisdir/$objdir\"
+"
+	fi
+
+	$ECHO "\
+
+  if test -f \"\$progdir/\$program\"; then"
+
+	# fixup the dll searchpath if we need to.
+	#
+	# Fix the DLL searchpath if we need to.  Do this before prepending
+	# to shlibpath, because on Windows, both are PATH and uninstalled
+	# libraries must come first.
+	if test -n "$dllsearchpath"; then
+	  $ECHO "\
+    # Add the dll search path components to the executable PATH
+    PATH=$dllsearchpath:\$PATH
+"
+	fi
+
+	# Export our shlibpath_var if we have one.
+	if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+	  $ECHO "\
+    # Add our own library path to $shlibpath_var
+    $shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
+
+    # Some systems cannot cope with colon-terminated $shlibpath_var
+    # The second colon is a workaround for a bug in BeOS R4 sed
+    $shlibpath_var=\`\$ECHO \"\$$shlibpath_var\" | $SED 's/::*\$//'\`
+
+    export $shlibpath_var
+"
+	fi
+
+	$ECHO "\
+    if test \"\$libtool_execute_magic\" != \"$magic\"; then
+      # Run the actual program with our arguments.
+      func_exec_program \${1+\"\$@\"}
+    fi
+  else
+    # The program doesn't exist.
+    \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2
+    \$ECHO \"This script is just a wrapper for \$program.\" 1>&2
+    \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
+    exit 1
+  fi
+fi\
+"
+}
+
+
+# func_emit_cwrapperexe_src
+# emit the source code for a wrapper executable on stdout
+# Must ONLY be called from within func_mode_link because
+# it depends on a number of variable set therein.
+func_emit_cwrapperexe_src ()
+{
+	cat <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+   Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+
+   The $output program cannot be directly executed until all the libtool
+   libraries that it depends on are installed.
+
+   This wrapper executable should never be moved out of the build directory.
+   If it is, it will not operate correctly.
+*/
+EOF
+	    cat <<"EOF"
+#ifdef _MSC_VER
+# define _CRT_SECURE_NO_DEPRECATE 1
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _MSC_VER
+# include <direct.h>
+# include <process.h>
+# include <io.h>
+#else
+# include <unistd.h>
+# include <stdint.h>
+# ifdef __CYGWIN__
+#  include <io.h>
+# endif
+#endif
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
+
+/* declarations of non-ANSI functions */
+#if defined __MINGW32__
+# ifdef __STRICT_ANSI__
+int _putenv (const char *);
+# endif
+#elif defined __CYGWIN__
+# ifdef __STRICT_ANSI__
+char *realpath (const char *, char *);
+int putenv (char *);
+int setenv (const char *, const char *, int);
+# endif
+/* #elif defined other_platform || defined ... */
+#endif
+
+/* portability defines, excluding path handling macros */
+#if defined _MSC_VER
+# define setmode _setmode
+# define stat    _stat
+# define chmod   _chmod
+# define getcwd  _getcwd
+# define putenv  _putenv
+# define S_IXUSR _S_IEXEC
+#elif defined __MINGW32__
+# define setmode _setmode
+# define stat    _stat
+# define chmod   _chmod
+# define getcwd  _getcwd
+# define putenv  _putenv
+#elif defined __CYGWIN__
+# define HAVE_SETENV
+# define FOPEN_WB "wb"
+/* #elif defined other platforms ... */
+#endif
+
+#if defined PATH_MAX
+# define LT_PATHMAX PATH_MAX
+#elif defined MAXPATHLEN
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef S_IXOTH
+# define S_IXOTH 0
+#endif
+#ifndef S_IXGRP
+# define S_IXGRP 0
+#endif
+
+/* path handling portability macros */
+#ifndef DIR_SEPARATOR
+# define DIR_SEPARATOR '/'
+# define PATH_SEPARATOR ':'
+#endif
+
+#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \
+  defined __OS2__
+# define HAVE_DOS_BASED_FILE_SYSTEM
+# define FOPEN_WB "wb"
+# ifndef DIR_SEPARATOR_2
+#  define DIR_SEPARATOR_2 '\\'
+# endif
+# ifndef PATH_SEPARATOR_2
+#  define PATH_SEPARATOR_2 ';'
+# endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+	(((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#ifndef PATH_SEPARATOR_2
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR)
+#else /* PATH_SEPARATOR_2 */
+# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2)
+#endif /* PATH_SEPARATOR_2 */
+
+#ifndef FOPEN_WB
+# define FOPEN_WB "w"
+#endif
+#ifndef _O_BINARY
+# define _O_BINARY 0
+#endif
+
+#define XMALLOC(type, num)      ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+  if (stale) { free (stale); stale = 0; } \
+} while (0)
+
+#if defined LT_DEBUGWRAPPER
+static int lt_debug = 1;
+#else
+static int lt_debug = 0;
+#endif
+
+const char *program_name = "libtool-wrapper"; /* in case xstrdup fails */
+
+void *xmalloc (size_t num);
+char *xstrdup (const char *string);
+const char *base_name (const char *name);
+char *find_executable (const char *wrapper);
+char *chase_symlinks (const char *pathspec);
+int make_executable (const char *path);
+int check_executable (const char *path);
+char *strendzap (char *str, const char *pat);
+void lt_debugprintf (const char *file, int line, const char *fmt, ...);
+void lt_fatal (const char *file, int line, const char *message, ...);
+static const char *nonnull (const char *s);
+static const char *nonempty (const char *s);
+void lt_setenv (const char *name, const char *value);
+char *lt_extend_str (const char *orig_value, const char *add, int to_end);
+void lt_update_exe_path (const char *name, const char *value);
+void lt_update_lib_path (const char *name, const char *value);
+char **prepare_spawn (char **argv);
+void lt_dump_script (FILE *f);
+EOF
+
+	    cat <<EOF
+#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)
+# define externally_visible volatile
+#else
+# define externally_visible __attribute__((externally_visible)) volatile
+#endif
+externally_visible const char * MAGIC_EXE = "$magic_exe";
+const char * LIB_PATH_VARNAME = "$shlibpath_var";
+EOF
+
+	    if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+              func_to_host_path "$temp_rpath"
+	      cat <<EOF
+const char * LIB_PATH_VALUE   = "$func_to_host_path_result";
+EOF
+	    else
+	      cat <<"EOF"
+const char * LIB_PATH_VALUE   = "";
+EOF
+	    fi
+
+	    if test -n "$dllsearchpath"; then
+              func_to_host_path "$dllsearchpath:"
+	      cat <<EOF
+const char * EXE_PATH_VARNAME = "PATH";
+const char * EXE_PATH_VALUE   = "$func_to_host_path_result";
+EOF
+	    else
+	      cat <<"EOF"
+const char * EXE_PATH_VARNAME = "";
+const char * EXE_PATH_VALUE   = "";
+EOF
+	    fi
+
+	    if test yes = "$fast_install"; then
+	      cat <<EOF
+const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
+EOF
+	    else
+	      cat <<EOF
+const char * TARGET_PROGRAM_NAME = "$outputname"; /* hopefully, no .exe */
+EOF
+	    fi
+
+
+	    cat <<"EOF"
+
+#define LTWRAPPER_OPTION_PREFIX         "--lt-"
+
+static const char *ltwrapper_option_prefix = LTWRAPPER_OPTION_PREFIX;
+static const char *dumpscript_opt       = LTWRAPPER_OPTION_PREFIX "dump-script";
+static const char *debug_opt            = LTWRAPPER_OPTION_PREFIX "debug";
+
+int
+main (int argc, char *argv[])
+{
+  char **newargz;
+  int  newargc;
+  char *tmp_pathspec;
+  char *actual_cwrapper_path;
+  char *actual_cwrapper_name;
+  char *target_name;
+  char *lt_argv_zero;
+  int rval = 127;
+
+  int i;
+
+  program_name = (char *) xstrdup (base_name (argv[0]));
+  newargz = XMALLOC (char *, (size_t) argc + 1);
+
+  /* very simple arg parsing; don't want to rely on getopt
+   * also, copy all non cwrapper options to newargz, except
+   * argz[0], which is handled differently
+   */
+  newargc=0;
+  for (i = 1; i < argc; i++)
+    {
+      if (STREQ (argv[i], dumpscript_opt))
+	{
+EOF
+	    case $host in
+	      *mingw* | *cygwin* )
+		# make stdout use "unix" line endings
+		echo "          setmode(1,_O_BINARY);"
+		;;
+	      esac
+
+	    cat <<"EOF"
+	  lt_dump_script (stdout);
+	  return 0;
+	}
+      if (STREQ (argv[i], debug_opt))
+	{
+          lt_debug = 1;
+          continue;
+	}
+      if (STREQ (argv[i], ltwrapper_option_prefix))
+        {
+          /* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
+             namespace, but it is not one of the ones we know about and
+             have already dealt with, above (inluding dump-script), then
+             report an error. Otherwise, targets might begin to believe
+             they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
+             namespace. The first time any user complains about this, we'll
+             need to make LTWRAPPER_OPTION_PREFIX a configure-time option
+             or a configure.ac-settable value.
+           */
+          lt_fatal (__FILE__, __LINE__,
+		    "unrecognized %s option: '%s'",
+                    ltwrapper_option_prefix, argv[i]);
+        }
+      /* otherwise ... */
+      newargz[++newargc] = xstrdup (argv[i]);
+    }
+  newargz[++newargc] = NULL;
+
+EOF
+	    cat <<EOF
+  /* The GNU banner must be the first non-error debug message */
+  lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE) $VERSION\n");
+EOF
+	    cat <<"EOF"
+  lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]);
+  lt_debugprintf (__FILE__, __LINE__, "(main) program_name: %s\n", program_name);
+
+  tmp_pathspec = find_executable (argv[0]);
+  if (tmp_pathspec == NULL)
+    lt_fatal (__FILE__, __LINE__, "couldn't find %s", argv[0]);
+  lt_debugprintf (__FILE__, __LINE__,
+                  "(main) found exe (before symlink chase) at: %s\n",
+		  tmp_pathspec);
+
+  actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+  lt_debugprintf (__FILE__, __LINE__,
+                  "(main) found exe (after symlink chase) at: %s\n",
+		  actual_cwrapper_path);
+  XFREE (tmp_pathspec);
+
+  actual_cwrapper_name = xstrdup (base_name (actual_cwrapper_path));
+  strendzap (actual_cwrapper_path, actual_cwrapper_name);
+
+  /* wrapper name transforms */
+  strendzap (actual_cwrapper_name, ".exe");
+  tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1);
+  XFREE (actual_cwrapper_name);
+  actual_cwrapper_name = tmp_pathspec;
+  tmp_pathspec = 0;
+
+  /* target_name transforms -- use actual target program name; might have lt- prefix */
+  target_name = xstrdup (base_name (TARGET_PROGRAM_NAME));
+  strendzap (target_name, ".exe");
+  tmp_pathspec = lt_extend_str (target_name, ".exe", 1);
+  XFREE (target_name);
+  target_name = tmp_pathspec;
+  tmp_pathspec = 0;
+
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(main) libtool target name: %s\n",
+		  target_name);
+EOF
+
+	    cat <<EOF
+  newargz[0] =
+    XMALLOC (char, (strlen (actual_cwrapper_path) +
+		    strlen ("$objdir") + 1 + strlen (actual_cwrapper_name) + 1));
+  strcpy (newargz[0], actual_cwrapper_path);
+  strcat (newargz[0], "$objdir");
+  strcat (newargz[0], "/");
+EOF
+
+	    cat <<"EOF"
+  /* stop here, and copy so we don't have to do this twice */
+  tmp_pathspec = xstrdup (newargz[0]);
+
+  /* do NOT want the lt- prefix here, so use actual_cwrapper_name */
+  strcat (newargz[0], actual_cwrapper_name);
+
+  /* DO want the lt- prefix here if it exists, so use target_name */
+  lt_argv_zero = lt_extend_str (tmp_pathspec, target_name, 1);
+  XFREE (tmp_pathspec);
+  tmp_pathspec = NULL;
+EOF
+
+	    case $host_os in
+	      mingw*)
+	    cat <<"EOF"
+  {
+    char* p;
+    while ((p = strchr (newargz[0], '\\')) != NULL)
+      {
+	*p = '/';
+      }
+    while ((p = strchr (lt_argv_zero, '\\')) != NULL)
+      {
+	*p = '/';
+      }
+  }
+EOF
+	    ;;
+	    esac
+
+	    cat <<"EOF"
+  XFREE (target_name);
+  XFREE (actual_cwrapper_path);
+  XFREE (actual_cwrapper_name);
+
+  lt_setenv ("BIN_SH", "xpg4"); /* for Tru64 */
+  lt_setenv ("DUALCASE", "1");  /* for MSK sh */
+  /* Update the DLL searchpath.  EXE_PATH_VALUE ($dllsearchpath) must
+     be prepended before (that is, appear after) LIB_PATH_VALUE ($temp_rpath)
+     because on Windows, both *_VARNAMEs are PATH but uninstalled
+     libraries must come first. */
+  lt_update_exe_path (EXE_PATH_VARNAME, EXE_PATH_VALUE);
+  lt_update_lib_path (LIB_PATH_VARNAME, LIB_PATH_VALUE);
+
+  lt_debugprintf (__FILE__, __LINE__, "(main) lt_argv_zero: %s\n",
+		  nonnull (lt_argv_zero));
+  for (i = 0; i < newargc; i++)
+    {
+      lt_debugprintf (__FILE__, __LINE__, "(main) newargz[%d]: %s\n",
+		      i, nonnull (newargz[i]));
+    }
+
+EOF
+
+	    case $host_os in
+	      mingw*)
+		cat <<"EOF"
+  /* execv doesn't actually work on mingw as expected on unix */
+  newargz = prepare_spawn (newargz);
+  rval = (int) _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+  if (rval == -1)
+    {
+      /* failed to start process */
+      lt_debugprintf (__FILE__, __LINE__,
+		      "(main) failed to launch target \"%s\": %s\n",
+		      lt_argv_zero, nonnull (strerror (errno)));
+      return 127;
+    }
+  return rval;
+EOF
+		;;
+	      *)
+		cat <<"EOF"
+  execv (lt_argv_zero, newargz);
+  return rval; /* =127, but avoids unused variable warning */
+EOF
+		;;
+	    esac
+
+	    cat <<"EOF"
+}
+
+void *
+xmalloc (size_t num)
+{
+  void *p = (void *) malloc (num);
+  if (!p)
+    lt_fatal (__FILE__, __LINE__, "memory exhausted");
+
+  return p;
+}
+
+char *
+xstrdup (const char *string)
+{
+  return string ? strcpy ((char *) xmalloc (strlen (string) + 1),
+			  string) : NULL;
+}
+
+const char *
+base_name (const char *name)
+{
+  const char *base;
+
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+  /* Skip over the disk name in MSDOS pathnames. */
+  if (isalpha ((unsigned char) name[0]) && name[1] == ':')
+    name += 2;
+#endif
+
+  for (base = name; *name; name++)
+    if (IS_DIR_SEPARATOR (*name))
+      base = name + 1;
+  return base;
+}
+
+int
+check_executable (const char *path)
+{
+  struct stat st;
+
+  lt_debugprintf (__FILE__, __LINE__, "(check_executable): %s\n",
+                  nonempty (path));
+  if ((!path) || (!*path))
+    return 0;
+
+  if ((stat (path, &st) >= 0)
+      && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+    return 1;
+  else
+    return 0;
+}
+
+int
+make_executable (const char *path)
+{
+  int rval = 0;
+  struct stat st;
+
+  lt_debugprintf (__FILE__, __LINE__, "(make_executable): %s\n",
+                  nonempty (path));
+  if ((!path) || (!*path))
+    return 0;
+
+  if (stat (path, &st) >= 0)
+    {
+      rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR);
+    }
+  return rval;
+}
+
+/* Searches for the full path of the wrapper.  Returns
+   newly allocated full path name if found, NULL otherwise
+   Does not chase symlinks, even on platforms that support them.
+*/
+char *
+find_executable (const char *wrapper)
+{
+  int has_slash = 0;
+  const char *p;
+  const char *p_next;
+  /* static buffer for getcwd */
+  char tmp[LT_PATHMAX + 1];
+  size_t tmp_len;
+  char *concat_name;
+
+  lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n",
+                  nonempty (wrapper));
+
+  if ((wrapper == NULL) || (*wrapper == '\0'))
+    return NULL;
+
+  /* Absolute path? */
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+  if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
+    {
+      concat_name = xstrdup (wrapper);
+      if (check_executable (concat_name))
+	return concat_name;
+      XFREE (concat_name);
+    }
+  else
+    {
+#endif
+      if (IS_DIR_SEPARATOR (wrapper[0]))
+	{
+	  concat_name = xstrdup (wrapper);
+	  if (check_executable (concat_name))
+	    return concat_name;
+	  XFREE (concat_name);
+	}
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
+    }
+#endif
+
+  for (p = wrapper; *p; p++)
+    if (*p == '/')
+      {
+	has_slash = 1;
+	break;
+      }
+  if (!has_slash)
+    {
+      /* no slashes; search PATH */
+      const char *path = getenv ("PATH");
+      if (path != NULL)
+	{
+	  for (p = path; *p; p = p_next)
+	    {
+	      const char *q;
+	      size_t p_len;
+	      for (q = p; *q; q++)
+		if (IS_PATH_SEPARATOR (*q))
+		  break;
+	      p_len = (size_t) (q - p);
+	      p_next = (*q == '\0' ? q : q + 1);
+	      if (p_len == 0)
+		{
+		  /* empty path: current directory */
+		  if (getcwd (tmp, LT_PATHMAX) == NULL)
+		    lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+                              nonnull (strerror (errno)));
+		  tmp_len = strlen (tmp);
+		  concat_name =
+		    XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+		  memcpy (concat_name, tmp, tmp_len);
+		  concat_name[tmp_len] = '/';
+		  strcpy (concat_name + tmp_len + 1, wrapper);
+		}
+	      else
+		{
+		  concat_name =
+		    XMALLOC (char, p_len + 1 + strlen (wrapper) + 1);
+		  memcpy (concat_name, p, p_len);
+		  concat_name[p_len] = '/';
+		  strcpy (concat_name + p_len + 1, wrapper);
+		}
+	      if (check_executable (concat_name))
+		return concat_name;
+	      XFREE (concat_name);
+	    }
+	}
+      /* not found in PATH; assume curdir */
+    }
+  /* Relative path | not found in path: prepend cwd */
+  if (getcwd (tmp, LT_PATHMAX) == NULL)
+    lt_fatal (__FILE__, __LINE__, "getcwd failed: %s",
+              nonnull (strerror (errno)));
+  tmp_len = strlen (tmp);
+  concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1);
+  memcpy (concat_name, tmp, tmp_len);
+  concat_name[tmp_len] = '/';
+  strcpy (concat_name + tmp_len + 1, wrapper);
+
+  if (check_executable (concat_name))
+    return concat_name;
+  XFREE (concat_name);
+  return NULL;
+}
+
+char *
+chase_symlinks (const char *pathspec)
+{
+#ifndef S_ISLNK
+  return xstrdup (pathspec);
+#else
+  char buf[LT_PATHMAX];
+  struct stat s;
+  char *tmp_pathspec = xstrdup (pathspec);
+  char *p;
+  int has_symlinks = 0;
+  while (strlen (tmp_pathspec) && !has_symlinks)
+    {
+      lt_debugprintf (__FILE__, __LINE__,
+		      "checking path component for symlinks: %s\n",
+		      tmp_pathspec);
+      if (lstat (tmp_pathspec, &s) == 0)
+	{
+	  if (S_ISLNK (s.st_mode) != 0)
+	    {
+	      has_symlinks = 1;
+	      break;
+	    }
+
+	  /* search backwards for last DIR_SEPARATOR */
+	  p = tmp_pathspec + strlen (tmp_pathspec) - 1;
+	  while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+	    p--;
+	  if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p)))
+	    {
+	      /* no more DIR_SEPARATORS left */
+	      break;
+	    }
+	  *p = '\0';
+	}
+      else
+	{
+	  lt_fatal (__FILE__, __LINE__,
+		    "error accessing file \"%s\": %s",
+		    tmp_pathspec, nonnull (strerror (errno)));
+	}
+    }
+  XFREE (tmp_pathspec);
+
+  if (!has_symlinks)
+    {
+      return xstrdup (pathspec);
+    }
+
+  tmp_pathspec = realpath (pathspec, buf);
+  if (tmp_pathspec == 0)
+    {
+      lt_fatal (__FILE__, __LINE__,
+		"could not follow symlinks for %s", pathspec);
+    }
+  return xstrdup (tmp_pathspec);
+#endif
+}
+
+char *
+strendzap (char *str, const char *pat)
+{
+  size_t len, patlen;
+
+  assert (str != NULL);
+  assert (pat != NULL);
+
+  len = strlen (str);
+  patlen = strlen (pat);
+
+  if (patlen <= len)
+    {
+      str += len - patlen;
+      if (STREQ (str, pat))
+	*str = '\0';
+    }
+  return str;
+}
+
+void
+lt_debugprintf (const char *file, int line, const char *fmt, ...)
+{
+  va_list args;
+  if (lt_debug)
+    {
+      (void) fprintf (stderr, "%s:%s:%d: ", program_name, file, line);
+      va_start (args, fmt);
+      (void) vfprintf (stderr, fmt, args);
+      va_end (args);
+    }
+}
+
+static void
+lt_error_core (int exit_status, const char *file,
+	       int line, const char *mode,
+	       const char *message, va_list ap)
+{
+  fprintf (stderr, "%s:%s:%d: %s: ", program_name, file, line, mode);
+  vfprintf (stderr, message, ap);
+  fprintf (stderr, ".\n");
+
+  if (exit_status >= 0)
+    exit (exit_status);
+}
+
+void
+lt_fatal (const char *file, int line, const char *message, ...)
+{
+  va_list ap;
+  va_start (ap, message);
+  lt_error_core (EXIT_FAILURE, file, line, "FATAL", message, ap);
+  va_end (ap);
+}
+
+static const char *
+nonnull (const char *s)
+{
+  return s ? s : "(null)";
+}
+
+static const char *
+nonempty (const char *s)
+{
+  return (s && !*s) ? "(empty)" : nonnull (s);
+}
+
+void
+lt_setenv (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(lt_setenv) setting '%s' to '%s'\n",
+                  nonnull (name), nonnull (value));
+  {
+#ifdef HAVE_SETENV
+    /* always make a copy, for consistency with !HAVE_SETENV */
+    char *str = xstrdup (value);
+    setenv (name, str, 1);
+#else
+    size_t len = strlen (name) + 1 + strlen (value) + 1;
+    char *str = XMALLOC (char, len);
+    sprintf (str, "%s=%s", name, value);
+    if (putenv (str) != EXIT_SUCCESS)
+      {
+        XFREE (str);
+      }
+#endif
+  }
+}
+
+char *
+lt_extend_str (const char *orig_value, const char *add, int to_end)
+{
+  char *new_value;
+  if (orig_value && *orig_value)
+    {
+      size_t orig_value_len = strlen (orig_value);
+      size_t add_len = strlen (add);
+      new_value = XMALLOC (char, add_len + orig_value_len + 1);
+      if (to_end)
+        {
+          strcpy (new_value, orig_value);
+          strcpy (new_value + orig_value_len, add);
+        }
+      else
+        {
+          strcpy (new_value, add);
+          strcpy (new_value + add_len, orig_value);
+        }
+    }
+  else
+    {
+      new_value = xstrdup (add);
+    }
+  return new_value;
+}
+
+void
+lt_update_exe_path (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(lt_update_exe_path) modifying '%s' by prepending '%s'\n",
+                  nonnull (name), nonnull (value));
+
+  if (name && *name && value && *value)
+    {
+      char *new_value = lt_extend_str (getenv (name), value, 0);
+      /* some systems can't cope with a ':'-terminated path #' */
+      size_t len = strlen (new_value);
+      while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+        {
+          new_value[--len] = '\0';
+        }
+      lt_setenv (name, new_value);
+      XFREE (new_value);
+    }
+}
+
+void
+lt_update_lib_path (const char *name, const char *value)
+{
+  lt_debugprintf (__FILE__, __LINE__,
+		  "(lt_update_lib_path) modifying '%s' by prepending '%s'\n",
+                  nonnull (name), nonnull (value));
+
+  if (name && *name && value && *value)
+    {
+      char *new_value = lt_extend_str (getenv (name), value, 0);
+      lt_setenv (name, new_value);
+      XFREE (new_value);
+    }
+}
+
+EOF
+	    case $host_os in
+	      mingw*)
+		cat <<"EOF"
+
+/* Prepares an argument vector before calling spawn().
+   Note that spawn() does not by itself call the command interpreter
+     (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
+      ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+         GetVersionEx(&v);
+         v.dwPlatformId == VER_PLATFORM_WIN32_NT;
+      }) ? "cmd.exe" : "command.com").
+   Instead it simply concatenates the arguments, separated by ' ', and calls
+   CreateProcess().  We must quote the arguments since Win32 CreateProcess()
+   interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
+   special way:
+   - Space and tab are interpreted as delimiters. They are not treated as
+     delimiters if they are surrounded by double quotes: "...".
+   - Unescaped double quotes are removed from the input. Their only effect is
+     that within double quotes, space and tab are treated like normal
+     characters.
+   - Backslashes not followed by double quotes are not special.
+   - But 2*n+1 backslashes followed by a double quote become
+     n backslashes followed by a double quote (n >= 0):
+       \" -> "
+       \\\" -> \"
+       \\\\\" -> \\"
+ */
+#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+char **
+prepare_spawn (char **argv)
+{
+  size_t argc;
+  char **new_argv;
+  size_t i;
+
+  /* Count number of arguments.  */
+  for (argc = 0; argv[argc] != NULL; argc++)
+    ;
+
+  /* Allocate new argument vector.  */
+  new_argv = XMALLOC (char *, argc + 1);
+
+  /* Put quoted arguments into the new argument vector.  */
+  for (i = 0; i < argc; i++)
+    {
+      const char *string = argv[i];
+
+      if (string[0] == '\0')
+	new_argv[i] = xstrdup ("\"\"");
+      else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
+	{
+	  int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
+	  size_t length;
+	  unsigned int backslashes;
+	  const char *s;
+	  char *quoted_string;
+	  char *p;
+
+	  length = 0;
+	  backslashes = 0;
+	  if (quote_around)
+	    length++;
+	  for (s = string; *s != '\0'; s++)
+	    {
+	      char c = *s;
+	      if (c == '"')
+		length += backslashes + 1;
+	      length++;
+	      if (c == '\\')
+		backslashes++;
+	      else
+		backslashes = 0;
+	    }
+	  if (quote_around)
+	    length += backslashes + 1;
+
+	  quoted_string = XMALLOC (char, length + 1);
+
+	  p = quoted_string;
+	  backslashes = 0;
+	  if (quote_around)
+	    *p++ = '"';
+	  for (s = string; *s != '\0'; s++)
+	    {
+	      char c = *s;
+	      if (c == '"')
+		{
+		  unsigned int j;
+		  for (j = backslashes + 1; j > 0; j--)
+		    *p++ = '\\';
+		}
+	      *p++ = c;
+	      if (c == '\\')
+		backslashes++;
+	      else
+		backslashes = 0;
+	    }
+	  if (quote_around)
+	    {
+	      unsigned int j;
+	      for (j = backslashes; j > 0; j--)
+		*p++ = '\\';
+	      *p++ = '"';
+	    }
+	  *p = '\0';
+
+	  new_argv[i] = quoted_string;
+	}
+      else
+	new_argv[i] = (char *) string;
+    }
+  new_argv[argc] = NULL;
+
+  return new_argv;
+}
+EOF
+		;;
+	    esac
+
+            cat <<"EOF"
+void lt_dump_script (FILE* f)
+{
+EOF
+	    func_emit_wrapper yes |
+	      $SED -n -e '
+s/^\(.\{79\}\)\(..*\)/\1\
+\2/
+h
+s/\([\\"]\)/\\\1/g
+s/$/\\n/
+s/\([^\n]*\).*/  fputs ("\1", f);/p
+g
+D'
+            cat <<"EOF"
+}
+EOF
+}
+# end: func_emit_cwrapperexe_src
+
+# func_win32_import_lib_p ARG
+# True if ARG is an import lib, as indicated by $file_magic_cmd
+func_win32_import_lib_p ()
+{
+    $debug_cmd
+
+    case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in
+    *import*) : ;;
+    *) false ;;
+    esac
+}
+
+# func_suncc_cstd_abi
+# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!!
+# Several compiler flags select an ABI that is incompatible with the
+# Cstd library. Avoid specifying it if any are in CXXFLAGS.
+func_suncc_cstd_abi ()
+{
+    $debug_cmd
+
+    case " $compile_command " in
+    *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*)
+      suncc_use_cstd_abi=no
+      ;;
+    *)
+      suncc_use_cstd_abi=yes
+      ;;
+    esac
+}
+
+# func_mode_link arg...
+func_mode_link ()
+{
+    $debug_cmd
+
+    case $host in
+    *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+      # It is impossible to link a dll without this setting, and
+      # we shouldn't force the makefile maintainer to figure out
+      # what system we are compiling for in order to pass an extra
+      # flag for every libtool invocation.
+      # allow_undefined=no
+
+      # FIXME: Unfortunately, there are problems with the above when trying
+      # to make a dll that has undefined symbols, in which case not
+      # even a static library is built.  For now, we need to specify
+      # -no-undefined on the libtool link line when we can be certain
+      # that all symbols are satisfied, otherwise we get a static library.
+      allow_undefined=yes
+      ;;
+    *)
+      allow_undefined=yes
+      ;;
+    esac
+    libtool_args=$nonopt
+    base_compile="$nonopt $@"
+    compile_command=$nonopt
+    finalize_command=$nonopt
+
+    compile_rpath=
+    finalize_rpath=
+    compile_shlibpath=
+    finalize_shlibpath=
+    convenience=
+    old_convenience=
+    deplibs=
+    old_deplibs=
+    compiler_flags=
+    linker_flags=
+    dllsearchpath=
+    lib_search_path=`pwd`
+    inst_prefix_dir=
+    new_inherited_linker_flags=
+
+    avoid_version=no
+    bindir=
+    dlfiles=
+    dlprefiles=
+    dlself=no
+    export_dynamic=no
+    export_symbols=
+    export_symbols_regex=
+    generated=
+    libobjs=
+    ltlibs=
+    module=no
+    no_install=no
+    objs=
+    os2dllname=
+    non_pic_objects=
+    precious_files_regex=
+    prefer_static_libs=no
+    preload=false
+    prev=
+    prevarg=
+    release=
+    rpath=
+    xrpath=
+    perm_rpath=
+    temp_rpath=
+    thread_safe=no
+    vinfo=
+    vinfo_number=no
+    weak_libs=
+    single_module=$wl-single_module
+    func_infer_tag $base_compile
+
+    # We need to know -static, to get the right output filenames.
+    for arg
+    do
+      case $arg in
+      -shared)
+	test yes != "$build_libtool_libs" \
+	  && func_fatal_configuration "cannot build a shared library"
+	build_old_libs=no
+	break
+	;;
+      -all-static | -static | -static-libtool-libs)
+	case $arg in
+	-all-static)
+	  if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then
+	    func_warning "complete static linking is impossible in this configuration"
+	  fi
+	  if test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	  prefer_static_libs=yes
+	  ;;
+	-static)
+	  if test -z "$pic_flag" && test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	  prefer_static_libs=built
+	  ;;
+	-static-libtool-libs)
+	  if test -z "$pic_flag" && test -n "$link_static_flag"; then
+	    dlopen_self=$dlopen_self_static
+	  fi
+	  prefer_static_libs=yes
+	  ;;
+	esac
+	build_libtool_libs=no
+	build_old_libs=yes
+	break
+	;;
+      esac
+    done
+
+    # See if our shared archives depend on static archives.
+    test -n "$old_archive_from_new_cmds" && build_old_libs=yes
+
+    # Go through the arguments, transforming them on the way.
+    while test "$#" -gt 0; do
+      arg=$1
+      shift
+      func_quote_arg pretty,unquoted "$arg"
+      qarg=$func_quote_arg_unquoted_result
+      func_append libtool_args " $func_quote_arg_result"
+
+      # If the previous option needs an argument, assign it.
+      if test -n "$prev"; then
+	case $prev in
+	output)
+	  func_append compile_command " @OUTPUT@"
+	  func_append finalize_command " @OUTPUT@"
+	  ;;
+	esac
+
+	case $prev in
+	bindir)
+	  bindir=$arg
+	  prev=
+	  continue
+	  ;;
+	dlfiles|dlprefiles)
+	  $preload || {
+	    # Add the symbol object into the linking commands.
+	    func_append compile_command " @SYMFILE@"
+	    func_append finalize_command " @SYMFILE@"
+	    preload=:
+	  }
+	  case $arg in
+	  *.la | *.lo) ;;  # We handle these cases below.
+	  force)
+	    if test no = "$dlself"; then
+	      dlself=needless
+	      export_dynamic=yes
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  self)
+	    if test dlprefiles = "$prev"; then
+	      dlself=yes
+	    elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then
+	      dlself=yes
+	    else
+	      dlself=needless
+	      export_dynamic=yes
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  *)
+	    if test dlfiles = "$prev"; then
+	      func_append dlfiles " $arg"
+	    else
+	      func_append dlprefiles " $arg"
+	    fi
+	    prev=
+	    continue
+	    ;;
+	  esac
+	  ;;
+	expsyms)
+	  export_symbols=$arg
+	  test -f "$arg" \
+	    || func_fatal_error "symbol file '$arg' does not exist"
+	  prev=
+	  continue
+	  ;;
+	expsyms_regex)
+	  export_symbols_regex=$arg
+	  prev=
+	  continue
+	  ;;
+	framework)
+	  case $host in
+	    *-*-darwin*)
+	      case "$deplibs " in
+		*" $qarg.ltframework "*) ;;
+		*) func_append deplibs " $qarg.ltframework" # this is fixed later
+		   ;;
+	      esac
+	      ;;
+	  esac
+	  prev=
+	  continue
+	  ;;
+	inst_prefix)
+	  inst_prefix_dir=$arg
+	  prev=
+	  continue
+	  ;;
+	mllvm)
+	  # Clang does not use LLVM to link, so we can simply discard any
+	  # '-mllvm $arg' options when doing the link step.
+	  prev=
+	  continue
+	  ;;
+	objectlist)
+	  if test -f "$arg"; then
+	    save_arg=$arg
+	    moreargs=
+	    for fil in `cat "$save_arg"`
+	    do
+#	      func_append moreargs " $fil"
+	      arg=$fil
+	      # A libtool-controlled object.
+
+	      # Check to see that this really is a libtool object.
+	      if func_lalib_unsafe_p "$arg"; then
+		pic_object=
+		non_pic_object=
+
+		# Read the .lo file
+		func_source "$arg"
+
+		if test -z "$pic_object" ||
+		   test -z "$non_pic_object" ||
+		   test none = "$pic_object" &&
+		   test none = "$non_pic_object"; then
+		  func_fatal_error "cannot find name of object for '$arg'"
+		fi
+
+		# Extract subdirectory from the argument.
+		func_dirname "$arg" "/" ""
+		xdir=$func_dirname_result
+
+		if test none != "$pic_object"; then
+		  # Prepend the subdirectory the object is found in.
+		  pic_object=$xdir$pic_object
+
+		  if test dlfiles = "$prev"; then
+		    if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
+		      func_append dlfiles " $pic_object"
+		      prev=
+		      continue
+		    else
+		      # If libtool objects are unsupported, then we need to preload.
+		      prev=dlprefiles
+		    fi
+		  fi
+
+		  # CHECK ME:  I think I busted this.  -Ossama
+		  if test dlprefiles = "$prev"; then
+		    # Preload the old-style object.
+		    func_append dlprefiles " $pic_object"
+		    prev=
+		  fi
+
+		  # A PIC object.
+		  func_append libobjs " $pic_object"
+		  arg=$pic_object
+		fi
+
+		# Non-PIC object.
+		if test none != "$non_pic_object"; then
+		  # Prepend the subdirectory the object is found in.
+		  non_pic_object=$xdir$non_pic_object
+
+		  # A standard non-PIC object
+		  func_append non_pic_objects " $non_pic_object"
+		  if test -z "$pic_object" || test none = "$pic_object"; then
+		    arg=$non_pic_object
+		  fi
+		else
+		  # If the PIC object exists, use it instead.
+		  # $xdir was prepended to $pic_object above.
+		  non_pic_object=$pic_object
+		  func_append non_pic_objects " $non_pic_object"
+		fi
+	      else
+		# Only an error if not doing a dry-run.
+		if $opt_dry_run; then
+		  # Extract subdirectory from the argument.
+		  func_dirname "$arg" "/" ""
+		  xdir=$func_dirname_result
+
+		  func_lo2o "$arg"
+		  pic_object=$xdir$objdir/$func_lo2o_result
+		  non_pic_object=$xdir$func_lo2o_result
+		  func_append libobjs " $pic_object"
+		  func_append non_pic_objects " $non_pic_object"
+	        else
+		  func_fatal_error "'$arg' is not a valid libtool object"
+		fi
+	      fi
+	    done
+	  else
+	    func_fatal_error "link input file '$arg' does not exist"
+	  fi
+	  arg=$save_arg
+	  prev=
+	  continue
+	  ;;
+	os2dllname)
+	  os2dllname=$arg
+	  prev=
+	  continue
+	  ;;
+	precious_regex)
+	  precious_files_regex=$arg
+	  prev=
+	  continue
+	  ;;
+	release)
+	  release=-$arg
+	  prev=
+	  continue
+	  ;;
+	rpath | xrpath)
+	  # We need an absolute path.
+	  case $arg in
+	  [\\/]* | [A-Za-z]:[\\/]*) ;;
+	  *)
+	    func_fatal_error "only absolute run-paths are allowed"
+	    ;;
+	  esac
+	  if test rpath = "$prev"; then
+	    case "$rpath " in
+	    *" $arg "*) ;;
+	    *) func_append rpath " $arg" ;;
+	    esac
+	  else
+	    case "$xrpath " in
+	    *" $arg "*) ;;
+	    *) func_append xrpath " $arg" ;;
+	    esac
+	  fi
+	  prev=
+	  continue
+	  ;;
+	shrext)
+	  shrext_cmds=$arg
+	  prev=
+	  continue
+	  ;;
+	weak)
+	  func_append weak_libs " $arg"
+	  prev=
+	  continue
+	  ;;
+	xassembler)
+	  func_append compiler_flags " -Xassembler $qarg"
+	  prev=
+	  func_append compile_command " -Xassembler $qarg"
+	  func_append finalize_command " -Xassembler $qarg"
+	  continue
+	  ;;
+	xcclinker)
+	  func_append linker_flags " $qarg"
+	  func_append compiler_flags " $qarg"
+	  prev=
+	  func_append compile_command " $qarg"
+	  func_append finalize_command " $qarg"
+	  continue
+	  ;;
+	xcompiler)
+	  func_append compiler_flags " $qarg"
+	  prev=
+	  func_append compile_command " $qarg"
+	  func_append finalize_command " $qarg"
+	  continue
+	  ;;
+	xlinker)
+	  func_append linker_flags " $qarg"
+	  func_append compiler_flags " $wl$qarg"
+	  prev=
+	  func_append compile_command " $wl$qarg"
+	  func_append finalize_command " $wl$qarg"
+	  continue
+	  ;;
+	*)
+	  eval "$prev=\"\$arg\""
+	  prev=
+	  continue
+	  ;;
+	esac
+      fi # test -n "$prev"
+
+      prevarg=$arg
+
+      case $arg in
+      -all-static)
+	if test -n "$link_static_flag"; then
+	  # See comment for -static flag below, for more details.
+	  func_append compile_command " $link_static_flag"
+	  func_append finalize_command " $link_static_flag"
+	fi
+	continue
+	;;
+
+      -allow-undefined)
+	# FIXME: remove this flag sometime in the future.
+	func_fatal_error "'-allow-undefined' must not be used because it is the default"
+	;;
+
+      -avoid-version)
+	avoid_version=yes
+	continue
+	;;
+
+      -bindir)
+	prev=bindir
+	continue
+	;;
+
+      -dlopen)
+	prev=dlfiles
+	continue
+	;;
+
+      -dlpreopen)
+	prev=dlprefiles
+	continue
+	;;
+
+      -export-dynamic)
+	export_dynamic=yes
+	continue
+	;;
+
+      -export-symbols | -export-symbols-regex)
+	if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
+	  func_fatal_error "more than one -exported-symbols argument is not allowed"
+	fi
+	if test X-export-symbols = "X$arg"; then
+	  prev=expsyms
+	else
+	  prev=expsyms_regex
+	fi
+	continue
+	;;
+
+      -framework)
+	prev=framework
+	continue
+	;;
+
+      -inst-prefix-dir)
+	prev=inst_prefix
+	continue
+	;;
+
+      # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:*
+      # so, if we see these flags be careful not to treat them like -L
+      -L[A-Z][A-Z]*:*)
+	case $with_gcc/$host in
+	no/*-*-irix* | /*-*-irix*)
+	  func_append compile_command " $arg"
+	  func_append finalize_command " $arg"
+	  ;;
+	esac
+	continue
+	;;
+
+      -L*)
+	func_stripname "-L" '' "$arg"
+	if test -z "$func_stripname_result"; then
+	  if test "$#" -gt 0; then
+	    func_fatal_error "require no space between '-L' and '$1'"
+	  else
+	    func_fatal_error "need path for '-L' option"
+	  fi
+	fi
+	func_resolve_sysroot "$func_stripname_result"
+	dir=$func_resolve_sysroot_result
+	# We need an absolute path.
+	case $dir in
+	[\\/]* | [A-Za-z]:[\\/]*) ;;
+	*)
+	  absdir=`cd "$dir" && pwd`
+	  test -z "$absdir" && \
+	    func_fatal_error "cannot determine absolute directory name of '$dir'"
+	  dir=$absdir
+	  ;;
+	esac
+	case "$deplibs " in
+	*" -L$dir "* | *" $arg "*)
+	  # Will only happen for absolute or sysroot arguments
+	  ;;
+	*)
+	  # Preserve sysroot, but never include relative directories
+	  case $dir in
+	    [\\/]* | [A-Za-z]:[\\/]* | =*) func_append deplibs " $arg" ;;
+	    *) func_append deplibs " -L$dir" ;;
+	  esac
+	  func_append lib_search_path " $dir"
+	  ;;
+	esac
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+	  testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
+	  case :$dllsearchpath: in
+	  *":$dir:"*) ;;
+	  ::) dllsearchpath=$dir;;
+	  *) func_append dllsearchpath ":$dir";;
+	  esac
+	  case :$dllsearchpath: in
+	  *":$testbindir:"*) ;;
+	  ::) dllsearchpath=$testbindir;;
+	  *) func_append dllsearchpath ":$testbindir";;
+	  esac
+	  ;;
+	esac
+	continue
+	;;
+
+      -l*)
+	if test X-lc = "X$arg" || test X-lm = "X$arg"; then
+	  case $host in
+	  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
+	    # These systems don't actually have a C or math library (as such)
+	    continue
+	    ;;
+	  *-*-os2*)
+	    # These systems don't actually have a C library (as such)
+	    test X-lc = "X$arg" && continue
+	    ;;
+	  *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig* | *-*-midnightbsd*)
+	    # Do not include libc due to us having libc/libc_r.
+	    test X-lc = "X$arg" && continue
+	    ;;
+	  *-*-rhapsody* | *-*-darwin1.[012])
+	    # Rhapsody C and math libraries are in the System framework
+	    func_append deplibs " System.ltframework"
+	    continue
+	    ;;
+	  *-*-sco3.2v5* | *-*-sco5v6*)
+	    # Causes problems with __ctype
+	    test X-lc = "X$arg" && continue
+	    ;;
+	  *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+	    # Compiler inserts libc in the correct place for threads to work
+	    test X-lc = "X$arg" && continue
+	    ;;
+	  esac
+	elif test X-lc_r = "X$arg"; then
+	 case $host in
+	 *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig* | *-*-midnightbsd*)
+	   # Do not include libc_r directly, use -pthread flag.
+	   continue
+	   ;;
+	 esac
+	fi
+	func_append deplibs " $arg"
+	continue
+	;;
+
+      -mllvm)
+	prev=mllvm
+	continue
+	;;
+
+      -module)
+	module=yes
+	continue
+	;;
+
+      # Tru64 UNIX uses -model [arg] to determine the layout of C++
+      # classes, name mangling, and exception handling.
+      # Darwin uses the -arch flag to determine output architecture.
+      -model|-arch|-isysroot|--sysroot)
+	func_append compiler_flags " $arg"
+	func_append compile_command " $arg"
+	func_append finalize_command " $arg"
+	prev=xcompiler
+	continue
+	;;
+     # Solaris ld rejects as of 11.4. Refer to Oracle bug 22985199.
+     -pthread)
+	case $host in
+	  *solaris2*) ;;
+	  *)
+	    case "$new_inherited_linker_flags " in
+	        *" $arg "*) ;;
+	        * ) func_append new_inherited_linker_flags " $arg" ;;
+	    esac
+	  ;;
+	esac
+	continue
+	;;
+      -mt|-mthreads|-kthread|-Kthread|-pthreads|--thread-safe \
+      |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+	func_append compiler_flags " $arg"
+	func_append compile_command " $arg"
+	func_append finalize_command " $arg"
+	case "$new_inherited_linker_flags " in
+	    *" $arg "*) ;;
+	    * ) func_append new_inherited_linker_flags " $arg" ;;
+	esac
+	continue
+	;;
+
+      -multi_module)
+	single_module=$wl-multi_module
+	continue
+	;;
+
+      -no-fast-install)
+	fast_install=no
+	continue
+	;;
+
+      -no-install)
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+	  # The PATH hackery in wrapper scripts is required on Windows
+	  # and Darwin in order for the loader to find any dlls it needs.
+	  func_warning "'-no-install' is ignored for $host"
+	  func_warning "assuming '-no-fast-install' instead"
+	  fast_install=no
+	  ;;
+	*) no_install=yes ;;
+	esac
+	continue
+	;;
+
+      -no-undefined)
+	allow_undefined=no
+	continue
+	;;
+
+      -objectlist)
+	prev=objectlist
+	continue
+	;;
+
+      -os2dllname)
+	prev=os2dllname
+	continue
+	;;
+
+      -o) prev=output ;;
+
+      -precious-files-regex)
+	prev=precious_regex
+	continue
+	;;
+
+      -release)
+	prev=release
+	continue
+	;;
+
+      -rpath)
+	prev=rpath
+	continue
+	;;
+
+      -R)
+	prev=xrpath
+	continue
+	;;
+
+      -R*)
+	func_stripname '-R' '' "$arg"
+	dir=$func_stripname_result
+	# We need an absolute path.
+	case $dir in
+	[\\/]* | [A-Za-z]:[\\/]*) ;;
+	=*)
+	  func_stripname '=' '' "$dir"
+	  dir=$lt_sysroot$func_stripname_result
+	  ;;
+	*)
+	  func_fatal_error "only absolute run-paths are allowed"
+	  ;;
+	esac
+	case "$xrpath " in
+	*" $dir "*) ;;
+	*) func_append xrpath " $dir" ;;
+	esac
+	continue
+	;;
+
+      -shared)
+	# The effects of -shared are defined in a previous loop.
+	continue
+	;;
+
+      -shrext)
+	prev=shrext
+	continue
+	;;
+
+      -static | -static-libtool-libs)
+	# The effects of -static are defined in a previous loop.
+	# We used to do the same as -all-static on platforms that
+	# didn't have a PIC flag, but the assumption that the effects
+	# would be equivalent was wrong.  It would break on at least
+	# Digital Unix and AIX.
+	continue
+	;;
+
+      -thread-safe)
+	thread_safe=yes
+	continue
+	;;
+
+      -version-info)
+	prev=vinfo
+	continue
+	;;
+
+      -version-number)
+	prev=vinfo
+	vinfo_number=yes
+	continue
+	;;
+
+      -weak)
+        prev=weak
+	continue
+	;;
+
+      -Wc,*)
+	func_stripname '-Wc,' '' "$arg"
+	args=$func_stripname_result
+	arg=
+	save_ifs=$IFS; IFS=,
+	for flag in $args; do
+	  IFS=$save_ifs
+          func_quote_arg pretty "$flag"
+	  func_append arg " $func_quote_arg_result"
+	  func_append compiler_flags " $func_quote_arg_result"
+	done
+	IFS=$save_ifs
+	func_stripname ' ' '' "$arg"
+	arg=$func_stripname_result
+	;;
+
+      -Wl,*)
+	func_stripname '-Wl,' '' "$arg"
+	args=$func_stripname_result
+	arg=
+	save_ifs=$IFS; IFS=,
+	for flag in $args; do
+	  IFS=$save_ifs
+          func_quote_arg pretty "$flag"
+	  func_append arg " $wl$func_quote_arg_result"
+	  func_append compiler_flags " $wl$func_quote_arg_result"
+	  func_append linker_flags " $func_quote_arg_result"
+	done
+	IFS=$save_ifs
+	func_stripname ' ' '' "$arg"
+	arg=$func_stripname_result
+	;;
+
+      -Xassembler)
+        prev=xassembler
+        continue
+        ;;
+
+      -Xcompiler)
+	prev=xcompiler
+	continue
+	;;
+
+      -Xlinker)
+	prev=xlinker
+	continue
+	;;
+
+      -XCClinker)
+	prev=xcclinker
+	continue
+	;;
+
+      # -msg_* for osf cc
+      -msg_*)
+	func_quote_arg pretty "$arg"
+	arg=$func_quote_arg_result
+	;;
+
+      # Flags to be passed through unchanged, with rationale:
+      # -64, -mips[0-9]      enable 64-bit mode for the SGI compiler
+      # -r[0-9][0-9]*        specify processor for the SGI compiler
+      # -xarch=*, -xtarget=* enable 64-bit mode for the Sun compiler
+      # +DA*, +DD*           enable 64-bit mode for the HP compiler
+      # -q*                  compiler args for the IBM compiler
+      # -m*, -t[45]*, -txscale* architecture-specific flags for GCC
+      # -F/path              path to uninstalled frameworks, gcc on darwin
+      # -p, -pg, --coverage, -fprofile-*  profiling flags for GCC
+      # -fstack-protector*   stack protector flags for GCC
+      # @file                GCC response files
+      # -tp=*                Portland pgcc target processor selection
+      # --sysroot=*          for sysroot support
+      # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+      # -specs=*             GCC specs files
+      # -stdlib=*            select c++ std lib with clang
+      # -fsanitize=*         Clang/GCC memory and address sanitizer
+      # -fuse-ld=*           Linker select flags for GCC
+      # -Wa,*                Pass flags directly to the assembler
+      -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
+      -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
+      -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \
+      -specs=*|-fsanitize=*|-fuse-ld=*|-Wa,*)
+        func_quote_arg pretty "$arg"
+	arg=$func_quote_arg_result
+        func_append compile_command " $arg"
+        func_append finalize_command " $arg"
+        func_append compiler_flags " $arg"
+        continue
+        ;;
+
+      -Z*)
+        if test os2 = "`expr $host : '.*\(os2\)'`"; then
+          # OS/2 uses -Zxxx to specify OS/2-specific options
+	  compiler_flags="$compiler_flags $arg"
+	  func_append compile_command " $arg"
+	  func_append finalize_command " $arg"
+	  case $arg in
+	  -Zlinker | -Zstack)
+	    prev=xcompiler
+	    ;;
+	  esac
+	  continue
+        else
+	  # Otherwise treat like 'Some other compiler flag' below
+	  func_quote_arg pretty "$arg"
+	  arg=$func_quote_arg_result
+        fi
+	;;
+
+      # Some other compiler flag.
+      -* | +*)
+        func_quote_arg pretty "$arg"
+	arg=$func_quote_arg_result
+	;;
+
+      *.$objext)
+	# A standard object.
+	func_append objs " $arg"
+	;;
+
+      *.lo)
+	# A libtool-controlled object.
+
+	# Check to see that this really is a libtool object.
+	if func_lalib_unsafe_p "$arg"; then
+	  pic_object=
+	  non_pic_object=
+
+	  # Read the .lo file
+	  func_source "$arg"
+
+	  if test -z "$pic_object" ||
+	     test -z "$non_pic_object" ||
+	     test none = "$pic_object" &&
+	     test none = "$non_pic_object"; then
+	    func_fatal_error "cannot find name of object for '$arg'"
+	  fi
+
+	  # Extract subdirectory from the argument.
+	  func_dirname "$arg" "/" ""
+	  xdir=$func_dirname_result
+
+	  test none = "$pic_object" || {
+	    # Prepend the subdirectory the object is found in.
+	    pic_object=$xdir$pic_object
+
+	    if test dlfiles = "$prev"; then
+	      if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
+		func_append dlfiles " $pic_object"
+		prev=
+		continue
+	      else
+		# If libtool objects are unsupported, then we need to preload.
+		prev=dlprefiles
+	      fi
+	    fi
+
+	    # CHECK ME:  I think I busted this.  -Ossama
+	    if test dlprefiles = "$prev"; then
+	      # Preload the old-style object.
+	      func_append dlprefiles " $pic_object"
+	      prev=
+	    fi
+
+	    # A PIC object.
+	    func_append libobjs " $pic_object"
+	    arg=$pic_object
+	  }
+
+	  # Non-PIC object.
+	  if test none != "$non_pic_object"; then
+	    # Prepend the subdirectory the object is found in.
+	    non_pic_object=$xdir$non_pic_object
+
+	    # A standard non-PIC object
+	    func_append non_pic_objects " $non_pic_object"
+	    if test -z "$pic_object" || test none = "$pic_object"; then
+	      arg=$non_pic_object
+	    fi
+	  else
+	    # If the PIC object exists, use it instead.
+	    # $xdir was prepended to $pic_object above.
+	    non_pic_object=$pic_object
+	    func_append non_pic_objects " $non_pic_object"
+	  fi
+	else
+	  # Only an error if not doing a dry-run.
+	  if $opt_dry_run; then
+	    # Extract subdirectory from the argument.
+	    func_dirname "$arg" "/" ""
+	    xdir=$func_dirname_result
+
+	    func_lo2o "$arg"
+	    pic_object=$xdir$objdir/$func_lo2o_result
+	    non_pic_object=$xdir$func_lo2o_result
+	    func_append libobjs " $pic_object"
+	    func_append non_pic_objects " $non_pic_object"
+	  else
+	    func_fatal_error "'$arg' is not a valid libtool object"
+	  fi
+	fi
+	;;
+
+      *.$libext)
+	# An archive.
+	func_append deplibs " $arg"
+	func_append old_deplibs " $arg"
+	continue
+	;;
+
+      *.la)
+	# A libtool-controlled library.
+
+	func_resolve_sysroot "$arg"
+	if test dlfiles = "$prev"; then
+	  # This library was specified with -dlopen.
+	  func_append dlfiles " $func_resolve_sysroot_result"
+	  prev=
+	elif test dlprefiles = "$prev"; then
+	  # The library was specified with -dlpreopen.
+	  func_append dlprefiles " $func_resolve_sysroot_result"
+	  prev=
+	else
+	  func_append deplibs " $func_resolve_sysroot_result"
+	fi
+	continue
+	;;
+
+      # Some other compiler argument.
+      *)
+	# Unknown arguments in both finalize_command and compile_command need
+	# to be aesthetically quoted because they are evaled later.
+	func_quote_arg pretty "$arg"
+	arg=$func_quote_arg_result
+	;;
+      esac # arg
+
+      # Now actually substitute the argument into the commands.
+      if test -n "$arg"; then
+	func_append compile_command " $arg"
+	func_append finalize_command " $arg"
+      fi
+    done # argument parsing loop
+
+    test -n "$prev" && \
+      func_fatal_help "the '$prevarg' option requires an argument"
+
+    if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then
+      eval arg=\"$export_dynamic_flag_spec\"
+      func_append compile_command " $arg"
+      func_append finalize_command " $arg"
+    fi
+
+    oldlibs=
+    # calculate the name of the file, without its directory
+    func_basename "$output"
+    outputname=$func_basename_result
+    libobjs_save=$libobjs
+
+    if test -n "$shlibpath_var"; then
+      # get the directories listed in $shlibpath_var
+      eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\`
+    else
+      shlib_search_path=
+    fi
+    eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
+    eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+
+    # Definition is injected by LT_CONFIG during libtool generation.
+    func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH"
+
+    func_dirname "$output" "/" ""
+    output_objdir=$func_dirname_result$objdir
+    func_to_tool_file "$output_objdir/"
+    tool_output_objdir=$func_to_tool_file_result
+    # Create the object directory.
+    func_mkdir_p "$output_objdir"
+
+    # Determine the type of output
+    case $output in
+    "")
+      func_fatal_help "you must specify an output file"
+      ;;
+    *.$libext) linkmode=oldlib ;;
+    *.lo | *.$objext) linkmode=obj ;;
+    *.la) linkmode=lib ;;
+    *) linkmode=prog ;; # Anything else should be a program.
+    esac
+
+    specialdeplibs=
+
+    libs=
+    # Find all interdependent deplibs by searching for libraries
+    # that are linked more than once (e.g. -la -lb -la)
+    for deplib in $deplibs; do
+      if $opt_preserve_dup_deps; then
+	case "$libs " in
+	*" $deplib "*) func_append specialdeplibs " $deplib" ;;
+	esac
+      fi
+      func_append libs " $deplib"
+    done
+
+    if test lib = "$linkmode"; then
+      libs="$predeps $libs $compiler_lib_search_path $postdeps"
+
+      # Compute libraries that are listed more than once in $predeps
+      # $postdeps and mark them as special (i.e., whose duplicates are
+      # not to be eliminated).
+      pre_post_deps=
+      if $opt_duplicate_compiler_generated_deps; then
+	for pre_post_dep in $predeps $postdeps; do
+	  case "$pre_post_deps " in
+	  *" $pre_post_dep "*) func_append specialdeplibs " $pre_post_deps" ;;
+	  esac
+	  func_append pre_post_deps " $pre_post_dep"
+	done
+      fi
+      pre_post_deps=
+    fi
+
+    deplibs=
+    newdependency_libs=
+    newlib_search_path=
+    need_relink=no # whether we're linking any uninstalled libtool libraries
+    notinst_deplibs= # not-installed libtool libraries
+    notinst_path= # paths that contain not-installed libtool libraries
+
+    case $linkmode in
+    lib)
+	passes="conv dlpreopen link"
+	for file in $dlfiles $dlprefiles; do
+	  case $file in
+	  *.la) ;;
+	  *)
+	    func_fatal_help "libraries can '-dlopen' only libtool libraries: $file"
+	    ;;
+	  esac
+	done
+	;;
+    prog)
+	compile_deplibs=
+	finalize_deplibs=
+	alldeplibs=false
+	newdlfiles=
+	newdlprefiles=
+	passes="conv scan dlopen dlpreopen link"
+	;;
+    *)  passes="conv"
+	;;
+    esac
+
+    for pass in $passes; do
+      # The preopen pass in lib mode reverses $deplibs; put it back here
+      # so that -L comes before libs that need it for instance...
+      if test lib,link = "$linkmode,$pass"; then
+	## FIXME: Find the place where the list is rebuilt in the wrong
+	##        order, and fix it there properly
+        tmp_deplibs=
+	for deplib in $deplibs; do
+	  tmp_deplibs="$deplib $tmp_deplibs"
+	done
+	deplibs=$tmp_deplibs
+      fi
+
+      if test lib,link = "$linkmode,$pass" ||
+	 test prog,scan = "$linkmode,$pass"; then
+	libs=$deplibs
+	deplibs=
+      fi
+      if test prog = "$linkmode"; then
+	case $pass in
+	dlopen) libs=$dlfiles ;;
+	dlpreopen) libs=$dlprefiles ;;
+	link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
+	esac
+      fi
+      if test lib,dlpreopen = "$linkmode,$pass"; then
+	# Collect and forward deplibs of preopened libtool libs
+	for lib in $dlprefiles; do
+	  # Ignore non-libtool-libs
+	  dependency_libs=
+	  func_resolve_sysroot "$lib"
+	  case $lib in
+	  *.la)	func_source "$func_resolve_sysroot_result" ;;
+	  esac
+
+	  # Collect preopened libtool deplibs, except any this library
+	  # has declared as weak libs
+	  for deplib in $dependency_libs; do
+	    func_basename "$deplib"
+            deplib_base=$func_basename_result
+	    case " $weak_libs " in
+	    *" $deplib_base "*) ;;
+	    *) func_append deplibs " $deplib" ;;
+	    esac
+	  done
+	done
+	libs=$dlprefiles
+      fi
+      if test dlopen = "$pass"; then
+	# Collect dlpreopened libraries
+	save_deplibs=$deplibs
+	deplibs=
+      fi
+
+      for deplib in $libs; do
+	lib=
+	found=false
+	case $deplib in
+	-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+        |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+	  if test prog,link = "$linkmode,$pass"; then
+	    compile_deplibs="$deplib $compile_deplibs"
+	    finalize_deplibs="$deplib $finalize_deplibs"
+	  else
+	    func_append compiler_flags " $deplib"
+	    if test lib = "$linkmode"; then
+		case "$new_inherited_linker_flags " in
+		    *" $deplib "*) ;;
+		    * ) func_append new_inherited_linker_flags " $deplib" ;;
+		esac
+	    fi
+	  fi
+	  continue
+	  ;;
+	-l*)
+	  if test lib != "$linkmode" && test prog != "$linkmode"; then
+	    func_warning "'-l' is ignored for archives/objects"
+	    continue
+	  fi
+	  func_stripname '-l' '' "$deplib"
+	  name=$func_stripname_result
+	  if test lib = "$linkmode"; then
+	    searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
+	  else
+	    searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
+	  fi
+	  for searchdir in $searchdirs; do
+	    for search_ext in .la $std_shrext .so .a; do
+	      # Search the libtool library
+	      lib=$searchdir/lib$name$search_ext
+	      if test -f "$lib"; then
+		if test .la = "$search_ext"; then
+		  found=:
+		else
+		  found=false
+		fi
+		break 2
+	      fi
+	    done
+	  done
+	  if $found; then
+	    # deplib is a libtool library
+	    # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
+	    # We need to do some special things here, and not later.
+	    if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+	      case " $predeps $postdeps " in
+	      *" $deplib "*)
+		if func_lalib_p "$lib"; then
+		  library_names=
+		  old_library=
+		  func_source "$lib"
+		  for l in $old_library $library_names; do
+		    ll=$l
+		  done
+		  if test "X$ll" = "X$old_library"; then # only static version available
+		    found=false
+		    func_dirname "$lib" "" "."
+		    ladir=$func_dirname_result
+		    lib=$ladir/$old_library
+		    if test prog,link = "$linkmode,$pass"; then
+		      compile_deplibs="$deplib $compile_deplibs"
+		      finalize_deplibs="$deplib $finalize_deplibs"
+		    else
+		      deplibs="$deplib $deplibs"
+		      test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
+		    fi
+		    continue
+		  fi
+		fi
+		;;
+	      *) ;;
+	      esac
+	    fi
+	  else
+	    # deplib doesn't seem to be a libtool library
+	    if test prog,link = "$linkmode,$pass"; then
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    else
+	      deplibs="$deplib $deplibs"
+	      test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
+	    fi
+	    continue
+	  fi
+	  ;; # -l
+	*.ltframework)
+	  if test prog,link = "$linkmode,$pass"; then
+	    compile_deplibs="$deplib $compile_deplibs"
+	    finalize_deplibs="$deplib $finalize_deplibs"
+	  else
+	    deplibs="$deplib $deplibs"
+	    if test lib = "$linkmode"; then
+		case "$new_inherited_linker_flags " in
+		    *" $deplib "*) ;;
+		    * ) func_append new_inherited_linker_flags " $deplib" ;;
+		esac
+	    fi
+	  fi
+	  continue
+	  ;;
+	-L*)
+	  case $linkmode in
+	  lib)
+	    deplibs="$deplib $deplibs"
+	    test conv = "$pass" && continue
+	    newdependency_libs="$deplib $newdependency_libs"
+	    func_stripname '-L' '' "$deplib"
+	    func_resolve_sysroot "$func_stripname_result"
+	    func_append newlib_search_path " $func_resolve_sysroot_result"
+	    ;;
+	  prog)
+	    if test conv = "$pass"; then
+	      deplibs="$deplib $deplibs"
+	      continue
+	    fi
+	    if test scan = "$pass"; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    fi
+	    func_stripname '-L' '' "$deplib"
+	    func_resolve_sysroot "$func_stripname_result"
+	    func_append newlib_search_path " $func_resolve_sysroot_result"
+	    ;;
+	  *)
+	    func_warning "'-L' is ignored for archives/objects"
+	    ;;
+	  esac # linkmode
+	  continue
+	  ;; # -L
+	-R*)
+	  if test link = "$pass"; then
+	    func_stripname '-R' '' "$deplib"
+	    func_resolve_sysroot "$func_stripname_result"
+	    dir=$func_resolve_sysroot_result
+	    # Make sure the xrpath contains only unique directories.
+	    case "$xrpath " in
+	    *" $dir "*) ;;
+	    *) func_append xrpath " $dir" ;;
+	    esac
+	  fi
+	  deplibs="$deplib $deplibs"
+	  continue
+	  ;;
+	*.la)
+	  func_resolve_sysroot "$deplib"
+	  lib=$func_resolve_sysroot_result
+	  ;;
+	*.$libext)
+	  if test conv = "$pass"; then
+	    deplibs="$deplib $deplibs"
+	    continue
+	  fi
+	  case $linkmode in
+	  lib)
+	    # Linking convenience modules into shared libraries is allowed,
+	    # but linking other static libraries is non-portable.
+	    case " $dlpreconveniencelibs " in
+	    *" $deplib "*) ;;
+	    *)
+	      valid_a_lib=false
+	      case $deplibs_check_method in
+		match_pattern*)
+		  set dummy $deplibs_check_method; shift
+		  match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+		  if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \
+		    | $EGREP "$match_pattern_regex" > /dev/null; then
+		    valid_a_lib=:
+		  fi
+		;;
+		pass_all)
+		  valid_a_lib=:
+		;;
+	      esac
+	      if $valid_a_lib; then
+		echo
+		$ECHO "*** Warning: Linking the shared library $output against the"
+		$ECHO "*** static library $deplib is not portable!"
+		deplibs="$deplib $deplibs"
+	      else
+		echo
+		$ECHO "*** Warning: Trying to link with static lib archive $deplib."
+		echo "*** I have the capability to make that library automatically link in when"
+		echo "*** you link to this library.  But I can only do this if you have a"
+		echo "*** shared version of the library, which you do not appear to have"
+		echo "*** because the file extensions .$libext of this argument makes me believe"
+		echo "*** that it is just a static archive that I should not use here."
+	      fi
+	      ;;
+	    esac
+	    continue
+	    ;;
+	  prog)
+	    if test link != "$pass"; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    fi
+	    continue
+	    ;;
+	  esac # linkmode
+	  ;; # *.$libext
+	*.lo | *.$objext)
+	  if test conv = "$pass"; then
+	    deplibs="$deplib $deplibs"
+	  elif test prog = "$linkmode"; then
+	    if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then
+	      # If there is no dlopen support or we're linking statically,
+	      # we need to preload.
+	      func_append newdlprefiles " $deplib"
+	      compile_deplibs="$deplib $compile_deplibs"
+	      finalize_deplibs="$deplib $finalize_deplibs"
+	    else
+	      func_append newdlfiles " $deplib"
+	    fi
+	  fi
+	  continue
+	  ;;
+	%DEPLIBS%)
+	  alldeplibs=:
+	  continue
+	  ;;
+	esac # case $deplib
+
+	$found || test -f "$lib" \
+	  || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'"
+
+	# Check to see that this really is a libtool archive.
+	func_lalib_unsafe_p "$lib" \
+	  || func_fatal_error "'$lib' is not a valid libtool archive"
+
+	func_dirname "$lib" "" "."
+	ladir=$func_dirname_result
+
+	dlname=
+	dlopen=
+	dlpreopen=
+	libdir=
+	library_names=
+	old_library=
+	inherited_linker_flags=
+	# If the library was installed with an old release of libtool,
+	# it will not redefine variables installed, or shouldnotlink
+	installed=yes
+	shouldnotlink=no
+	avoidtemprpath=
+
+
+	# Read the .la file
+	func_source "$lib"
+
+	# Convert "-framework foo" to "foo.ltframework"
+	if test -n "$inherited_linker_flags"; then
+	  tmp_inherited_linker_flags=`$ECHO "$inherited_linker_flags" | $SED 's/-framework \([^ $]*\)/\1.ltframework/g'`
+	  for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do
+	    case " $new_inherited_linker_flags " in
+	      *" $tmp_inherited_linker_flag "*) ;;
+	      *) func_append new_inherited_linker_flags " $tmp_inherited_linker_flag";;
+	    esac
+	  done
+	fi
+	dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	if test lib,link = "$linkmode,$pass" ||
+	   test prog,scan = "$linkmode,$pass" ||
+	   { test prog != "$linkmode" && test lib != "$linkmode"; }; then
+	  test -n "$dlopen" && func_append dlfiles " $dlopen"
+	  test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen"
+	fi
+
+	if test conv = "$pass"; then
+	  # Only check for convenience libraries
+	  deplibs="$lib $deplibs"
+	  if test -z "$libdir"; then
+	    if test -z "$old_library"; then
+	      func_fatal_error "cannot find name of link library for '$lib'"
+	    fi
+	    # It is a libtool convenience library, so add in its objects.
+	    func_append convenience " $ladir/$objdir/$old_library"
+	    func_append old_convenience " $ladir/$objdir/$old_library"
+	  elif test prog != "$linkmode" && test lib != "$linkmode"; then
+	    func_fatal_error "'$lib' is not a convenience library"
+	  fi
+	  tmp_libs=
+	  for deplib in $dependency_libs; do
+	    deplibs="$deplib $deplibs"
+	    if $opt_preserve_dup_deps; then
+	      case "$tmp_libs " in
+	      *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+	      esac
+	    fi
+	    func_append tmp_libs " $deplib"
+	  done
+	  continue
+	fi # $pass = conv
+
+
+	# Get the name of the library we link against.
+	linklib=
+	if test -n "$old_library" &&
+	   { test yes = "$prefer_static_libs" ||
+	     test built,no = "$prefer_static_libs,$installed"; }; then
+	  linklib=$old_library
+	else
+	  for l in $old_library $library_names; do
+	    linklib=$l
+	  done
+	fi
+	if test -z "$linklib"; then
+	  func_fatal_error "cannot find name of link library for '$lib'"
+	fi
+
+	# This library was specified with -dlopen.
+	if test dlopen = "$pass"; then
+	  test -z "$libdir" \
+	    && func_fatal_error "cannot -dlopen a convenience library: '$lib'"
+	  if test -z "$dlname" ||
+	     test yes != "$dlopen_support" ||
+	     test no = "$build_libtool_libs"
+	  then
+	    # If there is no dlname, no dlopen support or we're linking
+	    # statically, we need to preload.  We also need to preload any
+	    # dependent libraries so libltdl's deplib preloader doesn't
+	    # bomb out in the load deplibs phase.
+	    func_append dlprefiles " $lib $dependency_libs"
+	  else
+	    func_append newdlfiles " $lib"
+	  fi
+	  continue
+	fi # $pass = dlopen
+
+	# We need an absolute path.
+	case $ladir in
+	[\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;;
+	*)
+	  abs_ladir=`cd "$ladir" && pwd`
+	  if test -z "$abs_ladir"; then
+	    func_warning "cannot determine absolute directory name of '$ladir'"
+	    func_warning "passing it literally to the linker, although it might fail"
+	    abs_ladir=$ladir
+	  fi
+	  ;;
+	esac
+	func_basename "$lib"
+	laname=$func_basename_result
+
+	# Find the relevant object directory and library name.
+	if test yes = "$installed"; then
+	  if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+	    func_warning "library '$lib' was moved."
+	    dir=$ladir
+	    absdir=$abs_ladir
+	    libdir=$abs_ladir
+	  else
+	    dir=$lt_sysroot$libdir
+	    absdir=$lt_sysroot$libdir
+	  fi
+	  test yes = "$hardcode_automatic" && avoidtemprpath=yes
+	else
+	  if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
+	    dir=$ladir
+	    absdir=$abs_ladir
+	    # Remove this search path later
+	    func_append notinst_path " $abs_ladir"
+	  else
+	    dir=$ladir/$objdir
+	    absdir=$abs_ladir/$objdir
+	    # Remove this search path later
+	    func_append notinst_path " $abs_ladir"
+	  fi
+	fi # $installed = yes
+	func_stripname 'lib' '.la' "$laname"
+	name=$func_stripname_result
+
+	# This library was specified with -dlpreopen.
+	if test dlpreopen = "$pass"; then
+	  if test -z "$libdir" && test prog = "$linkmode"; then
+	    func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'"
+	  fi
+	  case $host in
+	    # special handling for platforms with PE-DLLs.
+	    *cygwin* | *mingw* | *cegcc* )
+	      # Linker will automatically link against shared library if both
+	      # static and shared are present.  Therefore, ensure we extract
+	      # symbols from the import library if a shared library is present
+	      # (otherwise, the dlopen module name will be incorrect).  We do
+	      # this by putting the import library name into $newdlprefiles.
+	      # We recover the dlopen module name by 'saving' the la file
+	      # name in a special purpose variable, and (later) extracting the
+	      # dlname from the la file.
+	      if test -n "$dlname"; then
+	        func_tr_sh "$dir/$linklib"
+	        eval "libfile_$func_tr_sh_result=\$abs_ladir/\$laname"
+	        func_append newdlprefiles " $dir/$linklib"
+	      else
+	        func_append newdlprefiles " $dir/$old_library"
+	        # Keep a list of preopened convenience libraries to check
+	        # that they are being used correctly in the link pass.
+	        test -z "$libdir" && \
+	          func_append dlpreconveniencelibs " $dir/$old_library"
+	      fi
+	    ;;
+	    * )
+	      # Prefer using a static library (so that no silly _DYNAMIC symbols
+	      # are required to link).
+	      if test -n "$old_library"; then
+	        func_append newdlprefiles " $dir/$old_library"
+	        # Keep a list of preopened convenience libraries to check
+	        # that they are being used correctly in the link pass.
+	        test -z "$libdir" && \
+	          func_append dlpreconveniencelibs " $dir/$old_library"
+	      # Otherwise, use the dlname, so that lt_dlopen finds it.
+	      elif test -n "$dlname"; then
+	        func_append newdlprefiles " $dir/$dlname"
+	      else
+	        func_append newdlprefiles " $dir/$linklib"
+	      fi
+	    ;;
+	  esac
+	fi # $pass = dlpreopen
+
+	if test -z "$libdir"; then
+	  # Link the convenience library
+	  if test lib = "$linkmode"; then
+	    deplibs="$dir/$old_library $deplibs"
+	  elif test prog,link = "$linkmode,$pass"; then
+	    compile_deplibs="$dir/$old_library $compile_deplibs"
+	    finalize_deplibs="$dir/$old_library $finalize_deplibs"
+	  else
+	    deplibs="$lib $deplibs" # used for prog,scan pass
+	  fi
+	  continue
+	fi
+
+
+	if test prog = "$linkmode" && test link != "$pass"; then
+	  func_append newlib_search_path " $ladir"
+	  deplibs="$lib $deplibs"
+
+	  linkalldeplibs=false
+	  if test no != "$link_all_deplibs" || test -z "$library_names" ||
+	     test no = "$build_libtool_libs"; then
+	    linkalldeplibs=:
+	  fi
+
+	  tmp_libs=
+	  for deplib in $dependency_libs; do
+	    case $deplib in
+	    -L*) func_stripname '-L' '' "$deplib"
+	         func_resolve_sysroot "$func_stripname_result"
+	         func_append newlib_search_path " $func_resolve_sysroot_result"
+		 ;;
+	    esac
+	    # Need to link against all dependency_libs?
+	    if $linkalldeplibs; then
+	      deplibs="$deplib $deplibs"
+	    else
+	      # Need to hardcode shared library paths
+	      # or/and link against static libraries
+	      newdependency_libs="$deplib $newdependency_libs"
+	    fi
+	    if $opt_preserve_dup_deps; then
+	      case "$tmp_libs " in
+	      *" $deplib "*) func_append specialdeplibs " $deplib" ;;
+	      esac
+	    fi
+	    func_append tmp_libs " $deplib"
+	  done # for deplib
+	  continue
+	fi # $linkmode = prog...
+
+	if test prog,link = "$linkmode,$pass"; then
+	  if test -n "$library_names" &&
+	     { { test no = "$prefer_static_libs" ||
+	         test built,yes = "$prefer_static_libs,$installed"; } ||
+	       test -z "$old_library"; }; then
+	    # We need to hardcode the library path
+	    if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then
+	      # Make sure the rpath contains only unique directories.
+	      case $temp_rpath: in
+	      *"$absdir:"*) ;;
+	      *) func_append temp_rpath "$absdir:" ;;
+	      esac
+	    fi
+
+	    # Hardcode the library path.
+	    # Skip directories that are in the system default run-time
+	    # search path.
+	    case " $sys_lib_dlsearch_path " in
+	    *" $absdir "*) ;;
+	    *)
+	      case "$compile_rpath " in
+	      *" $absdir "*) ;;
+	      *) func_append compile_rpath " $absdir" ;;
+	      esac
+	      ;;
+	    esac
+	    case " $sys_lib_dlsearch_path " in
+	    *" $libdir "*) ;;
+	    *)
+	      case "$finalize_rpath " in
+	      *" $libdir "*) ;;
+	      *) func_append finalize_rpath " $libdir" ;;
+	      esac
+	      ;;
+	    esac
+	  fi # $linkmode,$pass = prog,link...
+
+	  if $alldeplibs &&
+	     { test pass_all = "$deplibs_check_method" ||
+	       { test yes = "$build_libtool_libs" &&
+		 test -n "$library_names"; }; }; then
+	    # We only need to search for static libraries
+	    continue
+	  fi
+	fi
+
+	link_static=no # Whether the deplib will be linked statically
+	use_static_libs=$prefer_static_libs
+	if test built = "$use_static_libs" && test yes = "$installed"; then
+	  use_static_libs=no
+	fi
+	if test -n "$library_names" &&
+	   { test no = "$use_static_libs" || test -z "$old_library"; }; then
+	  case $host in
+	  *cygwin* | *mingw* | *cegcc* | *os2*)
+	      # No point in relinking DLLs because paths are not encoded
+	      func_append notinst_deplibs " $lib"
+	      need_relink=no
+	    ;;
+	  *)
+	    if test no = "$installed"; then
+	      func_append notinst_deplibs " $lib"
+	      need_relink=yes
+	    fi
+	    ;;
+	  esac
+	  # This is a shared library
+
+	  # Warn about portability, can't link against -module's on some
+	  # systems (darwin).  Don't bleat about dlopened modules though!
+	  dlopenmodule=
+	  for dlpremoduletest in $dlprefiles; do
+	    if test "X$dlpremoduletest" = "X$lib"; then
+	      dlopenmodule=$dlpremoduletest
+	      break
+	    fi
+	  done
+	  if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then
+	    echo
+	    if test prog = "$linkmode"; then
+	      $ECHO "*** Warning: Linking the executable $output against the loadable module"
+	    else
+	      $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+	    fi
+	    $ECHO "*** $linklib is not portable!"
+	  fi
+	  if test lib = "$linkmode" &&
+	     test yes = "$hardcode_into_libs"; then
+	    # Hardcode the library path.
+	    # Skip directories that are in the system default run-time
+	    # search path.
+	    case " $sys_lib_dlsearch_path " in
+	    *" $absdir "*) ;;
+	    *)
+	      case "$compile_rpath " in
+	      *" $absdir "*) ;;
+	      *) func_append compile_rpath " $absdir" ;;
+	      esac
+	      ;;
+	    esac
+	    case " $sys_lib_dlsearch_path " in
+	    *" $libdir "*) ;;
+	    *)
+	      case "$finalize_rpath " in
+	      *" $libdir "*) ;;
+	      *) func_append finalize_rpath " $libdir" ;;
+	      esac
+	      ;;
+	    esac
+	  fi
+
+	  if test -n "$old_archive_from_expsyms_cmds"; then
+	    # figure out the soname
+	    set dummy $library_names
+	    shift
+	    realname=$1
+	    shift
+	    libname=`eval "\\$ECHO \"$libname_spec\""`
+	    # use dlname if we got it. it's perfectly good, no?
+	    if test -n "$dlname"; then
+	      soname=$dlname
+	    elif test -n "$soname_spec"; then
+	      # bleh windows
+	      case $host in
+	      *cygwin* | mingw* | *cegcc* | *os2*)
+	        func_arith $current - $age
+		major=$func_arith_result
+		versuffix=-$major
+		;;
+	      esac
+	      eval soname=\"$soname_spec\"
+	    else
+	      soname=$realname
+	    fi
+
+	    # Make a new name for the extract_expsyms_cmds to use
+	    soroot=$soname
+	    func_basename "$soroot"
+	    soname=$func_basename_result
+	    func_stripname 'lib' '.dll' "$soname"
+	    newlib=libimp-$func_stripname_result.a
+
+	    # If the library has no export list, then create one now
+	    if test -f "$output_objdir/$soname-def"; then :
+	    else
+	      func_verbose "extracting exported symbol list from '$soname'"
+	      func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
+	    fi
+
+	    # Create $newlib
+	    if test -f "$output_objdir/$newlib"; then :; else
+	      func_verbose "generating import library for '$soname'"
+	      func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
+	    fi
+	    # make sure the library variables are pointing to the new library
+	    dir=$output_objdir
+	    linklib=$newlib
+	  fi # test -n "$old_archive_from_expsyms_cmds"
+
+	  if test prog = "$linkmode" || test relink != "$opt_mode"; then
+	    add_shlibpath=
+	    add_dir=
+	    add=
+	    lib_linked=yes
+	    case $hardcode_action in
+	    immediate | unsupported)
+	      if test no = "$hardcode_direct"; then
+		add=$dir/$linklib
+		case $host in
+		  *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;;
+		  *-*-sysv4*uw2*) add_dir=-L$dir ;;
+		  *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
+		    *-*-unixware7*) add_dir=-L$dir ;;
+		  *-*-darwin* )
+		    # if the lib is a (non-dlopened) module then we cannot
+		    # link against it, someone is ignoring the earlier warnings
+		    if /usr/bin/file -L $add 2> /dev/null |
+			 $GREP ": [^:]* bundle" >/dev/null; then
+		      if test "X$dlopenmodule" != "X$lib"; then
+			$ECHO "*** Warning: lib $linklib is a module, not a shared library"
+			if test -z "$old_library"; then
+			  echo
+			  echo "*** And there doesn't seem to be a static archive available"
+			  echo "*** The link will probably fail, sorry"
+			else
+			  add=$dir/$old_library
+			fi
+		      elif test -n "$old_library"; then
+			add=$dir/$old_library
+		      fi
+		    fi
+		esac
+	      elif test no = "$hardcode_minus_L"; then
+		case $host in
+		*-*-sunos*) add_shlibpath=$dir ;;
+		esac
+		add_dir=-L$dir
+		add=-l$name
+	      elif test no = "$hardcode_shlibpath_var"; then
+		add_shlibpath=$dir
+		add=-l$name
+	      else
+		lib_linked=no
+	      fi
+	      ;;
+	    relink)
+	      if test yes = "$hardcode_direct" &&
+	         test no = "$hardcode_direct_absolute"; then
+		add=$dir/$linklib
+	      elif test yes = "$hardcode_minus_L"; then
+		add_dir=-L$absdir
+		# Try looking first in the location we're being installed to.
+		if test -n "$inst_prefix_dir"; then
+		  case $libdir in
+		    [\\/]*)
+		      func_append add_dir " -L$inst_prefix_dir$libdir"
+		      ;;
+		  esac
+		fi
+		add=-l$name
+	      elif test yes = "$hardcode_shlibpath_var"; then
+		add_shlibpath=$dir
+		add=-l$name
+	      else
+		lib_linked=no
+	      fi
+	      ;;
+	    *) lib_linked=no ;;
+	    esac
+
+	    if test yes != "$lib_linked"; then
+	      func_fatal_configuration "unsupported hardcode properties"
+	    fi
+
+	    if test -n "$add_shlibpath"; then
+	      case :$compile_shlibpath: in
+	      *":$add_shlibpath:"*) ;;
+	      *) func_append compile_shlibpath "$add_shlibpath:" ;;
+	      esac
+	    fi
+	    if test prog = "$linkmode"; then
+	      test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
+	      test -n "$add" && compile_deplibs="$add $compile_deplibs"
+	    else
+	      test -n "$add_dir" && deplibs="$add_dir $deplibs"
+	      test -n "$add" && deplibs="$add $deplibs"
+	      if test yes != "$hardcode_direct" &&
+		 test yes != "$hardcode_minus_L" &&
+		 test yes = "$hardcode_shlibpath_var"; then
+		case :$finalize_shlibpath: in
+		*":$libdir:"*) ;;
+		*) func_append finalize_shlibpath "$libdir:" ;;
+		esac
+	      fi
+	    fi
+	  fi
+
+	  if test prog = "$linkmode" || test relink = "$opt_mode"; then
+	    add_shlibpath=
+	    add_dir=
+	    add=
+	    # Finalize command for both is simple: just hardcode it.
+	    if test yes = "$hardcode_direct" &&
+	       test no = "$hardcode_direct_absolute"; then
+	      add=$libdir/$linklib
+	    elif test yes = "$hardcode_minus_L"; then
+	      add_dir=-L$libdir
+	      add=-l$name
+	    elif test yes = "$hardcode_shlibpath_var"; then
+	      case :$finalize_shlibpath: in
+	      *":$libdir:"*) ;;
+	      *) func_append finalize_shlibpath "$libdir:" ;;
+	      esac
+	      add=-l$name
+	    elif test yes = "$hardcode_automatic"; then
+	      if test -n "$inst_prefix_dir" &&
+		 test -f "$inst_prefix_dir$libdir/$linklib"; then
+		add=$inst_prefix_dir$libdir/$linklib
+	      else
+		add=$libdir/$linklib
+	      fi
+	    else
+	      # We cannot seem to hardcode it, guess we'll fake it.
+	      add_dir=-L$libdir
+	      # Try looking first in the location we're being installed to.
+	      if test -n "$inst_prefix_dir"; then
+		case $libdir in
+		  [\\/]*)
+		    func_append add_dir " -L$inst_prefix_dir$libdir"
+		    ;;
+		esac
+	      fi
+	      add=-l$name
+	    fi
+
+	    if test prog = "$linkmode"; then
+	      test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
+	      test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
+	    else
+	      test -n "$add_dir" && deplibs="$add_dir $deplibs"
+	      test -n "$add" && deplibs="$add $deplibs"
+	    fi
+	  fi
+	elif test prog = "$linkmode"; then
+	  # Here we assume that one of hardcode_direct or hardcode_minus_L
+	  # is not unsupported.  This is valid on all known static and
+	  # shared platforms.
+	  if test unsupported != "$hardcode_direct"; then
+	    test -n "$old_library" && linklib=$old_library
+	    compile_deplibs="$dir/$linklib $compile_deplibs"
+	    finalize_deplibs="$dir/$linklib $finalize_deplibs"
+	  else
+	    compile_deplibs="-l$name -L$dir $compile_deplibs"
+	    finalize_deplibs="-l$name -L$dir $finalize_deplibs"
+	  fi
+	elif test yes = "$build_libtool_libs"; then
+	  # Not a shared library
+	  if test pass_all != "$deplibs_check_method"; then
+	    # We're trying link a shared library against a static one
+	    # but the system doesn't support it.
+
+	    # Just print a warning and add the library to dependency_libs so
+	    # that the program can be linked against the static library.
+	    echo
+	    $ECHO "*** Warning: This system cannot link to static lib archive $lib."
+	    echo "*** I have the capability to make that library automatically link in when"
+	    echo "*** you link to this library.  But I can only do this if you have a"
+	    echo "*** shared version of the library, which you do not appear to have."
+	    if test yes = "$module"; then
+	      echo "*** But as you try to build a module library, libtool will still create "
+	      echo "*** a static module, that should work as long as the dlopening application"
+	      echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
+	      if test -z "$global_symbol_pipe"; then
+		echo
+		echo "*** However, this would only work if libtool was able to extract symbol"
+		echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
+		echo "*** not find such a program.  So, this module is probably useless."
+		echo "*** 'nm' from GNU binutils and a full rebuild may help."
+	      fi
+	      if test no = "$build_old_libs"; then
+		build_libtool_libs=module
+		build_old_libs=yes
+	      else
+		build_libtool_libs=no
+	      fi
+	    fi
+	  else
+	    deplibs="$dir/$old_library $deplibs"
+	    link_static=yes
+	  fi
+	fi # link shared/static library?
+
+	if test lib = "$linkmode"; then
+	  if test -n "$dependency_libs" &&
+	     { test yes != "$hardcode_into_libs" ||
+	       test yes = "$build_old_libs" ||
+	       test yes = "$link_static"; }; then
+	    # Extract -R from dependency_libs
+	    temp_deplibs=
+	    for libdir in $dependency_libs; do
+	      case $libdir in
+	      -R*) func_stripname '-R' '' "$libdir"
+	           temp_xrpath=$func_stripname_result
+		   case " $xrpath " in
+		   *" $temp_xrpath "*) ;;
+		   *) func_append xrpath " $temp_xrpath";;
+		   esac;;
+	      *) func_append temp_deplibs " $libdir";;
+	      esac
+	    done
+	    dependency_libs=$temp_deplibs
+	  fi
+
+	  func_append newlib_search_path " $absdir"
+	  # Link against this library
+	  test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+	  # ... and its dependency_libs
+	  tmp_libs=
+	  for deplib in $dependency_libs; do
+	    newdependency_libs="$deplib $newdependency_libs"
+	    case $deplib in
+              -L*) func_stripname '-L' '' "$deplib"
+                   func_resolve_sysroot "$func_stripname_result";;
+              *) func_resolve_sysroot "$deplib" ;;
+            esac
+	    if $opt_preserve_dup_deps; then
+	      case "$tmp_libs " in
+	      *" $func_resolve_sysroot_result "*)
+                func_append specialdeplibs " $func_resolve_sysroot_result" ;;
+	      esac
+	    fi
+	    func_append tmp_libs " $func_resolve_sysroot_result"
+	  done
+
+	  if test no != "$link_all_deplibs"; then
+	    # Add the search paths of all dependency libraries
+	    for deplib in $dependency_libs; do
+	      path=
+	      case $deplib in
+	      -L*) path=$deplib ;;
+	      *.la)
+	        func_resolve_sysroot "$deplib"
+	        deplib=$func_resolve_sysroot_result
+	        func_dirname "$deplib" "" "."
+		dir=$func_dirname_result
+		# We need an absolute path.
+		case $dir in
+		[\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;;
+		*)
+		  absdir=`cd "$dir" && pwd`
+		  if test -z "$absdir"; then
+		    func_warning "cannot determine absolute directory name of '$dir'"
+		    absdir=$dir
+		  fi
+		  ;;
+		esac
+		if $GREP "^installed=no" $deplib > /dev/null; then
+		case $host in
+		*-*-darwin*)
+		  depdepl=
+		  eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+		  if test -n "$deplibrary_names"; then
+		    for tmp in $deplibrary_names; do
+		      depdepl=$tmp
+		    done
+		    if test -f "$absdir/$objdir/$depdepl"; then
+		      depdepl=$absdir/$objdir/$depdepl
+		      darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+                      if test -z "$darwin_install_name"; then
+                          darwin_install_name=`$OTOOL64 -L $depdepl  | awk '{if (NR == 2) {print $1;exit}}'`
+                      fi
+		      func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl"
+		      func_append linker_flags " -dylib_file $darwin_install_name:$depdepl"
+		      path=
+		    fi
+		  fi
+		  ;;
+		*)
+		  path=-L$absdir/$objdir
+		  ;;
+		esac
+		else
+		  eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+		  test -z "$libdir" && \
+		    func_fatal_error "'$deplib' is not a valid libtool archive"
+		  test "$absdir" != "$libdir" && \
+		    func_warning "'$deplib' seems to be moved"
+
+		  path=-L$absdir
+		fi
+		;;
+	      esac
+	      case " $deplibs " in
+	      *" $path "*) ;;
+	      *) deplibs="$path $deplibs" ;;
+	      esac
+	    done
+	  fi # link_all_deplibs != no
+	fi # linkmode = lib
+      done # for deplib in $libs
+      if test link = "$pass"; then
+	if test prog = "$linkmode"; then
+	  compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
+	  finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
+	else
+	  compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	fi
+      fi
+      dependency_libs=$newdependency_libs
+      if test dlpreopen = "$pass"; then
+	# Link the dlpreopened libraries before other libraries
+	for deplib in $save_deplibs; do
+	  deplibs="$deplib $deplibs"
+	done
+      fi
+      if test dlopen != "$pass"; then
+	test conv = "$pass" || {
+	  # Make sure lib_search_path contains only unique directories.
+	  lib_search_path=
+	  for dir in $newlib_search_path; do
+	    case "$lib_search_path " in
+	    *" $dir "*) ;;
+	    *) func_append lib_search_path " $dir" ;;
+	    esac
+	  done
+	  newlib_search_path=
+	}
+
+	if test prog,link = "$linkmode,$pass"; then
+	  vars="compile_deplibs finalize_deplibs"
+	else
+	  vars=deplibs
+	fi
+	for var in $vars dependency_libs; do
+	  # Add libraries to $var in reverse order
+	  eval tmp_libs=\"\$$var\"
+	  new_libs=
+	  for deplib in $tmp_libs; do
+	    # FIXME: Pedantically, this is the right thing to do, so
+	    #        that some nasty dependency loop isn't accidentally
+	    #        broken:
+	    #new_libs="$deplib $new_libs"
+	    # Pragmatically, this seems to cause very few problems in
+	    # practice:
+	    case $deplib in
+	    -L*) new_libs="$deplib $new_libs" ;;
+	    -R*) ;;
+	    *)
+	      # And here is the reason: when a library appears more
+	      # than once as an explicit dependence of a library, or
+	      # is implicitly linked in more than once by the
+	      # compiler, it is considered special, and multiple
+	      # occurrences thereof are not removed.  Compare this
+	      # with having the same library being listed as a
+	      # dependency of multiple other libraries: in this case,
+	      # we know (pedantically, we assume) the library does not
+	      # need to be listed more than once, so we keep only the
+	      # last copy.  This is not always right, but it is rare
+	      # enough that we require users that really mean to play
+	      # such unportable linking tricks to link the library
+	      # using -Wl,-lname, so that libtool does not consider it
+	      # for duplicate removal.
+	      case " $specialdeplibs " in
+	      *" $deplib "*) new_libs="$deplib $new_libs" ;;
+	      *)
+		case " $new_libs " in
+		*" $deplib "*) ;;
+		*) new_libs="$deplib $new_libs" ;;
+		esac
+		;;
+	      esac
+	      ;;
+	    esac
+	  done
+	  tmp_libs=
+	  for deplib in $new_libs; do
+	    case $deplib in
+	    -L*)
+	      case " $tmp_libs " in
+	      *" $deplib "*) ;;
+	      *) func_append tmp_libs " $deplib" ;;
+	      esac
+	      ;;
+	    *) func_append tmp_libs " $deplib" ;;
+	    esac
+	  done
+	  eval $var=\"$tmp_libs\"
+	done # for var
+      fi
+
+      # Add Sun CC postdeps if required:
+      test CXX = "$tagname" && {
+        case $host_os in
+        linux*)
+          case `$CC -V 2>&1 | $SED 5q` in
+          *Sun\ C*) # Sun C++ 5.9
+            func_suncc_cstd_abi
+
+            if test no != "$suncc_use_cstd_abi"; then
+              func_append postdeps ' -library=Cstd -library=Crun'
+            fi
+            ;;
+          esac
+          ;;
+
+        solaris*)
+          func_cc_basename "$CC"
+          case $func_cc_basename_result in
+          CC* | sunCC*)
+            func_suncc_cstd_abi
+
+            if test no != "$suncc_use_cstd_abi"; then
+              func_append postdeps ' -library=Cstd -library=Crun'
+            fi
+            ;;
+          esac
+          ;;
+        esac
+      }
+
+      # Last step: remove runtime libs from dependency_libs
+      # (they stay in deplibs)
+      tmp_libs=
+      for i in $dependency_libs; do
+	case " $predeps $postdeps $compiler_lib_search_path " in
+	*" $i "*)
+	  i=
+	  ;;
+	esac
+	if test -n "$i"; then
+	  func_append tmp_libs " $i"
+	fi
+      done
+      dependency_libs=$tmp_libs
+    done # for pass
+    if test prog = "$linkmode"; then
+      dlfiles=$newdlfiles
+    fi
+    if test prog = "$linkmode" || test lib = "$linkmode"; then
+      dlprefiles=$newdlprefiles
+    fi
+
+    case $linkmode in
+    oldlib)
+      if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+	func_warning "'-dlopen' is ignored for archives"
+      fi
+
+      case " $deplibs" in
+      *\ -l* | *\ -L*)
+	func_warning "'-l' and '-L' are ignored for archives" ;;
+      esac
+
+      test -n "$rpath" && \
+	func_warning "'-rpath' is ignored for archives"
+
+      test -n "$xrpath" && \
+	func_warning "'-R' is ignored for archives"
+
+      test -n "$vinfo" && \
+	func_warning "'-version-info/-version-number' is ignored for archives"
+
+      test -n "$release" && \
+	func_warning "'-release' is ignored for archives"
+
+      test -n "$export_symbols$export_symbols_regex" && \
+	func_warning "'-export-symbols' is ignored for archives"
+
+      # Now set the variables for building old libraries.
+      build_libtool_libs=no
+      oldlibs=$output
+      func_append objs "$old_deplibs"
+      ;;
+
+    lib)
+      # Make sure we only generate libraries of the form 'libNAME.la'.
+      case $outputname in
+      lib*)
+	func_stripname 'lib' '.la' "$outputname"
+	name=$func_stripname_result
+	eval shared_ext=\"$shrext_cmds\"
+	eval libname=\"$libname_spec\"
+	;;
+      *)
+	test no = "$module" \
+	  && func_fatal_help "libtool library '$output' must begin with 'lib'"
+
+	if test no != "$need_lib_prefix"; then
+	  # Add the "lib" prefix for modules if required
+	  func_stripname '' '.la' "$outputname"
+	  name=$func_stripname_result
+	  eval shared_ext=\"$shrext_cmds\"
+	  eval libname=\"$libname_spec\"
+	else
+	  func_stripname '' '.la' "$outputname"
+	  libname=$func_stripname_result
+	fi
+	;;
+      esac
+
+      if test -n "$objs"; then
+	if test pass_all != "$deplibs_check_method"; then
+	  func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs"
+	else
+	  echo
+	  $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
+	  $ECHO "*** objects $objs is not portable!"
+	  func_append libobjs " $objs"
+	fi
+      fi
+
+      test no = "$dlself" \
+	|| func_warning "'-dlopen self' is ignored for libtool libraries"
+
+      set dummy $rpath
+      shift
+      test 1 -lt "$#" \
+	&& func_warning "ignoring multiple '-rpath's for a libtool library"
+
+      install_libdir=$1
+
+      oldlibs=
+      if test -z "$rpath"; then
+	if test yes = "$build_libtool_libs"; then
+	  # Building a libtool convenience library.
+	  # Some compilers have problems with a '.al' extension so
+	  # convenience libraries should have the same extension an
+	  # archive normally would.
+	  oldlibs="$output_objdir/$libname.$libext $oldlibs"
+	  build_libtool_libs=convenience
+	  build_old_libs=yes
+	fi
+
+	test -n "$vinfo" && \
+	  func_warning "'-version-info/-version-number' is ignored for convenience libraries"
+
+	test -n "$release" && \
+	  func_warning "'-release' is ignored for convenience libraries"
+      else
+
+	# Parse the version information argument.
+	save_ifs=$IFS; IFS=:
+	set dummy $vinfo 0 0 0
+	shift
+	IFS=$save_ifs
+
+	test -n "$7" && \
+	  func_fatal_help "too many parameters to '-version-info'"
+
+	# convert absolute version numbers to libtool ages
+	# this retains compatibility with .la files and attempts
+	# to make the code below a bit more comprehensible
+
+	case $vinfo_number in
+	yes)
+	  number_major=$1
+	  number_minor=$2
+	  number_revision=$3
+	  #
+	  # There are really only two kinds -- those that
+	  # use the current revision as the major version
+	  # and those that subtract age and use age as
+	  # a minor version.  But, then there is irix
+	  # that has an extra 1 added just for fun
+	  #
+	  case $version_type in
+	  # correct linux to gnu/linux during the next big refactor
+	  darwin|freebsd-elf|linux|midnightbsd-elf|osf|windows|none)
+	    func_arith $number_major + $number_minor
+	    current=$func_arith_result
+	    age=$number_minor
+	    revision=$number_revision
+	    ;;
+	  freebsd-aout|qnx|sunos)
+	    current=$number_major
+	    revision=$number_minor
+	    age=0
+	    ;;
+	  irix|nonstopux)
+	    func_arith $number_major + $number_minor
+	    current=$func_arith_result
+	    age=$number_minor
+	    revision=$number_minor
+	    lt_irix_increment=no
+	    ;;
+	  esac
+	  ;;
+	no)
+	  current=$1
+	  revision=$2
+	  age=$3
+	  ;;
+	esac
+
+	# Check that each of the things are valid numbers.
+	case $current in
+	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+	*)
+	  func_error "CURRENT '$current' must be a nonnegative integer"
+	  func_fatal_error "'$vinfo' is not valid version information"
+	  ;;
+	esac
+
+	case $revision in
+	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+	*)
+	  func_error "REVISION '$revision' must be a nonnegative integer"
+	  func_fatal_error "'$vinfo' is not valid version information"
+	  ;;
+	esac
+
+	case $age in
+	0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
+	*)
+	  func_error "AGE '$age' must be a nonnegative integer"
+	  func_fatal_error "'$vinfo' is not valid version information"
+	  ;;
+	esac
+
+	if test "$age" -gt "$current"; then
+	  func_error "AGE '$age' is greater than the current interface number '$current'"
+	  func_fatal_error "'$vinfo' is not valid version information"
+	fi
+
+	# Calculate the version variables.
+	major=
+	versuffix=
+	verstring=
+	case $version_type in
+	none) ;;
+
+	darwin)
+	  # Like Linux, but with the current version available in
+	  # verstring for coding it into the library header
+	  func_arith $current - $age
+	  major=.$func_arith_result
+	  versuffix=$major.$age.$revision
+	  # Darwin ld doesn't like 0 for these options...
+	  func_arith $current + 1
+	  minor_current=$func_arith_result
+	  xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
+	  verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+          # On Darwin other compilers
+          case $CC in
+              nagfor*)
+                  verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
+                  ;;
+              *)
+                  verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+                  ;;
+          esac
+	  ;;
+
+	freebsd-aout)
+	  major=.$current
+	  versuffix=.$current.$revision
+	  ;;
+
+	freebsd-elf | midnightbsd-elf)
+	  func_arith $current - $age
+	  major=.$func_arith_result
+	  versuffix=$major.$age.$revision
+	  ;;
+
+	irix | nonstopux)
+	  if test no = "$lt_irix_increment"; then
+	    func_arith $current - $age
+	  else
+	    func_arith $current - $age + 1
+	  fi
+	  major=$func_arith_result
+
+	  case $version_type in
+	    nonstopux) verstring_prefix=nonstopux ;;
+	    *)         verstring_prefix=sgi ;;
+	  esac
+	  verstring=$verstring_prefix$major.$revision
+
+	  # Add in all the interfaces that we are compatible with.
+	  loop=$revision
+	  while test 0 -ne "$loop"; do
+	    func_arith $revision - $loop
+	    iface=$func_arith_result
+	    func_arith $loop - 1
+	    loop=$func_arith_result
+	    verstring=$verstring_prefix$major.$iface:$verstring
+	  done
+
+	  # Before this point, $major must not contain '.'.
+	  major=.$major
+	  versuffix=$major.$revision
+	  ;;
+
+	linux) # correct to gnu/linux during the next big refactor
+	  func_arith $current - $age
+	  major=.$func_arith_result
+	  versuffix=$major.$age.$revision
+	  ;;
+
+	osf)
+	  func_arith $current - $age
+	  major=.$func_arith_result
+	  versuffix=.$current.$age.$revision
+	  verstring=$current.$age.$revision
+
+	  # Add in all the interfaces that we are compatible with.
+	  loop=$age
+	  while test 0 -ne "$loop"; do
+	    func_arith $current - $loop
+	    iface=$func_arith_result
+	    func_arith $loop - 1
+	    loop=$func_arith_result
+	    verstring=$verstring:$iface.0
+	  done
+
+	  # Make executables depend on our current version.
+	  func_append verstring ":$current.0"
+	  ;;
+
+	qnx)
+	  major=.$current
+	  versuffix=.$current
+	  ;;
+
+	sco)
+	  major=.$current
+	  versuffix=.$current
+	  ;;
+
+	sunos)
+	  major=.$current
+	  versuffix=.$current.$revision
+	  ;;
+
+	windows)
+	  # Use '-' rather than '.', since we only want one
+	  # extension on DOS 8.3 file systems.
+	  func_arith $current - $age
+	  major=$func_arith_result
+	  versuffix=-$major
+	  ;;
+
+	*)
+	  func_fatal_configuration "unknown library version type '$version_type'"
+	  ;;
+	esac
+
+	# Clear the version info if we defaulted, and they specified a release.
+	if test -z "$vinfo" && test -n "$release"; then
+	  major=
+	  case $version_type in
+	  darwin)
+	    # we can't check for "0.0" in archive_cmds due to quoting
+	    # problems, so we reset it completely
+	    verstring=
+	    ;;
+	  *)
+	    verstring=0.0
+	    ;;
+	  esac
+	  if test no = "$need_version"; then
+	    versuffix=
+	  else
+	    versuffix=.0.0
+	  fi
+	fi
+
+	# Remove version info from name if versioning should be avoided
+	if test yes,no = "$avoid_version,$need_version"; then
+	  major=
+	  versuffix=
+	  verstring=
+	fi
+
+	# Check to see if the archive will have undefined symbols.
+	if test yes = "$allow_undefined"; then
+	  if test unsupported = "$allow_undefined_flag"; then
+	    if test yes = "$build_old_libs"; then
+	      func_warning "undefined symbols not allowed in $host shared libraries; building static only"
+	      build_libtool_libs=no
+	    else
+	      func_fatal_error "can't build $host shared library unless -no-undefined is specified"
+	    fi
+	  fi
+	else
+	  # Don't allow undefined symbols.
+	  allow_undefined_flag=$no_undefined_flag
+	fi
+
+      fi
+
+      func_generate_dlsyms "$libname" "$libname" :
+      func_append libobjs " $symfileobj"
+      test " " = "$libobjs" && libobjs=
+
+      if test relink != "$opt_mode"; then
+	# Remove our outputs, but don't remove object files since they
+	# may have been created when compiling PIC objects.
+	removelist=
+	tempremovelist=`$ECHO "$output_objdir/*"`
+	for p in $tempremovelist; do
+	  case $p in
+	    *.$objext | *.gcno)
+	       ;;
+	    $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*)
+	       if test -n "$precious_files_regex"; then
+		 if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
+		 then
+		   continue
+		 fi
+	       fi
+	       func_append removelist " $p"
+	       ;;
+	    *) ;;
+	  esac
+	done
+	test -n "$removelist" && \
+	  func_show_eval "${RM}r \$removelist"
+      fi
+
+      # Now set the variables for building old libraries.
+      if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then
+	func_append oldlibs " $output_objdir/$libname.$libext"
+
+	# Transform .lo files to .o files.
+	oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP`
+      fi
+
+      # Eliminate all temporary directories.
+      #for path in $notinst_path; do
+      #	lib_search_path=`$ECHO "$lib_search_path " | $SED "s% $path % %g"`
+      #	deplibs=`$ECHO "$deplibs " | $SED "s% -L$path % %g"`
+      #	dependency_libs=`$ECHO "$dependency_libs " | $SED "s% -L$path % %g"`
+      #done
+
+      if test -n "$xrpath"; then
+	# If the user specified any rpath flags, then add them.
+	temp_xrpath=
+	for libdir in $xrpath; do
+	  func_replace_sysroot "$libdir"
+	  func_append temp_xrpath " -R$func_replace_sysroot_result"
+	  case "$finalize_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append finalize_rpath " $libdir" ;;
+	  esac
+	done
+	if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then
+	  dependency_libs="$temp_xrpath $dependency_libs"
+	fi
+      fi
+
+      # Make sure dlfiles contains only unique files that won't be dlpreopened
+      old_dlfiles=$dlfiles
+      dlfiles=
+      for lib in $old_dlfiles; do
+	case " $dlprefiles $dlfiles " in
+	*" $lib "*) ;;
+	*) func_append dlfiles " $lib" ;;
+	esac
+      done
+
+      # Make sure dlprefiles contains only unique files
+      old_dlprefiles=$dlprefiles
+      dlprefiles=
+      for lib in $old_dlprefiles; do
+	case "$dlprefiles " in
+	*" $lib "*) ;;
+	*) func_append dlprefiles " $lib" ;;
+	esac
+      done
+
+      if test yes = "$build_libtool_libs"; then
+	if test -n "$rpath"; then
+	  case $host in
+	  *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
+	    # these systems don't actually have a c library (as such)!
+	    ;;
+	  *-*-rhapsody* | *-*-darwin1.[012])
+	    # Rhapsody C library is in the System framework
+	    func_append deplibs " System.ltframework"
+	    ;;
+	  *-*-netbsd*)
+	    # Don't link with libc until the a.out ld.so is fixed.
+	    ;;
+	  *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*)
+	    # Do not include libc due to us having libc/libc_r.
+	    ;;
+	  *-*-sco3.2v5* | *-*-sco5v6*)
+	    # Causes problems with __ctype
+	    ;;
+	  *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
+	    # Compiler inserts libc in the correct place for threads to work
+	    ;;
+	  *)
+	    # Add libc to deplibs on all other systems if necessary.
+	    if test yes = "$build_libtool_need_lc"; then
+	      func_append deplibs " -lc"
+	    fi
+	    ;;
+	  esac
+	fi
+
+	# Transform deplibs into only deplibs that can be linked in shared.
+	name_save=$name
+	libname_save=$libname
+	release_save=$release
+	versuffix_save=$versuffix
+	major_save=$major
+	# I'm not sure if I'm treating the release correctly.  I think
+	# release should show up in the -l (ie -lgmp5) so we don't want to
+	# add it in twice.  Is that correct?
+	release=
+	versuffix=
+	major=
+	newdeplibs=
+	droppeddeps=no
+	case $deplibs_check_method in
+	pass_all)
+	  # Don't check for shared/static.  Everything works.
+	  # This might be a little naive.  We might want to check
+	  # whether the library exists or not.  But this is on
+	  # osf3 & osf4 and I'm not really sure... Just
+	  # implementing what was already the behavior.
+	  newdeplibs=$deplibs
+	  ;;
+	test_compile)
+	  # This code stresses the "libraries are programs" paradigm to its
+	  # limits. Maybe even breaks it.  We compile a program, linking it
+	  # against the deplibs as a proxy for the library.  Then we can check
+	  # whether they linked in statically or dynamically with ldd.
+	  $opt_dry_run || $RM conftest.c
+	  cat > conftest.c <<EOF
+	  int main() { return 0; }
+EOF
+	  $opt_dry_run || $RM conftest
+	  if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
+	    ldd_output=`ldd conftest`
+	    for i in $deplibs; do
+	      case $i in
+	      -l*)
+		func_stripname -l '' "$i"
+		name=$func_stripname_result
+		if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+		  case " $predeps $postdeps " in
+		  *" $i "*)
+		    func_append newdeplibs " $i"
+		    i=
+		    ;;
+		  esac
+		fi
+		if test -n "$i"; then
+		  libname=`eval "\\$ECHO \"$libname_spec\""`
+		  deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+		  set dummy $deplib_matches; shift
+		  deplib_match=$1
+		  if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
+		    func_append newdeplibs " $i"
+		  else
+		    droppeddeps=yes
+		    echo
+		    $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+		    echo "*** I have the capability to make that library automatically link in when"
+		    echo "*** you link to this library.  But I can only do this if you have a"
+		    echo "*** shared version of the library, which I believe you do not have"
+		    echo "*** because a test_compile did reveal that the linker did not use it for"
+		    echo "*** its dynamic dependency list that programs get resolved with at runtime."
+		  fi
+		fi
+		;;
+	      *)
+		func_append newdeplibs " $i"
+		;;
+	      esac
+	    done
+	  else
+	    # Error occurred in the first compile.  Let's try to salvage
+	    # the situation: Compile a separate program for each library.
+	    for i in $deplibs; do
+	      case $i in
+	      -l*)
+		func_stripname -l '' "$i"
+		name=$func_stripname_result
+		$opt_dry_run || $RM conftest
+		if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
+		  ldd_output=`ldd conftest`
+		  if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+		    case " $predeps $postdeps " in
+		    *" $i "*)
+		      func_append newdeplibs " $i"
+		      i=
+		      ;;
+		    esac
+		  fi
+		  if test -n "$i"; then
+		    libname=`eval "\\$ECHO \"$libname_spec\""`
+		    deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
+		    set dummy $deplib_matches; shift
+		    deplib_match=$1
+		    if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
+		      func_append newdeplibs " $i"
+		    else
+		      droppeddeps=yes
+		      echo
+		      $ECHO "*** Warning: dynamic linker does not accept needed library $i."
+		      echo "*** I have the capability to make that library automatically link in when"
+		      echo "*** you link to this library.  But I can only do this if you have a"
+		      echo "*** shared version of the library, which you do not appear to have"
+		      echo "*** because a test_compile did reveal that the linker did not use this one"
+		      echo "*** as a dynamic dependency that programs can get resolved with at runtime."
+		    fi
+		  fi
+		else
+		  droppeddeps=yes
+		  echo
+		  $ECHO "*** Warning!  Library $i is needed by this library but I was not able to"
+		  echo "*** make it link in!  You will probably need to install it or some"
+		  echo "*** library that it depends on before this library will be fully"
+		  echo "*** functional.  Installing it before continuing would be even better."
+		fi
+		;;
+	      *)
+		func_append newdeplibs " $i"
+		;;
+	      esac
+	    done
+	  fi
+	  ;;
+	file_magic*)
+	  set dummy $deplibs_check_method; shift
+	  file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+	  for a_deplib in $deplibs; do
+	    case $a_deplib in
+	    -l*)
+	      func_stripname -l '' "$a_deplib"
+	      name=$func_stripname_result
+	      if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+		case " $predeps $postdeps " in
+		*" $a_deplib "*)
+		  func_append newdeplibs " $a_deplib"
+		  a_deplib=
+		  ;;
+		esac
+	      fi
+	      if test -n "$a_deplib"; then
+		libname=`eval "\\$ECHO \"$libname_spec\""`
+		if test -n "$file_magic_glob"; then
+		  libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob`
+		else
+		  libnameglob=$libname
+		fi
+		test yes = "$want_nocaseglob" && nocaseglob=`shopt -p nocaseglob`
+		for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+		  if test yes = "$want_nocaseglob"; then
+		    shopt -s nocaseglob
+		    potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+		    $nocaseglob
+		  else
+		    potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
+		  fi
+		  for potent_lib in $potential_libs; do
+		      # Follow soft links.
+		      if ls -lLd "$potent_lib" 2>/dev/null |
+			 $GREP " -> " >/dev/null; then
+			continue
+		      fi
+		      # The statement above tries to avoid entering an
+		      # endless loop below, in case of cyclic links.
+		      # We might still enter an endless loop, since a link
+		      # loop can be closed while we follow links,
+		      # but so what?
+		      potlib=$potent_lib
+		      while test -h "$potlib" 2>/dev/null; do
+			potliblink=`ls -ld $potlib | $SED 's/.* -> //'`
+			case $potliblink in
+			[\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;;
+			*) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";;
+			esac
+		      done
+		      if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
+			 $SED -e 10q |
+			 $EGREP "$file_magic_regex" > /dev/null; then
+			func_append newdeplibs " $a_deplib"
+			a_deplib=
+			break 2
+		      fi
+		  done
+		done
+	      fi
+	      if test -n "$a_deplib"; then
+		droppeddeps=yes
+		echo
+		$ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+		echo "*** I have the capability to make that library automatically link in when"
+		echo "*** you link to this library.  But I can only do this if you have a"
+		echo "*** shared version of the library, which you do not appear to have"
+		echo "*** because I did check the linker path looking for a file starting"
+		if test -z "$potlib"; then
+		  $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+		else
+		  $ECHO "*** with $libname and none of the candidates passed a file format test"
+		  $ECHO "*** using a file magic. Last file checked: $potlib"
+		fi
+	      fi
+	      ;;
+	    *)
+	      # Add a -L argument.
+	      func_append newdeplibs " $a_deplib"
+	      ;;
+	    esac
+	  done # Gone through all deplibs.
+	  ;;
+	match_pattern*)
+	  set dummy $deplibs_check_method; shift
+	  match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
+	  for a_deplib in $deplibs; do
+	    case $a_deplib in
+	    -l*)
+	      func_stripname -l '' "$a_deplib"
+	      name=$func_stripname_result
+	      if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+		case " $predeps $postdeps " in
+		*" $a_deplib "*)
+		  func_append newdeplibs " $a_deplib"
+		  a_deplib=
+		  ;;
+		esac
+	      fi
+	      if test -n "$a_deplib"; then
+		libname=`eval "\\$ECHO \"$libname_spec\""`
+		for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
+		  potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
+		  for potent_lib in $potential_libs; do
+		    potlib=$potent_lib # see symlink-check above in file_magic test
+		    if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \
+		       $EGREP "$match_pattern_regex" > /dev/null; then
+		      func_append newdeplibs " $a_deplib"
+		      a_deplib=
+		      break 2
+		    fi
+		  done
+		done
+	      fi
+	      if test -n "$a_deplib"; then
+		droppeddeps=yes
+		echo
+		$ECHO "*** Warning: linker path does not have real file for library $a_deplib."
+		echo "*** I have the capability to make that library automatically link in when"
+		echo "*** you link to this library.  But I can only do this if you have a"
+		echo "*** shared version of the library, which you do not appear to have"
+		echo "*** because I did check the linker path looking for a file starting"
+		if test -z "$potlib"; then
+		  $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+		else
+		  $ECHO "*** with $libname and none of the candidates passed a file format test"
+		  $ECHO "*** using a regex pattern. Last file checked: $potlib"
+		fi
+	      fi
+	      ;;
+	    *)
+	      # Add a -L argument.
+	      func_append newdeplibs " $a_deplib"
+	      ;;
+	    esac
+	  done # Gone through all deplibs.
+	  ;;
+	none | unknown | *)
+	  newdeplibs=
+	  tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'`
+	  if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+	    for i in $predeps $postdeps; do
+	      # can't use Xsed below, because $i might contain '/'
+	      tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"`
+	    done
+	  fi
+	  case $tmp_deplibs in
+	  *[!\	\ ]*)
+	    echo
+	    if test none = "$deplibs_check_method"; then
+	      echo "*** Warning: inter-library dependencies are not supported in this platform."
+	    else
+	      echo "*** Warning: inter-library dependencies are not known to be supported."
+	    fi
+	    echo "*** All declared inter-library dependencies are being dropped."
+	    droppeddeps=yes
+	    ;;
+	  esac
+	  ;;
+	esac
+	versuffix=$versuffix_save
+	major=$major_save
+	release=$release_save
+	libname=$libname_save
+	name=$name_save
+
+	case $host in
+	*-*-rhapsody* | *-*-darwin1.[012])
+	  # On Rhapsody replace the C library with the System framework
+	  newdeplibs=`$ECHO " $newdeplibs" | $SED 's/ -lc / System.ltframework /'`
+	  ;;
+	esac
+
+	if test yes = "$droppeddeps"; then
+	  if test yes = "$module"; then
+	    echo
+	    echo "*** Warning: libtool could not satisfy all declared inter-library"
+	    $ECHO "*** dependencies of module $libname.  Therefore, libtool will create"
+	    echo "*** a static module, that should work as long as the dlopening"
+	    echo "*** application is linked with the -dlopen flag."
+	    if test -z "$global_symbol_pipe"; then
+	      echo
+	      echo "*** However, this would only work if libtool was able to extract symbol"
+	      echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
+	      echo "*** not find such a program.  So, this module is probably useless."
+	      echo "*** 'nm' from GNU binutils and a full rebuild may help."
+	    fi
+	    if test no = "$build_old_libs"; then
+	      oldlibs=$output_objdir/$libname.$libext
+	      build_libtool_libs=module
+	      build_old_libs=yes
+	    else
+	      build_libtool_libs=no
+	    fi
+	  else
+	    echo "*** The inter-library dependencies that have been dropped here will be"
+	    echo "*** automatically added whenever a program is linked with this library"
+	    echo "*** or is declared to -dlopen it."
+
+	    if test no = "$allow_undefined"; then
+	      echo
+	      echo "*** Since this library must not contain undefined symbols,"
+	      echo "*** because either the platform does not support them or"
+	      echo "*** it was explicitly requested with -no-undefined,"
+	      echo "*** libtool will only create a static version of it."
+	      if test no = "$build_old_libs"; then
+		oldlibs=$output_objdir/$libname.$libext
+		build_libtool_libs=module
+		build_old_libs=yes
+	      else
+		build_libtool_libs=no
+	      fi
+	    fi
+	  fi
+	fi
+	# Done checking deplibs!
+	deplibs=$newdeplibs
+      fi
+      # Time to change all our "foo.ltframework" stuff back to "-framework foo"
+      case $host in
+	*-*-darwin*)
+	  newdeplibs=`$ECHO " $newdeplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	  new_inherited_linker_flags=`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	  deplibs=`$ECHO " $deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	  ;;
+      esac
+
+      # move library search paths that coincide with paths to not yet
+      # installed libraries to the beginning of the library search list
+      new_libs=
+      for path in $notinst_path; do
+	case " $new_libs " in
+	*" -L$path/$objdir "*) ;;
+	*)
+	  case " $deplibs " in
+	  *" -L$path/$objdir "*)
+	    func_append new_libs " -L$path/$objdir" ;;
+	  esac
+	  ;;
+	esac
+      done
+      for deplib in $deplibs; do
+	case $deplib in
+	-L*)
+	  case " $new_libs " in
+	  *" $deplib "*) ;;
+	  *) func_append new_libs " $deplib" ;;
+	  esac
+	  ;;
+	*) func_append new_libs " $deplib" ;;
+	esac
+      done
+      deplibs=$new_libs
+
+      # All the library-specific variables (install_libdir is set above).
+      library_names=
+      old_library=
+      dlname=
+
+      # Test again, we may have decided not to build it any more
+      if test yes = "$build_libtool_libs"; then
+	# Remove $wl instances when linking with ld.
+	# FIXME: should test the right _cmds variable.
+	case $archive_cmds in
+	  *\$LD\ *) wl= ;;
+        esac
+	if test yes = "$hardcode_into_libs"; then
+	  # Hardcode the library paths
+	  hardcode_libdirs=
+	  dep_rpath=
+	  rpath=$finalize_rpath
+	  test relink = "$opt_mode" || rpath=$compile_rpath$rpath
+	  for libdir in $rpath; do
+	    if test -n "$hardcode_libdir_flag_spec"; then
+	      if test -n "$hardcode_libdir_separator"; then
+		func_replace_sysroot "$libdir"
+		libdir=$func_replace_sysroot_result
+		if test -z "$hardcode_libdirs"; then
+		  hardcode_libdirs=$libdir
+		else
+		  # Just accumulate the unique libdirs.
+		  case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+		  *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		    ;;
+		  *)
+		    func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+		    ;;
+		  esac
+		fi
+	      else
+		eval flag=\"$hardcode_libdir_flag_spec\"
+		func_append dep_rpath " $flag"
+	      fi
+	    elif test -n "$runpath_var"; then
+	      case "$perm_rpath " in
+	      *" $libdir "*) ;;
+	      *) func_append perm_rpath " $libdir" ;;
+	      esac
+	    fi
+	  done
+	  # Substitute the hardcoded libdirs into the rpath.
+	  if test -n "$hardcode_libdir_separator" &&
+	     test -n "$hardcode_libdirs"; then
+	    libdir=$hardcode_libdirs
+	    eval "dep_rpath=\"$hardcode_libdir_flag_spec\""
+	  fi
+	  if test -n "$runpath_var" && test -n "$perm_rpath"; then
+	    # We should set the runpath_var.
+	    rpath=
+	    for dir in $perm_rpath; do
+	      func_append rpath "$dir:"
+	    done
+	    eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var"
+	  fi
+	  test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
+	fi
+
+	shlibpath=$finalize_shlibpath
+	test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath
+	if test -n "$shlibpath"; then
+	  eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
+	fi
+
+	# Get the real and link names of the library.
+	eval shared_ext=\"$shrext_cmds\"
+	eval library_names=\"$library_names_spec\"
+	set dummy $library_names
+	shift
+	realname=$1
+	shift
+
+	if test -n "$soname_spec"; then
+	  eval soname=\"$soname_spec\"
+	else
+	  soname=$realname
+	fi
+	if test -z "$dlname"; then
+	  dlname=$soname
+	fi
+
+	lib=$output_objdir/$realname
+	linknames=
+	for link
+	do
+	  func_append linknames " $link"
+	done
+
+	# Use standard objects if they are pic
+	test -z "$pic_flag" && libobjs=`$ECHO "$libobjs" | $SP2NL | $SED "$lo2o" | $NL2SP`
+	test "X$libobjs" = "X " && libobjs=
+
+	delfiles=
+	if test -n "$export_symbols" && test -n "$include_expsyms"; then
+	  $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
+	  export_symbols=$output_objdir/$libname.uexp
+	  func_append delfiles " $export_symbols"
+	fi
+
+	orig_export_symbols=
+	case $host_os in
+	cygwin* | mingw* | cegcc*)
+	  if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
+	    # exporting using user supplied symfile
+	    func_dll_def_p "$export_symbols" || {
+	      # and it's NOT already a .def file. Must figure out
+	      # which of the given symbols are data symbols and tag
+	      # them as such. So, trigger use of export_symbols_cmds.
+	      # export_symbols gets reassigned inside the "prepare
+	      # the list of exported symbols" if statement, so the
+	      # include_expsyms logic still works.
+	      orig_export_symbols=$export_symbols
+	      export_symbols=
+	      always_export_symbols=yes
+	    }
+	  fi
+	  ;;
+	esac
+
+	# Prepare the list of exported symbols
+	if test -z "$export_symbols"; then
+	  if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then
+	    func_verbose "generating symbol list for '$libname.la'"
+	    export_symbols=$output_objdir/$libname.exp
+	    $opt_dry_run || $RM $export_symbols
+	    cmds=$export_symbols_cmds
+	    save_ifs=$IFS; IFS='~'
+	    for cmd1 in $cmds; do
+	      IFS=$save_ifs
+	      # Take the normal branch if the nm_file_list_spec branch
+	      # doesn't work or if tool conversion is not needed.
+	      case $nm_file_list_spec~$to_tool_file_cmd in
+		*~func_convert_file_noop | *~func_convert_file_msys_to_w32 | ~*)
+		  try_normal_branch=yes
+		  eval cmd=\"$cmd1\"
+		  func_len " $cmd"
+		  len=$func_len_result
+		  ;;
+		*)
+		  try_normal_branch=no
+		  ;;
+	      esac
+	      if test yes = "$try_normal_branch" \
+		 && { test "$len" -lt "$max_cmd_len" \
+		      || test "$max_cmd_len" -le -1; }
+	      then
+		func_show_eval "$cmd" 'exit $?'
+		skipped_export=false
+	      elif test -n "$nm_file_list_spec"; then
+		func_basename "$output"
+		output_la=$func_basename_result
+		save_libobjs=$libobjs
+		save_output=$output
+		output=$output_objdir/$output_la.nm
+		func_to_tool_file "$output"
+		libobjs=$nm_file_list_spec$func_to_tool_file_result
+		func_append delfiles " $output"
+		func_verbose "creating $NM input file list: $output"
+		for obj in $save_libobjs; do
+		  func_to_tool_file "$obj"
+		  $ECHO "$func_to_tool_file_result"
+		done > "$output"
+		eval cmd=\"$cmd1\"
+		func_show_eval "$cmd" 'exit $?'
+		output=$save_output
+		libobjs=$save_libobjs
+		skipped_export=false
+	      else
+		# The command line is too long to execute in one step.
+		func_verbose "using reloadable object file for export list..."
+		skipped_export=:
+		# Break out early, otherwise skipped_export may be
+		# set to false by a later but shorter cmd.
+		break
+	      fi
+	    done
+	    IFS=$save_ifs
+	    if test -n "$export_symbols_regex" && test : != "$skipped_export"; then
+	      func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+	      func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+	    fi
+	  fi
+	fi
+
+	if test -n "$export_symbols" && test -n "$include_expsyms"; then
+	  tmp_export_symbols=$export_symbols
+	  test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
+	  $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+	fi
+
+	if test : != "$skipped_export" && test -n "$orig_export_symbols"; then
+	  # The given exports_symbols file has to be filtered, so filter it.
+	  func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
+	  # FIXME: $output_objdir/$libname.filter potentially contains lots of
+	  # 's' commands, which not all seds can handle. GNU sed should be fine
+	  # though. Also, the filter scales superlinearly with the number of
+	  # global variables. join(1) would be nice here, but unfortunately
+	  # isn't a blessed tool.
+	  $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+	  func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+	  export_symbols=$output_objdir/$libname.def
+	  $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+	fi
+
+	tmp_deplibs=
+	for test_deplib in $deplibs; do
+	  case " $convenience " in
+	  *" $test_deplib "*) ;;
+	  *)
+	    func_append tmp_deplibs " $test_deplib"
+	    ;;
+	  esac
+	done
+	deplibs=$tmp_deplibs
+
+	if test -n "$convenience"; then
+	  if test -n "$whole_archive_flag_spec" &&
+	    test yes = "$compiler_needs_object" &&
+	    test -z "$libobjs"; then
+	    # extract the archives, so we have objects to list.
+	    # TODO: could optimize this to just extract one archive.
+	    whole_archive_flag_spec=
+	  fi
+	  if test -n "$whole_archive_flag_spec"; then
+	    save_libobjs=$libobjs
+	    eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+	    test "X$libobjs" = "X " && libobjs=
+	  else
+	    gentop=$output_objdir/${outputname}x
+	    func_append generated " $gentop"
+
+	    func_extract_archives $gentop $convenience
+	    func_append libobjs " $func_extract_archives_result"
+	    test "X$libobjs" = "X " && libobjs=
+	  fi
+	fi
+
+	if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then
+	  eval flag=\"$thread_safe_flag_spec\"
+	  func_append linker_flags " $flag"
+	fi
+
+	# Make a backup of the uninstalled library when relinking
+	if test relink = "$opt_mode"; then
+	  $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
+	fi
+
+	# Do each of the archive commands.
+	if test yes = "$module" && test -n "$module_cmds"; then
+	  if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+	    eval test_cmds=\"$module_expsym_cmds\"
+	    cmds=$module_expsym_cmds
+	  else
+	    eval test_cmds=\"$module_cmds\"
+	    cmds=$module_cmds
+	  fi
+	else
+	  if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+	    eval test_cmds=\"$archive_expsym_cmds\"
+	    cmds=$archive_expsym_cmds
+	  else
+	    eval test_cmds=\"$archive_cmds\"
+	    cmds=$archive_cmds
+	  fi
+	fi
+
+	if test : != "$skipped_export" &&
+	   func_len " $test_cmds" &&
+	   len=$func_len_result &&
+	   test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+	  :
+	else
+	  # The command line is too long to link in one step, link piecewise
+	  # or, if using GNU ld and skipped_export is not :, use a linker
+	  # script.
+
+	  # Save the value of $output and $libobjs because we want to
+	  # use them later.  If we have whole_archive_flag_spec, we
+	  # want to use save_libobjs as it was before
+	  # whole_archive_flag_spec was expanded, because we can't
+	  # assume the linker understands whole_archive_flag_spec.
+	  # This may have to be revisited, in case too many
+	  # convenience libraries get linked in and end up exceeding
+	  # the spec.
+	  if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then
+	    save_libobjs=$libobjs
+	  fi
+	  save_output=$output
+	  func_basename "$output"
+	  output_la=$func_basename_result
+
+	  # Clear the reloadable object creation command queue and
+	  # initialize k to one.
+	  test_cmds=
+	  concat_cmds=
+	  objlist=
+	  last_robj=
+	  k=1
+
+	  if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
+	    output=$output_objdir/$output_la.lnkscript
+	    func_verbose "creating GNU ld script: $output"
+	    echo 'INPUT (' > $output
+	    for obj in $save_libobjs
+	    do
+	      func_to_tool_file "$obj"
+	      $ECHO "$func_to_tool_file_result" >> $output
+	    done
+	    echo ')' >> $output
+	    func_append delfiles " $output"
+	    func_to_tool_file "$output"
+	    output=$func_to_tool_file_result
+	  elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
+	    output=$output_objdir/$output_la.lnk
+	    func_verbose "creating linker input file list: $output"
+	    : > $output
+	    set x $save_libobjs
+	    shift
+	    firstobj=
+	    if test yes = "$compiler_needs_object"; then
+	      firstobj="$1 "
+	      shift
+	    fi
+	    for obj
+	    do
+	      func_to_tool_file "$obj"
+	      $ECHO "$func_to_tool_file_result" >> $output
+	    done
+	    func_append delfiles " $output"
+	    func_to_tool_file "$output"
+	    output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
+	  else
+	    if test -n "$save_libobjs"; then
+	      func_verbose "creating reloadable object files..."
+	      output=$output_objdir/$output_la-$k.$objext
+	      eval test_cmds=\"$reload_cmds\"
+	      func_len " $test_cmds"
+	      len0=$func_len_result
+	      len=$len0
+
+	      # Loop over the list of objects to be linked.
+	      for obj in $save_libobjs
+	      do
+		func_len " $obj"
+		func_arith $len + $func_len_result
+		len=$func_arith_result
+		if test -z "$objlist" ||
+		   test "$len" -lt "$max_cmd_len"; then
+		  func_append objlist " $obj"
+		else
+		  # The command $test_cmds is almost too long, add a
+		  # command to the queue.
+		  if test 1 -eq "$k"; then
+		    # The first file doesn't have a previous command to add.
+		    reload_objs=$objlist
+		    eval concat_cmds=\"$reload_cmds\"
+		  else
+		    # All subsequent reloadable object files will link in
+		    # the last one created.
+		    reload_objs="$objlist $last_robj"
+		    eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\"
+		  fi
+		  last_robj=$output_objdir/$output_la-$k.$objext
+		  func_arith $k + 1
+		  k=$func_arith_result
+		  output=$output_objdir/$output_la-$k.$objext
+		  objlist=" $obj"
+		  func_len " $last_robj"
+		  func_arith $len0 + $func_len_result
+		  len=$func_arith_result
+		fi
+	      done
+	      # Handle the remaining objects by creating one last
+	      # reloadable object file.  All subsequent reloadable object
+	      # files will link in the last one created.
+	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	      reload_objs="$objlist $last_robj"
+	      eval concat_cmds=\"\$concat_cmds$reload_cmds\"
+	      if test -n "$last_robj"; then
+	        eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+	      fi
+	      func_append delfiles " $output"
+
+	    else
+	      output=
+	    fi
+
+	    ${skipped_export-false} && {
+	      func_verbose "generating symbol list for '$libname.la'"
+	      export_symbols=$output_objdir/$libname.exp
+	      $opt_dry_run || $RM $export_symbols
+	      libobjs=$output
+	      # Append the command to create the export file.
+	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	      eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\"
+	      if test -n "$last_robj"; then
+		eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
+	      fi
+	    }
+
+	    test -n "$save_libobjs" &&
+	      func_verbose "creating a temporary reloadable object file: $output"
+
+	    # Loop through the commands generated above and execute them.
+	    save_ifs=$IFS; IFS='~'
+	    for cmd in $concat_cmds; do
+	      IFS=$save_ifs
+	      $opt_quiet || {
+		  func_quote_arg expand,pretty "$cmd"
+		  eval "func_echo $func_quote_arg_result"
+	      }
+	      $opt_dry_run || eval "$cmd" || {
+		lt_exit=$?
+
+		# Restore the uninstalled library and exit
+		if test relink = "$opt_mode"; then
+		  ( cd "$output_objdir" && \
+		    $RM "${realname}T" && \
+		    $MV "${realname}U" "$realname" )
+		fi
+
+		exit $lt_exit
+	      }
+	    done
+	    IFS=$save_ifs
+
+	    if test -n "$export_symbols_regex" && ${skipped_export-false}; then
+	      func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
+	      func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
+	    fi
+	  fi
+
+          ${skipped_export-false} && {
+	    if test -n "$export_symbols" && test -n "$include_expsyms"; then
+	      tmp_export_symbols=$export_symbols
+	      test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
+	      $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
+	    fi
+
+	    if test -n "$orig_export_symbols"; then
+	      # The given exports_symbols file has to be filtered, so filter it.
+	      func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
+	      # FIXME: $output_objdir/$libname.filter potentially contains lots of
+	      # 's' commands, which not all seds can handle. GNU sed should be fine
+	      # though. Also, the filter scales superlinearly with the number of
+	      # global variables. join(1) would be nice here, but unfortunately
+	      # isn't a blessed tool.
+	      $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter
+	      func_append delfiles " $export_symbols $output_objdir/$libname.filter"
+	      export_symbols=$output_objdir/$libname.def
+	      $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
+	    fi
+	  }
+
+	  libobjs=$output
+	  # Restore the value of output.
+	  output=$save_output
+
+	  if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then
+	    eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
+	    test "X$libobjs" = "X " && libobjs=
+	  fi
+	  # Expand the library linking commands again to reset the
+	  # value of $libobjs for piecewise linking.
+
+	  # Do each of the archive commands.
+	  if test yes = "$module" && test -n "$module_cmds"; then
+	    if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
+	      cmds=$module_expsym_cmds
+	    else
+	      cmds=$module_cmds
+	    fi
+	  else
+	    if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then
+	      cmds=$archive_expsym_cmds
+	    else
+	      cmds=$archive_cmds
+	    fi
+	  fi
+	fi
+
+	if test -n "$delfiles"; then
+	  # Append the command to remove temporary files to $cmds.
+	  eval cmds=\"\$cmds~\$RM $delfiles\"
+	fi
+
+	# Add any objects from preloaded convenience libraries
+	if test -n "$dlprefiles"; then
+	  gentop=$output_objdir/${outputname}x
+	  func_append generated " $gentop"
+
+	  func_extract_archives $gentop $dlprefiles
+	  func_append libobjs " $func_extract_archives_result"
+	  test "X$libobjs" = "X " && libobjs=
+	fi
+
+	save_ifs=$IFS; IFS='~'
+	for cmd in $cmds; do
+	  IFS=$sp$nl
+	  eval cmd=\"$cmd\"
+	  IFS=$save_ifs
+	  $opt_quiet || {
+	    func_quote_arg expand,pretty "$cmd"
+	    eval "func_echo $func_quote_arg_result"
+	  }
+	  $opt_dry_run || eval "$cmd" || {
+	    lt_exit=$?
+
+	    # Restore the uninstalled library and exit
+	    if test relink = "$opt_mode"; then
+	      ( cd "$output_objdir" && \
+	        $RM "${realname}T" && \
+		$MV "${realname}U" "$realname" )
+	    fi
+
+	    exit $lt_exit
+	  }
+	done
+	IFS=$save_ifs
+
+	# Restore the uninstalled library and exit
+	if test relink = "$opt_mode"; then
+	  $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
+
+	  if test -n "$convenience"; then
+	    if test -z "$whole_archive_flag_spec"; then
+	      func_show_eval '${RM}r "$gentop"'
+	    fi
+	  fi
+
+	  exit $EXIT_SUCCESS
+	fi
+
+	# Create links to the real library.
+	for linkname in $linknames; do
+	  if test "$realname" != "$linkname"; then
+	    func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?'
+	  fi
+	done
+
+	# If -module or -export-dynamic was specified, set the dlname.
+	if test yes = "$module" || test yes = "$export_dynamic"; then
+	  # On all known operating systems, these are identical.
+	  dlname=$soname
+	fi
+      fi
+      ;;
+
+    obj)
+      if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+	func_warning "'-dlopen' is ignored for objects"
+      fi
+
+      case " $deplibs" in
+      *\ -l* | *\ -L*)
+	func_warning "'-l' and '-L' are ignored for objects" ;;
+      esac
+
+      test -n "$rpath" && \
+	func_warning "'-rpath' is ignored for objects"
+
+      test -n "$xrpath" && \
+	func_warning "'-R' is ignored for objects"
+
+      test -n "$vinfo" && \
+	func_warning "'-version-info' is ignored for objects"
+
+      test -n "$release" && \
+	func_warning "'-release' is ignored for objects"
+
+      case $output in
+      *.lo)
+	test -n "$objs$old_deplibs" && \
+	  func_fatal_error "cannot build library object '$output' from non-libtool objects"
+
+	libobj=$output
+	func_lo2o "$libobj"
+	obj=$func_lo2o_result
+	;;
+      *)
+	libobj=
+	obj=$output
+	;;
+      esac
+
+      # Delete the old objects.
+      $opt_dry_run || $RM $obj $libobj
+
+      # Objects from convenience libraries.  This assumes
+      # single-version convenience libraries.  Whenever we create
+      # different ones for PIC/non-PIC, this we'll have to duplicate
+      # the extraction.
+      reload_conv_objs=
+      gentop=
+      # if reload_cmds runs $LD directly, get rid of -Wl from
+      # whole_archive_flag_spec and hope we can get by with turning comma
+      # into space.
+      case $reload_cmds in
+        *\$LD[\ \$]*) wl= ;;
+      esac
+      if test -n "$convenience"; then
+	if test -n "$whole_archive_flag_spec"; then
+	  eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
+	  test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'`
+	  reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags
+	else
+	  gentop=$output_objdir/${obj}x
+	  func_append generated " $gentop"
+
+	  func_extract_archives $gentop $convenience
+	  reload_conv_objs="$reload_objs $func_extract_archives_result"
+	fi
+      fi
+
+      # If we're not building shared, we need to use non_pic_objs
+      test yes = "$build_libtool_libs" || libobjs=$non_pic_objects
+
+      # Create the old-style object.
+      reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs
+
+      output=$obj
+      func_execute_cmds "$reload_cmds" 'exit $?'
+
+      # Exit if we aren't doing a library object file.
+      if test -z "$libobj"; then
+	if test -n "$gentop"; then
+	  func_show_eval '${RM}r "$gentop"'
+	fi
+
+	exit $EXIT_SUCCESS
+      fi
+
+      test yes = "$build_libtool_libs" || {
+	if test -n "$gentop"; then
+	  func_show_eval '${RM}r "$gentop"'
+	fi
+
+	# Create an invalid libtool object if no PIC, so that we don't
+	# accidentally link it into a program.
+	# $show "echo timestamp > $libobj"
+	# $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
+	exit $EXIT_SUCCESS
+      }
+
+      if test -n "$pic_flag" || test default != "$pic_mode"; then
+	# Only do commands if we really have different PIC objects.
+	reload_objs="$libobjs $reload_conv_objs"
+	output=$libobj
+	func_execute_cmds "$reload_cmds" 'exit $?'
+      fi
+
+      if test -n "$gentop"; then
+	func_show_eval '${RM}r "$gentop"'
+      fi
+
+      exit $EXIT_SUCCESS
+      ;;
+
+    prog)
+      case $host in
+	*cygwin*) func_stripname '' '.exe' "$output"
+	          output=$func_stripname_result.exe;;
+      esac
+      test -n "$vinfo" && \
+	func_warning "'-version-info' is ignored for programs"
+
+      test -n "$release" && \
+	func_warning "'-release' is ignored for programs"
+
+      $preload \
+	&& test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \
+	&& func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support."
+
+      case $host in
+      *-*-rhapsody* | *-*-darwin1.[012])
+	# On Rhapsody replace the C library is the System framework
+	compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's/ -lc / System.ltframework /'`
+	finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's/ -lc / System.ltframework /'`
+	;;
+      esac
+
+      case $host in
+      *-*-darwin*)
+	# Don't allow lazy linking, it breaks C++ global constructors
+	# But is supposedly fixed on 10.4 or later (yay!).
+	if test CXX = "$tagname"; then
+	  case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
+	    10.[0123])
+	      func_append compile_command " $wl-bind_at_load"
+	      func_append finalize_command " $wl-bind_at_load"
+	    ;;
+	  esac
+	fi
+	# Time to change all our "foo.ltframework" stuff back to "-framework foo"
+	compile_deplibs=`$ECHO " $compile_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	finalize_deplibs=`$ECHO " $finalize_deplibs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
+	;;
+      esac
+
+
+      # move library search paths that coincide with paths to not yet
+      # installed libraries to the beginning of the library search list
+      new_libs=
+      for path in $notinst_path; do
+	case " $new_libs " in
+	*" -L$path/$objdir "*) ;;
+	*)
+	  case " $compile_deplibs " in
+	  *" -L$path/$objdir "*)
+	    func_append new_libs " -L$path/$objdir" ;;
+	  esac
+	  ;;
+	esac
+      done
+      for deplib in $compile_deplibs; do
+	case $deplib in
+	-L*)
+	  case " $new_libs " in
+	  *" $deplib "*) ;;
+	  *) func_append new_libs " $deplib" ;;
+	  esac
+	  ;;
+	*) func_append new_libs " $deplib" ;;
+	esac
+      done
+      compile_deplibs=$new_libs
+
+
+      func_append compile_command " $compile_deplibs"
+      func_append finalize_command " $finalize_deplibs"
+
+      if test -n "$rpath$xrpath"; then
+	# If the user specified any rpath flags, then add them.
+	for libdir in $rpath $xrpath; do
+	  # This is the magic to use -rpath.
+	  case "$finalize_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append finalize_rpath " $libdir" ;;
+	  esac
+	done
+      fi
+
+      # Now hardcode the library paths
+      rpath=
+      hardcode_libdirs=
+      for libdir in $compile_rpath $finalize_rpath; do
+	if test -n "$hardcode_libdir_flag_spec"; then
+	  if test -n "$hardcode_libdir_separator"; then
+	    if test -z "$hardcode_libdirs"; then
+	      hardcode_libdirs=$libdir
+	    else
+	      # Just accumulate the unique libdirs.
+	      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+	      *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		;;
+	      *)
+		func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+		;;
+	      esac
+	    fi
+	  else
+	    eval flag=\"$hardcode_libdir_flag_spec\"
+	    func_append rpath " $flag"
+	  fi
+	elif test -n "$runpath_var"; then
+	  case "$perm_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append perm_rpath " $libdir" ;;
+	  esac
+	fi
+	case $host in
+	*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+	  testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'`
+	  case :$dllsearchpath: in
+	  *":$libdir:"*) ;;
+	  ::) dllsearchpath=$libdir;;
+	  *) func_append dllsearchpath ":$libdir";;
+	  esac
+	  case :$dllsearchpath: in
+	  *":$testbindir:"*) ;;
+	  ::) dllsearchpath=$testbindir;;
+	  *) func_append dllsearchpath ":$testbindir";;
+	  esac
+	  ;;
+	esac
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+	 test -n "$hardcode_libdirs"; then
+	libdir=$hardcode_libdirs
+	eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      compile_rpath=$rpath
+
+      rpath=
+      hardcode_libdirs=
+      for libdir in $finalize_rpath; do
+	if test -n "$hardcode_libdir_flag_spec"; then
+	  if test -n "$hardcode_libdir_separator"; then
+	    if test -z "$hardcode_libdirs"; then
+	      hardcode_libdirs=$libdir
+	    else
+	      # Just accumulate the unique libdirs.
+	      case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
+	      *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*)
+		;;
+	      *)
+		func_append hardcode_libdirs "$hardcode_libdir_separator$libdir"
+		;;
+	      esac
+	    fi
+	  else
+	    eval flag=\"$hardcode_libdir_flag_spec\"
+	    func_append rpath " $flag"
+	  fi
+	elif test -n "$runpath_var"; then
+	  case "$finalize_perm_rpath " in
+	  *" $libdir "*) ;;
+	  *) func_append finalize_perm_rpath " $libdir" ;;
+	  esac
+	fi
+      done
+      # Substitute the hardcoded libdirs into the rpath.
+      if test -n "$hardcode_libdir_separator" &&
+	 test -n "$hardcode_libdirs"; then
+	libdir=$hardcode_libdirs
+	eval rpath=\" $hardcode_libdir_flag_spec\"
+      fi
+      finalize_rpath=$rpath
+
+      if test -n "$libobjs" && test yes = "$build_old_libs"; then
+	# Transform all the library objects into standard objects.
+	compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+	finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
+      fi
+
+      func_generate_dlsyms "$outputname" "@PROGRAM@" false
+
+      # template prelinking step
+      if test -n "$prelink_cmds"; then
+	func_execute_cmds "$prelink_cmds" 'exit $?'
+      fi
+
+      wrappers_required=:
+      case $host in
+      *cegcc* | *mingw32ce*)
+        # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
+        wrappers_required=false
+        ;;
+      *cygwin* | *mingw* )
+        test yes = "$build_libtool_libs" || wrappers_required=false
+        ;;
+      *)
+        if test no = "$need_relink" || test yes != "$build_libtool_libs"; then
+          wrappers_required=false
+        fi
+        ;;
+      esac
+      $wrappers_required || {
+	# Replace the output file specification.
+	compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+	link_command=$compile_command$compile_rpath
+
+	# We have no uninstalled library dependencies, so finalize right now.
+	exit_status=0
+	func_show_eval "$link_command" 'exit_status=$?'
+
+	if test -n "$postlink_cmds"; then
+	  func_to_tool_file "$output"
+	  postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+	  func_execute_cmds "$postlink_cmds" 'exit $?'
+	fi
+
+	# Delete the generated files.
+	if test -f "$output_objdir/${outputname}S.$objext"; then
+	  func_show_eval '$RM "$output_objdir/${outputname}S.$objext"'
+	fi
+
+	exit $exit_status
+      }
+
+      if test -n "$compile_shlibpath$finalize_shlibpath"; then
+	compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
+      fi
+      if test -n "$finalize_shlibpath"; then
+	finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command"
+      fi
+
+      compile_var=
+      finalize_var=
+      if test -n "$runpath_var"; then
+	if test -n "$perm_rpath"; then
+	  # We should set the runpath_var.
+	  rpath=
+	  for dir in $perm_rpath; do
+	    func_append rpath "$dir:"
+	  done
+	  compile_var="$runpath_var=\"$rpath\$$runpath_var\" "
+	fi
+	if test -n "$finalize_perm_rpath"; then
+	  # We should set the runpath_var.
+	  rpath=
+	  for dir in $finalize_perm_rpath; do
+	    func_append rpath "$dir:"
+	  done
+	  finalize_var="$runpath_var=\"$rpath\$$runpath_var\" "
+	fi
+      fi
+
+      if test yes = "$no_install"; then
+	# We don't need to create a wrapper script.
+	link_command=$compile_var$compile_command$compile_rpath
+	# Replace the output file specification.
+	link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
+	# Delete the old output file.
+	$opt_dry_run || $RM $output
+	# Link the executable and exit
+	func_show_eval "$link_command" 'exit $?'
+
+	if test -n "$postlink_cmds"; then
+	  func_to_tool_file "$output"
+	  postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+	  func_execute_cmds "$postlink_cmds" 'exit $?'
+	fi
+
+	exit $EXIT_SUCCESS
+      fi
+
+      case $hardcode_action,$fast_install in
+        relink,*)
+	  # Fast installation is not supported
+	  link_command=$compile_var$compile_command$compile_rpath
+	  relink_command=$finalize_var$finalize_command$finalize_rpath
+
+	  func_warning "this platform does not like uninstalled shared libraries"
+	  func_warning "'$output' will be relinked during installation"
+	  ;;
+        *,yes)
+	  link_command=$finalize_var$compile_command$finalize_rpath
+	  relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'`
+          ;;
+	*,no)
+	  link_command=$compile_var$compile_command$compile_rpath
+	  relink_command=$finalize_var$finalize_command$finalize_rpath
+          ;;
+	*,needless)
+	  link_command=$finalize_var$compile_command$finalize_rpath
+	  relink_command=
+          ;;
+      esac
+
+      # Replace the output file specification.
+      link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
+
+      # Delete the old output files.
+      $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname
+
+      func_show_eval "$link_command" 'exit $?'
+
+      if test -n "$postlink_cmds"; then
+	func_to_tool_file "$output_objdir/$outputname"
+	postlink_cmds=`func_echo_all "$postlink_cmds" | $SED -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g' -e 's%@TOOL_OUTPUT@%'"$func_to_tool_file_result"'%g'`
+	func_execute_cmds "$postlink_cmds" 'exit $?'
+      fi
+
+      # Now create the wrapper script.
+      func_verbose "creating $output"
+
+      # Quote the relink command for shipping.
+      if test -n "$relink_command"; then
+	# Preserve any variables that may affect compiler behavior
+	for var in $variables_saved_for_relink; do
+	  if eval test -z \"\${$var+set}\"; then
+	    relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+	  elif eval var_value=\$$var; test -z "$var_value"; then
+	    relink_command="$var=; export $var; $relink_command"
+	  else
+	    func_quote_arg pretty "$var_value"
+	    relink_command="$var=$func_quote_arg_result; export $var; $relink_command"
+	  fi
+	done
+	func_quote eval cd "`pwd`"
+	func_quote_arg pretty,unquoted "($func_quote_result; $relink_command)"
+	relink_command=$func_quote_arg_unquoted_result
+      fi
+
+      # Only actually do things if not in dry run mode.
+      $opt_dry_run || {
+	# win32 will think the script is a binary if it has
+	# a .exe suffix, so we strip it off here.
+	case $output in
+	  *.exe) func_stripname '' '.exe' "$output"
+	         output=$func_stripname_result ;;
+	esac
+	# test for cygwin because mv fails w/o .exe extensions
+	case $host in
+	  *cygwin*)
+	    exeext=.exe
+	    func_stripname '' '.exe' "$outputname"
+	    outputname=$func_stripname_result ;;
+	  *) exeext= ;;
+	esac
+	case $host in
+	  *cygwin* | *mingw* )
+	    func_dirname_and_basename "$output" "" "."
+	    output_name=$func_basename_result
+	    output_path=$func_dirname_result
+	    cwrappersource=$output_path/$objdir/lt-$output_name.c
+	    cwrapper=$output_path/$output_name.exe
+	    $RM $cwrappersource $cwrapper
+	    trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
+
+	    func_emit_cwrapperexe_src > $cwrappersource
+
+	    # The wrapper executable is built using the $host compiler,
+	    # because it contains $host paths and files. If cross-
+	    # compiling, it, like the target executable, must be
+	    # executed on the $host or under an emulation environment.
+	    $opt_dry_run || {
+	      $LTCC $LTCFLAGS -o $cwrapper $cwrappersource
+	      $STRIP $cwrapper
+	    }
+
+	    # Now, create the wrapper script for func_source use:
+	    func_ltwrapper_scriptname $cwrapper
+	    $RM $func_ltwrapper_scriptname_result
+	    trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
+	    $opt_dry_run || {
+	      # note: this script will not be executed, so do not chmod.
+	      if test "x$build" = "x$host"; then
+		$cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
+	      else
+		func_emit_wrapper no > $func_ltwrapper_scriptname_result
+	      fi
+	    }
+	  ;;
+	  * )
+	    $RM $output
+	    trap "$RM $output; exit $EXIT_FAILURE" 1 2 15
+
+	    func_emit_wrapper no > $output
+	    chmod +x $output
+	  ;;
+	esac
+      }
+      exit $EXIT_SUCCESS
+      ;;
+    esac
+
+    # See if we need to build an old-fashioned archive.
+    for oldlib in $oldlibs; do
+
+      case $build_libtool_libs in
+        convenience)
+	  oldobjs="$libobjs_save $symfileobj"
+	  addlibs=$convenience
+	  build_libtool_libs=no
+	  ;;
+	module)
+	  oldobjs=$libobjs_save
+	  addlibs=$old_convenience
+	  build_libtool_libs=no
+          ;;
+	*)
+	  oldobjs="$old_deplibs $non_pic_objects"
+	  $preload && test -f "$symfileobj" \
+	    && func_append oldobjs " $symfileobj"
+	  addlibs=$old_convenience
+	  ;;
+      esac
+
+      if test -n "$addlibs"; then
+	gentop=$output_objdir/${outputname}x
+	func_append generated " $gentop"
+
+	func_extract_archives $gentop $addlibs
+	func_append oldobjs " $func_extract_archives_result"
+      fi
+
+      # Do each command in the archive commands.
+      if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then
+	cmds=$old_archive_from_new_cmds
+      else
+
+	# Add any objects from preloaded convenience libraries
+	if test -n "$dlprefiles"; then
+	  gentop=$output_objdir/${outputname}x
+	  func_append generated " $gentop"
+
+	  func_extract_archives $gentop $dlprefiles
+	  func_append oldobjs " $func_extract_archives_result"
+	fi
+
+	# POSIX demands no paths to be encoded in archives.  We have
+	# to avoid creating archives with duplicate basenames if we
+	# might have to extract them afterwards, e.g., when creating a
+	# static archive out of a convenience library, or when linking
+	# the entirety of a libtool archive into another (currently
+	# not supported by libtool).
+	if (for obj in $oldobjs
+	    do
+	      func_basename "$obj"
+	      $ECHO "$func_basename_result"
+	    done | sort | sort -uc >/dev/null 2>&1); then
+	  :
+	else
+	  echo "copying selected object files to avoid basename conflicts..."
+	  gentop=$output_objdir/${outputname}x
+	  func_append generated " $gentop"
+	  func_mkdir_p "$gentop"
+	  save_oldobjs=$oldobjs
+	  oldobjs=
+	  counter=1
+	  for obj in $save_oldobjs
+	  do
+	    func_basename "$obj"
+	    objbase=$func_basename_result
+	    case " $oldobjs " in
+	    " ") oldobjs=$obj ;;
+	    *[\ /]"$objbase "*)
+	      while :; do
+		# Make sure we don't pick an alternate name that also
+		# overlaps.
+		newobj=lt$counter-$objbase
+		func_arith $counter + 1
+		counter=$func_arith_result
+		case " $oldobjs " in
+		*[\ /]"$newobj "*) ;;
+		*) if test ! -f "$gentop/$newobj"; then break; fi ;;
+		esac
+	      done
+	      func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj"
+	      func_append oldobjs " $gentop/$newobj"
+	      ;;
+	    *) func_append oldobjs " $obj" ;;
+	    esac
+	  done
+	fi
+	func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+	tool_oldlib=$func_to_tool_file_result
+	eval cmds=\"$old_archive_cmds\"
+
+	func_len " $cmds"
+	len=$func_len_result
+	if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
+	  cmds=$old_archive_cmds
+	elif test -n "$archiver_list_spec"; then
+	  func_verbose "using command file archive linking..."
+	  for obj in $oldobjs
+	  do
+	    func_to_tool_file "$obj"
+	    $ECHO "$func_to_tool_file_result"
+	  done > $output_objdir/$libname.libcmd
+	  func_to_tool_file "$output_objdir/$libname.libcmd"
+	  oldobjs=" $archiver_list_spec$func_to_tool_file_result"
+	  cmds=$old_archive_cmds
+	else
+	  # the command line is too long to link in one step, link in parts
+	  func_verbose "using piecewise archive linking..."
+	  save_RANLIB=$RANLIB
+	  RANLIB=:
+	  objlist=
+	  concat_cmds=
+	  save_oldobjs=$oldobjs
+	  oldobjs=
+	  # Is there a better way of finding the last object in the list?
+	  for obj in $save_oldobjs
+	  do
+	    last_oldobj=$obj
+	  done
+	  eval test_cmds=\"$old_archive_cmds\"
+	  func_len " $test_cmds"
+	  len0=$func_len_result
+	  len=$len0
+	  for obj in $save_oldobjs
+	  do
+	    func_len " $obj"
+	    func_arith $len + $func_len_result
+	    len=$func_arith_result
+	    func_append objlist " $obj"
+	    if test "$len" -lt "$max_cmd_len"; then
+	      :
+	    else
+	      # the above command should be used before it gets too long
+	      oldobjs=$objlist
+	      if test "$obj" = "$last_oldobj"; then
+		RANLIB=$save_RANLIB
+	      fi
+	      test -z "$concat_cmds" || concat_cmds=$concat_cmds~
+	      eval concat_cmds=\"\$concat_cmds$old_archive_cmds\"
+	      objlist=
+	      len=$len0
+	    fi
+	  done
+	  RANLIB=$save_RANLIB
+	  oldobjs=$objlist
+	  if test -z "$oldobjs"; then
+	    eval cmds=\"\$concat_cmds\"
+	  else
+	    eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
+	  fi
+	fi
+      fi
+      func_execute_cmds "$cmds" 'exit $?'
+    done
+
+    test -n "$generated" && \
+      func_show_eval "${RM}r$generated"
+
+    # Now create the libtool archive.
+    case $output in
+    *.la)
+      old_library=
+      test yes = "$build_old_libs" && old_library=$libname.$libext
+      func_verbose "creating $output"
+
+      # Preserve any variables that may affect compiler behavior
+      for var in $variables_saved_for_relink; do
+	if eval test -z \"\${$var+set}\"; then
+	  relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command"
+	elif eval var_value=\$$var; test -z "$var_value"; then
+	  relink_command="$var=; export $var; $relink_command"
+	else
+	  func_quote_arg pretty,unquoted "$var_value"
+	  relink_command="$var=$func_quote_arg_unquoted_result; export $var; $relink_command"
+	fi
+      done
+      # Quote the link command for shipping.
+      func_quote eval cd "`pwd`"
+      relink_command="($func_quote_result; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+      func_quote_arg pretty,unquoted "$relink_command"
+      relink_command=$func_quote_arg_unquoted_result
+      if test yes = "$hardcode_automatic"; then
+	relink_command=
+      fi
+
+      # Only create the output if not a dry run.
+      $opt_dry_run || {
+	for installed in no yes; do
+	  if test yes = "$installed"; then
+	    if test -z "$install_libdir"; then
+	      break
+	    fi
+	    output=$output_objdir/${outputname}i
+	    # Replace all uninstalled libtool libraries with the installed ones
+	    newdependency_libs=
+	    for deplib in $dependency_libs; do
+	      case $deplib in
+	      *.la)
+		func_basename "$deplib"
+		name=$func_basename_result
+		func_resolve_sysroot "$deplib"
+		eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result`
+		test -z "$libdir" && \
+		  func_fatal_error "'$deplib' is not a valid libtool archive"
+		func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name"
+		;;
+	      -L*)
+		func_stripname -L '' "$deplib"
+		func_replace_sysroot "$func_stripname_result"
+		func_append newdependency_libs " -L$func_replace_sysroot_result"
+		;;
+	      -R*)
+		func_stripname -R '' "$deplib"
+		func_replace_sysroot "$func_stripname_result"
+		func_append newdependency_libs " -R$func_replace_sysroot_result"
+		;;
+	      *) func_append newdependency_libs " $deplib" ;;
+	      esac
+	    done
+	    dependency_libs=$newdependency_libs
+	    newdlfiles=
+
+	    for lib in $dlfiles; do
+	      case $lib in
+	      *.la)
+	        func_basename "$lib"
+		name=$func_basename_result
+		eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+		test -z "$libdir" && \
+		  func_fatal_error "'$lib' is not a valid libtool archive"
+		func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name"
+		;;
+	      *) func_append newdlfiles " $lib" ;;
+	      esac
+	    done
+	    dlfiles=$newdlfiles
+	    newdlprefiles=
+	    for lib in $dlprefiles; do
+	      case $lib in
+	      *.la)
+		# Only pass preopened files to the pseudo-archive (for
+		# eventual linking with the app. that links it) if we
+		# didn't already link the preopened objects directly into
+		# the library:
+		func_basename "$lib"
+		name=$func_basename_result
+		eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+		test -z "$libdir" && \
+		  func_fatal_error "'$lib' is not a valid libtool archive"
+		func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name"
+		;;
+	      esac
+	    done
+	    dlprefiles=$newdlprefiles
+	  else
+	    newdlfiles=
+	    for lib in $dlfiles; do
+	      case $lib in
+		[\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
+		*) abs=`pwd`"/$lib" ;;
+	      esac
+	      func_append newdlfiles " $abs"
+	    done
+	    dlfiles=$newdlfiles
+	    newdlprefiles=
+	    for lib in $dlprefiles; do
+	      case $lib in
+		[\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
+		*) abs=`pwd`"/$lib" ;;
+	      esac
+	      func_append newdlprefiles " $abs"
+	    done
+	    dlprefiles=$newdlprefiles
+	  fi
+	  $RM $output
+	  # place dlname in correct position for cygwin
+	  # In fact, it would be nice if we could use this code for all target
+	  # systems that can't hard-code library paths into their executables
+	  # and that have no shared library path variable independent of PATH,
+	  # but it turns out we can't easily determine that from inspecting
+	  # libtool variables, so we have to hard-code the OSs to which it
+	  # applies here; at the moment, that means platforms that use the PE
+	  # object format with DLL files.  See the long comment at the top of
+	  # tests/bindir.at for full details.
+	  tdlname=$dlname
+	  case $host,$output,$installed,$module,$dlname in
+	    *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
+	      # If a -bindir argument was supplied, place the dll there.
+	      if test -n "$bindir"; then
+		func_relative_path "$install_libdir" "$bindir"
+		tdlname=$func_relative_path_result/$dlname
+	      else
+		# Otherwise fall back on heuristic.
+		tdlname=../bin/$dlname
+	      fi
+	      ;;
+	  esac
+	  $ECHO > $output "\
+# $outputname - a libtool library file
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
+#
+# Please DO NOT delete this file!
+# It is necessary for linking the library.
+
+# The name that we can dlopen(3).
+dlname='$tdlname'
+
+# Names of this library.
+library_names='$library_names'
+
+# The name of the static archive.
+old_library='$old_library'
+
+# Linker flags that cannot go in dependency_libs.
+inherited_linker_flags='$new_inherited_linker_flags'
+
+# Libraries that this one depends upon.
+dependency_libs='$dependency_libs'
+
+# Names of additional weak libraries provided by this library
+weak_library_names='$weak_libs'
+
+# Version information for $libname.
+current=$current
+age=$age
+revision=$revision
+
+# Is this an already installed library?
+installed=$installed
+
+# Should we warn about portability when linking against -modules?
+shouldnotlink=$module
+
+# Files to dlopen/dlpreopen
+dlopen='$dlfiles'
+dlpreopen='$dlprefiles'
+
+# Directory that this library needs to be installed in:
+libdir='$install_libdir'"
+	  if test no,yes = "$installed,$need_relink"; then
+	    $ECHO >> $output "\
+relink_command=\"$relink_command\""
+	  fi
+	done
+      }
+
+      # Do a symbolic link so that the libtool archive can be found in
+      # LD_LIBRARY_PATH before the program is installed.
+      func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?'
+      ;;
+    esac
+    exit $EXIT_SUCCESS
+}
+
+if test link = "$opt_mode" || test relink = "$opt_mode"; then
+  func_mode_link ${1+"$@"}
+fi
+
+
+# func_mode_uninstall arg...
+func_mode_uninstall ()
+{
+    $debug_cmd
+
+    RM=$nonopt
+    files=
+    rmforce=false
+    exit_status=0
+
+    # This variable tells wrapper scripts just to set variables rather
+    # than running their programs.
+    libtool_install_magic=$magic
+
+    for arg
+    do
+      case $arg in
+      -f) func_append RM " $arg"; rmforce=: ;;
+      -*) func_append RM " $arg" ;;
+      *) func_append files " $arg" ;;
+      esac
+    done
+
+    test -z "$RM" && \
+      func_fatal_help "you must specify an RM program"
+
+    rmdirs=
+
+    for file in $files; do
+      func_dirname "$file" "" "."
+      dir=$func_dirname_result
+      if test . = "$dir"; then
+	odir=$objdir
+      else
+	odir=$dir/$objdir
+      fi
+      func_basename "$file"
+      name=$func_basename_result
+      test uninstall = "$opt_mode" && odir=$dir
+
+      # Remember odir for removal later, being careful to avoid duplicates
+      if test clean = "$opt_mode"; then
+	case " $rmdirs " in
+	  *" $odir "*) ;;
+	  *) func_append rmdirs " $odir" ;;
+	esac
+      fi
+
+      # Don't error if the file doesn't exist and rm -f was used.
+      if { test -L "$file"; } >/dev/null 2>&1 ||
+	 { test -h "$file"; } >/dev/null 2>&1 ||
+	 test -f "$file"; then
+	:
+      elif test -d "$file"; then
+	exit_status=1
+	continue
+      elif $rmforce; then
+	continue
+      fi
+
+      rmfiles=$file
+
+      case $name in
+      *.la)
+	# Possibly a libtool archive, so verify it.
+	if func_lalib_p "$file"; then
+	  func_source $dir/$name
+
+	  # Delete the libtool libraries and symlinks.
+	  for n in $library_names; do
+	    func_append rmfiles " $odir/$n"
+	  done
+	  test -n "$old_library" && func_append rmfiles " $odir/$old_library"
+
+	  case $opt_mode in
+	  clean)
+	    case " $library_names " in
+	    *" $dlname "*) ;;
+	    *) test -n "$dlname" && func_append rmfiles " $odir/$dlname" ;;
+	    esac
+	    test -n "$libdir" && func_append rmfiles " $odir/$name $odir/${name}i"
+	    ;;
+	  uninstall)
+	    if test -n "$library_names"; then
+	      # Do each command in the postuninstall commands.
+	      func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1'
+	    fi
+
+	    if test -n "$old_library"; then
+	      # Do each command in the old_postuninstall commands.
+	      func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1'
+	    fi
+	    # FIXME: should reinstall the best remaining shared library.
+	    ;;
+	  esac
+	fi
+	;;
+
+      *.lo)
+	# Possibly a libtool object, so verify it.
+	if func_lalib_p "$file"; then
+
+	  # Read the .lo file
+	  func_source $dir/$name
+
+	  # Add PIC object to the list of files to remove.
+	  if test -n "$pic_object" && test none != "$pic_object"; then
+	    func_append rmfiles " $dir/$pic_object"
+	  fi
+
+	  # Add non-PIC object to the list of files to remove.
+	  if test -n "$non_pic_object" && test none != "$non_pic_object"; then
+	    func_append rmfiles " $dir/$non_pic_object"
+	  fi
+	fi
+	;;
+
+      *)
+	if test clean = "$opt_mode"; then
+	  noexename=$name
+	  case $file in
+	  *.exe)
+	    func_stripname '' '.exe' "$file"
+	    file=$func_stripname_result
+	    func_stripname '' '.exe' "$name"
+	    noexename=$func_stripname_result
+	    # $file with .exe has already been added to rmfiles,
+	    # add $file without .exe
+	    func_append rmfiles " $file"
+	    ;;
+	  esac
+	  # Do a test to see if this is a libtool program.
+	  if func_ltwrapper_p "$file"; then
+	    if func_ltwrapper_executable_p "$file"; then
+	      func_ltwrapper_scriptname "$file"
+	      relink_command=
+	      func_source $func_ltwrapper_scriptname_result
+	      func_append rmfiles " $func_ltwrapper_scriptname_result"
+	    else
+	      relink_command=
+	      func_source $dir/$noexename
+	    fi
+
+	    # note $name still contains .exe if it was in $file originally
+	    # as does the version of $file that was added into $rmfiles
+	    func_append rmfiles " $odir/$name $odir/${name}S.$objext"
+	    if test yes = "$fast_install" && test -n "$relink_command"; then
+	      func_append rmfiles " $odir/lt-$name"
+	    fi
+	    if test "X$noexename" != "X$name"; then
+	      func_append rmfiles " $odir/lt-$noexename.c"
+	    fi
+	  fi
+	fi
+	;;
+      esac
+      func_show_eval "$RM $rmfiles" 'exit_status=1'
+    done
+
+    # Try to remove the $objdir's in the directories where we deleted files
+    for dir in $rmdirs; do
+      if test -d "$dir"; then
+	func_show_eval "rmdir $dir >/dev/null 2>&1"
+      fi
+    done
+
+    exit $exit_status
+}
+
+if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then
+  func_mode_uninstall ${1+"$@"}
+fi
+
+test -z "$opt_mode" && {
+  help=$generic_help
+  func_fatal_help "you must specify a MODE"
+}
+
+test -z "$exec_cmd" && \
+  func_fatal_help "invalid operation mode '$opt_mode'"
+
+if test -n "$exec_cmd"; then
+  eval exec "$exec_cmd"
+  exit $EXIT_FAILURE
+fi
+
+exit $exit_status
+
+
+# The TAGs below are defined such that we never get into a situation
+# where we disable both kinds of libraries.  Given conflicting
+# choices, we go for a static library, that is the most portable,
+# since we can't tell whether shared libraries were disabled because
+# the user asked for that or because the platform doesn't support
+# them.  This is particularly important on AIX, because we don't
+# support having both static and shared libraries enabled at the same
+# time on that platform, so we default to a shared-only configuration.
+# If a disable-shared tag is given, we'll fallback to a static-only
+# configuration.  But we'll never go from static-only to shared-only.
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-shared
+build_libtool_libs=no
+build_old_libs=yes
+# ### END LIBTOOL TAG CONFIG: disable-shared
+
+# ### BEGIN LIBTOOL TAG CONFIG: disable-static
+build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
+# ### END LIBTOOL TAG CONFIG: disable-static
+
+# Local Variables:
+# mode:shell-script
+# sh-indentation:2
+# End:
diff --git a/c/archive.h b/c/archive.h
new file mode 100644
--- /dev/null
+++ b/c/archive.h
@@ -0,0 +1,1265 @@
+/*-
+ * 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.
+ */
+
+#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 3008002
+
+#include <sys/stat.h>
+#include <stddef.h>  /* for wchar_t */
+#include <stdio.h> /* For FILE * */
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* time_t is slated to be removed from public includes in 4.0 */
+#include <time.h> /* For time_t */
+#endif
+
+/*
+ * 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;
+typedef unsigned __int64 la_uint64_t;
+# else
+# include <unistd.h>  /* ssize_t */
+#  if defined(_SCO_DS) || defined(__osf__)
+typedef long long la_int64_t;
+typedef unsigned long long la_uint64_t;
+#  else
+typedef int64_t la_int64_t;
+typedef uint64_t la_uint64_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
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Use the platform types for time_t */
+#define __LA_TIME_T time_t
+#else
+/* Use 64-bits integer types for time_t */
+#define __LA_TIME_T la_int64_t
+#endif
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Use the platform types for dev_t */
+#define __LA_DEV_T dev_t
+#else
+/* Use 64-bits integer types for dev_t */
+#define __LA_DEV_T la_int64_t
+#endif
+
+/* Large file support for Android */
+#if defined(__LIBARCHIVE_BUILD) && defined(__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
+#elif defined __LIBARCHIVE_ENABLE_VISIBILITY
+#  define __LA_DECL __attribute__((visibility("default")))
+#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__ == 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.8.2"
+#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);
+__LA_DECL const char *  archive_liblzo2_version(void);
+__LA_DECL const char *  archive_libexpat_version(void);
+__LA_DECL const char *  archive_libbsdxml_version(void);
+__LA_DECL const char *  archive_libxml2_version(void);
+__LA_DECL const char *  archive_mbedtls_version(void);
+__LA_DECL const char *  archive_nettle_version(void);
+__LA_DECL const char *  archive_openssl_version(void);
+__LA_DECL const char *  archive_libmd_version(void);
+__LA_DECL const char *  archive_commoncrypto_version(void);
+__LA_DECL const char *  archive_cng_version(void);
+__LA_DECL const char *  archive_wincrypt_version(void);
+__LA_DECL const char *  archive_librichacl_version(void);
+__LA_DECL const char *  archive_libacl_version(void);
+__LA_DECL const char *  archive_libattr_version(void);
+__LA_DECL const char *  archive_libiconv_version(void);
+__LA_DECL const char *  archive_libpcre_version(void);
+__LA_DECL const char *  archive_libpcre2_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);
+
+typedef int	archive_free_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_CPIO_PWB			(ARCHIVE_FORMAT_CPIO | 7)
+#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_by_code(struct archive *, int);
+__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 *);
+/* archive_read_support_format_gnutar() is an alias for historical reasons
+ * of archive_read_support_format_tar(). */
+__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);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+__LA_DECL int archive_read_open_filenames_w(struct archive *,
+		     const wchar_t **_filenames, size_t _block_size);
+#endif
+/* 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_bin(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_odc(struct archive *);
+__LA_DECL int archive_write_set_format_cpio_pwb(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_zip_set_compression_lzma(struct archive *);
+__LA_DECL int archive_write_zip_set_compression_xz(struct archive *);
+__LA_DECL int archive_write_zip_set_compression_bzip2(struct archive *);
+__LA_DECL int archive_write_zip_set_compression_zstd(struct archive *);
+/* Deprecated; use archive_write_open2 instead */
+__LA_DECL int archive_write_open(struct archive *, void *,
+		     archive_open_callback *, archive_write_callback *,
+		     archive_close_callback *);
+__LA_DECL int archive_write_open2(struct archive *, void *,
+		     archive_open_callback *, archive_write_callback *,
+		     archive_close_callback *, archive_free_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 an 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)
+/* Default: Sparse file information is read from disk. */
+#define	ARCHIVE_READDISK_NO_SPARSE		(0x0080)
+
+__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
+
+/* Parses a date string relative to the current time.
+ * NOTE: This is not intended for general date parsing, and the resulting timestamp should only be used for libarchive. */
+__LA_DECL time_t	archive_parse_date(time_t now, const char *datestr);
+
+__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 */
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Convenience function to sort a NULL terminated list of strings */
+__LA_DECL int archive_utility_string_sort(char **);
+#endif
+
+#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,2136 @@
+/*-
+ * 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"
+
+#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 size_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, size_t *l, 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;
+
+	/* Reject an invalid type */
+	switch (type) {
+	case ARCHIVE_ENTRY_ACL_TYPE_ACCESS:
+	case ARCHIVE_ENTRY_ACL_TYPE_DEFAULT:
+	case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:
+	case ARCHIVE_ENTRY_ACL_TYPE_DENY:
+	case ARCHIVE_ENTRY_ACL_TYPE_AUDIT:
+	case ARCHIVE_ENTRY_ACL_TYPE_ALARM:
+		break;
+	default:
+		return (NULL);
+	}
+
+	/* 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 = 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 size_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;
+	size_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(a, &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;
+	size_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 = malloc(length * sizeof(*wp));
+	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 (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;
+	default:
+		**wp = '\0';
+		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:
+			*(*wp) = L'\0';
+			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;
+	size_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 = malloc(length * sizeof(*p));
+	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(
+		    NULL, &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 (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;
+	default:
+		**p = '\0';
+		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;
+		default:
+			*(*p) = '\0';
+			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].end == NULL) {
+			/* This should never happen */
+			return (ARCHIVE_FATAL);
+		}
+
+		if (*(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 < L'0' || *start > L'9')
+			return (0);
+		if (n > (INT_MAX / 10) ||
+		    (n == INT_MAX / 10 && (*start - L'0') > INT_MAX % 10)) {
+			n = INT_MAX;
+		} else {
+			n *= 10;
+			n += *start - L'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)
+{
+	return archive_acl_from_text_nl(acl, text, strlen(text), want_type, sc);
+}
+
+int
+archive_acl_from_text_nl(struct archive_acl *acl, const char *text,
+    size_t length, 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 && length > 0 && *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, &length, &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].end == NULL) {
+			/* This should never happen */
+			return (ARCHIVE_FATAL);
+		}
+
+		if (*(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]".  *p 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, size_t *l, const char **start,
+    const char **end, char *sep)
+{
+	/* Skip leading whitespace to find start of field. */
+	while (*l > 0 && (**p == ' ' || **p == '\t' || **p == '\n')) {
+		(*p)++;
+		(*l)--;
+	}
+	*start = *p;
+
+	/* Locate end of field, trim trailing whitespace if necessary */
+	while (*l > 0 && **p != ' ' && **p != '\t' && **p != '\n' && **p != ',' && **p != ':' && **p != '#') {
+		(*p)++;
+		(*l)--;
+	}
+	*end = *p;
+
+	/* Scan for the separator. */
+	while (*l > 0 && **p != ',' && **p != ':' && **p != '\n' && **p != '#') {
+		(*p)++;
+		(*l)--;
+	}
+	*sep = **p;
+
+	/* Handle in-field comments */
+	if (*sep == '#') {
+		while (*l > 0 && **p != ',' && **p != '\n') {
+			(*p)++;
+			(*l)--;
+		}
+		*sep = **p;
+	}
+
+	/* Skip separator. */
+	if (*l > 0) {
+		(*p)++;
+		(*l)--;
+	}
+}
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.
+ */
+
+#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 *);
+int archive_acl_from_text_nl(struct archive_acl *, const char * /* text */,
+    size_t /* size of text */, int /* type */, struct archive_string_conv *);
+
+#endif /* !ARCHIVE_ACL_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,197 @@
+/*
+   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))
+#elif defined(__GNUC__)
+#define BLAKE2_PACKED(x) x __attribute__((packed))
+#else
+#define BLAKE2_PACKED(x) _Pragma("pack 1") x _Pragma("pack 0")
+#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 *(__LA_LIBC_CC *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,369 @@
+/*
+   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 "archive_platform.h"
+
+#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,361 @@
+/*
+   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 "archive_platform.h"
+
+#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,182 @@
+/*-
+ * 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"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <errno.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, s);
+		if (written == 0)
+			return;
+		if (written < 0)
+		{
+			if (errno == EINTR)
+				continue;
+			return;
+		}
+		m += written;
+		s -= written;
+	}
+}
+
+static __LA_NORETURN 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 void
+write_all_states(char *buff, unsigned int states)
+{
+	unsigned int lowbit;
+
+	*buff = '\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, "/");
+	}
+}
+
+/*
+ * 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) {
+			write_all_states(states1, a->state);
+			write_all_states(states2, state);
+			archive_set_error(a, -1,
+			    "INTERNAL ERROR: Function '%s' invoked with"
+			    " archive structure in state '%s',"
+			    " should be in state '%s'",
+			    function,
+			    states1,
+			    states2);
+		}
+		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,225 @@
+/*-
+ * 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"
+
+#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 == ' ')
+		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,45 @@
+/*-
+ * 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.
+ */
+
+#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,86 @@
+/*-
+ * 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.
+ */
+
+#ifndef ARCHIVE_CRC32_H
+#define ARCHIVE_CRC32_H
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#include <stddef.h>
+
+/*
+ * 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 (_p == NULL)
+		return (0);
+
+	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,534 @@
+/*-
+* 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) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#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 CRYPTOR_STUB_FUNCTION; /* 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) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+
+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)
+{
+#if NETTLE_VERSION_MAJOR < 3
+	aes_set_encrypt_key(&ctx->ctx, ctx->key_len, ctx->key);
+	aes_encrypt(&ctx->ctx, AES_BLOCK_SIZE, ctx->encr_buf, ctx->nonce);
+#else
+	switch(ctx->key_len) {
+	case AES128_KEY_SIZE:
+		aes128_set_encrypt_key(&ctx->ctx.c128, ctx->key);
+		aes128_encrypt(&ctx->ctx.c128, AES_BLOCK_SIZE, ctx->encr_buf,
+		    ctx->nonce);
+		break;
+	case AES192_KEY_SIZE:
+		aes192_set_encrypt_key(&ctx->ctx.c192, ctx->key);
+		aes192_encrypt(&ctx->ctx.c192, AES_BLOCK_SIZE, ctx->encr_buf,
+		    ctx->nonce);
+		break;
+	case AES256_KEY_SIZE:
+		aes256_set_encrypt_key(&ctx->ctx.c256, ctx->key);
+		aes256_encrypt(&ctx->ctx.c256, AES_BLOCK_SIZE, ctx->encr_buf,
+		    ctx->nonce);
+		break;
+	default:
+		return -1;
+		break;
+	}
+#endif
+	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;
+	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);
+	OPENSSL_cleanse(ctx->key, ctx->key_len);
+	OPENSSL_cleanse(ctx->nonce, 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 CRYPTOR_STUB_FUNCTION;
+}
+
+static int
+aes_ctr_encrypt_counter(archive_crypto_ctx *ctx)
+{
+	(void)ctx; /* UNUSED */
+	return CRYPTOR_STUB_FUNCTION;
+}
+
+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 CRYPTOR_STUB_FUNCTION;
+}
+
+#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,205 @@
+/*-
+* 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 1
+# 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) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#include <bcrypt.h>
+#define	ARCHIVE_CRYPTOR_USE_CNG 1
+
+/* 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	ARCHIVE_CRYPTOR_USE_MBED 1
+
+#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>
+#include <nettle/version.h>
+#define	ARCHIVE_CRYPTOR_USE_NETTLE 1
+
+typedef struct {
+#if NETTLE_VERSION_MAJOR < 3
+	struct aes_ctx	ctx;
+#else
+	union {
+		struct aes128_ctx c128;
+		struct aes192_ctx c192;
+		struct aes256_ctx c256;
+	}		ctx;
+#endif
+	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	ARCHIVE_CRYPTOR_USE_OPENSSL 1
+#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
+
+#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)
+#if defined(_WIN32) && !defined(__CYGWIN__) && !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA)
+#define ARCHIVE_CRYPTOR_USE_WINCRYPT 1
+#endif
+#endif
+
+#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)
+
+/* Stub return value if no encryption support exists. */
+#define CRYPTOR_STUB_FUNCTION	-2
+
+/* 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,1649 @@
+/*-
+* 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
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#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.
+ */
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+static int
+win_crypto_init(Digest_CTX *ctx, const WCHAR *algo)
+{
+	NTSTATUS status;
+	ctx->valid = 0;
+
+	status = BCryptOpenAlgorithmProvider(&ctx->hAlg, algo, NULL, 0);
+	if (!BCRYPT_SUCCESS(status))
+		return (ARCHIVE_FAILED);
+	status = BCryptCreateHash(ctx->hAlg, &ctx->hHash, NULL, 0, NULL, 0, 0);
+	if (!BCRYPT_SUCCESS(status)) {
+		BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+		return (ARCHIVE_FAILED);
+	}
+
+	ctx->valid = 1;
+	return (ARCHIVE_OK);
+}
+#else
+static int
+win_crypto_init(Digest_CTX *ctx, DWORD prov, ALG_ID algId)
+{
+
+	ctx->valid = 0;
+	if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
+	    prov, CRYPT_VERIFYCONTEXT)) {
+		if (GetLastError() != (DWORD)NTE_BAD_KEYSET)
+			return (ARCHIVE_FAILED);
+		if (!CryptAcquireContext(&ctx->cryptProv, NULL, NULL,
+		    prov, 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);
+}
+#endif
+
+/*
+ * 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);
+
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	BCryptHashData(ctx->hHash,
+		      (PUCHAR)(uintptr_t)buf,
+		      (ULONG)len, 0);
+#else
+	CryptHashData(ctx->hash,
+		      (unsigned char *)(uintptr_t)buf,
+		      (DWORD)len, 0);
+#endif
+	return (ARCHIVE_OK);
+}
+
+static int
+win_crypto_Final(unsigned char *buf, size_t bufsize, Digest_CTX *ctx)
+{
+#if !(defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA)
+	DWORD siglen = (DWORD)bufsize;
+#endif
+
+	if (!ctx->valid)
+		return (ARCHIVE_FAILED);
+
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	BCryptFinishHash(ctx->hHash, buf, (ULONG)bufsize, 0);
+	BCryptDestroyHash(ctx->hHash);
+	BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+#else
+	CryptGetHashParam(ctx->hash, HP_HASHVAL, buf, &siglen, 0);
+	CryptDestroyHash(ctx->hash);
+	CryptReleaseContext(ctx->cryptProv, 0);
+#endif
+	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)
+
+// These functions are available in macOS 10.4 and later, but deprecated from 10.15 onwards.
+// We need to continue supporting this feature regardless, so suppress the warnings.
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+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);
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+#elif defined(ARCHIVE_CRYPTO_MD5_WIN)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+  return (win_crypto_init(ctx, BCRYPT_MD5_ALGORITHM));
+#else
+  return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_MD5));
+#endif
+}
+
+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));
+}
+
+#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+
+static int
+__archive_md5init(archive_md5_ctx *ctx)
+{
+  mbedtls_md5_init(ctx);
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_md5_starts(ctx) == 0)
+#else
+  if (mbedtls_md5_starts_ret(ctx) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_md5update(archive_md5_ctx *ctx, const void *indata,
+    size_t insize)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_md5_update(ctx, indata, insize) == 0)
+#else
+  if (mbedtls_md5_update_ret(ctx, indata, insize) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_md5final(archive_md5_ctx *ctx, void *md)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_md5_finish(ctx, md) == 0) {
+#else
+  if (mbedtls_md5_finish_ret(ctx, md) == 0) {
+#endif
+    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);
+  if (!EVP_DigestInit(*ctx, EVP_md5()))
+	return (ARCHIVE_FAILED);
+  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);
+}
+
+#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_VERSION_NUMBER > 0x03000000
+  if (mbedtls_ripemd160_starts(ctx) == 0)
+#else
+  if (mbedtls_ripemd160_starts_ret(ctx) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_ripemd160update(archive_rmd160_ctx *ctx, const void *indata,
+    size_t insize)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_ripemd160_update(ctx, indata, insize) == 0)
+#else
+  if (mbedtls_ripemd160_update_ret(ctx, indata, insize) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_ripemd160final(archive_rmd160_ctx *ctx, void *md)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_ripemd160_finish(ctx, md) == 0) {
+#else
+  if (mbedtls_ripemd160_finish_ret(ctx, md) == 0) {
+#endif
+    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);
+  if (!EVP_DigestInit(*ctx, EVP_ripemd160()))
+	return (ARCHIVE_FAILED);
+  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_WIN)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+  return (win_crypto_init(ctx, BCRYPT_SHA1_ALGORITHM));
+#else
+  return (win_crypto_init(ctx, PROV_RSA_FULL, CALG_SHA1));
+#endif
+}
+
+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));
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+
+static int
+__archive_sha1init(archive_sha1_ctx *ctx)
+{
+  mbedtls_sha1_init(ctx);
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha1_starts(ctx) == 0)
+#else
+  if (mbedtls_sha1_starts_ret(ctx) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha1update(archive_sha1_ctx *ctx, const void *indata,
+    size_t insize)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha1_update(ctx, indata, insize) == 0)
+#else
+  if (mbedtls_sha1_update_ret(ctx, indata, insize) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha1final(archive_sha1_ctx *ctx, void *md)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha1_finish(ctx, md) == 0) {
+#else
+  if (mbedtls_sha1_finish_ret(ctx, md) == 0) {
+#endif
+    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);
+  if (!EVP_DigestInit(*ctx, EVP_sha1()))
+	return (ARCHIVE_FAILED);
+  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);
+}
+
+#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_WIN)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+  return (win_crypto_init(ctx, BCRYPT_SHA256_ALGORITHM));
+#else
+  return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_256));
+#endif
+}
+
+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));
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+
+static int
+__archive_sha256init(archive_sha256_ctx *ctx)
+{
+  mbedtls_sha256_init(ctx);
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha256_starts(ctx, 0) == 0)
+#else
+  if (mbedtls_sha256_starts_ret(ctx, 0) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha256update(archive_sha256_ctx *ctx, const void *indata,
+    size_t insize)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha256_update(ctx, indata, insize) == 0)
+#else
+  if (mbedtls_sha256_update_ret(ctx, indata, insize) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha256final(archive_sha256_ctx *ctx, void *md)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha256_finish(ctx, md) == 0) {
+#else
+  if (mbedtls_sha256_finish_ret(ctx, md) == 0) {
+#endif
+    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);
+  if (!EVP_DigestInit(*ctx, EVP_sha256()))
+	return (ARCHIVE_FAILED);
+  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);
+}
+
+#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_WIN)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+  return (win_crypto_init(ctx, BCRYPT_SHA384_ALGORITHM));
+#else
+  return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_384));
+#endif
+}
+
+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));
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
+
+static int
+__archive_sha384init(archive_sha384_ctx *ctx)
+{
+  mbedtls_sha512_init(ctx);
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha512_starts(ctx, 1) == 0)
+#else
+  if (mbedtls_sha512_starts_ret(ctx, 1) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha384update(archive_sha384_ctx *ctx, const void *indata,
+    size_t insize)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha512_update(ctx, indata, insize) == 0)
+#else
+  if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha384final(archive_sha384_ctx *ctx, void *md)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha512_finish(ctx, md) == 0) {
+#else
+  if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
+#endif
+    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);
+  if (!EVP_DigestInit(*ctx, EVP_sha384()))
+	return (ARCHIVE_FAILED);
+  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);
+}
+
+#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_WIN)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+  return (win_crypto_init(ctx, BCRYPT_SHA512_ALGORITHM));
+#else
+  return (win_crypto_init(ctx, PROV_RSA_AES, CALG_SHA_512));
+#endif
+}
+
+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));
+}
+
+#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+
+static int
+__archive_sha512init(archive_sha512_ctx *ctx)
+{
+  mbedtls_sha512_init(ctx);
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha512_starts(ctx, 0) == 0)
+#else
+  if (mbedtls_sha512_starts_ret(ctx, 0) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha512update(archive_sha512_ctx *ctx, const void *indata,
+    size_t insize)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha512_update(ctx, indata, insize) == 0)
+#else
+  if (mbedtls_sha512_update_ret(ctx, indata, insize) == 0)
+#endif
+    return (ARCHIVE_OK);
+  else
+    return (ARCHIVE_FATAL);
+}
+
+static int
+__archive_sha512final(archive_sha512_ctx *ctx, void *md)
+{
+#if MBEDTLS_VERSION_NUMBER > 0x03000000
+  if (mbedtls_sha512_finish(ctx, md) == 0) {
+#else
+  if (mbedtls_sha512_finish_ret(ctx, md) == 0) {
+#endif
+    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);
+  if (!EVP_DigestInit(*ctx, EVP_sha512()))
+	return (ARCHIVE_FAILED);
+  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);
+}
+
+#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. libmd
+ * 5. libSystem
+ * 6. Windows API
+ * 7. mbedTLS
+ * 8. Nettle
+ * 9. OpenSSL
+ */
+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,441 @@
+/*-
+* 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
+#ifndef __LIBARCHIVE_CONFIG_H_INCLUDED
+#error "Should have include config.h first!"
+#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>
+#define	ARCHIVE_CRYPTO_CommonCrypto 1
+#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)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+#define	ARCHIVE_CRYPTO_CNG 1
+typedef struct {
+  int   valid;
+  BCRYPT_ALG_HANDLE  hAlg;
+  BCRYPT_HASH_HANDLE hHash;
+} Digest_CTX;
+#else
+#include <windows.h>
+#include <wincrypt.h>
+#define	ARCHIVE_CRYPTO_WINCRYPT 1
+typedef struct {
+  int   valid;
+  HCRYPTPROV  cryptProv;
+  HCRYPTHASH  hash;
+} Digest_CTX;
+#endif
+#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_WIN)
+typedef Digest_CTX archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_MBEDTLS)
+#define	ARCHIVE_CRYPTO_MBED 1
+typedef mbedtls_md5_context archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_NETTLE)
+#define	ARCHIVE_CRYPTO_NETTLE 1
+typedef struct md5_ctx archive_md5_ctx;
+#elif defined(ARCHIVE_CRYPTO_MD5_OPENSSL)
+typedef EVP_MD_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)
+#define	ARCHIVE_CRYPTO_MBED 1
+typedef mbedtls_ripemd160_context archive_rmd160_ctx;
+#elif defined(ARCHIVE_CRYPTO_RMD160_NETTLE)
+#define	ARCHIVE_CRYPTO_NETTLE 1
+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_WIN)
+typedef Digest_CTX archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_MBEDTLS)
+#define	ARCHIVE_CRYPTO_MBED 1
+typedef mbedtls_sha1_context archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_NETTLE)
+#define	ARCHIVE_CRYPTO_NETTLE 1
+typedef struct sha1_ctx archive_sha1_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA1_OPENSSL)
+typedef EVP_MD_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_WIN)
+typedef Digest_CTX archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_MBEDTLS)
+#define	ARCHIVE_CRYPTO_MBED 1
+typedef mbedtls_sha256_context archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_NETTLE)
+#define	ARCHIVE_CRYPTO_NETTLE 1
+typedef struct sha256_ctx archive_sha256_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA256_OPENSSL)
+typedef EVP_MD_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_WIN)
+typedef Digest_CTX archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_MBEDTLS)
+#define	ARCHIVE_CRYPTO_MBED 1
+typedef mbedtls_sha512_context archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_NETTLE)
+#define	ARCHIVE_CRYPTO_NETTLE 1
+typedef struct sha384_ctx archive_sha384_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA384_OPENSSL)
+typedef EVP_MD_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_WIN)
+typedef Digest_CTX archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_MBEDTLS)
+#define	ARCHIVE_CRYPTO_MBED 1
+typedef mbedtls_sha512_context archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_NETTLE)
+#define	ARCHIVE_CRYPTO_NETTLE 1
+typedef struct sha512_ctx archive_sha512_ctx;
+#elif defined(ARCHIVE_CRYPTO_SHA512_OPENSSL)
+typedef EVP_MD_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 < sizeof(tacl_entry) / sizeof(tacl_entry[0]); 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,712 @@
+/*-
+ * 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_flag_t)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, __LA_MODE_T mode,
+    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);
+	}
+
+	if (acl_type == ACL_TYPE_DEFAULT && !S_ISDIR(mode)) {
+		errno = EINVAL;
+		archive_set_error(a, errno,
+		    "Cannot set default ACL on non-directory");
+		return (ARCHIVE_WARN);
+	}
+
+	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_flag_t)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 (S_ISLNK(mode)) {
+	    /* acl_set_file() follows symbolic links, skip */
+	    ret = ARCHIVE_OK;
+	} 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, mode,
+			    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, mode,
+			    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, mode,
+		    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,760 @@
+/*-
+ * 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);
+	}
+
+	if (S_ISLNK(mode)) {
+		/* Linux does not support RichACLs on symbolic links */
+		return (ARCHIVE_OK);
+	}
+
+	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, __LA_MODE_T mode,
+    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);
+	}
+
+	if (S_ISLNK(mode)) {
+		/* Linux does not support ACLs on symbolic links */
+		return (ARCHIVE_OK);
+	}
+
+	if (acl_type == ACL_TYPE_DEFAULT && !S_ISDIR(mode)) {
+		errno = EINVAL;
+		archive_set_error(a, errno,
+		    "Cannot set default ACL on non-directory");
+		return (ARCHIVE_WARN);
+	}
+
+	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, mode,
+			    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, mode,
+			    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,824 @@
+/*-
+ * 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, __LA_MODE_T mode,
+    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);
+	}
+
+        if (S_ISLNK(mode)) {
+                /* Skip ACLs on symbolic links */
+		ret = ARCHIVE_OK;
+		goto exit_free;
+        }
+
+	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, mode,
+		    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, mode,
+		    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,193 @@
+/*-
+ * 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.
+ *
+ * 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,2293 @@
+/*-
+ * 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"
+
+#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, size_t length,
+		    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_linkname);
+	archive_mstring_clean(&entry->ae_pathname);
+	archive_mstring_clean(&entry->ae_sourcepath);
+	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_linkname, &entry->ae_linkname);
+	archive_mstring_copy(&entry2->ae_pathname, &entry->ae_pathname);
+	archive_mstring_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath);
+	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 digests */
+#define copy_digest(_e2, _e, _t) \
+	memcpy(_e2->digest._t, _e->digest._t, sizeof(_e2->digest._t))
+
+	copy_digest(entry2, entry, md5);
+	copy_digest(entry2, entry, rmd160);
+	copy_digest(entry2, entry, sha1);
+	copy_digest(entry2, entry, sha256);
+	copy_digest(entry2, entry, sha384);
+	copy_digest(entry2, entry, sha512);
+
+#undef copy_digest
+	
+	/* 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 = 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.
+ */
+
+__LA_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);
+}
+
+__LA_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);
+}
+
+__LA_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);
+}
+
+__LA_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);
+}
+
+__LA_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);
+}
+
+__LA_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);
+}
+
+int
+archive_entry_filetype_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_FILETYPE);
+}
+
+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);
+}
+
+int
+archive_entry_gid_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_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->archive, &entry->ae_gname, p, len, sc));
+}
+
+void
+archive_entry_set_link_to_hardlink(struct archive_entry *entry)
+{
+	if ((entry->ae_set & AE_SET_SYMLINK) != 0) {
+		entry->ae_set &= ~AE_SET_SYMLINK;
+	}
+	entry->ae_set |= AE_SET_HARDLINK;
+}
+
+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_linkname, &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_linkname, &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_linkname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+int
+archive_entry_hardlink_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_HARDLINK) != 0;
+}
+
+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->archive, &entry->ae_linkname, 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);
+}
+
+__LA_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);
+}
+
+/* Instead, our caller could have chosen a specific encoding
+ * (archive_mstring_get_mbs, archive_mstring_get_utf8,
+ * archive_mstring_get_wcs).  So we should try multiple
+ * encodings.  Try mbs first because of history, even though
+ * utf8 might be better for pathname portability.
+ * Also omit wcs because of type mismatch (char * versus wchar *)
+ */
+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 HAVE_EILSEQ  /*{*/
+    if (errno == EILSEQ) {
+	    if (archive_mstring_get_utf8(
+	        entry->archive, &entry->ae_pathname, &p) == 0)
+		    return (p);
+    }
+#endif  /*}*/
+	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->archive, &entry->ae_pathname, p, len, sc));
+}
+
+__LA_MODE_T
+archive_entry_perm(struct archive_entry *entry)
+{
+	return (~AE_IFMT & entry->acl.mode);
+}
+
+int
+archive_entry_perm_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_PERM);
+}
+
+int
+archive_entry_rdev_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_RDEV);
+}
+
+__LA_DEV_T
+archive_entry_rdev(struct archive_entry *entry)
+{
+	if (archive_entry_rdev_is_set(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);
+	} else {
+		return 0;
+	}
+}
+
+__LA_DEV_T
+archive_entry_rdevmajor(struct archive_entry *entry)
+{
+	if (archive_entry_rdev_is_set(entry)) {
+		if (entry->ae_stat.aest_rdev_is_broken_down)
+			return (entry->ae_stat.aest_rdevmajor);
+		else
+			return major(entry->ae_stat.aest_rdev);
+	} else {
+		return 0;
+	}
+}
+
+__LA_DEV_T
+archive_entry_rdevminor(struct archive_entry *entry)
+{
+	if (archive_entry_rdev_is_set(entry)) {
+		if (entry->ae_stat.aest_rdev_is_broken_down)
+			return (entry->ae_stat.aest_rdevminor);
+		else
+			return minor(entry->ae_stat.aest_rdev);
+	} else {
+		return 0;
+	}
+}
+
+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_linkname, &p) == 0)
+		return (p);
+	if (errno == ENOMEM)
+		__archive_errx(1, "No memory");
+	return (NULL);
+}
+
+void
+archive_entry_set_link_to_symlink(struct archive_entry *entry)
+{
+	if ((entry->ae_set & AE_SET_HARDLINK) != 0) {
+		entry->ae_set &= ~AE_SET_HARDLINK;
+	}
+	entry->ae_set |= AE_SET_SYMLINK;
+}
+
+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_linkname, &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_linkname, &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->archive, &entry->ae_linkname, p, len, sc));
+}
+
+la_int64_t
+archive_entry_uid(struct archive_entry *entry)
+{
+	return (entry->ae_stat.aest_uid);
+}
+
+int
+archive_entry_uid_is_set(struct archive_entry *entry)
+{
+	return (entry->ae_set & AE_SET_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->archive, &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;
+	entry->ae_set |= AE_SET_FILETYPE;
+}
+
+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)
+{
+	return archive_entry_copy_fflags_text_len(entry, flags, strlen(flags));
+}
+
+const char *
+archive_entry_copy_fflags_text_len(struct archive_entry *entry,
+    const char *flags, size_t flags_length)
+{
+	archive_mstring_copy_mbs_len(&entry->ae_fflags_text, flags, flags_length);
+	return (ae_strtofflags(flags, flags_length,
+		    &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)
+{
+	if (g < 0) {
+		g = 0;
+	}
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_gid = g;
+	entry->ae_set |= AE_SET_GID;
+}
+
+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)
+{
+	if (ino < 0) {
+		entry->stat_valid = 0;
+		entry->ae_set &= ~AE_SET_INO;
+		return;
+	}
+	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)
+{
+	if (ino < 0) {
+		entry->stat_valid = 0;
+		entry->ae_set &= ~AE_SET_INO;
+		return;
+	}
+	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)
+{
+	if (target == NULL) {
+		entry->ae_set &= ~AE_SET_HARDLINK;
+		if (entry->ae_set & AE_SET_SYMLINK) {
+			return;
+		}
+	} else {
+		entry->ae_set |= AE_SET_HARDLINK;
+	}
+	entry->ae_set &= ~AE_SET_SYMLINK;
+	archive_mstring_copy_mbs(&entry->ae_linkname, target);
+}
+
+void
+archive_entry_set_hardlink_utf8(struct archive_entry *entry, const char *target)
+{
+	if (target == NULL && (entry->ae_set & AE_SET_SYMLINK))
+		return;
+	archive_mstring_copy_utf8(&entry->ae_linkname, 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)
+{
+	if (target == NULL && (entry->ae_set & AE_SET_SYMLINK))
+		return;
+	archive_mstring_copy_mbs(&entry->ae_linkname, 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)
+{
+	if (target == NULL && (entry->ae_set & AE_SET_SYMLINK))
+		return;
+	archive_mstring_copy_wcs(&entry->ae_linkname, 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_SYMLINK))
+		return (0);
+	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_linkname, 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;
+
+	if (target == NULL && (entry->ae_set & AE_SET_SYMLINK))
+		return (0);
+	r = archive_mstring_copy_mbs_len_l(&entry->ae_linkname,
+	    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, __LA_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, __LA_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, __LA_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, __LA_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, __LA_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, __LA_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)
+{
+	archive_mstring_copy_mbs(&entry->ae_linkname, target);
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0) {
+		entry->ae_set |= AE_SET_HARDLINK;
+	}
+}
+
+void
+archive_entry_set_link_utf8(struct archive_entry *entry, const char *target)
+{
+	archive_mstring_copy_utf8(&entry->ae_linkname, target);
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0) {
+		entry->ae_set |= AE_SET_HARDLINK;
+	}
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link(struct archive_entry *entry, const char *target)
+{
+	archive_mstring_copy_mbs(&entry->ae_linkname, target);
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0) {
+		entry->ae_set |= AE_SET_HARDLINK;
+	}
+}
+
+/* Set symlink if symlink is already set, else set hardlink. */
+void
+archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
+{
+	archive_mstring_copy_wcs(&entry->ae_linkname, target);
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0) {
+		entry->ae_set |= AE_SET_HARDLINK;
+	}
+}
+
+int
+archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
+{
+	int r;
+	r = archive_mstring_update_utf8(entry->archive,
+		    &entry->ae_linkname, target);
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0) {
+		entry->ae_set |= AE_SET_HARDLINK;
+	}
+	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;
+
+	r = archive_mstring_copy_mbs_len_l(&entry->ae_linkname,
+		    target, len, sc);
+	if ((entry->ae_set & AE_SET_SYMLINK) == 0) {
+		entry->ae_set |= AE_SET_HARDLINK;
+	}
+	return (r);
+}
+
+void
+archive_entry_set_mode(struct archive_entry *entry, mode_t m)
+{
+	entry->stat_valid = 0;
+	entry->acl.mode = m;
+	entry->ae_set |= AE_SET_PERM | AE_SET_FILETYPE;
+}
+
+void
+archive_entry_set_mtime(struct archive_entry *entry, __LA_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;
+	entry->ae_set |= AE_SET_PERM;
+}
+
+void
+archive_entry_set_rdev(struct archive_entry *entry, __LA_DEV_T m)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_rdev = m;
+	entry->ae_stat.aest_rdev_is_broken_down = 0;
+	entry->ae_stat.aest_rdevmajor = 0;
+	entry->ae_stat.aest_rdevminor = 0;
+	entry->ae_set |= AE_SET_RDEV;
+}
+
+void
+archive_entry_set_rdevmajor(struct archive_entry *entry, __LA_DEV_T m)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_rdev_is_broken_down = 1;
+	entry->ae_stat.aest_rdev = 0;
+	entry->ae_stat.aest_rdevmajor = m;
+	entry->ae_set |= AE_SET_RDEV;
+}
+
+void
+archive_entry_set_rdevminor(struct archive_entry *entry, __LA_DEV_T m)
+{
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_rdev_is_broken_down = 1;
+	entry->ae_stat.aest_rdev = 0;
+	entry->ae_stat.aest_rdevminor = m;
+	entry->ae_set |= AE_SET_RDEV;
+}
+
+void
+archive_entry_set_size(struct archive_entry *entry, la_int64_t s)
+{
+	if (s < 0) {
+		s = 0;
+	}
+	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)
+{
+	if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK))
+		return;
+	archive_mstring_copy_mbs(&entry->ae_linkname, linkname);
+	entry->ae_set &= ~AE_SET_HARDLINK;
+	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)
+{
+	if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK))
+		return;
+	archive_mstring_copy_utf8(&entry->ae_linkname, linkname);
+	entry->ae_set &= ~AE_SET_HARDLINK;
+	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)
+{
+	if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK))
+		return;
+	archive_mstring_copy_mbs(&entry->ae_linkname, linkname);
+	entry->ae_set &= ~AE_SET_HARDLINK;
+	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)
+{
+	if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK))
+		return;
+	archive_mstring_copy_wcs(&entry->ae_linkname, linkname);
+	entry->ae_set &= ~AE_SET_HARDLINK;
+	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_HARDLINK))
+		return (0);
+	entry->ae_set &= ~AE_SET_HARDLINK;
+	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_linkname, 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;
+
+	if (linkname == NULL && (entry->ae_set & AE_SET_HARDLINK))
+		return (0);
+	entry->ae_set &= ~AE_SET_HARDLINK;
+	r = archive_mstring_copy_mbs_len_l(&entry->ae_linkname,
+	    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)
+{
+	if (u < 0) {
+		u = 0;
+	}
+	entry->stat_valid = 0;
+	entry->ae_stat.aest_uid = u;
+	entry->ae_set |= AE_SET_UID;
+}
+
+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);
+  }
+}
+
+/* Digest handling */
+const unsigned char *
+archive_entry_digest(struct archive_entry *entry, int type)
+{
+	switch (type) {
+	case ARCHIVE_ENTRY_DIGEST_MD5:
+		return entry->digest.md5;
+	case ARCHIVE_ENTRY_DIGEST_RMD160:
+		return entry->digest.rmd160;
+	case ARCHIVE_ENTRY_DIGEST_SHA1:
+		return entry->digest.sha1;
+	case ARCHIVE_ENTRY_DIGEST_SHA256:
+		return entry->digest.sha256;
+	case ARCHIVE_ENTRY_DIGEST_SHA384:
+		return entry->digest.sha384;
+	case ARCHIVE_ENTRY_DIGEST_SHA512:
+		return entry->digest.sha512;
+	default:
+		return NULL;
+	}
+}
+
+int
+archive_entry_set_digest(struct archive_entry *entry, int type,
+    const unsigned char *digest)
+{
+#define copy_digest(_e, _t, _d)\
+	memcpy(_e->digest._t, _d, sizeof(_e->digest._t))
+
+	switch (type) {
+	case ARCHIVE_ENTRY_DIGEST_MD5:
+		copy_digest(entry, md5, digest);
+		entry->mset_digest |= AE_MSET_DIGEST_MD5;
+		break;
+	case ARCHIVE_ENTRY_DIGEST_RMD160:
+		copy_digest(entry, rmd160, digest);
+		entry->mset_digest |= AE_MSET_DIGEST_RMD160;
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA1:
+		copy_digest(entry, sha1, digest);
+		entry->mset_digest |= AE_MSET_DIGEST_SHA1;
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA256:
+		copy_digest(entry, sha256, digest);
+		entry->mset_digest |= AE_MSET_DIGEST_SHA256;
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA384:
+		copy_digest(entry, sha384, digest);
+		entry->mset_digest |= AE_MSET_DIGEST_SHA384;
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA512:
+		copy_digest(entry, sha512, digest);
+		entry->mset_digest |= AE_MSET_DIGEST_SHA512;
+		break;
+	default:
+		return ARCHIVE_WARN;
+	}
+
+	return ARCHIVE_OK;
+#undef copy_digest
+}
+
+/*
+ * 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 = 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, size_t l, 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 (l > 0 && (*start == '\t'  ||  *start == ' '  ||  *start == ',')) {
+		start++;
+		l--;
+	}
+	while (l > 0) {
+		size_t length;
+		/* Locate end of token. */
+		end = start;
+		while (l > 0 && *end != '\t'  &&
+		    *end != ' '  &&  *end != ',') {
+			end++;
+			l--;
+		}
+		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 (l > 0 && (*start == '\t'  ||  *start == ' '  ||  *start == ',')) {
+			start++;
+			l--;
+		}
+
+	}
+
+	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,763 @@
+/*-
+ * 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.
+ */
+
+#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 3008002
+
+/*
+ * 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>  /* for C99 int64_t, etc. */
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* time_t is slated to be removed from public includes in 4.0 */
+#include <time.h>
+#endif
+
+#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;
+typedef unsigned __int64 la_uint64_t;
+# else
+#include <unistd.h>
+#  if defined(_SCO_DS) || defined(__osf__)
+typedef long long la_int64_t;
+typedef unsigned long long la_uint64_t;
+#  else
+typedef int64_t la_int64_t;
+typedef uint64_t la_uint64_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
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Use the platform types for time_t */
+#define __LA_TIME_T time_t
+#else
+/* Use 64-bits integer types for time_t */
+#define __LA_TIME_T la_int64_t
+#endif
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Use the platform types for dev_t */
+#define __LA_DEV_T dev_t
+#else
+/* Use 64-bits integer types for dev_t */
+#define __LA_DEV_T la_int64_t
+#endif
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/* Libarchive 3.x used signed int64 for inode numbers */
+#define __LA_INO_T la_int64_t
+#else
+/* Switch to unsigned for libarchive 4.0 */
+#define __LA_INO_T la_uint64_t
+#endif
+
+/* Large file support for Android */
+#if defined(__LIBARCHIVE_BUILD) && defined(__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
+#elif defined __LIBARCHIVE_ENABLE_VISIBILITY
+#  define __LA_DECL __attribute__((visibility("default")))
+#else
+/* Static libraries on all platforms and shared libraries on non-Windows. */
+# define __LA_DECL
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ > 3 || \
+    (__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 __LA_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 __LA_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 __LA_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 __LA_DEV_T		 archive_entry_dev(struct archive_entry *);
+__LA_DECL int		 archive_entry_dev_is_set(struct archive_entry *);
+__LA_DECL __LA_DEV_T		 archive_entry_devmajor(struct archive_entry *);
+__LA_DECL __LA_DEV_T		 archive_entry_devminor(struct archive_entry *);
+__LA_DECL __LA_MODE_T	 archive_entry_filetype(struct archive_entry *);
+__LA_DECL int		 archive_entry_filetype_is_set(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 int		 archive_entry_gid_is_set(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 void		 archive_entry_set_link_to_hardlink(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 int		 archive_entry_hardlink_is_set(struct archive_entry *);
+__LA_DECL __LA_INO_T	 archive_entry_ino(struct archive_entry *);
+__LA_DECL __LA_INO_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 int		 archive_entry_perm_is_set(struct archive_entry *);
+__LA_DECL int		 archive_entry_rdev_is_set(struct archive_entry *);
+__LA_DECL __LA_DEV_T		 archive_entry_rdev(struct archive_entry *);
+__LA_DECL __LA_DEV_T		 archive_entry_rdevmajor(struct archive_entry *);
+__LA_DECL __LA_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 void		 archive_entry_set_link_to_symlink(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 int		 archive_entry_uid_is_set(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 *, __LA_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 *, __LA_TIME_T, long);
+__LA_DECL void  archive_entry_unset_birthtime(struct archive_entry *);
+__LA_DECL void	archive_entry_set_ctime(struct archive_entry *, __LA_TIME_T, long);
+__LA_DECL void  archive_entry_unset_ctime(struct archive_entry *);
+__LA_DECL void	archive_entry_set_dev(struct archive_entry *, __LA_DEV_T);
+__LA_DECL void	archive_entry_set_devmajor(struct archive_entry *, __LA_DEV_T);
+__LA_DECL void	archive_entry_set_devminor(struct archive_entry *, __LA_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 char *archive_entry_copy_fflags_text_len(struct archive_entry *,
+	    const char *, size_t);
+__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_INO_T);
+__LA_DECL void	archive_entry_set_ino64(struct archive_entry *, __LA_INO_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 *, __LA_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 *, __LA_DEV_T);
+__LA_DECL void	archive_entry_set_rdevmajor(struct archive_entry *, __LA_DEV_T);
+__LA_DECL void	archive_entry_set_rdevminor(struct archive_entry *, __LA_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);
+
+/*
+ * Digest routine. This is used to query the raw hex digest for the
+ * given entry. The type of digest is provided as an argument.
+ */
+#define ARCHIVE_ENTRY_DIGEST_MD5              0x00000001
+#define ARCHIVE_ENTRY_DIGEST_RMD160           0x00000002
+#define ARCHIVE_ENTRY_DIGEST_SHA1             0x00000003
+#define ARCHIVE_ENTRY_DIGEST_SHA256           0x00000004
+#define ARCHIVE_ENTRY_DIGEST_SHA384           0x00000005
+#define ARCHIVE_ENTRY_DIGEST_SHA512           0x00000006
+
+__LA_DECL const unsigned char * archive_entry_digest(struct archive_entry *, int /* type */);
+__LA_DECL int archive_entry_set_digest(struct archive_entry *, int, const unsigned char *);
+
+/*
+ * 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,55 @@
+/*-
+ * 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"
+#include "archive_time_private.h"
+#include "archive_private.h"
+#include "archive_entry.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+void
+archive_entry_copy_bhfi(struct archive_entry *entry,
+			BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+	int64_t secs;
+	uint32_t nsecs;
+
+	ntfs_to_unix(FILETIME_to_ntfs(&bhfi->ftLastAccessTime), &secs, &nsecs);
+	archive_entry_set_atime(entry, secs, nsecs);
+	ntfs_to_unix(FILETIME_to_ntfs(&bhfi->ftLastWriteTime), &secs, &nsecs);
+	archive_entry_set_mtime(entry, secs, nsecs);
+	ntfs_to_unix(FILETIME_to_ntfs(&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,82 @@
+/*-
+ * 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"
+
+#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,469 @@
+/*-
+ * 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"
+
+#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);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+			archive_entry_copy_hardlink_w(*e,
+			    archive_entry_pathname_w(le->canonical));
+#else
+			archive_entry_copy_hardlink(*e,
+			    archive_entry_pathname(le->canonical));
+#endif
+		} else
+			insert_entry(res, *e);
+		return;
+	case ARCHIVE_ENTRY_LINKIFY_LIKE_MTREE:
+		le = find_entry(res, *e);
+		if (le != NULL) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+			archive_entry_copy_hardlink_w(*e,
+			    archive_entry_pathname_w(le->canonical));
+#else
+			archive_entry_copy_hardlink(*e,
+			    archive_entry_pathname(le->canonical));
+#endif
+		} 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);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+			archive_entry_copy_hardlink_w(*e,
+			    archive_entry_pathname_w(le->canonical));
+#else
+			archive_entry_copy_hardlink(*e,
+			    archive_entry_pathname(le->canonical));
+#endif
+			/* 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;
+
+	if (!archive_entry_ino_is_set(entry) || !archive_entry_dev_is_set(entry)) {
+		return (NULL);
+	}
+
+	/* 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;
+
+	if (!archive_entry_ino_is_set(entry) || !archive_entry_dev_is_set(entry)) {
+		return (NULL);
+	}
+
+	/* 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,90 @@
+/*-
+ * 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.
+ */
+
+#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,205 @@
+/*-
+ * 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_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;
+};
+
+struct ae_digest {
+	unsigned char md5[16];
+	unsigned char rmd160[20];
+	unsigned char sha1[20];
+	unsigned char sha256[32];
+	unsigned char sha384[48];
+	unsigned char sha512[64];
+};
+
+/*
+ * 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
+#define	AE_SET_PERM	512
+#define	AE_SET_FILETYPE	1024
+#define	AE_SET_UID	2048
+#define	AE_SET_GID	4096
+#define	AE_SET_RDEV	8192
+
+	/*
+	 * 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_linkname;	/* Name of target for hardlink or symlink */
+	struct archive_mstring ae_pathname;	/* Name of entry */
+	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;
+
+	/* Digest support. */
+#define AE_MSET_DIGEST_MD5	 1
+#define AE_MSET_DIGEST_RMD160	 2
+#define AE_MSET_DIGEST_SHA1	 4
+#define AE_MSET_DIGEST_SHA256	 8
+#define AE_MSET_DIGEST_SHA384	16
+#define AE_MSET_DIGEST_SHA512	32
+	uint_least32_t mset_digest;
+	struct ae_digest digest;
+
+	/* 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,155 @@
+/*-
+ * 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"
+
+#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 = 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,121 @@
+/*-
+ * 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"
+
+#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)
+{
+	int64_t size;
+	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);
+	size = archive_entry_size(entry);
+	st->st_size = (off_t)size;
+	if (st->st_size < 0 || (int64_t)st->st_size != size)
+		st->st_size = 0;
+	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,86 @@
+/*-
+ * 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"
+
+#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,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"
+
+#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 = 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_hmac.c b/c/archive_hmac.c
new file mode 100644
--- /dev/null
+++ b/c/archive_hmac.c
@@ -0,0 +1,339 @@
+/*-
+* 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) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+
+#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)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC *mac;
+
+	char sha1[] = "SHA1";
+	OSSL_PARAM params[] = {
+		OSSL_PARAM_utf8_string("digest", sha1, sizeof(sha1) - 1),
+		OSSL_PARAM_END
+	};
+
+	mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+	*ctx = EVP_MAC_CTX_new(mac);
+	EVP_MAC_free(mac);
+	if (*ctx == NULL)
+		return -1;
+
+	EVP_MAC_init(*ctx, key, key_len, params);
+#else
+	*ctx = HMAC_CTX_new();
+	if (*ctx == NULL)
+		return -1;
+	HMAC_Init_ex(*ctx, key, key_len, EVP_sha1(), NULL);
+#endif
+	return 0;
+}
+
+static void
+__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data,
+    size_t data_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC_update(*ctx, data, data_len);
+#else
+	HMAC_Update(*ctx, data, data_len);
+#endif
+}
+
+static void
+__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	size_t len = *out_len;
+#else
+	unsigned int len = (unsigned int)*out_len;
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC_final(*ctx, out, &len, *out_len);
+#else
+	HMAC_Final(*ctx, out, &len);
+#endif
+	*out_len = len;
+}
+
+static void
+__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+	EVP_MAC_CTX_free(*ctx);
+#else
+	HMAC_CTX_free(*ctx);
+#endif
+	*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,119 @@
+/*-
+* 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) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+#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 <openssl/opensslv.h>
+#include <openssl/hmac.h>
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+#include <openssl/params.h>
+
+typedef EVP_MAC_CTX *archive_hmac_sha1_ctx;
+
+#else
+#include "archive_openssl_hmac_private.h"
+
+typedef	HMAC_CTX* archive_hmac_sha1_ctx;
+#endif
+
+#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,1846 @@
+/*-
+ * 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"
+
+#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_private.h"
+#include "archive_entry.h"
+#include "archive_pathmatch.h"
+#include "archive_rb.h"
+#include "archive_string.h"
+#include "archive_time_private.h"
+
+struct match {
+	struct match		*next;
+	int			 matched;
+	struct archive_mstring	 pattern;
+};
+
+struct match_list {
+	struct match		*first;
+	struct match		**last;
+	size_t			 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;
+};
+
+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 *);
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static int	cmp_key_mbs(const struct archive_rb_node *, const void *);
+static int	cmp_node_mbs(const struct archive_rb_node *,
+		    const struct archive_rb_node *);
+#else
+static int	cmp_key_wcs(const struct archive_rb_node *, const void *);
+static int	cmp_node_wcs(const struct archive_rb_node *,
+		    const struct archive_rb_node *);
+#endif
+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_parse_date
+
+static const struct archive_rb_tree_ops rb_ops = {
+#if !defined(_WIN32) || defined(__CYGWIN__)
+	cmp_node_mbs, cmp_key_mbs
+#else
+	cmp_node_wcs, cmp_key_wcs
+#endif
+};
+
+/*
+ * 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 = 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);
+	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;
+
+	if (a->inclusions.unmatched_count > (size_t)INT_MAX)
+		return INT_MAX;
+	return (int)(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);
+	if (r == ARCHIVE_OK)
+		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->matched &&
+		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
+			if (r < 0)
+				return (r);
+			a->inclusions.unmatched_count--;
+			match->matched = 1;
+			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->matched &&
+		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
+			if (r < 0)
+				return (r);
+			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);
+}
+
+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->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->matched)
+			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__)
+static int
+set_timefilter_find_data(struct archive_match *a, int timetype,
+    const FILETIME* ftLastWriteTime, const FILETIME* ftCreationTime)
+{
+	time_t ctime_sec, mtime_sec;
+	uint32_t ctime_ns, mtime_ns;
+
+	ntfs_to_unix(FILETIME_to_ntfs(ftLastWriteTime), &mtime_sec, &mtime_ns);
+	ntfs_to_unix(FILETIME_to_ntfs(ftCreationTime), &ctime_sec, &ctime_ns);
+	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, &d.ftCreationTime);
+}
+
+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, &d.ftCreationTime);
+}
+
+#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.
+ */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+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));
+}
+#else
+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));
+}
+#endif
+
+static void
+entry_list_init(struct entry_list *list)
+{
+	list->first = NULL;
+	list->last = &(list->first);
+}
+
+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);
+}
+
+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);
+#else
+	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);
+#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.first == NULL)
+		return (0);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	pathname = archive_entry_pathname_w(entry);
+#else
+	pathname = archive_entry_pathname(entry);
+#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)
+{
+	size_t 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)
+{
+	size_t b, m, t;
+
+	t = 0;
+	b = 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->matched = 1;
+			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->matched = 1;
+			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.first != NULL) {
+#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.first != NULL) {
+#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,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_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 || \
+    (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
+#include <stdlib.h> /* malloc, free */
+#include <string.h> /* memset */
+static inline EVP_MD_CTX *EVP_MD_CTX_new(void)
+{
+	EVP_MD_CTX *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 = 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,217 @@
+/*-
+ * 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"
+
+#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,
+    unsigned 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,
+    unsigned 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,49 @@
+/*-
+ * 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"
+#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,
+    unsigned int magic, const char *fn, option_handler use_option);
+
+int
+_archive_set_options(struct archive *a, const char *options,
+    unsigned 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,330 @@
+/*	$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"
+
+#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	__LA_LIBC_CC 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
+__LA_LIBC_CC
+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_parse_date.c b/c/archive_parse_date.c
new file mode 100644
--- /dev/null
+++ b/c/archive_parse_date.c
@@ -0,0 +1,1145 @@
+/*
+ * 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"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "archive.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_LOCALTIME_S)
+	struct tm	tmbuf;
+#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;
+	if (Year < EPOCH || (sizeof(time_t) <= 4 && 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_S)
+	ltime = localtime_s(&tmbuf, &Julian) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+	ltime = localtime_r(&Julian, &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_LOCALTIME_S)
+	struct tm	tmbuf;
+#endif
+#if defined(HAVE_LOCALTIME_S)
+	ltime = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+	ltime = localtime_r(&Start, &tmbuf);
+#else
+	ltime = localtime(&Start);
+#endif
+	StartDay = (ltime->tm_hour + 1) % 24;
+#if defined(HAVE_LOCALTIME_S)
+	ltime = localtime_s(&tmbuf, &Future) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+	ltime = localtime_r(&Future, &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_GMTIME_S)
+	struct tm	tmbuf;
+#endif
+
+	t = Start - zone;
+#if defined(HAVE_GMTIME_S)
+	tm = gmtime_s(&tmbuf, &t) ? NULL : &tmbuf;
+#elif defined(HAVE_GMTIME_R)
+	tm = gmtime_r(&t, &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_LOCALTIME_S)
+	struct tm	tmbuf;
+#endif
+
+	if (RelMonth == 0)
+		return 0;
+#if defined(HAVE_LOCALTIME_S)
+	tm = localtime_s(&tmbuf, &Start) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+	tm = localtime_r(&Start, &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));
+}
+
+/*
+ * Parses and consumes an unsigned number.
+ * Returns 1 if any number is parsed. Otherwise, *value is unchanged.
+ */
+static char
+consume_unsigned_number(const char **in, time_t *value)
+{
+	char c;
+	if (isdigit((unsigned char)(c = **in))) {
+		for (*value = 0; isdigit((unsigned char)(c = *(*in)++)); )
+			*value = 10 * *value + c - '0';
+		(*in)--;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * 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++] = (char)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 (consume_unsigned_number(in, value)) {
+			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);
+	long 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));
+}
+
+/*
+ * Parses a Unix epoch timestamp (seconds).
+ * This supports a subset of what GNU tar accepts from black box testing,
+ * but covers common use cases.
+ */
+static time_t
+parse_unix_epoch(const char *p)
+{
+	time_t epoch;
+
+	/* may begin with + */
+	if (*p == '+') {
+		p++;
+	}
+
+	/* followed by some number */
+	if (!consume_unsigned_number(&p, &epoch))
+		return (time_t)-1;
+
+	/* ...and nothing else */
+	if (*p != '\0')
+		return (time_t)-1;
+
+	return epoch;
+}
+
+/*
+ *
+ * The public function.
+ *
+ * TODO: tokens[] array should be dynamically sized.
+ */
+time_t
+archive_parse_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;
+
+	/*
+	 * @-prefixed Unix epoch timestamps (seconds)
+	 * Skip the complex tokenizer - We do not want to accept strings like "@tenth"
+	 */
+	if (*p == '@')
+		return parse_unix_epoch(p + 1);
+
+	/* 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_S)
+	tm = localtime_s(&local, &now) ? NULL : &local;
+#elif defined(HAVE_LOCALTIME_R)
+	tm = localtime_r(&now, &local);
+#else
+	memset(&local, 0, sizeof(local));
+	tm = localtime(&now);
+#endif
+	if (tm == NULL)
+		return -1;
+#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S)
+	local = *tm;
+#endif
+
+	/* Look up UTC if we can and use that to determine the current
+	 * timezone offset. */
+#if defined(HAVE_GMTIME_S)
+	gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
+#elif defined(HAVE_GMTIME_R)
+	gmt_ptr = gmtime_r(&now, &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_S)
+		gmt_ptr = gmtime_s(&gmt, &now) ? NULL : &gmt;
+#elif defined(HAVE_GMTIME_R)
+		gmt_ptr = gmtime_r(&now, &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_pathmatch.c b/c/archive_pathmatch.c
new file mode 100644
--- /dev/null
+++ b/c/archive_pathmatch.c
@@ -0,0 +1,462 @@
+/*-
+ * 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"
+
+#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');
+	else if (s == NULL)
+		return (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');
+	else if (s == NULL)
+		return (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,50 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    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.
+ */
+
+#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,213 @@
+/*-
+ * 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.
+ */
+
+/* !!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
+
+/* For cygwin, to avoid missing LONG, ULONG, PUCHAR, ... definitions */
+#ifdef __CYGWIN__
+#include <windef.h>
+#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"
+/* The C library on Windows specifies a calling convention for callback
+ * functions and exports; when we interact with them (capture pointers,
+ * call and pass function pointers) we need to match their calling
+ * convention.
+ * This only matters when libarchive is built with /Gr, /Gz or /Gv
+ * (which change the default calling convention.) */
+#define __LA_LIBC_CC __cdecl
+#else
+#define la_stat(path,stref)		stat(path,stref)
+#define __LA_LIBC_CC
+#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.
+ */
+
+/* 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
+
+/* Some platforms lack the standard PRIxN/PRIdN definitions. */
+#if !HAVE_INTTYPES_H || !defined(PRIx32) || !defined(PRId32)
+#ifndef PRIx32
+#if SIZEOF_INT == 4
+#define PRIx32 "x"
+#elif SIZEOF_LONG == 4
+#define PRIx32 "lx"
+#else
+#error No suitable 32-bit unsigned integer type found for this platform
+#endif
+#endif // PRIx32
+#ifndef PRId32
+#if SIZEOF_INT == 4
+#define PRId32 "d"
+#elif SIZEOF_LONG == 4
+#define PRId32 "ld"
+#else
+#error No suitable 32-bit signed integer type found for this platform
+#endif
+#endif // PRId32
+#endif // !HAVE_INTTYPES_H || !defined(PRIx32) || !defined(PRId32)
+
+/*
+ * If we can't restore metadata using a file descriptor, then
+ * for compatibility's sake, close files before trying to restore metadata.
+ */
+#if defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(HAVE_ACL_SET_FD) || defined(HAVE_ACL_SET_FD_NP) || defined(HAVE_FCHOWN)
+#define	CAN_RESTORE_METADATA_FD
+#endif
+
+/* Set up defaults for internal error codes. */
+#ifndef ARCHIVE_ERRNO_FILE_FORMAT
+#if HAVE_EFTYPE
+#define	ARCHIVE_ERRNO_FILE_FORMAT EFTYPE
+#else
+#if HAVE_EILSEQ
+#define	ARCHIVE_ERRNO_FILE_FORMAT EILSEQ
+#else
+#define	ARCHIVE_ERRNO_FILE_FORMAT EINVAL
+#endif
+#endif
+#endif
+
+#ifndef ARCHIVE_ERRNO_PROGRAMMER
+#define	ARCHIVE_ERRNO_PROGRAMMER EINVAL
+#endif
+
+#ifndef ARCHIVE_ERRNO_MISC
+#define	ARCHIVE_ERRNO_MISC (-1)
+#endif
+
+#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,53 @@
+/*-
+ * 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.
+ */
+
+/* !!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_stat.h b/c/archive_platform_stat.h
new file mode 100644
--- /dev/null
+++ b/c/archive_platform_stat.h
@@ -0,0 +1,45 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Tobias Stoeckmann
+ * All rights reserved.
+ */
+
+/* !!ONLY FOR USE INTERNALLY TO LIBARCHIVE!! */
+
+#ifndef ARCHIVE_PLATFORM_STAT_H_INCLUDED
+#define ARCHIVE_PLATFORM_STAT_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+/* We use _lseeki64() on Windows. */
+typedef int64_t la_seek_t;
+
+struct la_seek_stat {
+	int64_t		st_mtime;
+	ino_t		st_ino;
+	unsigned short	st_mode;
+	uint32_t	st_nlink;
+	gid_t		st_gid;
+	la_seek_t	st_size;
+	uid_t		st_uid;
+	dev_t		st_dev;
+	dev_t		st_rdev;
+};
+typedef struct la_seek_stat la_seek_stat_t;
+
+#define la_seek_fstat(fd, st)	__la_seek_fstat((fd), (st))
+#define la_seek_stat(fd, st)	__la_seek_stat((fd), (st))
+
+#else
+typedef off_t la_seek_t;
+typedef struct stat la_seek_stat_t;
+
+#define la_seek_fstat(fd, st)	fstat((fd), (st))
+#define la_seek_stat(fd, st)	stat((fd), (st))
+#endif
+
+#endif	/* !ARCHIVE_PLATFORM_STAT_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,45 @@
+/*-
+ * 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.
+ */
+
+/* !!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,1173 @@
+/* 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 <stdlib.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 = 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) do {					\
+	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);							\
+} while (0)
+
+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,1293 @@
+/* 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 = 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) do {					\
+	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);							\
+} while (0)
+
+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) do {						\
+	p->Text = p->Base + p->AlignOffset + (offs);			\
+} while (0)
+
+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)));
+  }
+
+  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 = 0;
+    c1->Flags = flags;
+    *ONE_STATE(c1) = upState;
+    c1->Suffix = REF(c);
+    SetSuccessor(ps[--numPs], REF(c1));
+    c = c1;
+  }
+
+  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,159 @@
+/* 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) do {						\
+	if ((p)->Shift < PPMD_PERIOD_BITS && --(p)->Count == 0) {	\
+		(p)->Summ <<= 1;					\
+		(p)->Count = (Byte)(3 << (p)->Shift++);			\
+    	}								\
+} while (0)
+
+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) do {				\
+	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;	\
+	}								\
+} while (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,183 @@
+/*-
+ * 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_PRIVATE_H_INCLUDED
+#define ARCHIVE_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#ifndef __LIBARCHIVE_TEST
+#error This header is only to be used internally to libarchive.
+#endif
+#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_NORETURN __attribute__((__noreturn__))
+#elif defined(_MSC_VER)
+#define __LA_NORETURN __declspec(noreturn)
+#else
+#define __LA_NORETURN
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ > 2 || \
+			  (__GNUC__ == 2 && __GNUC_MINOR__ >= 7))
+#define	__LA_UNUSED	__attribute__((__unused__))
+#else
+#define	__LA_UNUSED
+#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.
+	 */
+	const struct archive_vtable *vtable;
+
+	int		  archive_format;
+	const char	 *archive_format_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)
+
+__LA_NORETURN void	__archive_errx(int retvalue, const char *msg);
+
+void	__archive_ensure_cloexec_flag(int fd);
+int	__archive_get_tempdir(struct archive_string *);
+int	__archive_mktemp(const char *tmpdir);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+int	__archive_mkstemp(wchar_t *templates);
+#else
+int	__archive_mkstemp(char *templates);
+#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,300 @@
+/*-
+ * 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_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 la_arc4random_buf(void *, size_t);
+
+#endif /* HAVE_ARC4RANDOM_BUF */
+
+#include "archive.h"
+#include "archive_random_private.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#elif defined(HAVE_WINCRYPT_H)
+#include <wincrypt.h>
+#endif
+#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__)
+# if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	NTSTATUS status;
+	BCRYPT_ALG_HANDLE hAlg;
+
+	status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM, NULL, 0);
+	if (!BCRYPT_SUCCESS(status))
+		return ARCHIVE_FAILED;
+	status = BCryptGenRandom(hAlg, buf, (ULONG)nbytes, 0);
+	BCryptCloseAlgorithmProvider(hAlg, 0);
+	if (!BCRYPT_SUCCESS(status))
+		return ARCHIVE_FAILED;
+
+	return ARCHIVE_OK;
+# else
+	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;
+# endif
+#elif !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
+	la_arc4random_buf(buf, nbytes);
+	return ARCHIVE_OK;
+#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(uint8_t *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;
+		uint8_t		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((uint8_t *)&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
+la_arc4random_buf(void *_buf, size_t n)
+{
+	uint8_t *buf = (uint8_t *)_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_INCLUDED */
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,1750 @@
+/*-
+ * 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"
+
+#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 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 const struct archive_vtable
+archive_read_vtable = {
+	.archive_filter_bytes = _archive_filter_bytes,
+	.archive_filter_code = _archive_filter_code,
+	.archive_filter_name = _archive_filter_name,
+	.archive_filter_count = _archive_filter_count,
+	.archive_read_data_block = _archive_read_data_block,
+	.archive_read_next_header = _archive_read_next_header,
+	.archive_read_next_header2 = _archive_read_next_header2,
+	.archive_free = _archive_read_free,
+	.archive_close = _archive_read_close,
+};
+
+/*
+ * Allocate, initialize and return a struct archive object.
+ */
+struct archive *
+archive_read_new(void)
+{
+	struct archive_read *a;
+
+	a = 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) {
+		int64_t total = 0;
+		for (;;) {
+			int64_t get, ask = request;
+			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
+read_client_close_proxy(struct archive_read *a)
+{
+	int r = ARCHIVE_OK, r2;
+	unsigned int i;
+
+	if (a->client.closer == NULL)
+		return (r);
+	for (i = 0; i < a->client.nodes; i++)
+	{
+		r2 = (a->client.closer)
+			((struct archive *)a, a->client.dataset[i].data);
+		if (r > r2)
+			r = r2;
+	}
+	return (r);
+}
+
+static int
+client_close_proxy(struct archive_read_filter *self)
+{
+	return read_client_close_proxy(self->archive);
+}
+
+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;
+		r2 = client_open_proxy(self);
+	}
+	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);
+}
+
+static const struct archive_read_filter_vtable
+none_reader_vtable = {
+	.read = client_read_proxy,
+	.close = client_close_proxy,
+};
+
+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;
+
+	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. */
+			read_client_close_proxy(a);
+			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->vtable = &none_reader_vtable;
+	filter->name = "none";
+	filter->code = ARCHIVE_FILTER_NONE;
+	filter->can_skip = 1;
+	filter->can_seek = 1;
+
+	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->vtable == NULL)
+				continue;
+			bid = (bidder->vtable->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);
+			}
+			return (ARCHIVE_OK);
+		}
+
+		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->vtable->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->vtable->read_header)
+		return (ARCHIVE_OK);
+	return a->filter->vtable->read_header(a->filter, entry);
+}
+
+/*
+ * 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 &&
+			    a->read_data_offset == a->read_data_output_offset &&
+			    a->read_data_remaining == 0)
+				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->vtable != NULL) {
+			int r1 = (f->vtable->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].vtable == NULL ||
+		    a->bidders[i].vtable->free == NULL)
+			continue;
+		(a->bidders[i].vtable->free)(&a->bidders[i]);
+	}
+
+	/* 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_register_bidder(struct archive_read *a,
+	void *bidder_data,
+	const char *name,
+	const struct archive_read_filter_bidder_vtable *vtable)
+{
+	struct archive_read_filter_bidder *bidder;
+	int i, number_slots;
+
+	archive_check_magic(&a->archive, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "__archive_read_register_bidder");
+
+	number_slots = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+	for (i = 0; i < number_slots; i++) {
+		if (a->bidders[i].vtable != NULL)
+			continue;
+		memset(a->bidders + i, 0, sizeof(a->bidders[0]));
+		bidder = (a->bidders + i);
+		bidder->data = bidder_data;
+		bidder->name = name;
+		bidder->vtable = vtable;
+		if (bidder->vtable->bid == NULL || bidder->vtable->init == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+					"Internal error: "
+					"no bid/init for filter bidder");
+			return (ARCHIVE_FATAL);
+		}
+
+		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 = filter->avail;
+				return (NULL);
+			}
+			bytes_read = (filter->vtable->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 = 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->can_skip != 0) {
+		bytes_skipped = client_skip_proxy(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->vtable->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->can_seek == 0)
+		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,189 @@
+/*-
+ * 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_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,205 @@
+/*-
+ * 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"
+
+#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_LZOP:
+      strcpy(str, "lzop");
+      r1 = archive_read_support_filter_lzop(_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 = 1; 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 = 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->vtable->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 = 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->vtable->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,143 @@
+/*-
+ * 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"
+
+#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);
+		if (!nulls) {
+			r = ARCHIVE_FATAL;
+			goto cleanup;
+		}
+	}
+
+	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,1084 @@
+/*-
+ * 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"
+
+/* 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);
+#ifdef ARCHIVE_XATTR_FREEBSD
+static int setup_xattrs_namespace(struct archive_read_disk *,
+    struct archive_entry *, int *, int);
+#endif
+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;
+	}
+	if ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) {
+		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;
+	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);
+
+	archive_string_init(&tempfile);
+	if (__archive_get_tempdir(&tempfile) != ARCHIVE_OK) {
+		ret = ARCHIVE_WARN;
+		goto cleanup;
+	}
+	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");
+		free(value);
+		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_namespace(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd, int namespace)
+{
+	char buff[512];
+	char *list, *p;
+	ssize_t list_size;
+	const char *path;
+
+	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 && errno == EPERM)
+		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;
+
+		if (namespace == EXTATTR_NAMESPACE_SYSTEM) {
+			if (!strcmp(p + 1, "nfs4.acl") ||
+			    !strcmp(p + 1, "posix1e.acl_access") ||
+			    !strcmp(p + 1, "posix1e.acl_default")) {
+				p += 1 + len;
+				continue;
+			}
+			strcpy(buff, "system.");
+		} else {
+			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);
+}
+
+static int
+setup_xattrs(struct archive_read_disk *a,
+    struct archive_entry *entry, int *fd)
+{
+	int namespaces[2];
+	int i, res;
+
+	namespaces[0] = EXTATTR_NAMESPACE_USER;
+	namespaces[1] = EXTATTR_NAMESPACE_SYSTEM;
+
+	for (i = 0; i < 2; i++) {
+		res = setup_xattrs_namespace(a, entry, fd,
+		    namespaces[i]);
+		switch (res) {
+			case (ARCHIVE_OK):
+			case (ARCHIVE_WARN):
+				break;
+			default:
+				return (res);
+		}
+	}
+
+	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,2633 @@
+/*-
+ * 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"
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.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>
+#elif HAVE_SYS_MOUNT_H
+#include <sys/mount.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
+
+#if defined(__hpux) && !defined(HAVE_DIRFD)
+#define dirfd(x) ((x)->__dd_fd)
+#define HAVE_DIRFD
+#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;
+	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;
+	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 *, char, 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 const struct archive_vtable
+archive_read_disk_vtable = {
+	.archive_free = _archive_read_free,
+	.archive_close = _archive_read_close,
+	.archive_read_data_block = _archive_read_data_block,
+	.archive_read_next_header = _archive_read_next_header,
+	.archive_read_next_header2 = _archive_read_next_header2,
+};
+
+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 = 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,
+    char 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) !=
+		    t->current_sparse->offset) {
+			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, t->tree_errno,
+			    "%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 (!archive_read_disk_can_descend(_a))
+		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_STATVFS)
+static inline __LA_UNUSED void
+set_statvfs_transfer_size(struct filesystem *fs, const struct statvfs *sfs)
+{
+	fs->xfer_align = sfs->f_frsize > 0 ? (long)sfs->f_frsize : -1;
+	fs->max_xfer_size = -1;
+#if defined(HAVE_STRUCT_STATVFS_F_IOSIZE)
+	fs->min_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+	fs->incr_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+#else
+	fs->min_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+	fs->incr_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+#endif
+}
+#endif
+
+#if defined(HAVE_STRUCT_STATFS)
+static inline __LA_UNUSED void
+set_statfs_transfer_size(struct filesystem *fs, const struct statfs *sfs)
+{
+	fs->xfer_align = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+	fs->max_xfer_size = -1;
+#if defined(HAVE_STRUCT_STATFS_F_IOSIZE)
+	fs->min_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+	fs->incr_xfer_size = sfs->f_iosize > 0 ? (long)sfs->f_iosize : -1;
+#else
+	fs->min_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+	fs->incr_xfer_size = sfs->f_bsize > 0 ? (long)sfs->f_bsize : -1;
+#endif
+}
+#endif
+
+#if defined(HAVE_STRUCT_STATFS) && 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;
+
+	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. */
+		set_statfs_transfer_size(t->current_filesystem, &sfs);
+	}
+	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;
+
+	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. */
+		set_statvfs_transfer_size(t->current_filesystem, &svfs);
+	}
+	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)
+		set_statvfs_transfer_size(t->current_filesystem, &svfs);
+#else
+		set_statfs_transfer_size(t->current_filesystem, &sfs);
+#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;
+
+	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. */
+		set_statvfs_transfer_size(t->current_filesystem, &svfs);
+	}
+
+#if defined(ST_NOATIME)
+	if (svfs.f_flag & ST_NOATIME)
+		t->current_filesystem->noatime = 1;
+	else
+#endif
+		t->current_filesystem->noatime = 0;
+
+	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;
+	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;
+
+	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);
+	if (new_fd != -1) {
+		__archive_ensure_cloexec_flag(new_fd);
+		return (new_fd);
+	}
+	return (-1);
+}
+
+/*
+ * 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));
+	if (te == NULL)
+		__archive_errx(1, "Out of memory");
+	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, char 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);
+		if (t->initial_dir_fd < 0)
+			return NULL;
+	}
+#endif
+	__archive_ensure_cloexec_flag(t->initial_dir_fd);
+	t->working_dir_fd = tree_dup(t->initial_dir_fd);
+	if (t->working_dir_fd < 0)
+		return NULL;
+	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(HAVE_FDOPENDIR)
+		int fd = tree_dup(t->working_dir_fd);
+		if (fd != -1)
+			t->d = fdopendir(fd);
+#else /* HAVE_FDOPENDIR */
+		if (tree_enter_working_dir(t) == 0) {
+			t->d = opendir(".");
+#ifdef HAVE_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);
+		}
+	}
+	for (;;) {
+		errno = 0;
+		t->de = readdir(t->d);
+		if (t->de == NULL) {
+			r = errno;
+			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;
+#ifdef HAVE_LSTAT
+		if (lstat(tree_current_access_path(t), &t->lst) != 0)
+#else
+		if (la_stat(tree_current_access_path(t), &t->lst) != 0)
+#endif
+#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);
+	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,97 @@
+/*-
+ * 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.
+ */
+
+#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, governed by the above
+	 * symlink_mode.  'L' sets this true, 'P' sets it false, 'H' changes it
+	 * as we traverse.
+	 */
+	char	follow_symlinks;  /* Either 0 or 1. */
+
+	/* 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,312 @@
+/*-
+ * 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"
+
+#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,2549 @@
+/*-
+ * 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"
+
+#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"
+#include "archive_time_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
+/* To deal with absolute symlink issues */
+#define START_ABSOLUTE_SYMLINK_REPARSE L"\\??\\"
+
+/*-
+ * 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 + 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';
+	if (wcsncmp(tbuf, START_ABSOLUTE_SYMLINK_REPARSE, 4) == 0) {
+		/* Absolute symlink, so we'll change the NT path into a verbatim one */
+		tbuf[1] = L'\\';
+	} else {
+		/* Relative symlink, so we can translate backslashes to slashes */
+		wchar_t *temp = tbuf;
+		do {
+			if (*temp == L'\\')
+				*temp = L'/';
+			temp++;
+		} while(*temp != L'\0');
+	}
+
+	*linkname = 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;
+
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+	ZeroMemory(&createExParams, sizeof(createExParams));
+	createExParams.dwSize = sizeof(createExParams);
+	createExParams.dwFileFlags = flag;
+	h = CreateFile2(path, 0,
+	    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+	    OPEN_EXISTING, &createExParams);
+#else
+	h = CreateFileW(path, 0,
+	    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+	    OPEN_EXISTING, flag, NULL);
+#endif
+	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 const struct archive_vtable
+archive_read_disk_vtable = {
+	.archive_free = _archive_read_free,
+	.archive_close = _archive_read_close,
+	.archive_read_data_block = _archive_read_data_block,
+	.archive_read_next_header = _archive_read_next_header,
+	.archive_read_next_header2 = _archive_read_next_header2,
+};
+
+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 = 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, 
+    char 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 _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+		if (t->async_io)
+			flags |= FILE_FLAG_OVERLAPPED;
+		if (t->direct_io)
+			flags |= FILE_FLAG_NO_BUFFERING;
+		else
+			flags |= FILE_FLAG_SEQUENTIAL_SCAN;
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		ZeroMemory(&createExParams, sizeof(createExParams));
+		createExParams.dwSize = sizeof(createExParams);
+		createExParams.dwFileFlags = flags;
+		t->entry_fh = CreateFile2(tree_current_access_path(t),
+		    GENERIC_READ,
+		    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+		    OPEN_EXISTING, &createExParams);
+#else
+		t->entry_fh = CreateFileW(tree_current_access_path(t),
+		    GENERIC_READ,
+		    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+		    NULL, OPEN_EXISTING, flags, NULL);
+#endif
+		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 ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) {
+			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 (!archive_read_disk_can_descend(_a))
+		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 _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+	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);
+
+#if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	ZeroMemory(&createExParams, sizeof(createExParams));
+	createExParams.dwSize = sizeof(createExParams);
+	createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+	handle = CreateFile2(rt->full_path, FILE_WRITE_ATTRIBUTES,
+		    0, OPEN_EXISTING, &createExParams);
+#else
+	handle = CreateFileW(rt->full_path, FILE_WRITE_ATTRIBUTES,
+		    0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
+	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 int
+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;
+	if (archive_wstring_ensure(&t->path, size_needed) == NULL)
+		return (TREE_ERROR_FATAL);
+	/* 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;
+		if (archive_wstring_ensure(&t->full_path, size_needed) == NULL)
+			return (TREE_ERROR_FATAL);
+		/* 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;
+	}
+	return (0);
+}
+
+/*
+ * 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);
+	if (archive_wstring_ensure(&t->path, 15) == NULL) {
+		free(t);
+		return (NULL);
+	}
+	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';
+			if (tree_append(t, base, p - base))
+				goto failed;
+			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(t->stack->full_path.s, &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;
+			r = tree_append(t, t->stack->name.s,
+			    archive_strlen(&(t->stack->name)));
+			if (r != 0)
+				return (r);
+			//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;
+			r = tree_append(t, t->stack->name.s,
+			    archive_strlen(&(t->stack->name)));
+			if (r != 0)
+				return (r);
+			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);
+			if (archive_wstring_ensure(&pt,
+			    archive_strlen(&(t->full_path))
+			      + 2 + wcslen(pattern)) == NULL)
+				return (TREE_ERROR_FATAL);
+			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);
+			}
+			/* Top stack item needs a regular visit. */
+			t->current = t->stack;
+			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;
+		r = tree_append(t, name, namelen);
+		if (r != 0)
+			return (r);
+		return (t->visit_type = TREE_REGULAR);
+	}
+}
+
+static void
+entry_copy_bhfi(struct archive_entry *entry, const wchar_t *path,
+	const WIN32_FIND_DATAW *findData,
+	const BY_HANDLE_FILE_INFORMATION *bhfi)
+{
+	int64_t secs;
+	uint32_t nsecs;
+	mode_t mode;
+
+	ntfs_to_unix(FILETIME_to_ntfs(&bhfi->ftLastAccessTime), &secs, &nsecs);
+	archive_entry_set_atime(entry, secs, nsecs);
+	ntfs_to_unix(FILETIME_to_ntfs(&bhfi->ftLastWriteTime), &secs, &nsecs);
+	archive_entry_set_mtime(entry, secs, nsecs);
+	ntfs_to_unix(FILETIME_to_ntfs(&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 _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+	if (sim_lstat && tree_current_is_physical_link(t))
+		flag |= FILE_FLAG_OPEN_REPARSE_POINT;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	ZeroMemory(&createExParams, sizeof(createExParams));
+	createExParams.dwSize = sizeof(createExParams);
+	createExParams.dwFileFlags = flag;
+	h = CreateFile2(tree_current_access_path(t), 0,
+	    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+	    OPEN_EXISTING, &createExParams);
+#else
+	h = CreateFileW(tree_current_access_path(t), 0,
+	    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+	    OPEN_EXISTING, flag, NULL);
+#endif
+	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;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+			CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+			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;
+
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+			ZeroMemory(&createExParams, sizeof(createExParams));
+			createExParams.dwSize = sizeof(createExParams);
+			createExParams.dwFileFlags = flag;
+			h = CreateFile2(path, desiredAccess,
+			    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+			    OPEN_EXISTING, &createExParams);
+#else
+			h = CreateFileW(path, desiredAccess,
+			    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+			    OPEN_EXISTING, flag, NULL);
+#endif
+			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 {
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+			CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+			ZeroMemory(&createExParams, sizeof(createExParams));
+			createExParams.dwSize = sizeof(createExParams);
+			createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+			h = CreateFile2(path, GENERIC_READ,
+			    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+			    OPEN_EXISTING, &createExParams);
+#else
+			h = CreateFileW(path, GENERIC_READ,
+			    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+			    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
+			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 = ARCHIVE_OK;
+	if ((a->flags & ARCHIVE_READDISK_NO_SPARSE) == 0) {
+		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 = 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,59 @@
+/*-
+ * 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"
+
+#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,154 @@
+/*-
+ * 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"
+
+#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 = 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,233 @@
+/*-
+ * 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"
+
+#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"
+#include "archive_platform_stat.h"
+
+struct read_fd_data {
+	int	 fd;
+	size_t	 block_size;
+	int64_t	 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)
+{
+	la_seek_stat_t st;
+	struct read_fd_data *mine;
+	void *b;
+
+	archive_clear_error(a);
+	if (la_seek_fstat(fd, &st) != 0) {
+		archive_set_error(a, errno, "Can't stat fd %d", fd);
+		return (ARCHIVE_FATAL);
+	}
+
+	mine = 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;
+		mine->size = st.st_size;
+	}
+#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;
+	la_seek_t skip = (la_seek_t)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)) {
+		const int64_t max_skip =
+		    (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+		if (request > max_skip)
+			skip = (off_t)max_skip;
+	}
+
+	/* Reduce 'skip' to the next smallest multiple of block_size */
+	skip = (la_seek_t)(((int64_t)skip / mine->block_size) * mine->block_size);
+
+	if (skip == 0)
+		return (0);
+
+	if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0) {
+		if (old_offset >= mine->size ||
+		    skip > mine->size - old_offset) {
+			/* Do not seek past end of file. */
+			errno = ESPIPE;
+		} else if ((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;
+	la_seek_t seek = (la_seek_t)request;
+	int64_t r;
+	int seek_bits = sizeof(seek) * 8 - 1;  /* off_t is a signed type. */
+
+	/* We use off_t here because lseek() is declared that way. */
+
+	/* Do not perform a seek which cannot be fulfilled. */
+	if (sizeof(request) > sizeof(seek)) {
+		const int64_t max_seek =
+		    (((int64_t)1 << (seek_bits - 1)) - 1) * 2 + 1;
+		const int64_t min_seek = ~max_seek;
+		if (request < min_seek || request > max_seek) {
+			errno = EOVERFLOW;
+			goto err;
+		}
+	}
+
+	r = lseek(mine->fd, seek, whence);
+	if (r >= 0)
+		return r;
+
+err:
+	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,254 @@
+/*-
+ * 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"
+
+#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"
+#include "archive_platform_stat.h"
+
+struct read_FILE_data {
+	FILE    *f;
+	size_t	 block_size;
+	int64_t	 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_seek(struct archive *, void *, int64_t, int);
+static int64_t	FILE_skip(struct archive *, void *, int64_t);
+
+int
+archive_read_open_FILE(struct archive *a, FILE *f)
+{
+	la_seek_stat_t st;
+	struct read_FILE_data *mine;
+	size_t block_size = 128 * 1024;
+	void *b;
+
+	archive_clear_error(a);
+	mine = 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->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 (la_seek_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;
+		mine->size = st.st_size;
+	}
+
+#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_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_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__FSEEKI64
+	int64_t skip = request;
+#elif HAVE_FSEEKO
+	off_t skip = (off_t)request;
+#else
+	long skip = (long)request;
+#endif
+	int64_t old_offset, new_offset = -1;
+	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)) {
+		const 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. */
+	old_offset = lseek(fileno(mine->f), 0, SEEK_CUR);
+#elif HAVE__FSEEKI64
+	old_offset = _ftelli64(mine->f);
+#elif HAVE_FSEEKO
+	old_offset = ftello(mine->f);
+#else
+	old_offset = ftell(mine->f);
+#endif
+	if (old_offset >= 0) {
+		if (old_offset < mine->size &&
+		    skip <= mine->size - old_offset) {
+#ifdef __ANDROID__
+			new_offset = lseek(fileno(mine->f), skip, SEEK_CUR);
+#elif HAVE__FSEEKI64
+			if (_fseeki64(mine->f, skip, SEEK_CUR) == 0)
+				new_offset = _ftelli64(mine->f);
+#elif HAVE_FSEEKO
+			if (fseeko(mine->f, skip, SEEK_CUR) == 0)
+				new_offset = ftello(mine->f);
+#else
+			if (fseek(mine->f, skip, SEEK_CUR) == 0)
+				new_offset = ftell(mine->f);
+#endif
+			if (new_offset >= 0)
+				return (new_offset - old_offset);
+		}
+	}
+
+	mine->can_skip = 0;
+	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;
+#if HAVE__FSEEKI64
+	int64_t seek = request;
+#elif HAVE_FSEEKO
+	off_t seek = (off_t)request;
+#else
+	long seek = (long)request;
+#endif
+	int seek_bits = sizeof(seek) * 8 - 1;
+	(void)a; /* UNUSED */
+
+	/* Do not perform a seek which cannot be fulfilled. */
+	if (sizeof(request) > sizeof(seek)) {
+		const int64_t max_seek =
+		    (((int64_t)1 << (seek_bits - 1)) - 1) * 2 + 1;
+		const int64_t min_seek = ~max_seek;
+		if (request < min_seek || request > max_seek) {
+			errno = EOVERFLOW;
+			goto err;
+		}
+	}
+
+#ifdef __ANDROID__
+	/* Newer Android versions have fseeko...to meditate. */
+	int64_t ret = lseek(fileno(mine->f), seek, whence);
+	if (ret >= 0) {
+		return ret;
+	}
+#elif HAVE__FSEEKI64
+	if (_fseeki64(mine->f, seek, whence) == 0) {
+		return _ftelli64(mine->f);
+	}
+#elif HAVE_FSEEKO
+	if (fseeko(mine->f, seek, whence) == 0) {
+		return ftello(mine->f);
+	}
+#else
+	if (fseek(mine->f, seek, whence) == 0) {
+		return ftell(mine->f);
+	}
+#endif
+	/* If we arrive here, the input is corrupted or truncated so fail. */
+err:
+	archive_set_error(a, errno, "Error seeking in FILE* pointer");
+	return (ARCHIVE_FATAL);
+}
+
+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,648 @@
+/*-
+ * 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"
+
+#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_platform_stat.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. */
+	int64_t	 size;
+	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 = 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)) {
+			free(mine);
+			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);
+}
+
+/*
+ * This function is an implementation detail of archive_read_open_filename_w,
+ * which is exposed as a separate API on Windows.
+ */
+#if !defined(_WIN32) || defined(__CYGWIN__)
+static
+#endif
+int
+archive_read_open_filenames_w(struct archive *a, const wchar_t **wfilenames,
+    size_t block_size)
+{
+	struct read_file_data *mine;
+	const wchar_t *wfilename = NULL;
+	if (wfilenames)
+		wfilename = *(wfilenames++);
+
+	archive_clear_error(a);
+	do
+	{
+		if (wfilename == NULL)
+			wfilename = L"";
+		mine = calloc(1,
+			sizeof(*mine) + wcslen(wfilename) * sizeof(wchar_t));
+		if (mine == NULL)
+			goto no_memory;
+		mine->block_size = block_size;
+		mine->fd = -1;
+
+		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)) {
+			free(mine);
+			return (ARCHIVE_FATAL);
+		}
+		if (wfilenames == NULL)
+			break;
+		wfilename = *(wfilenames++);
+	} while (wfilename != NULL && wfilename[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)
+{
+	const wchar_t *wfilenames[2];
+	wfilenames[0] = wfilename;
+	wfilenames[1] = NULL;
+	return archive_read_open_filenames_w(a, wfilenames, block_size);
+}
+
+static int
+file_open(struct archive *a, void *client_data)
+{
+	la_seek_stat_t 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 '%ls'", wfilename);
+			return (ARCHIVE_FATAL);
+		}
+#else
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+		    "Unexpedted operation in archive_read_open_filename");
+		goto fail;
+#endif
+	}
+	if (la_seek_fstat(fd, &st) != 0) {
+#if defined(_WIN32) && !defined(__CYGWIN__)
+		if (mine->filename_type == FNT_WCS)
+			archive_set_error(a, errno, "Can't stat '%ls'",
+			    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;
+		mine->size = st.st_size;
+	}
+
+	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 '%ls'", 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
+	la_seek_t skip = (la_seek_t)request;
+	int skip_bits = sizeof(skip) * 8 - 1;
+
+	/* We use off_t here because lseek() is declared that way. */
+
+	/* Reduce a request that would overflow the 'skip' variable. */
+	if (sizeof(request) > sizeof(skip)) {
+		const int64_t max_skip =
+		    (((int64_t)1 << (skip_bits - 1)) - 1) * 2 + 1;
+		if (request > max_skip)
+			skip = max_skip;
+	}
+
+	if ((old_offset = lseek(mine->fd, 0, SEEK_CUR)) >= 0) {
+		if (old_offset >= mine->size ||
+		    skip > mine->size - old_offset) {
+			/* Do not seek past end of file. */
+			errno = ESPIPE;
+		} else if ((new_offset = lseek(mine->fd, skip, 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 '%ls'",
+		    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;
+	la_seek_t seek = (la_seek_t)request;
+	int64_t r;
+	int seek_bits = sizeof(seek) * 8 - 1;
+
+	/* We use off_t here because lseek() is declared that way. */
+
+	/* Do not perform a seek which cannot be fulfilled. */
+	if (sizeof(request) > sizeof(seek)) {
+		const int64_t max_seek =
+		    (((int64_t)1 << (seek_bits - 1)) - 1) * 2 + 1;
+		const int64_t min_seek = ~max_seek;
+		if (request < min_seek || request > max_seek) {
+			errno = EOVERFLOW;
+			goto err;
+		}
+	}
+
+	r = lseek(mine->fd, seek, whence);
+	if (r >= 0)
+		return r;
+
+	/* If the input is corrupted or truncated, fail. */
+err:
+	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 '%ls'",
+		    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,185 @@
+/*-
+ * 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"
+
+#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 = 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,265 @@
+/*-
+ * 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_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;
+
+struct archive_read_filter_bidder_vtable {
+	/* 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 *);
+	/* Release the bidder's configuration data. */
+	void (*free)(struct archive_read_filter_bidder *);
+};
+
+/*
+ * 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;
+	const struct archive_read_filter_bidder_vtable *vtable;
+};
+
+struct archive_read_filter_vtable {
+	/* Return next block. */
+	ssize_t (*read)(struct archive_read_filter *, const void **);
+	/* Close (just this filter) and free(self). */
+	int (*close)(struct archive_read_filter *self);
+	/* Read any header metadata if available. */
+	int (*read_header)(struct archive_read_filter *self, struct archive_entry *entry);
+};
+
+/*
+ * 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. */
+	const struct archive_read_filter_vtable *vtable;
+	/* My private data. */
+	void *data;
+
+	const char	*name;
+	int		 code;
+	int		 can_skip;
+	int		 can_seek;
+
+	/* 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_register_bidder(struct archive_read *a,
+		void *bidder_data,
+		const char *name,
+		const struct archive_read_filter_bidder_vtable *vtable);
+
+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,116 @@
+/*-
+ * 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"
+
+#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;
+  const char *str;
+  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:
+      str = "7zip";
+      break;
+    case ARCHIVE_FORMAT_AR:
+      str = "ar";
+      break;
+    case ARCHIVE_FORMAT_CAB:
+      str = "cab";
+      break;
+    case ARCHIVE_FORMAT_CPIO:
+      str = "cpio";
+      break;
+    case ARCHIVE_FORMAT_EMPTY:
+      str = "empty";
+      break;
+    case ARCHIVE_FORMAT_ISO9660:
+      str = "iso9660";
+      break;
+    case ARCHIVE_FORMAT_LHA:
+      str = "lha";
+      break;
+    case ARCHIVE_FORMAT_MTREE:
+      str = "mtree";
+      break;
+    case ARCHIVE_FORMAT_RAR:
+      str = "rar";
+      break;
+    case ARCHIVE_FORMAT_RAR_V5:
+      str = "rar5";
+      break;
+    case ARCHIVE_FORMAT_RAW:
+      str = "raw";
+      break;
+    case ARCHIVE_FORMAT_TAR:
+      str = "tar";
+      break;
+    case ARCHIVE_FORMAT_WARC:
+      str = "warc";
+      break;
+    case ARCHIVE_FORMAT_XAR:
+      str = "xar";
+      break;
+    case ARCHIVE_FORMAT_ZIP:
+      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,132 @@
+/*-
+ * 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"
+
+#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)
+{
+	(void)_a; /* UNUSED */
+	(void)o; /* UNUSED */
+	(void)v; /* UNUSED */
+
+	/* If the filter name didn't match, return a special code for
+	 * _archive_set_option[s]. */
+	if (m != NULL)
+		return ARCHIVE_WARN - 1;
+	return ARCHIVE_WARN;
+}
+
+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,84 @@
+/*-
+ * 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"
+
+#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_by_code.c b/c/archive_read_support_filter_by_code.c
new file mode 100644
--- /dev/null
+++ b/c/archive_read_support_filter_by_code.c
@@ -0,0 +1,68 @@
+/*-
+ * 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.
+ */
+
+#include "archive_platform.h"
+
+#include "archive.h"
+#include "archive_private.h"
+
+int
+archive_read_support_filter_by_code(struct archive *a, int filter_code)
+{
+	archive_check_magic(a, ARCHIVE_READ_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_read_support_filter_by_code");
+
+	switch (filter_code) {
+	case ARCHIVE_FILTER_NONE:
+		return archive_read_support_filter_none(a);
+	case ARCHIVE_FILTER_GZIP:
+		return archive_read_support_filter_gzip(a);
+	case ARCHIVE_FILTER_BZIP2:
+		return archive_read_support_filter_bzip2(a);
+	case ARCHIVE_FILTER_COMPRESS:
+		return archive_read_support_filter_compress(a);
+	case ARCHIVE_FILTER_LZMA:
+		return archive_read_support_filter_lzma(a);
+	case ARCHIVE_FILTER_XZ:
+		return archive_read_support_filter_xz(a);
+	case ARCHIVE_FILTER_UU:
+		return archive_read_support_filter_uu(a);
+	case ARCHIVE_FILTER_RPM:
+		return archive_read_support_filter_rpm(a);
+	case ARCHIVE_FILTER_LZIP:
+		return archive_read_support_filter_lzip(a);
+	case ARCHIVE_FILTER_LRZIP:
+		return archive_read_support_filter_lrzip(a);
+	case ARCHIVE_FILTER_LZOP:
+		return archive_read_support_filter_lzop(a);
+	case ARCHIVE_FILTER_GRZIP:
+		return archive_read_support_filter_grzip(a);
+	case ARCHIVE_FILTER_LZ4:
+		return archive_read_support_filter_lz4(a);
+	case ARCHIVE_FILTER_ZSTD:
+		return archive_read_support_filter_zstd(a);
+	}
+	return (ARCHIVE_FATAL);
+}
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,363 @@
+/*-
+ * 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"
+
+#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 *);
+
+#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
+
+static const struct archive_read_filter_bidder_vtable
+bzip2_bidder_vtable = {
+	.bid = bzip2_reader_bid,
+	.init = bzip2_reader_init,
+};
+
+int
+archive_read_support_filter_bzip2(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (__archive_read_register_bidder(a, NULL, "bzip2",
+				&bzip2_bidder_vtable) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+#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
+}
+
+/*
+ * 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
+
+static const struct archive_read_filter_vtable
+bzip2_reader_vtable = {
+	.read = bzip2_filter_read,
+	.close = bzip2_filter_close,
+};
+
+/*
+ * 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 = calloc(1, sizeof(*state));
+	out_block = 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->vtable = &bzip2_reader_vtable;
+
+	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 = (uint32_t)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 = (uint32_t)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,452 @@
+/*-
+ * 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"
+
+#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 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
+
+static const struct archive_read_filter_bidder_vtable
+compress_bidder_vtable = {
+	.bid = compress_bidder_bid,
+	.init = compress_bidder_init,
+};
+
+int
+archive_read_support_filter_compress(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	return __archive_read_register_bidder(a, NULL, "compress (.Z)",
+			&compress_bidder_vtable);
+}
+
+/*
+ * 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);
+}
+
+static const struct archive_read_filter_vtable
+compress_reader_vtable = {
+	.read = compress_filter_read,
+	.close = compress_filter_close,
+};
+
+/*
+ * 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 = calloc(1, sizeof(*state));
+	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->vtable = &compress_reader_vtable;
+
+	/* 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);
+}
+
+/*
+ * 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;
+
+again:
+	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;
+		goto again;
+	}
+
+	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,109 @@
+/*-
+ * 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"
+
+#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 const struct archive_read_filter_bidder_vtable
+grzip_bidder_vtable = {
+	.bid = grzip_bidder_bid,
+	.init = grzip_bidder_init,
+};
+
+int
+archive_read_support_filter_grzip(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (__archive_read_register_bidder(a, NULL, NULL,
+				&grzip_bidder_vtable) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* 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,543 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+#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
+
+static const struct archive_read_filter_bidder_vtable
+gzip_bidder_vtable = {
+	.bid = gzip_bidder_bid,
+	.init = gzip_bidder_init,
+};
+
+int
+archive_read_support_filter_gzip(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (__archive_read_register_bidder(a, NULL, "gzip",
+				&gzip_bidder_vtable) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* 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.
+ */
+#define MAX_FILENAME_LENGTH (1024 * 1024L)
+#define MAX_COMMENT_LENGTH (1024 * 1024L)
+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) {
+				if (avail > MAX_FILENAME_LENGTH) {
+					return (0);
+				}
+				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) {
+				if (avail > MAX_COMMENT_LENGTH) {
+					return (0);
+				}
+				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);
+}
+
+static const struct archive_read_filter_vtable
+gzip_reader_vtable = {
+	.read = gzip_filter_read,
+	.close = gzip_filter_close,
+#ifdef HAVE_ZLIB_H
+	.read_header = gzip_read_header,
+#endif
+};
+
+/*
+ * 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 = calloc(1, sizeof(*state));
+	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 gzip decompression");
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = state;
+	state->out_block_size = out_block_size;
+	state->out_block = out_block;
+	self->vtable = &gzip_reader_vtable;
+
+	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,119 @@
+/*-
+ * 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"
+
+#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 const struct archive_read_filter_bidder_vtable
+lrzip_bidder_vtable = {
+	.bid = lrzip_bidder_bid,
+	.init = lrzip_bidder_init,
+};
+
+int
+archive_read_support_filter_lrzip(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (__archive_read_register_bidder(a, NULL, "lrzip",
+				&lrzip_bidder_vtable) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* 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,739 @@
+/*-
+ * 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_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 *);
+#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
+
+static const struct archive_read_filter_bidder_vtable
+lz4_bidder_vtable = {
+	.bid = lz4_reader_bid,
+	.init = lz4_reader_init,
+};
+
+int
+archive_read_support_filter_lz4(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (__archive_read_register_bidder(a, NULL, "lz4",
+				&lz4_bidder_vtable) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+#if defined(HAVE_LIBLZ4)
+	return (ARCHIVE_OK);
+#else
+	archive_set_error(_a, ARCHIVE_ERRNO_MISC,
+	    "Using external lz4 program");
+	return (ARCHIVE_WARN);
+#endif
+}
+
+/*
+ * 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
+
+static const struct archive_read_filter_vtable
+lz4_reader_vtable = {
+	.read = lz4_filter_read,
+	.close = lz4_filter_close,
+};
+
+/*
+ * 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 = calloc(1, sizeof(*state));
+	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->vtable = &lz4_reader_vtable;
+
+	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 = 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 = 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);
+	}
+
+	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;
+#ifndef DONT_FAIL_ON_CRC_ERROR
+	if (chsum != chsum_verifier)
+		goto malformed_error;
+#endif
+
+	__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);
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		if (chsum != chsum_block)
+			goto malformed_error;
+#endif
+	}
+
+
+	/* 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,
+		    (int)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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+				archive_set_error(&self->archive->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "lz4 stream checksum error");
+				return (ARCHIVE_FATAL);
+#endif
+			}
+		} 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,498 @@
+/*-
+ * 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"
+
+#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 *);
+
+static const struct archive_read_filter_bidder_vtable
+lzop_bidder_vtable = {
+	.bid = lzop_bidder_bid,
+	.init = lzop_bidder_init,
+};
+
+int
+archive_read_support_filter_lzop(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (__archive_read_register_bidder(a, NULL, NULL,
+				&lzop_bidder_vtable) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+	/* 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
+
+static const struct archive_read_filter_vtable
+lzop_reader_vtable = {
+	.read = lzop_filter_read,
+	.close = lzop_filter_close
+};
+
+/*
+ * 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 = calloc(1, sizeof(*state));
+	if (state == NULL) {
+		archive_set_error(&self->archive->archive, ENOMEM,
+		    "Can't allocate data for lzop decompression");
+		return (ARCHIVE_FATAL);
+	}
+
+	self->data = state;
+	self->vtable = &lzop_reader_vtable;
+
+	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);
+#ifndef DONT_FAIL_ON_CRC_ERROR
+	if (archive_be32dec(p + len) != checksum)
+		goto corrupted;
+#endif
+	__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,
+		    (int64_t)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,51 @@
+/*-
+ * 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"
+
+#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,492 @@
+/*-
+ * 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"
+
+#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 void	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;
+	pid_t		 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 const struct archive_read_filter_bidder_vtable
+program_bidder_vtable = {
+	.bid = program_bidder_bid,
+	.init = program_bidder_init,
+	.free = program_bidder_free,
+};
+
+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 program_bidder *state;
+
+	/*
+	 * Allocate our private state.
+	 */
+	state = calloc(1, sizeof (*state));
+	if (state == NULL)
+		goto memerr;
+	state->cmd = strdup(cmd);
+	if (state->cmd == NULL)
+		goto memerr;
+
+	if (signature != NULL && signature_len > 0) {
+		state->signature_len = signature_len;
+		state->signature = malloc(signature_len);
+		memcpy(state->signature, signature, signature_len);
+	}
+
+	if (__archive_read_register_bidder(a, state, NULL,
+				&program_bidder_vtable) != ARCHIVE_OK) {
+		free_state(state);
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+
+memerr:
+	free_state(state);
+	archive_set_error(_a, ENOMEM, "Can't allocate memory");
+	return (ARCHIVE_FATAL);
+}
+
+static void
+program_bidder_free(struct archive_read_filter_bidder *self)
+{
+	struct program_bidder *state = (struct program_bidder *)self->data;
+
+	free_state(state);
+}
+
+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);
+		state->child = 0;
+	}
+
+	if (state->waitpid_return < 0) {
+		/* waitpid() failed?  This is ugly. */
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "Error closing child process");
+		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);
+		}
+	}
+}
+
+static const struct archive_read_filter_vtable
+program_reader_vtable = {
+	.read = program_filter_read,
+	.close = program_filter_close,
+};
+
+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 = calloc(1, sizeof(*state));
+	out_buf = 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->vtable = &program_reader_vtable;
+
+	/* 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,294 @@
+/*-
+ * 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;
+	uint64_t	 hpos;
+	uint64_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. */
+#define RPM_MIN_HEAD_SIZE	16	/* Minimum size of 'Head'. */
+
+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 *);
+
+static inline size_t rpm_limit_bytes(uint64_t, size_t);
+
+#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
+
+static const struct archive_read_filter_bidder_vtable
+rpm_bidder_vtable = {
+	.bid = rpm_bidder_bid,
+	.init = rpm_bidder_init,
+};
+
+int
+archive_read_support_filter_rpm(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	return __archive_read_register_bidder(a, NULL, "rpm",
+			&rpm_bidder_vtable);
+}
+
+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 const struct archive_read_filter_vtable
+rpm_reader_vtable = {
+	.read = rpm_filter_read,
+	.close = rpm_filter_close,
+};
+
+static int
+rpm_bidder_init(struct archive_read_filter *self)
+{
+	struct rpm   *rpm;
+
+	self->code = ARCHIVE_FILTER_RPM;
+	self->name = "rpm";
+
+	rpm = calloc(1, sizeof(*rpm));
+	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;
+	self->vtable = &rpm_reader_vtable;
+
+	return (ARCHIVE_OK);
+}
+
+static inline size_t
+rpm_limit_bytes(uint64_t bytes, size_t max)
+{
+	return (bytes > max ? max : (size_t)bytes);
+}
+
+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, used;
+	size_t n;
+	uint64_t section;
+	uint64_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 = rpm_limit_bytes(RPM_MIN_HEAD_SIZE - rpm->hpos,
+			    avail_in - used);
+			memcpy(rpm->header+rpm->hpos, b, n);
+			b += n;
+			used += n;
+			rpm->hpos += n;
+
+			if (rpm->hpos == RPM_MIN_HEAD_SIZE) {
+				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,
+						    "Unrecognized rpm header");
+						return (ARCHIVE_FATAL);
+					}
+					rpm->state = ST_ARCHIVE;
+					*buff = rpm->header;
+					total = RPM_MIN_HEAD_SIZE;
+					break;
+				}
+				/* Calculate 'Header' length. */
+				section = archive_be32dec(rpm->header+8);
+				bytes = archive_be32dec(rpm->header+12);
+				rpm->hlen = rpm->hpos + section * 16 + bytes;
+				rpm->state = ST_HEADER_DATA;
+				rpm->first_header = 0;
+			}
+			break;
+		case ST_HEADER_DATA:
+			n = rpm_limit_bytes(rpm->hlen - rpm->hpos,
+			    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 < 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 == 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,738 @@
+/*-
+ * 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"
+
+#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_read_private.h"
+
+/* Maximum lookahead during bid phase */
+#define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */
+
+#define UUENCODE_MAX_LINE_LENGTH 34*1024 /* in bytes */
+
+struct uudecode {
+	int64_t		 total;
+	unsigned char	*in_buff;
+#define IN_BUFF_SIZE	(1024)
+	ssize_t		 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
+	mode_t		mode;
+	int		mode_set;
+	char		*name;
+};
+
+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 int	uudecode_read_header(struct archive_read_filter *,
+		    struct archive_entry *entry);
+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
+
+static const struct archive_read_filter_bidder_vtable
+uudecode_bidder_vtable = {
+	.bid = uudecode_bidder_bid,
+	.init = uudecode_bidder_init,
+};
+
+int
+archive_read_support_filter_uu(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	return __archive_read_register_bidder(a, NULL, "uu",
+			&uudecode_bidder_vtable);
+}
+
+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 determined. */
+		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 const struct archive_read_filter_vtable
+uudecode_reader_vtable = {
+	.read = uudecode_filter_read,
+	.close = uudecode_filter_close,
+	.read_header = uudecode_read_header
+};
+
+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";
+
+	uudecode = calloc(1, sizeof(*uudecode));
+	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;
+	uudecode->mode_set = 0;
+	uudecode->name = NULL;
+	self->vtable = &uudecode_reader_vtable;
+
+	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 int
+uudecode_read_header(struct archive_read_filter *self, struct archive_entry *entry)
+{
+
+	struct uudecode *uudecode;
+	uudecode = (struct uudecode *)self->data;
+
+	if (uudecode->mode_set != 0)
+		archive_entry_set_mode(entry, S_IFREG | uudecode->mode);
+
+	if (uudecode->name != NULL)
+		archive_entry_set_pathname(entry, uudecode->name);
+
+	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, namelen;
+
+	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 (uudecode->in_cnt > UUENCODE_MAX_LINE_LENGTH) {
+			archive_set_error(&self->archive->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Invalid format data");
+			return (ARCHIVE_FATAL);
+		}
+		/*
+		 * 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) {
+		ssize_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 = 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;
+				uudecode->mode = (mode_t)(
+				    ((int)(b[l] - '0') * 64) +
+				    ((int)(b[l+1] - '0') * 8) +
+				     (int)(b[l+2] - '0'));
+				uudecode->mode_set = 1;
+				namelen = len - nl - 4 - l;
+				if (namelen > 1) {
+					if (uudecode->name != NULL)
+						free(uudecode->name);
+					uudecode->name = malloc(namelen + 1);
+			                if (uudecode->name == NULL) {
+					archive_set_error(
+					    &self->archive->archive,
+					    ENOMEM,
+					    "Can't allocate data for uudecode");
+						return (ARCHIVE_FATAL);
+					}
+					strncpy(uudecode->name,
+					    (const char *)(b + l + 4),
+					    namelen);
+					uudecode->name[namelen] = '\0';
+				}
+			}
+			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->name);
+	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,803 @@
+/*-
+ * 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"
+
+#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
+
+static const struct archive_read_filter_bidder_vtable
+xz_bidder_vtable = {
+	.bid = xz_bidder_bid,
+	.init = xz_bidder_init,
+};
+
+int
+archive_read_support_filter_xz(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (__archive_read_register_bidder(a, NULL, "xz",
+				&xz_bidder_vtable) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+#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
+
+static const struct archive_read_filter_bidder_vtable
+lzma_bidder_vtable = {
+	.bid = lzma_bidder_bid,
+	.init = lzma_bidder_init,
+};
+
+int
+archive_read_support_filter_lzma(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (__archive_read_register_bidder(a, NULL, "lzma",
+				&lzma_bidder_vtable) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+#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
+
+static const struct archive_read_filter_bidder_vtable
+lzip_bidder_vtable = {
+	.bid = lzip_bidder_bid,
+	.init = lzip_bidder_init,
+};
+
+int
+archive_read_support_filter_lzip(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (__archive_read_register_bidder(a, NULL, "lzip",
+				&lzip_bidder_vtable) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+#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 << 29(512MiB)
+	 * which the one uses with option -d29.
+	 * 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 > 29)
+		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;
+	}
+}
+
+static const struct archive_read_filter_vtable
+xz_lzma_reader_vtable = {
+	.read = xz_filter_read,
+	.close = xz_filter_close,
+};
+
+/*
+ * 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 = calloc(1, sizeof(*state));
+	out_block = 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->vtable = &xz_lzma_reader_vtable;
+
+	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 > 29)
+		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)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		archive_set_error(&self->archive->archive, ARCHIVE_ERRNO_MISC,
+		    "Lzip: CRC32 error");
+		return (ARCHIVE_FAILED);
+#endif
+	}
+
+	/* 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;
+	int64_t member_in;
+	int ret;
+
+	state = (struct private_data *)self->data;
+
+	redo:
+	/* Empty our output buffer. */
+	state->stream.next_out = state->out_block;
+	state->stream.avail_out = state->out_block_size;
+	member_in = state->member_in;
+
+	/* 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) {
+		if (member_in != state->member_in &&
+		    self->code == ARCHIVE_FILTER_LZIP &&
+		    state->eof) {
+			ret = lzip_tail(self);
+			if (ret != ARCHIVE_OK)
+				return (ret);
+			if (!state->eof)
+				goto redo;
+		}
+		*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,295 @@
+/*-
+ * 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"
+
+#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 *);
+
+static const struct archive_read_filter_bidder_vtable
+zstd_bidder_vtable = {
+	.bid = zstd_bidder_bid,
+	.init = zstd_bidder_init,
+};
+
+int
+archive_read_support_filter_zstd(struct archive *_a)
+{
+	struct archive_read *a = (struct archive_read *)_a;
+
+	if (__archive_read_register_bidder(a, NULL, "zstd",
+				&zstd_bidder_vtable) != ARCHIVE_OK)
+		return (ARCHIVE_FATAL);
+
+#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 */
+	unsigned zstd_magic = 0xFD2FB528U;
+	unsigned zstd_magic_skippable_start = 0x184D2A50U;
+	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
+
+static const struct archive_read_filter_vtable
+zstd_reader_vtable = {
+	.read = zstd_filter_read,
+	.close = zstd_filter_close,
+};
+
+/*
+ * Initialize the filter object
+ */
+static int
+zstd_bidder_init(struct archive_read_filter *self)
+{
+	struct private_data *state;
+	size_t out_block_size = ZSTD_DStreamOutSize();
+	void *out_block;
+	ZSTD_DStream *dstream;
+
+	self->code = ARCHIVE_FILTER_ZSTD;
+	self->name = "zstd";
+
+	state = calloc(1, sizeof(*state));
+	out_block = 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->vtable = &zstd_reader_vtable;
+
+	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;
+	size_t ret;
+
+	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) {
+			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;
+
+		{
+			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,4526 @@
+/*-
+ * 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"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_STDINT_H
+#include <stdint.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
+#ifdef HAVE_ZSTD_H
+#include <zstd.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_time_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
+#define SFX_MAX_OFFSET	(SFX_MAX_ADDR - SFX_MIN_ADDR)
+
+/*
+ * PE format
+ */
+#define PE_DOS_HDR_LEN				0x40
+#define PE_DOS_HDR_ELFANEW_OFFSET	0x3c
+#define PE_COFF_HDR_LEN				0x18
+#define PE_COFF_HDR_SEC_CNT_OFFSET	0x6
+#define PE_COFF_HDR_OPT_SZ_OFFSET	0x14
+#define PE_SEC_HDR_LEN 				0x28
+#define PE_SEC_HDR_RAW_ADDR_OFFSET	0x14
+#define PE_SEC_HDR_RAW_SZ_OFFSET	0x10
+
+/*
+ * ELF format
+ */
+#define ELF_HDR_MIN_LEN 0x3f
+#define ELF_HDR_EI_CLASS_OFFSET 0x04
+#define ELF_HDR_EI_DATA_OFFSET 0x05
+
+/*
+ * 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_ARM64	0xa
+#define _7Z_RISCV	0xb
+#define _7Z_SPARC	0x03030805
+
+#define _7Z_ZSTD	0x4F71101 /* Copied from https://github.com/mcmilk/7-Zip-zstd.git */
+
+/*
+ * 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
+
+// Check that some windows file attribute constants are defined.
+// Reference: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+#ifndef FILE_ATTRIBUTE_READONLY
+#define FILE_ATTRIBUTE_READONLY 0x00000001
+#endif
+
+#ifndef FILE_ATTRIBUTE_HIDDEN
+#define FILE_ATTRIBUTE_HIDDEN 0x00000002
+#endif
+
+#ifndef FILE_ATTRIBUTE_SYSTEM
+#define FILE_ATTRIBUTE_SYSTEM 0x00000004
+#endif
+
+#ifndef FILE_ATTRIBUTE_DIRECTORY
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#endif
+
+// This value is defined in 7zip with the comment "trick for Unix".
+//
+// 7z archives created on unix have this bit set in the high 16 bits of
+// the attr field along with the unix permissions.
+#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000
+
+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)
+
+	int64_t			 mtime;
+	int64_t			 atime;
+	int64_t			 ctime;
+	uint32_t		 mtime_ns;
+	uint32_t		 atime_ns;
+	uint32_t		 ctime_ns;
+	__LA_MODE_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 Zstandard data. */
+#if HAVE_ZSTD_H
+	ZSTD_DStream		 *zstd_dstream;
+	int		         zstdstream_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;
+		int64_t			 stream_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 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 *, const ssize_t);
+static ssize_t	find_pe_overlay(struct archive_read *);
+static ssize_t	find_elf_data_sec(struct archive_read *);
+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 void	arm_Init(struct _7zip *);
+static size_t	arm_Convert(struct _7zip *, uint8_t *, size_t);
+static size_t	arm64_Convert(struct _7zip *, uint8_t *, size_t);
+static ssize_t		Bcj2_Decode(struct _7zip *, uint8_t *, size_t);
+static size_t	sparc_Convert(struct _7zip *, uint8_t *, size_t);
+static size_t	powerpc_Convert(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. While find_pe_overlay can be performed without
+	 * performing a seek, find_elf_data_sec requires one,
+	 * thus a performance difference between the two is expected. 
+	 */
+	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+		const ssize_t min_addr = p[0] == 'M' ? find_pe_overlay(a) :
+						       find_elf_data_sec(a);
+		ssize_t offset = min_addr;
+		ssize_t window = 4096;
+		ssize_t bytes_avail;
+		while (offset + window <= (min_addr + SFX_MAX_OFFSET)) {
+			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, const ssize_t min_addr)
+{
+	const void *h;
+	const char *p, *q;
+	size_t skip, offset;
+	ssize_t bytes, window;
+
+	if (__archive_read_seek(a, 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 = 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 ssize_t
+find_pe_overlay(struct archive_read *a)
+{
+	const char *h;
+	ssize_t bytes, max_offset, offset, sec_end;
+	ssize_t opt_hdr_sz, sec_cnt;
+
+	for (;;) {
+		/*
+		 * Read Dos header to find e_lfanew
+		 */
+		h = __archive_read_ahead(a, PE_DOS_HDR_LEN, &bytes);
+		if (h == NULL || h[0] != 'M' || h[1] != 'Z') {
+			break;
+		}
+		offset = archive_le32dec(h + PE_DOS_HDR_ELFANEW_OFFSET);
+
+		/*
+		 * Read COFF header to find opt header size and sec cnt
+		 */
+		if (bytes < offset + PE_COFF_HDR_LEN) {
+			h = __archive_read_ahead(a, offset + PE_COFF_HDR_LEN,
+			    &bytes);
+			if (h == NULL || h[offset] != 'P' ||
+			    h[offset + 1] != 'E') {
+				break;
+			}
+		}
+		sec_cnt = archive_le16dec(
+		    h + offset + PE_COFF_HDR_SEC_CNT_OFFSET);
+		opt_hdr_sz = archive_le16dec(
+		    h + offset + PE_COFF_HDR_OPT_SZ_OFFSET);
+
+		/*
+		 * Skip optional header
+		 */
+		if (opt_hdr_sz != 0) {
+			offset += PE_COFF_HDR_LEN + opt_hdr_sz;
+		} else {
+			break;
+		}
+
+		/*
+		 * Traverse sec table to find max raw offset (i.e., overlay)
+		 */
+		if (bytes < offset + sec_cnt * PE_SEC_HDR_LEN) {
+			h = __archive_read_ahead(a,
+			    offset + sec_cnt * PE_SEC_HDR_LEN, NULL);
+			if (h == NULL) {
+				break;
+			}
+		}
+		max_offset = offset;
+		while (sec_cnt > 0) {
+			sec_end = archive_le32dec(
+				      h + offset + PE_SEC_HDR_RAW_SZ_OFFSET) +
+			    archive_le32dec(
+				h + offset + PE_SEC_HDR_RAW_ADDR_OFFSET);
+			if (sec_end > max_offset) {
+				max_offset = sec_end;
+			}
+			offset += PE_SEC_HDR_LEN;
+			sec_cnt--;
+		}
+		return (max_offset);
+	}
+
+	/*
+	 * If encounter any weirdness, revert to old brute-force style search
+	 */
+	return (SFX_MIN_ADDR);
+}
+
+static ssize_t
+find_elf_data_sec(struct archive_read *a)
+{
+	const char *h;
+	char big_endian, format_64;
+	ssize_t bytes, min_addr = SFX_MIN_ADDR;
+	uint64_t e_shoff, strtab_offset, strtab_size;
+	uint16_t e_shentsize, e_shnum, e_shstrndx;
+	uint16_t (*dec16)(const void *);
+	uint32_t (*dec32)(const void *);
+	uint64_t (*dec64)(const void *);
+
+	for (;;) {
+		/*
+		 * Read Elf header to find bitness & endianness
+		 */
+		h = __archive_read_ahead(a, ELF_HDR_MIN_LEN, &bytes);
+		if (h == NULL || memcmp(h, "\x7F\x45LF", 4) != 0) {
+			break;
+		}
+		format_64 = h[ELF_HDR_EI_CLASS_OFFSET] == 0x2;
+		big_endian = h[ELF_HDR_EI_DATA_OFFSET] == 0x2;
+		if (big_endian) {
+			dec16 = &archive_be16dec;
+			dec32 = &archive_be32dec;
+			dec64 = &archive_be64dec;
+		} else {
+			dec16 = &archive_le16dec;
+			dec32 = &archive_le32dec;
+			dec64 = &archive_le64dec;
+		}
+
+		/*
+		 * Read section header table info
+		 */
+		if (format_64) {
+			e_shoff = (*dec64)(h + 0x28);
+			e_shentsize = (*dec16)(h + 0x3A);
+			e_shnum = (*dec16)(h + 0x3C);
+			e_shstrndx = (*dec16)(h + 0x3E);
+			if (e_shnum < e_shstrndx || e_shentsize < 0x28)
+				break;
+
+		} else {
+			e_shoff = (*dec32)(h + 0x20);
+			e_shentsize = (*dec16)(h + 0x2E);
+			e_shnum = (*dec16)(h + 0x30);
+			e_shstrndx = (*dec16)(h + 0x32);
+			if (e_shnum < e_shstrndx || e_shentsize < 0x18)
+				break;
+		}
+
+		/*
+		 * Reading the section table to find strtab section
+		 */
+		if (__archive_read_seek(a, e_shoff, SEEK_SET) < 0) {
+			break;
+		}
+		h = __archive_read_ahead(a, (size_t)e_shnum * (size_t)e_shentsize, NULL);
+		if (h == NULL) {
+			break;
+		}
+		if (format_64) {
+			strtab_offset = (*dec64)(
+			    h + e_shstrndx * e_shentsize + 0x18);
+			strtab_size = (*dec64)(
+			    h + e_shstrndx * e_shentsize + 0x20);
+		} else {
+			strtab_offset = (*dec32)(
+			    h + e_shstrndx * e_shentsize + 0x10);
+			strtab_size = (*dec32)(
+			    h + e_shstrndx * e_shentsize + 0x14);
+		}
+		if (strtab_size < 6 || strtab_size > SIZE_MAX)
+			break;
+
+		/*
+		 * Read the STRTAB section to find the .data offset
+		 */
+		if (__archive_read_seek(a, strtab_offset, SEEK_SET) < 0) {
+			break;
+		}
+		h = __archive_read_ahead(a, strtab_size, NULL);
+		if (h == NULL) {
+			break;
+		}
+		ssize_t data_sym_offset = -1;
+		for (size_t offset = 0; offset < strtab_size - 6; offset++) {
+			if (memcmp(h + offset, ".data\00", 6) == 0) {
+				data_sym_offset = offset;
+				break;
+			}
+		}
+		if (data_sym_offset == -1) {
+			break;
+		}
+
+		/*
+		 * Find the section with the .data name
+		 */
+		if (__archive_read_seek(a, e_shoff, SEEK_SET) < 0) {
+			break;
+		}
+		h = __archive_read_ahead(a, (size_t)e_shnum * (size_t)e_shentsize, NULL);
+		if (h == NULL) {
+			break;
+		}
+		ssize_t sec_tbl_offset = 0, name_offset;
+		while (e_shnum > 0) {
+			name_offset = (*dec32)(h + sec_tbl_offset);
+			if (name_offset == data_sym_offset) {
+				if (format_64) {
+					min_addr = (*dec64)(
+					    h + sec_tbl_offset + 0x18);
+				} else {
+					min_addr = (*dec32)(
+					    h + sec_tbl_offset + 0x10);
+				}
+				break;
+			}
+			sec_tbl_offset += e_shentsize;
+			e_shnum--;
+		}
+		break;
+	}
+
+	__archive_read_seek(a, 0, SEEK_SET);
+	return (min_addr);
+}
+
+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);
+	}
+
+	// These attributes are supported by the windows implementation of archive_write_disk.
+	const int supported_attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
+
+	if (zip_entry->attr & supported_attrs) {
+		char *fflags_text, *ptr;
+		/* allocate for ",rdonly,hidden,system" */
+		fflags_text = malloc(22 * sizeof(*fflags_text));
+		if (fflags_text != NULL) {
+			ptr = fflags_text;
+			if (zip_entry->attr & FILE_ATTRIBUTE_READONLY) {
+				strcpy(ptr, ",rdonly");
+				ptr = ptr + 7;
+			}
+			if (zip_entry->attr & FILE_ATTRIBUTE_HIDDEN) {
+				strcpy(ptr, ",hidden");
+				ptr = ptr + 7;
+			}
+			if (zip_entry->attr & FILE_ATTRIBUTE_SYSTEM) {
+				strcpy(ptr, ",system");
+				ptr = ptr + 7;
+			}
+			if (ptr > fflags_text) {
+				archive_entry_copy_fflags_text(entry,
+				    fflags_text + 1);
+			}
+			free(fflags_text);
+		}
+	}
+
+	/* 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 {
+			struct archive_string_conv* utf8_conv;
+
+			symname[symsize] = '\0';
+
+			/* Symbolic links are embedded as UTF-8 strings */
+			utf8_conv = archive_string_conversion_from_charset(&a->archive,
+			    "UTF-8", 1);
+			if (utf8_conv == NULL) {
+				free(symname);
+				return ARCHIVE_FATAL;
+			}
+
+			archive_entry_copy_symlink_l(entry, (const char*)symname, symsize,
+			    utf8_conv);
+		}
+		free(symname);
+		archive_entry_set_size(entry, 0);
+	}
+
+	/* Set up a more descriptive format name. */
+	snprintf(zip->format_name, sizeof(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);
+
+	size_t bytes_to_read = 16 * 1024 * 1024;  // Don't try to read more than 16 MB at a time
+	if ((uint64_t)bytes_to_read > zip->entry_bytes_remaining) {
+		bytes_to_read = (size_t)zip->entry_bytes_remaining;
+	}
+	bytes = read_stream(a, buff, bytes_to_read, 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) {
+		/*
+		 * Ppmd7_DecodeSymbol might require reading multiple bytes
+		 * and we are on boundary;
+		 * last resort to read using __archive_read_ahead.
+		 */
+		ssize_t bytes_avail = 0;
+		const uint8_t* data = __archive_read_ahead(a,
+		    (size_t)zip->ppstream.stream_in+1, &bytes_avail);
+		if(data == NULL || bytes_avail < zip->ppstream.stream_in+1) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_FILE_FORMAT,
+			    "Truncated 7z file data");
+			zip->ppstream.overconsumed = 1;
+			return (0);
+		}
+		zip->ppstream.next_in++;
+		b = data[zip->ppstream.stream_in];
+	} else {
+		b = *zip->ppstream.next_in++;
+	}
+	zip->ppstream.avail_in--;
+	zip->ppstream.total_in++;
+	zip->ppstream.stream_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_ZSTD:
+	case _7Z_PPMD:
+		if (coder2 != NULL) {
+			if (coder2->codec != _7Z_X86 &&
+			    coder2->codec != _7Z_X86_BCJ2 &&
+			    coder2->codec != _7Z_ARM &&
+			    coder2->codec != _7Z_ARM64 &&
+			    coder2->codec != _7Z_POWERPC &&
+			    coder2->codec != _7Z_SPARC) {
+				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);
+			else if (coder2->codec == _7Z_ARM)
+				arm_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.
+		 *
+		 * Reference: https://web.archive.org/web/20240405171610/https://www.mail-archive.com/xz-devel@tukaani.org/msg00373.html
+		 */
+		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;
+#ifdef LZMA_FILTER_ARM64
+			case _7Z_ARM64:
+				filters[fi].id = LZMA_FILTER_ARM64;
+				fi++;
+				break;
+#endif
+#ifdef LZMA_FILTER_RISCV
+			case _7Z_RISCV:
+				filters[fi].id = LZMA_FILTER_RISCV;
+				fi++;
+				break;
+#endif
+			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_ZSTD:
+	{
+#if defined(HAVE_ZSTD_H)
+		if (zip->zstdstream_valid) {
+			ZSTD_freeDStream(zip->zstd_dstream);
+			zip->zstdstream_valid = 0;
+		}
+		zip->zstd_dstream = ZSTD_createDStream();
+		zip->zstdstream_valid = 1;
+		break;
+#else
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"ZSTD 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_ARM64:
+	case _7Z_RISCV:
+	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 = (uint32_t)t_avail_in;
+		zip->bzstream.next_out = (char *)(uintptr_t)t_next_out;
+		zip->bzstream.avail_out = (uint32_t)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
+#ifdef HAVE_ZSTD_H
+	case _7Z_ZSTD:
+	{
+		ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos
+		ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos
+
+		size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input);
+		if (ZSTD_isError(zret)) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret));
+			return ARCHIVE_FAILED;
+		}
+		t_avail_in -= input.pos;
+		t_avail_out -= output.pos;
+		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.stream_in = 0;
+		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 decoder");
+				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) {
+		if (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;
+		} else if (zip->codec2 == _7Z_ARM) {
+			*outbytes = arm_Convert(zip, buff, *outbytes);
+		} else if (zip->codec2 == _7Z_ARM64) {
+			*outbytes = arm64_Convert(zip, buff, *outbytes);
+		} else if (zip->codec2 == _7Z_SPARC) {
+			*outbytes = sparc_Convert(zip, buff, *outbytes);
+		} else if (zip->codec2 == _7Z_POWERPC) {
+			*outbytes = powerpc_Convert(zip, buff, *outbytes);
+		}
+	}
+
+	/*
+	 * 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
+#ifdef HAVE_ZSTD_H
+	if (zip->zstdstream_valid)
+		ZSTD_freeDStream(zip->zstd_dstream);
+#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 (UMAX_ENTRY < f->coders[i].propertiesSize)
+				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 size, 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);
+				if (*usizes > UINT64_MAX - sum)
+					return (-1);
+				sum += *usizes++;
+			}
+		}
+		size = folder_uncompressed_size(&f[i]);
+		if (size < sum)
+			return (-1);
+		*usizes++ = size - 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;
+			if (packPos > UINT64_MAX - si->pi.sizes[i])
+				return (-1);
+			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;
+			if (f[i].numPackedStreams > UINT32_MAX)
+				return (-1);
+			if (packIndex > UINT32_MAX - (uint32_t)f[i].numPackedStreams)
+				return (-1);
+			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].attr & FILE_ATTRIBUTE_UNIX_EXTENSION)) {
+			// Only windows permissions specified for this entry. Translate to
+			// reasonable corresponding unix permissions.
+
+			if (entries[i].attr & FILE_ATTRIBUTE_DIRECTORY) {
+				if (entries[i].attr & FILE_ATTRIBUTE_READONLY) {
+					// Read-only directory.
+					entries[i].mode = AE_IFDIR | 0555;
+				} else {
+					// Read-write directory.
+					entries[i].mode = AE_IFDIR | 0755;
+				}
+			} else if (entries[i].attr & FILE_ATTRIBUTE_READONLY) {
+				// Readonly file.
+				entries[i].mode = AE_IFREG | 0444;
+			} else {
+				// Assume read-write file.
+				entries[i].mode = AE_IFREG | 0644;
+			}
+		}
+
+		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 & FILE_ATTRIBUTE_READONLY)
+			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);
+}
+
+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:
+			ntfs_to_unix(archive_le64dec(p),
+			    &(entries[i].ctime),
+			    &(entries[i].ctime_ns));
+			entries[i].flg |= CTIME_IS_SET;
+			break;
+		case kATime:
+			ntfs_to_unix(archive_le64dec(p),
+			    &(entries[i].atime),
+			    &(entries[i].atime_ns));
+			entries[i].flg |= ATIME_IS_SET;
+			break;
+		case kMTime:
+			ntfs_to_unix(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... */
+		const ssize_t min_addr = p[0] == 'M' ? find_pe_overlay(a) :
+						       find_elf_data_sec(a);
+		r = skip_sfx(a, min_addr);
+		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)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		archive_set_error(&a->archive, -1, "Header CRC error");
+		return (ARCHIVE_FATAL);
+#endif
+	}
+
+	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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+			archive_set_error(&a->archive, -1,
+			    "Damaged 7-Zip archive");
+			r = -1;
+#endif
+		}
+		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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+			archive_set_error(&a->archive, -1,
+			    "Malformed 7-Zip archive");
+			return (ARCHIVE_FATAL);
+#endif
+		}
+		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 (*buff == NULL) {
+			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 ((uint64_t)bytes_avail > zip->pack_stream_inbytes_remaining)
+			bytes_avail = (ssize_t)zip->pack_stream_inbytes_remaining;
+		zip->pack_stream_inbytes_remaining -= bytes_avail;
+		if ((uint64_t)bytes_avail > 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 (1) {
+		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);
+			}
+		}
+
+		if (!skip_bytes)
+			break;
+
+		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);
+}
+
+static void
+arm_Init(struct _7zip *zip)
+{
+	zip->bcj_ip = 8;
+}
+
+static size_t
+arm_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+	// This function was adapted from
+	// static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+	// in https://git.tukaani.org/xz-embedded.git
+
+	/*
+	 * Branch/Call/Jump (BCJ) filter decoders
+	 *
+	 * Authors: Lasse Collin <lasse.collin@tukaani.org>
+	 *          Igor Pavlov <https://7-zip.org/>
+	 *
+	 * This file has been put into the public domain.
+	 * You can do whatever you want with this file.
+	 */
+
+	size_t i;
+	uint32_t addr;
+
+	for (i = 0; i + 4 <= size; i += 4) {
+		if (buf[i + 3] == 0xEB) {
+			// Calculate the transformed addr.
+			addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+				| ((uint32_t)buf[i + 2] << 16);
+			addr <<= 2;
+			addr -= zip->bcj_ip + (uint32_t)i;
+			addr >>= 2;
+
+			// Store the transformed addr in buf.
+			buf[i] = (uint8_t)addr;
+			buf[i + 1] = (uint8_t)(addr >> 8);
+			buf[i + 2] = (uint8_t)(addr >> 16);
+		}
+	}
+
+	zip->bcj_ip += (uint32_t)i;
+
+	return i;
+}
+
+static size_t
+arm64_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+	// This function was adapted from
+	// static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+	// in https://git.tukaani.org/xz-embedded.git
+
+	/*
+	 * Branch/Call/Jump (BCJ) filter decoders
+	 *
+	 * Authors: Lasse Collin <lasse.collin@tukaani.org>
+	 *          Igor Pavlov <https://7-zip.org/>
+	 *
+	 * This file has been put into the public domain.
+	 * You can do whatever you want with this file.
+	 */
+
+	size_t i;
+	uint32_t instr;
+	uint32_t addr;
+
+	for (i = 0; i + 4 <= size; i += 4) {
+		instr = (uint32_t)buf[i]
+			| ((uint32_t)buf[i+1] << 8)
+			| ((uint32_t)buf[i+2] << 16)
+			| ((uint32_t)buf[i+3] << 24);
+
+		if ((instr >> 26) == 0x25) {
+			/* BL instruction */
+			addr = instr - ((zip->bcj_ip + (uint32_t)i) >> 2);
+			instr = 0x94000000 | (addr & 0x03FFFFFF);
+
+			buf[i]   = (uint8_t)instr;
+			buf[i+1] = (uint8_t)(instr >> 8);
+			buf[i+2] = (uint8_t)(instr >> 16);
+			buf[i+3] = (uint8_t)(instr >> 24);
+		} else if ((instr & 0x9F000000) == 0x90000000) {
+			/* ADRP instruction */
+			addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC);
+
+			/* Only convert values in the range +/-512 MiB. */
+			if ((addr + 0x020000) & 0x1C0000)
+				continue;
+
+			addr -= (zip->bcj_ip + (uint32_t)i) >> 12;
+
+			instr &= 0x9000001F;
+			instr |= (addr & 3) << 29;
+			instr |= (addr & 0x03FFFC) << 3;
+			instr |= (0U - (addr & 0x020000)) & 0xE00000;
+
+			buf[i]   = (uint8_t)instr;
+			buf[i+1] = (uint8_t)(instr >> 8);
+			buf[i+2] = (uint8_t)(instr >> 16);
+			buf[i+3] = (uint8_t)(instr >> 24);
+		}
+	}
+
+	zip->bcj_ip += (uint32_t)i;
+
+	return i;
+}
+
+static size_t
+sparc_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+	// This function was adapted from
+	// static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+	// in https://git.tukaani.org/xz-embedded.git
+
+	/*
+	 * Branch/Call/Jump (BCJ) filter decoders
+	 *
+	 * Authors: Lasse Collin <lasse.collin@tukaani.org>
+	 *          Igor Pavlov <https://7-zip.org/>
+	 *
+	 * Copyright (C) The XZ Embedded authors and contributors
+	 *
+	 * Permission to use, copy, modify, and/or distribute this
+	 * software for any purpose with or without fee is hereby granted.
+	 *
+	 * 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.
+	 */
+
+	size_t i;
+	uint32_t instr;
+
+	size &= ~(size_t)3;
+
+	for (i = 0; i < size; i += 4) {
+		instr = (uint32_t)(buf[i] << 24)
+			| ((uint32_t)buf[i+1] << 16)
+			| ((uint32_t)buf[i+2] << 8)
+			| (uint32_t)buf[i+3];
+
+		if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) {
+			instr <<= 2;
+			instr -= zip->bcj_ip + (uint32_t)i;
+			instr >>= 2;
+			instr = ((uint32_t)0x40000000 - (instr & 0x400000))
+			        | 0x40000000 | (instr & 0x3FFFFF);
+
+			buf[i] = (uint8_t)(instr >> 24);
+			buf[i+1] = (uint8_t)(instr >> 16);
+			buf[i+2] = (uint8_t)(instr >> 8);
+			buf[i+3] = (uint8_t)instr;
+		}
+	}
+
+	zip->bcj_ip += (uint32_t)i;
+
+	return i;
+}
+
+static size_t
+powerpc_Convert(struct _7zip *zip, uint8_t *buf, size_t size)
+{
+	// This function was adapted from
+	// static size_t powerpc_code(void *simple, uint32_t now_pos, bool is_encoder, uint8_t *buffer, size_t size)
+	// in https://git.tukaani.org/xz.git
+
+	/*
+	 * Filter for PowerPC (big endian) binaries
+	 *
+	 * Authors: Igor Pavlov
+	 *          Lasse Collin
+	 *
+	 * Copyright (C) The XZ Utils authors and contributors
+	 *
+	 * Permission to use, copy, modify, and/or distribute this
+	 * software for any purpose with or without fee is hereby granted.
+	 *
+	 * 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.
+	 */
+
+	size &= ~(size_t)3;
+
+	size_t i;
+	for (i = 0; i < size; i += 4) {
+		// PowerPC branch 6(48) 24(Offset) 1(Abs) 1(Link)
+		if ((buf[i] >> 2) == 0x12
+			&& ((buf[i + 3] & 3) == 1)) {
+
+			const uint32_t src
+				= (((uint32_t)(buf[i + 0]) & 3) << 24)
+				| ((uint32_t)(buf[i + 1]) << 16)
+				| ((uint32_t)(buf[i + 2]) << 8)
+				| ((uint32_t)(buf[i + 3]) & ~UINT32_C(3));
+
+			uint32_t dest = src - (zip->bcj_ip + (uint32_t)(i));
+
+			buf[i + 0] = 0x48 | ((dest >> 24) &  0x03);
+			buf[i + 1] = (dest >> 16);
+			buf[i + 2] = (dest >> 8);
+			buf[i + 3] &= 0x03;
+			buf[i + 3] |= dest;
+		}
+	}
+
+	zip->bcj_ip += (uint32_t)i;
+
+	return i;
+}
+
+/*
+ * 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 do {							\
+	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; \
+		}							\
+	}								\
+} while (0)
+
+#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,88 @@
+/*-
+ * 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"
+
+#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 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,637 @@
+/*-
+ * 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"
+
+#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 = 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 table exists");
+			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 = 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_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));
+	archive_entry_set_filetype(entry, AE_IFREG);
+	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,76 @@
+/*-
+ * 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"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#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);
+	case ARCHIVE_FORMAT_AR:
+		return archive_read_support_format_ar(a);
+	case ARCHIVE_FORMAT_CAB:
+		return archive_read_support_format_cab(a);
+	case ARCHIVE_FORMAT_CPIO:
+		return archive_read_support_format_cpio(a);
+	case ARCHIVE_FORMAT_EMPTY:
+		return archive_read_support_format_empty(a);
+	case ARCHIVE_FORMAT_ISO9660:
+		return archive_read_support_format_iso9660(a);
+	case ARCHIVE_FORMAT_LHA:
+		return archive_read_support_format_lha(a);
+	case ARCHIVE_FORMAT_MTREE:
+		return archive_read_support_format_mtree(a);
+	case ARCHIVE_FORMAT_RAR:
+		return archive_read_support_format_rar(a);
+	case ARCHIVE_FORMAT_RAR_V5:
+		return archive_read_support_format_rar5(a);
+	case ARCHIVE_FORMAT_RAW:
+		return archive_read_support_format_raw(a);
+	case ARCHIVE_FORMAT_TAR:
+		return archive_read_support_format_tar(a);
+	case ARCHIVE_FORMAT_WARC:
+		return archive_read_support_format_warc(a);
+	case ARCHIVE_FORMAT_XAR:
+		return archive_read_support_format_xar(a);
+	case ARCHIVE_FORMAT_ZIP:
+		return archive_read_support_format_zip(a);
+	}
+	archive_set_error(a, ARCHIVE_ERRNO_PROGRAMMER,
+	    "Invalid format code specified");
+	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,3233 @@
+/*-
+ * 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 = 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);
+	if (archive_wstring_ensure(&cab->ws, 256) == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		free(cab);
+		return (ARCHIVE_FATAL);
+	}
+
+	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 = 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 = 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. */
+	snprintf(cab->format_name, sizeof(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 ((int)(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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Checksum error CFDATA[%d] %" PRIx32 ":%" PRIx32 " in %d bytes",
+		    cab->entry_cffolder->cfdata_index -1,
+		    cfdata->sum, cfdata->sum_calculated,
+		    cfdata->compressed_size);
+		return (ARCHIVE_FAILED);
+#endif
+	}
+	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
+		    = 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
+		    = 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 (d == NULL) {
+			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);
+	}
+
+	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 |
+				    ((uint64_t)strm->next_in[3]) << 24 |
+				    ((uint64_t)strm->next_in[2]) << 16 |
+				    ((uint64_t)strm->next_in[5]) << 8 |
+				     (uint64_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,1121 @@
+/*-
+ * 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"
+
+#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;
+
+	int			  option_pwb;
+};
+
+static int64_t	atol16(const char *, unsigned);
+static uint64_t	atol16u(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 = 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);
+	} else if (strcmp(key, "pwb")  == 0) {
+		if (val != NULL && val[0] != 0)
+			cpio->option_pwb = 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 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!!!",
+	    10) == 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)
+{
+	int64_t t;
+	uint64_t u;
+	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));
+	u = atol16u(header + afiol_ino_offset, afiol_ino_size);
+#if ARCHIVE_VERSION_NUMBER < 4000000
+	archive_entry_set_ino(entry, (int64_t)(u & INT64_MAX));
+#else
+	archive_entry_set_ino(entry, u);
+#endif
+	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. */
+
+	t = atol16(header + afiol_filesize_offset, afiol_filesize_size);
+	if (t < 0) {
+		archive_set_error(&a->archive, 0, "Nonsensical file size");
+		return (ARCHIVE_FATAL);
+	}
+	cpio->entry_bytes_remaining = t;
+	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);
+	if (cpio->option_pwb) {
+		/* turn off random bits left over from V6 inode */
+		archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777);
+		if ((archive_entry_mode(entry) & AE_IFMT) == 0)
+			archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG);
+	}
+	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]);
+	if (cpio->option_pwb) {
+		/* turn off random bits left over from V6 inode */
+		archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777);
+		if ((archive_entry_mode(entry) & AE_IFMT) == 0)
+			archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG);
+	}
+	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)
+{
+	uint64_t l;
+	int digit;
+
+	l = 0;
+	while (char_cnt-- > 0) {
+		if (*p >= '0' && *p <= '7')
+			digit = *p - '0';
+		else
+			return ((int64_t)l);
+		p++;
+		l <<= 3;
+		l |= digit;
+	}
+	return ((int64_t)l);
+}
+
+static int64_t
+atol16(const char *p, unsigned char_cnt)
+{
+	return ((int64_t)atol16u(p, char_cnt));
+}
+
+static uint64_t
+atol16u(const char *p, unsigned char_cnt)
+{
+	uint64_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 ((int64_t)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 = 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,95 @@
+/*-
+ * 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"
+
+#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,
+	    "empty",
+	    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,3534 @@
+/*-
+ * 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"
+
+#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		 time_is_set;	/* Bitmask indicating which times are known */
+	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;
+};
+
+#define BIRTHTIME_IS_SET 1
+#define MTIME_IS_SET 2
+#define ATIME_IS_SET 4
+#define CTIME_IS_SET 8
+
+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 int	isodate17_valid(const unsigned char *);
+static time_t	isodate7(const unsigned char *);
+static int	isodate7_valid(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	isRootDirectoryRecord(const unsigned char *);
+static int	isValid723Integer(const unsigned char *);
+static int	isValid733Integer(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 = 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 (!isRootDirectoryRecord(p)) {
+		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 (!isRootDirectoryRecord(p)) {
+		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);
+
+	/* Volume space size must be encoded according to 7.3.3 */
+	if (!isValid733Integer(h + PVD_volume_space_size_offset)) {
+		return (0);
+	}
+	volume_block = archive_le32dec(h + PVD_volume_space_size_offset);
+	if (volume_block <= SYSTEM_AREA_BLOCK+4)
+		return (0);
+
+	/* Reserved field must be 0. */
+	if (!isNull(iso9660, h, PVD_reserved3_offset, PVD_reserved3_size))
+		return (0);
+
+	/* Volume set size must be encoded according to 7.2.3 */
+	if (!isValid723Integer(h + PVD_volume_set_size_offset)) {
+		return (0);
+	}
+
+	/* Volume sequence number must be encoded according to 7.2.3 */
+	if (!isValid723Integer(h + PVD_volume_sequence_number_offset)) {
+		return (0);
+	}
+
+	/* Logical block size must be > 0. */
+	/* I've looked at Ecma 119 and can't find any stronger
+	 * restriction on this field. */
+	if (!isValid723Integer(h + PVD_logical_block_size_offset)) {
+		return (0);
+	}
+	logical_block_size =
+	    archive_le16dec(h + PVD_logical_block_size_offset);
+	if (logical_block_size <= 0)
+		return (0);
+
+	/* Path Table size must be encoded according to 7.3.3 */
+	if (!isValid733Integer(h + PVD_path_table_size_offset)) {
+		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 (!isRootDirectoryRecord(p)) {
+		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
+isRootDirectoryRecord(const unsigned char *p) {
+	int flags;
+
+	/* ECMA119/ISO9660 requires that the root directory record be _exactly_ 34 bytes.
+	 * However, we've seen images that have root directory records up to 68 bytes. */
+	if (p[DR_length_offset] < 34 || p[DR_length_offset] > 68) {
+		return (0);
+	}
+
+	/* The root directory location must be a 7.3.3 32-bit integer. */
+	if (!isValid733Integer(p + DR_extent_offset)) {
+		return (0);
+	}
+
+	/* The root directory size must be a 7.3.3 integer. */
+	if (!isValid733Integer(p + DR_size_offset)) {
+		return (0);
+	}
+
+	/* According to the standard, certain bits must be one or zero:
+	 * Bit 1: must be 1 (this is a directory)
+	 * Bit 2: must be 0 (not an associated file)
+	 * Bit 3: must be 0 (doesn't use extended attribute record)
+	 * Bit 7: must be 0 (final directory record for this file)
+	 */
+	flags = p[DR_flags_offset];
+	if ((flags & 0x8E) != 0x02) {
+		return (0);
+	}
+
+	/* Volume sequence number must be a 7.2.3 integer. */
+	if (!isValid723Integer(p + DR_volume_sequence_number_offset)) {
+		return (0);
+	}
+
+	/* Root directory name is a single zero byte... */
+	if (p[DR_name_len_offset] != 1 || p[DR_name_offset] != 0) {
+		return (0);
+	}
+
+	/* Nothing looked wrong, so let's accept it. */
+	return (1);
+}
+
+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 + DR_name_offset < 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 = calloc(1, 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->time_is_set & BIRTHTIME_IS_SET))
+		archive_entry_set_birthtime(entry, file->birthtime, 0);
+	else
+		archive_entry_unset_birthtime(entry);
+	if ((file->time_is_set & MTIME_IS_SET))
+		archive_entry_set_mtime(entry, file->mtime, 0);
+	else
+		archive_entry_unset_mtime(entry);
+	if ((file->time_is_set & CTIME_IS_SET))
+		archive_entry_set_ctime(entry, file->ctime, 0);
+	else
+		archive_entry_unset_ctime(entry);
+	if ((file->time_is_set & ATIME_IS_SET))
+		archive_entry_set_atime(entry, file->atime, 0);
+	else
+		archive_entry_unset_atime(entry);
+	/* 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",
+			    (uintmax_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 = 0;
+	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 = 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;
+	if (isodate7_valid(isodirrec + DR_date_offset)) {
+		file->time_is_set |= MTIME_IS_SET | ATIME_IS_SET | CTIME_IS_SET;
+		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
+						    = (__LA_MODE_T)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. */
+			if (isodate17_valid(data)) {
+				file->time_is_set |= BIRTHTIME_IS_SET;
+				file->birthtime = isodate17(data);
+			}
+			data += 17;
+			data_length -= 17;
+		}
+		if ((flag & 2) && data_length >= 17) {
+			/* Modify time. */
+			if (isodate17_valid(data)) {
+				file->time_is_set |= MTIME_IS_SET;
+				file->mtime = isodate17(data);
+			}
+			data += 17;
+			data_length -= 17;
+		}
+		if ((flag & 4) && data_length >= 17) {
+			/* Access time. */
+			if (isodate17_valid(data)) {
+				file->time_is_set |= ATIME_IS_SET;
+				file->atime = isodate17(data);
+			}
+			data += 17;
+			data_length -= 17;
+		}
+		if ((flag & 8) && data_length >= 17) {
+			/* Attribute change time. */
+			if (isodate17_valid(data)) {
+				file->time_is_set |= CTIME_IS_SET;
+				file->ctime = isodate17(data);
+			}
+		}
+	} else {
+		/* Use 7-byte time format. */
+		if ((flag & 1) && data_length >= 7) {
+			/* Create time. */
+			if (isodate7_valid(data)) {
+				file->time_is_set |= BIRTHTIME_IS_SET;
+				file->birthtime = isodate7(data);
+			}
+			data += 7;
+			data_length -= 7;
+		}
+		if ((flag & 2) && data_length >= 7) {
+			/* Modify time. */
+			if (isodate7_valid(data)) {
+				file->time_is_set |= MTIME_IS_SET;
+				file->mtime = isodate7(data);
+			}
+			data += 7;
+			data_length -= 7;
+		}
+		if ((flag & 4) && data_length >= 7) {
+			/* Access time. */
+			if (isodate7_valid(data)) {
+				file->time_is_set |= ATIME_IS_SET;
+				file->atime = isodate7(data);
+			}
+			data += 7;
+			data_length -= 7;
+		}
+		if ((flag & 8) && data_length >= 7) {
+			/* Attribute change time. */
+			if (isodate7_valid(data)) {
+				file->time_is_set |= CTIME_IS_SET;
+				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;
+
+	/* Reserve 16 bits for possible key collisions (needed for linked items) */
+	/* For ISO files with more than 65535 entries, reordering will still occur */
+	key <<= 16;
+	key += heap->used & 0xFFFF;
+
+	/* 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 **)
+		    calloc(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);
+}
+
+/*
+ * ECMA119/ISO9660 stores multi-byte integers in one of
+ * three different formats:
+ *  * Little-endian (specified in section 7.2.1 and 7.3.1)
+ *  * Big-endian (specified in section 7.2.2 and 7.3.2)
+ *  * Both (specified in section 7.2.3 and 7.3.3)
+ *
+ * For values that follow section 7.2.3 (16-bit) or 7.3.3 (32-bit), we
+ * can check that the little-endian and big-endian forms agree with
+ * each other.  This helps us avoid trying to decode files that are
+ * not really ISO images.
+ */
+static int
+isValid723Integer(const unsigned char *p) {
+	return (p[0] == p[3] && p[1] == p[2]);
+}
+
+static int
+isValid733Integer(const unsigned char *p)
+{
+	return (p[0] == p[7]
+		&& p[1] == p[6]
+		&& p[2] == p[5]
+		&& p[3] == p[4]);
+}
+
+static int
+isodate7_valid(const unsigned char *v)
+{
+	int year = v[0];
+	int month = v[1];
+	int day = v[2];
+	int hour = v[3];
+	int minute = v[4];
+	int second = v[5];
+	int gmt_off = (signed char)v[6];
+
+	/* ECMA-119 9.1.5 "If all seven values are zero, it shall mean
+	 * that the date is unspecified" */
+	if (year == 0
+	    && month == 0
+	    && day == 0
+	    && hour == 0
+	    && minute == 0
+	    && second == 0
+	    && gmt_off == 0)
+		return 0;
+	/*
+	 * Sanity-test each individual field
+	 */
+	/* Year can have any value */
+	/* Month must be 1-12 */
+	if (month < 1 || month > 12)
+		return 0;
+	/* Day must be 1-31 */
+	if (day < 1 || day > 31)
+		return 0;
+	/* Hour must be 0-23 */
+	if (hour > 23)
+		return 0;
+	/* Minute must be 0-59 */
+	if (minute > 59)
+		return 0;
+	/* second must be 0-59 according to ECMA-119 9.1.5 */
+	/* BUT: we should probably allow for the time being in UTC, which
+	   allows up to 61 seconds in a minute in certain cases */
+	if (second > 61)
+		return 0;
+	/* Offset from GMT must be -48 to +52 */
+	if (gmt_off < -48 || gmt_off > +52)
+		return 0;
+
+	/* All tests pass, this is OK */
+	return 1;
+}
+
+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 int
+isodate17_valid(const unsigned char *v)
+{
+	/* First 16 bytes are all ASCII digits */
+	for (int i = 0; i < 16; i++) {
+		if (v[i] < '0' || v[i] > '9')
+			return 0;
+	}
+
+	int year = (v[0] - '0') * 1000 + (v[1] - '0') * 100
+		+ (v[2] - '0') * 10 + (v[3] - '0');
+	int month = (v[4] - '0') * 10 + (v[5] - '0');
+	int day = (v[6] - '0') * 10 + (v[7] - '0');
+	int hour = (v[8] - '0') * 10 + (v[9] - '0');
+	int minute = (v[10] - '0') * 10 + (v[11] - '0');
+	int second = (v[12] - '0') * 10 + (v[13] - '0');
+	int hundredths = (v[14] - '0') * 10 + (v[15] - '0');
+	int gmt_off = (signed char)v[16];
+
+	if (year == 0 && month == 0 && day == 0
+	    && hour == 0 && minute == 0 && second == 0
+	    && hundredths == 0 && gmt_off == 0)
+		return 0;
+	/*
+	 * Sanity-test each individual field
+	 */
+
+	/* Year must be 1900-2300 */
+	/* (Not specified in ECMA-119, but these seem
+	   like reasonable limits. */
+	if (year < 1900 || year > 2300)
+		return 0;
+	/* Month must be 1-12 */
+	if (month < 1 || month > 12)
+		return 0;
+	/* Day must be 1-31 */
+	if (day < 1 || day > 31)
+		return 0;
+	/* Hour must be 0-23 */
+	if (hour > 23)
+		return 0;
+	/* Minute must be 0-59 */
+	if (minute > 59)
+		return 0;
+	/* second must be 0-59 according to ECMA-119 9.1.5 */
+	/* BUT: we should probably allow for the time being in UTC, which
+	   allows up to 61 seconds in a minute in certain cases */
+	if (second > 61)
+		return 0;
+	/* Hundredths must be 0-99 */
+	if (hundredths > 99)
+		return 0;
+	/* Offset from GMT must be -48 to +52 */
+	if (gmt_off < -48 || gmt_off > +52)
+		return 0;
+
+	/* All tests pass, this is OK */
+	return 1;
+
+}
+
+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') - 1;
+	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__MKGMTIME
+        return _mkgmtime(t);
+#elif HAVE_TIMEGM
+        /* Use platform timegm() if available. */
+        return (timegm(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,2879 @@
+/*-
+ * 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_time_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
+	int64_t			 birthtime;
+	uint32_t		 birthtime_tv_nsec;
+	int64_t			 mtime;
+	uint32_t		 mtime_tv_nsec;
+	int64_t			 atime;
+	uint32_t		 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, uint64_t, size_t *);
+static size_t	lha_check_header_format(const void *);
+static int	lha_skip_sfx(struct archive_read *);
+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 = 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;
+
+	snprintf(lha->format_name, sizeof(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 = dos_to_unix(archive_le32dec(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 = dos_to_unix(archive_le32dec(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,
+	    (uint64_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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "LHa header CRC error");
+		return (ARCHIVE_FATAL);
+#endif
+	}
+	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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "LHa header CRC error");
+		return (ARCHIVE_FATAL);
+#endif
+	}
+	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, uint64_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) > 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)) {
+				ntfs_to_unix(archive_le64dec(extdheader),
+					&lha->birthtime,
+				    &lha->birthtime_tv_nsec);
+				extdheader += sizeof(uint64_t);
+				ntfs_to_unix(archive_le64dec(extdheader),
+					&lha->mtime,
+				    &lha->mtime_tv_nsec);
+				extdheader += sizeof(uint64_t);
+				ntfs_to_unix(archive_le64dec(extdheader),
+					&lha->atime,
+				    &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);
+				if (lha->compsize < 0 || lha->origsize < 0)
+					goto invalid;
+			}
+			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 an 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);
+}
+
+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
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+#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__) && __has_builtin(__builtin_bswap16)
+/* Newer 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 |
+				    ((uint64_t)strm->next_in[3]) << 24 |
+				    ((uint64_t)strm->next_in[4]) << 16 |
+				    ((uint64_t)strm->next_in[5]) << 8 |
+				     (uint64_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 |
+				    ((uint64_t)strm->next_in[2]) << 24 |
+				    ((uint64_t)strm->next_in[3]) << 16 |
+				    ((uint64_t)strm->next_in[4]) << 8 |
+				     (uint64_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 tries 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,2170 @@
+/*-
+ * 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"
+
+#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_entry_private.h"
+#include "archive_platform_stat.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);
+#ifndef HAVE_STRNLEN
+static size_t	mtree_strnlen(const char *, size_t);
+#endif
+
+/*
+ * 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
+}
+
+#ifdef HAVE_STRNLEN
+#define mtree_strnlen(a,b) strnlen(a,b)
+#else
+static size_t
+mtree_strnlen(const 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 (i);
+}
+#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 = 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 determined. */
+		len = get_line_size(*b + len, *avail - len, nl);
+		if (len >= 0)
+			len += tested;
+	}
+	return (len);
+}
+
+/*
+ * Compare characters with an mtree keyword.
+ * Returns the length of an 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 keywords.
+ * 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 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 (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) {
+				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 an 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);
+			if (alt != NULL) {
+				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((unsigned char)*s) && *s != '\t') {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+					"Non-printable character 0x%02X", (unsigned char)(*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;
+	la_seek_stat_t 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 < 0 && (
+#if defined(_WIN32) && !defined(__CYGWIN__)
+        /*
+         * On Windows, attempting to open a file with an
+         * invalid name result in EINVAL (Error 22)
+         */
+				(errno != ENOENT && errno != EINVAL)
+#else
+				errno != ENOENT
+#endif
+        || 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 (la_seek_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;
+			}
+		}
+#ifdef HAVE_LSTAT
+		else if (lstat(path, st) == -1)
+#else
+		else if (la_seek_stat(path, st) == -1)
+#endif
+		{
+			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
+}
+
+static int
+parse_hex_nibble(char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return 10 + c - 'a';
+#if 0
+	/* XXX: Is uppercase something we should support? */
+	if (c >= 'A' && c <= 'F')
+		return 10 + c - 'A';
+#endif
+
+	return -1;
+}
+
+static int
+parse_digest(struct archive_read *a, struct archive_entry *entry,
+    const char *digest, int type)
+{
+	unsigned char digest_buf[64];
+	int high, low;
+	size_t i, j, len;
+
+	switch (type) {
+	case ARCHIVE_ENTRY_DIGEST_MD5:
+		len = sizeof(entry->digest.md5);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_RMD160:
+		len = sizeof(entry->digest.rmd160);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA1:
+		len = sizeof(entry->digest.sha1);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA256:
+		len = sizeof(entry->digest.sha256);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA384:
+		len = sizeof(entry->digest.sha384);
+		break;
+	case ARCHIVE_ENTRY_DIGEST_SHA512:
+		len = sizeof(entry->digest.sha512);
+		break;
+	default:
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+			"Internal error: Unknown digest type");
+		return ARCHIVE_FATAL;
+	}
+
+	if (len > sizeof(digest_buf)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+			"Internal error: Digest storage too large");
+		return ARCHIVE_FATAL;
+	}
+
+	len *= 2;
+
+	if (mtree_strnlen(digest, len+1) != len) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				  "incorrect digest length, ignoring");
+		return ARCHIVE_WARN;
+	}
+
+	for (i = 0, j = 0; i < len; i += 2, j++) {
+		high = parse_hex_nibble(digest[i]);
+		low = parse_hex_nibble(digest[i+1]);
+		if (high == -1 || low == -1) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+					  "invalid digest data, ignoring");
+			return ARCHIVE_WARN;
+		}
+
+		digest_buf[j] = high << 4 | low;
+	}
+
+	return archive_entry_set_digest(entry, type, digest_buf);
+}
+
+/*
+ * 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);
+			return (ARCHIVE_OK);
+		}
+		if (strcmp(key, "cksum") == 0)
+			return (ARCHIVE_OK);
+		break;
+	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;
+		}
+		break;
+	case 'f':
+		if (strcmp(key, "flags") == 0) {
+			*parsed_kws |= MTREE_HAS_FFLAGS;
+			archive_entry_copy_fflags_text(entry, val);
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'g':
+		if (strcmp(key, "gid") == 0) {
+			*parsed_kws |= MTREE_HAS_GID;
+			archive_entry_set_gid(entry, mtree_atol(&val, 10));
+			return (ARCHIVE_OK);
+		}
+		if (strcmp(key, "gname") == 0) {
+			*parsed_kws |= MTREE_HAS_GNAME;
+			archive_entry_copy_gname(entry, val);
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'i':
+		if (strcmp(key, "inode") == 0) {
+			archive_entry_set_ino(entry, mtree_atol(&val, 10));
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'l':
+		if (strcmp(key, "link") == 0) {
+			parse_escapes(val, NULL);
+			archive_entry_copy_symlink(entry, val);
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'm':
+		if (strcmp(key, "md5") == 0 || strcmp(key, "md5digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_MD5);
+		}
+		if (strcmp(key, "mode") == 0) {
+			if (val[0] < '0' || val[0] > '7') {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Symbolic or non-octal mode \"%s\" unsupported", val);
+				return (ARCHIVE_WARN);
+			}
+			*parsed_kws |= MTREE_HAS_PERM;
+			archive_entry_set_perm(entry, (mode_t)mtree_atol(&val, 8));
+			return (ARCHIVE_OK);
+		}
+		break;
+	case 'n':
+		if (strcmp(key, "nlink") == 0) {
+			*parsed_kws |= MTREE_HAS_NLINK;
+			archive_entry_set_nlink(entry,
+				(unsigned int)mtree_atol(&val, 10));
+			return (ARCHIVE_OK);
+		}
+		break;
+	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) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_RMD160);
+		}
+		break;
+	case 's':
+		if (strcmp(key, "sha1") == 0 ||
+		    strcmp(key, "sha1digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_SHA1);
+		}
+		if (strcmp(key, "sha256") == 0 ||
+		    strcmp(key, "sha256digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_SHA256);
+		}
+		if (strcmp(key, "sha384") == 0 ||
+		    strcmp(key, "sha384digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_SHA384);
+		}
+		if (strcmp(key, "sha512") == 0 ||
+		    strcmp(key, "sha512digest") == 0) {
+			return parse_digest(a, entry, val,
+			    ARCHIVE_ENTRY_DIGEST_SHA512);
+		}
+		if (strcmp(key, "size") == 0) {
+			archive_entry_set_size(entry, mtree_atol(&val, 10));
+			return (ARCHIVE_OK);
+		}
+		break;
+	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.
+			 */
+			return (ARCHIVE_OK);
+		}
+		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);
+			return (ARCHIVE_OK);
+		}
+		if (strcmp(key, "type") == 0) {
+			switch (val[0]) {
+			case 'b':
+				if (strcmp(val, "block") == 0) {
+					*parsed_kws |= MTREE_HAS_TYPE;
+					archive_entry_set_filetype(entry,
+						AE_IFBLK);
+					return (ARCHIVE_OK);
+				}
+				break;
+			case 'c':
+				if (strcmp(val, "char") == 0) {
+					*parsed_kws |= MTREE_HAS_TYPE;
+					archive_entry_set_filetype(entry,
+						AE_IFCHR);
+					return (ARCHIVE_OK);
+				}
+				break;
+			case 'd':
+				if (strcmp(val, "dir") == 0) {
+					*parsed_kws |= MTREE_HAS_TYPE;
+					archive_entry_set_filetype(entry,
+						AE_IFDIR);
+					return (ARCHIVE_OK);
+				}
+				break;
+			case 'f':
+				if (strcmp(val, "fifo") == 0) {
+					*parsed_kws |= MTREE_HAS_TYPE;
+					archive_entry_set_filetype(entry,
+						AE_IFIFO);
+					return (ARCHIVE_OK);
+				}
+				if (strcmp(val, "file") == 0) {
+					*parsed_kws |= MTREE_HAS_TYPE;
+					archive_entry_set_filetype(entry,
+						AE_IFREG);
+					return (ARCHIVE_OK);
+				}
+				break;
+			case 'l':
+				if (strcmp(val, "link") == 0) {
+					*parsed_kws |= MTREE_HAS_TYPE;
+					archive_entry_set_filetype(entry,
+						AE_IFLNK);
+					return (ARCHIVE_OK);
+				}
+				break;
+			default:
+				break;
+			}
+			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);
+		}
+		break;
+	case 'u':
+		if (strcmp(key, "uid") == 0) {
+			*parsed_kws |= MTREE_HAS_UID;
+			archive_entry_set_uid(entry, mtree_atol(&val, 10));
+			return (ARCHIVE_OK);
+		}
+		if (strcmp(key, "uname") == 0) {
+			*parsed_kws |= MTREE_HAS_UNAME;
+			archive_entry_copy_uname(entry, val);
+			return (ARCHIVE_OK);
+		}
+		break;
+	default:
+		break;
+	}
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+	    "Unrecognized key %s=%s", key, val);
+	return (ARCHIVE_WARN);
+}
+
+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. */
+				/* Check if preceded by '\r' for CRLF handling */
+				if (u > mtree->line.s && u[-1] == '\r') {
+					/* CRLF ending - remove the '\r' */
+					u[-1] = '\n';
+					u[0] = '\0';
+					total_size--;
+				}
+				*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] == '\r' && u[2] == '\n') {
+					/* Trim escaped CRLF. */
+					total_size -= 3;
+					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,3918 @@
+/*-
+* 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
+
+/* Virtual Machine Properties */
+#define VM_MEMORY_SIZE 0x40000
+#define VM_MEMORY_MASK (VM_MEMORY_SIZE - 1)
+#define PROGRAM_WORK_SIZE 0x3C000
+#define PROGRAM_GLOBAL_SIZE 0x2000
+#define PROGRAM_SYSTEM_GLOBAL_ADDRESS PROGRAM_WORK_SIZE
+#define PROGRAM_SYSTEM_GLOBAL_SIZE 0x40
+#define PROGRAM_USER_GLOBAL_ADDRESS (PROGRAM_SYSTEM_GLOBAL_ADDRESS + PROGRAM_SYSTEM_GLOBAL_SIZE)
+#define PROGRAM_USER_GLOBAL_SIZE (PROGRAM_GLOBAL_SIZE - PROGRAM_SYSTEM_GLOBAL_SIZE)
+
+/*
+ * 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))
+
+/* Stack overflow check */
+#define MAX_COMPRESS_DEPTH 1024
+
+/* 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_program_code
+{
+  uint8_t *staticdata;
+  uint32_t staticdatalen;
+  uint8_t *globalbackup;
+  uint32_t globalbackuplen;
+  uint64_t fingerprint;
+  uint32_t usagecount;
+  uint32_t oldfilterlength;
+  struct rar_program_code *next;
+};
+
+struct rar_filter
+{
+  struct rar_program_code *prog;
+  uint32_t initialregisters[8];
+  uint8_t *globaldata;
+  uint32_t globaldatalen;
+  size_t blockstartpos;
+  uint32_t blocklength;
+  uint32_t filteredblockaddress;
+  uint32_t filteredblocklength;
+  struct rar_filter *next;
+};
+
+struct memory_bit_reader
+{
+  const uint8_t *bytes;
+  size_t length;
+  size_t offset;
+  uint64_t bits;
+  int available;
+  int at_eof;
+};
+
+struct rar_virtual_machine
+{
+  uint32_t registers[8];
+  uint8_t memory[VM_MEMORY_SIZE + sizeof(uint32_t)];
+};
+
+struct rar_filters
+{
+  struct rar_virtual_machine *vm;
+  struct rar_program_code *progs;
+  struct rar_filter *stack;
+  int64_t filterstart;
+  uint32_t lastfilternum;
+  int64_t lastend;
+  uint8_t *bytes;
+  size_t bytes_ready;
+};
+
+struct audio_state
+{
+  int8_t weight[5];
+  int16_t delta[4];
+  int8_t lastdelta;
+  int error[11];
+  int count;
+  uint8_t lastbyte;
+};
+
+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;
+  size_t cursor;
+  size_t 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;
+  unsigned int lastlength;
+  unsigned int lastoffset;
+  unsigned int oldoffset[4];
+  unsigned int lastlowoffset;
+  unsigned int numlowoffsetrepeats;
+  char start_new_table;
+
+  /* Filters */
+  struct rar_filters filters;
+
+  /* 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 *, size_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 int expand(struct archive_read *, int64_t *);
+static int copy_from_lzss_window_to_unp(struct archive_read *, const void **,
+                                        int64_t, size_t);
+static const void *rar_read_ahead(struct archive_read *, size_t, ssize_t *);
+static int parse_filter(struct archive_read *, const uint8_t *, uint16_t,
+                        uint8_t);
+static int run_filters(struct archive_read *);
+static void clear_filters(struct rar_filters *);
+static struct rar_filter *create_filter(struct rar_program_code *,
+                                        const uint8_t *, uint32_t,
+                                        uint32_t[8], size_t, uint32_t);
+static void delete_filter(struct rar_filter *filter);
+static struct rar_program_code *compile_program(const uint8_t *, size_t);
+static void delete_program_code(struct rar_program_code *prog);
+static uint32_t membr_next_rarvm_number(struct memory_bit_reader *br);
+static inline uint32_t membr_bits(struct memory_bit_reader *br, int bits);
+static int membr_fill(struct memory_bit_reader *br, int bits);
+static int read_filter(struct archive_read *, int64_t *);
+static int rar_decode_byte(struct archive_read*, uint8_t *);
+static int execute_filter(struct archive_read*, struct rar_filter *,
+                          struct rar_virtual_machine *, size_t);
+static int copy_from_lzss_window(struct archive_read *, uint8_t *, int64_t, int);
+static inline void vm_write_32(struct rar_virtual_machine*, size_t, uint32_t);
+static inline uint32_t vm_read_32(struct rar_virtual_machine*, size_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_unaligned_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 = calloc(1, sizeof(*rar));
+  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;
+  int64_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) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Failed to read next header.");
+      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 ((size_t)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 ((size_t)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)) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+          "Header CRC error");
+        return (ARCHIVE_FATAL);
+#endif
+      }
+      __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) {
+        unsigned to_read;
+        if (skip > 32 * 1024)
+          to_read = 32 * 1024;
+        else
+          to_read = (unsigned)skip;
+        if ((h = __archive_read_ahead(a, to_read, NULL)) == NULL) {
+          archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+            "Bad RAR file");
+          return (ARCHIVE_FATAL);
+        }
+        p = h;
+        crc32_val = crc32(crc32_val, (const unsigned char *)p, to_read);
+        __archive_read_consume(a, to_read);
+        skip -= to_read;
+      }
+      if ((crc32_val & 0xffff) != crc32_expected) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+          "Header CRC error");
+        return (ARCHIVE_FATAL);
+#endif
+      }
+      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;
+    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, 0);
+    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;
+  size_t 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);
+  clear_filters(&rar->filters);
+  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;
+  uint32_t crc32_computed, crc32_read;
+  int ret = (ARCHIVE_OK), ret2;
+  char *newptr;
+  size_t newsize;
+
+  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_computed = 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)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Failed to read full header content.");
+    return (ARCHIVE_FATAL);
+  }
+
+  /* File Header CRC check. */
+  crc32_computed = crc32(crc32_computed, h, (unsigned)(header_size - 7));
+  crc32_read = archive_le16dec(rar_header.crc);
+  if ((crc32_computed & 0xffff) != crc32_read) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+      "Header CRC error");
+    return (ARCHIVE_FATAL);
+#endif
+  }
+  /* 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)
+  {
+    if (p + 8 > endp) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Invalid header size");
+      return (ARCHIVE_FATAL);
+    }
+    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;
+    if (rar->packed_size > INT64_MAX - header_size) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Extended header size too large.");
+      return (ARCHIVE_FATAL);
+    }
+    header_size += rar->packed_size;
+    if ((uintmax_t)header_size > SIZE_MAX) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Unable to read extended header data.");
+      return (ARCHIVE_FATAL);
+    }
+    /* Make sure we have the extended data. */
+    if ((h = __archive_read_ahead(a, (size_t)header_size - 7, NULL)) == NULL) {
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "Failed to read extended header data.");
+      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) {
+    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 = offset >= end ? 0 : *(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:
+            if (offset >= end)
+              continue;
+            filename[filename_size++] = '\0';
+            filename[filename_size++] = *(p + offset++);
+            break;
+          case 1:
+            if (offset >= end)
+              continue;
+            filename[filename_size++] = highbyte;
+            filename[filename_size++] = *(p + offset++);
+            break;
+          case 2:
+            if (offset >= end - 1) {
+              offset = end;
+              continue;
+            }
+            filename[filename_size++] = *(p + offset + 1);
+            filename[filename_size++] = *(p + offset);
+            offset += 2;
+            break;
+          case 3:
+          {
+            char extra, high;
+            uint8_t length;
+
+            if (offset >= end)
+              continue;
+
+            length = *(p + offset++);
+            if (length & 0x80) {
+              if (offset >= end)
+                continue;
+              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->br.avail_in = 0;
+    rar->br.next_in = NULL;
+    rar->cursor++;
+    if (rar->cursor >= rar->nodes)
+    {
+      struct data_block_offsets *newdbo;
+
+      newsize = sizeof(*rar->dbo) * (rar->nodes + 1);
+      if ((newdbo = realloc(rar->dbo, newsize)) == NULL)
+      {
+        archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+        return (ARCHIVE_FATAL);
+      }
+      rar->dbo = newdbo;
+      rar->nodes++;
+      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)
+    {
+      if (rar->packed_size > INT64_MAX - a->filter->position)
+      {
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "Unable to store offsets.");
+        return (ARCHIVE_FATAL);
+      }
+      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);
+  }
+
+  newsize = filename_size + 1;
+  if ((newptr = realloc(rar->filename_save, newsize)) == NULL)
+  {
+    archive_set_error(&a->archive, ENOMEM, "Couldn't allocate memory.");
+    return (ARCHIVE_FATAL);
+  }
+  rar->filename_save = newptr;
+  memcpy(rar->filename_save, rar->filename, newsize);
+  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);
+  if (rar->packed_size > INT64_MAX - a->filter->position) {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Unable to store offsets.");
+    return (ARCHIVE_FATAL);
+  }
+  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 = (__LA_MODE_T)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 = (__LA_MODE_T)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->br.next_in = NULL;
+  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;
+  rar->filters.filterstart = INT64_MAX;
+
+  /* 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_LOCALTIME_S)
+  struct tm tmbuf;
+#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_S)
+      tm = localtime_s(&tmbuf, &t) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+      tm = localtime_r(&t, &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 ((uintmax_t)rar->packed_size > SIZE_MAX)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Unable to read link.");
+    return (ARCHIVE_FATAL);
+  }
+  if ((h = rar_read_ahead(a, (size_t)rar->packed_size, NULL)) == NULL)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                      "Failed to read link.");
+    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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+      archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                        "File CRC error");
+      return (ARCHIVE_FATAL);
+#endif
+    }
+    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, size_t looper)
+{
+  if (looper++ > MAX_COMPRESS_DEPTH)
+    return (ARCHIVE_FATAL);
+
+  struct rar *rar;
+  int64_t start, end;
+  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->filters.bytes_ready > 0)
+    {
+      /* Flush unp_buffer first */
+      if (rar->unp_offset > 0)
+      {
+        *buff = rar->unp_buffer;
+        *size = rar->unp_offset;
+        rar->unp_offset = 0;
+        *offset = rar->offset_outgoing;
+        rar->offset_outgoing += *size;
+      }
+      else
+      {
+        *buff = rar->filters.bytes;
+        *size = rar->filters.bytes_ready;
+
+        rar->offset += *size;
+        *offset = rar->offset_outgoing;
+        rar->offset_outgoing += *size;
+
+        rar->filters.bytes_ready -= *size;
+        rar->filters.bytes += *size;
+      }
+      goto ending_block;
+    }
+
+    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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+        archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                          "File CRC error");
+        return (ARCHIVE_FATAL);
+#endif
+      }
+      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_to_unp(a, buff, rar->offset, 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->filters.lastend == rar->filters.filterstart)
+    {
+      if (!run_filters(a))
+        return (ARCHIVE_FATAL);
+      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, looper);
+
+          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;
+
+      /* We don't want to overflow the window and overwrite data that we write
+       * at 'start'. Therefore, reduce the end length by the maximum match size,
+       * which is 260 bytes. You can compute this maximum by looking at the
+       * definition of 'expand', in particular when 'symbol >= 271'. */
+      /* NOTE: It's possible for 'dictionary_size' to be less than this 260
+       * value, however that will only be the case when 'unp_size' is small,
+       * which should only happen when the entry size is small and there's no
+       * risk of overflowing the buffer */
+      if (rar->dictionary_size > 260) {
+        end -= 260;
+      }
+
+      if (rar->filters.filterstart < end) {
+        end = rar->filters.filterstart;
+      }
+
+      ret = expand(a, &end);
+      if (ret != ARCHIVE_OK)
+        return (ret);
+
+      rar->bytes_uncopied = end - start;
+      rar->filters.lastend = end;
+      if (rar->filters.lastend != rar->filters.filterstart && 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_to_unp(a, buff, rar->offset, 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;
+ending_block:
+  /* 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_unaligned_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_context is freed before Ppmd7_Construct
+       * because reading a broken file causes 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 = calloc(((size_t)1U) << code->tablesize, sizeof(*code->table));
+
+  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 int
+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->filters.filterstart < *end)
+    *end = rar->filters.filterstart;
+
+  while (1)
+  {
+    if(lzss_position(&rar->lzss) >= *end) {
+      return (ARCHIVE_OK);
+    }
+
+    if(rar->is_ppmd_block) {
+      *end = lzss_position(&rar->lzss);
+      return (ARCHIVE_OK);
+    }
+
+    if ((symbol = read_next_symbol(a, &rar->maincode)) < 0)
+      goto bad_data;
+
+    if (symbol < 256)
+    {
+      lzss_emit_literal(rar, (uint8_t)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);
+        *end = lzss_position(&rar->lzss);
+        return (ARCHIVE_OK);
+      }
+      else
+      {
+        if (parse_codes(a) != ARCHIVE_OK)
+          goto bad_data;
+        continue;
+      }
+    }
+    else if(symbol==257)
+    {
+      if (!read_filter(a, end))
+          goto bad_data;
+      continue;
+    }
+    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)
+              goto bad_data;
+            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;
+
+    lzss_emit_match(rar, rar->lastoffset, rar->lastlength);
+  }
+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, uint8_t *buffer,
+                      int64_t startpos, int length)
+{
+  int windowoffs, firstpart;
+  struct rar *rar = (struct rar *)(a->format->data);
+
+  windowoffs = lzss_offset_for_position(&rar->lzss, startpos);
+  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(buffer, &rar->lzss.window[windowoffs], firstpart);
+    memcpy(buffer + firstpart, &rar->lzss.window[0], length - firstpart);
+  } else {
+    memcpy(buffer, &rar->lzss.window[windowoffs], length);
+  }
+  return (ARCHIVE_OK);
+}
+
+static int
+copy_from_lzss_window_to_unp(struct archive_read *a, const void **buffer,
+                             int64_t startpos, size_t length)
+{
+  int windowoffs, firstpart;
+  struct rar *rar = (struct rar *)(a->format->data);
+
+  if (length > rar->unp_buffer_size)
+  {
+    goto fatal;
+  }
+
+  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 <= (size_t)lzss_size(&rar->lzss)) {
+    memcpy(&rar->unp_buffer[rar->unp_offset], &rar->lzss.window[windowoffs],
+           length);
+  } else if (length <= (size_t)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 ((size_t)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 {
+      goto fatal;
+  }
+  rar->unp_offset += (unsigned int) length;
+  if (rar->unp_offset >= rar->unp_buffer_size)
+    *buffer = rar->unp_buffer;
+  else
+    *buffer = NULL;
+  return (ARCHIVE_OK);
+
+fatal:
+  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                    "Bad RAR file data");
+  return (ARCHIVE_FATAL);
+}
+
+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;
+  int ret;
+
+again:
+  h = __archive_read_ahead(a, min, avail);
+
+  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;
+      goto again;
+    }
+  }
+  return h;
+}
+
+static int
+parse_filter(struct archive_read *a, const uint8_t *bytes, uint16_t length, uint8_t flags)
+{
+  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar_filters *filters = &rar->filters;
+
+  struct memory_bit_reader br = { 0 };
+  struct rar_program_code *prog;
+  struct rar_filter *filter, **nextfilter;
+
+  uint32_t numprogs, num, blocklength, globaldatalen;
+  uint8_t *globaldata;
+  size_t blockstartpos;
+  uint32_t registers[8] = { 0 };
+  uint32_t i;
+
+  br.bytes = bytes;
+  br.length = length;
+
+  numprogs = 0;
+  for (prog = filters->progs; prog; prog = prog->next)
+    numprogs++;
+
+  if ((flags & 0x80))
+  {
+    num = membr_next_rarvm_number(&br);
+    if (num == 0)
+    {
+      delete_filter(filters->stack);
+      filters->stack = NULL;
+      delete_program_code(filters->progs);
+      filters->progs = NULL;
+    }
+    else
+      num--;
+    if (num > numprogs) {
+      return 0;
+    }
+    filters->lastfilternum = num;
+  }
+  else
+    num = filters->lastfilternum;
+
+  prog = filters->progs;
+  for (i = 0; i < num; i++)
+    prog = prog->next;
+  if (prog)
+    prog->usagecount++;
+
+  blockstartpos = membr_next_rarvm_number(&br) + (size_t)lzss_position(&rar->lzss);
+  if ((flags & 0x40))
+    blockstartpos += 258;
+  if ((flags & 0x20))
+    blocklength = membr_next_rarvm_number(&br);
+  else
+    blocklength = prog ? prog->oldfilterlength : 0;
+
+  if (blocklength > rar->dictionary_size)
+    return 0;
+
+  registers[3] = PROGRAM_SYSTEM_GLOBAL_ADDRESS;
+  registers[4] = blocklength;
+  registers[5] = prog ? prog->usagecount : 0;
+  registers[7] = VM_MEMORY_SIZE;
+
+  if ((flags & 0x10))
+  {
+    uint8_t mask = (uint8_t)membr_bits(&br, 7);
+    for (i = 0; i < 7; i++)
+      if ((mask & (1 << i)))
+        registers[i] = membr_next_rarvm_number(&br);
+  }
+
+  if (!prog)
+  {
+    uint32_t len = membr_next_rarvm_number(&br);
+    uint8_t *bytecode;
+    struct rar_program_code **next;
+
+    if (len == 0 || len > 0x10000)
+      return 0;
+    bytecode = malloc(len);
+    if (!bytecode)
+      return 0;
+    for (i = 0; i < len; i++)
+      bytecode[i] = (uint8_t)membr_bits(&br, 8);
+    prog = compile_program(bytecode, len);
+    if (!prog) {
+      free(bytecode);
+      return 0;
+    }
+    free(bytecode);
+    next = &filters->progs;
+    while (*next)
+      next = &(*next)->next;
+    *next = prog;
+  }
+  prog->oldfilterlength = blocklength;
+
+  globaldata = NULL;
+  globaldatalen = 0;
+  if ((flags & 0x08))
+  {
+    globaldatalen = membr_next_rarvm_number(&br);
+    if (globaldatalen > PROGRAM_USER_GLOBAL_SIZE)
+      return 0;
+    globaldata = malloc(globaldatalen + PROGRAM_SYSTEM_GLOBAL_SIZE);
+    if (!globaldata)
+      return 0;
+    for (i = 0; i < globaldatalen; i++)
+      globaldata[i + PROGRAM_SYSTEM_GLOBAL_SIZE] = (uint8_t)membr_bits(&br, 8);
+  }
+
+  if (br.at_eof)
+  {
+      free(globaldata);
+      return 0;
+  }
+
+  filter = create_filter(prog, globaldata, globaldatalen, registers, blockstartpos, blocklength);
+  free(globaldata);
+  if (!filter)
+    return 0;
+
+  for (i = 0; i < 7; i++)
+    archive_le32enc(&filter->globaldata[i * 4], registers[i]);
+  archive_le32enc(&filter->globaldata[0x1C], blocklength);
+  archive_le32enc(&filter->globaldata[0x20], 0);
+  archive_le32enc(&filter->globaldata[0x2C], prog->usagecount);
+
+  nextfilter = &filters->stack;
+  while (*nextfilter)
+    nextfilter = &(*nextfilter)->next;
+  *nextfilter = filter;
+
+  if (!filters->stack->next)
+    filters->filterstart = blockstartpos;
+
+  return 1;
+}
+
+static struct rar_filter *
+create_filter(struct rar_program_code *prog, const uint8_t *globaldata, uint32_t globaldatalen, uint32_t registers[8], size_t startpos, uint32_t length)
+{
+  struct rar_filter *filter;
+
+  filter = calloc(1, sizeof(*filter));
+  if (!filter)
+    return NULL;
+  filter->prog = prog;
+  filter->globaldatalen = globaldatalen > PROGRAM_SYSTEM_GLOBAL_SIZE ? globaldatalen : PROGRAM_SYSTEM_GLOBAL_SIZE;
+  filter->globaldata = calloc(1, filter->globaldatalen);
+  if (!filter->globaldata)
+  {
+    free(filter);
+    return NULL;
+  }
+  if (globaldata)
+    memcpy(filter->globaldata, globaldata, globaldatalen);
+  if (registers)
+    memcpy(filter->initialregisters, registers, sizeof(filter->initialregisters));
+  filter->blockstartpos = startpos;
+  filter->blocklength = length;
+
+  return filter;
+}
+
+static int
+run_filters(struct archive_read *a)
+{
+  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar_filters *filters = &rar->filters;
+  struct rar_filter *filter = filters->stack;
+  struct rar_filter *f;
+  size_t start, end;
+  int64_t tend;
+  uint32_t lastfilteraddress;
+  uint32_t lastfilterlength;
+  int ret;
+
+  if (filters == NULL || filter == NULL)
+    return (0);
+
+  start = (size_t)filters->filterstart;
+  end = start + filter->blocklength;
+
+  filters->filterstart = INT64_MAX;
+  tend = (int64_t)end;
+  ret = expand(a, &tend);
+  if (ret != ARCHIVE_OK)
+    return 0;
+
+  /* Check if filter stack was modified in expand() */
+  ret = ARCHIVE_FATAL;
+  f = filters->stack;
+  while (f)
+  {
+    if (f == filter)
+    {
+      ret = ARCHIVE_OK;
+      break;
+    }
+    f = f->next;
+  }
+  if (ret != ARCHIVE_OK)
+    return 0;
+
+  if (tend < 0)
+    return 0;
+  end = (size_t)tend;
+  if (end != start + filter->blocklength)
+    return 0;
+
+  if (!filters->vm)
+  {
+    filters->vm = calloc(1, sizeof(*filters->vm));
+    if (!filters->vm)
+      return 0;
+  }
+
+  if (filter->blocklength > VM_MEMORY_SIZE)
+  {
+    archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Bad RAR file data");
+    return 0;
+  }
+
+  ret = copy_from_lzss_window(a, filters->vm->memory, start, filter->blocklength);
+  if (ret != ARCHIVE_OK)
+    return 0;
+  if (!execute_filter(a, filter, filters->vm, (size_t)rar->offset))
+    return 0;
+
+  lastfilteraddress = filter->filteredblockaddress;
+  lastfilterlength = filter->filteredblocklength;
+  filters->stack = filter->next;
+  filter->next = NULL;
+  delete_filter(filter);
+
+  while ((filter = filters->stack) != NULL && (int64_t)filter->blockstartpos == filters->filterstart && filter->blocklength == lastfilterlength)
+  {
+    memmove(&filters->vm->memory[0], &filters->vm->memory[lastfilteraddress], lastfilterlength);
+    if (!execute_filter(a, filter, filters->vm, (size_t)rar->offset))
+      return 0;
+
+    lastfilteraddress = filter->filteredblockaddress;
+    lastfilterlength = filter->filteredblocklength;
+    filters->stack = filter->next;
+    filter->next = NULL;
+    delete_filter(filter);
+  }
+
+  if (filters->stack)
+  {
+    if (filters->stack->blockstartpos < end)
+      return 0;
+    filters->filterstart = filters->stack->blockstartpos;
+  }
+
+  filters->lastend = end;
+  filters->bytes = &filters->vm->memory[lastfilteraddress];
+  filters->bytes_ready = lastfilterlength;
+
+  return 1;
+}
+
+static struct rar_program_code *
+compile_program(const uint8_t *bytes, size_t length)
+{
+  struct memory_bit_reader br = { 0 };
+  struct rar_program_code *prog;
+  // uint32_t instrcount = 0;
+  uint8_t xor;
+  size_t i;
+
+  xor = 0;
+  for (i = 1; i < length; i++)
+    xor ^= bytes[i];
+  if (!length || xor != bytes[0])
+    return NULL;
+
+  br.bytes = bytes;
+  br.length = length;
+  br.offset = 1;
+
+  prog = calloc(1, sizeof(*prog));
+  if (!prog)
+    return NULL;
+  prog->fingerprint = crc32(0, bytes, (unsigned int)length) | ((uint64_t)length << 32);
+
+  if (membr_bits(&br, 1))
+  {
+    prog->staticdatalen = membr_next_rarvm_number(&br) + 1;
+    prog->staticdata = malloc(prog->staticdatalen);
+    if (!prog->staticdata)
+    {
+      delete_program_code(prog);
+      return NULL;
+    }
+    for (i = 0; i < prog->staticdatalen; i++)
+      prog->staticdata[i] = (uint8_t)membr_bits(&br, 8);
+  }
+
+  return prog;
+}
+
+static void
+delete_filter(struct rar_filter *filter)
+{
+  while (filter)
+  {
+    struct rar_filter *next = filter->next;
+    free(filter->globaldata);
+    free(filter);
+    filter = next;
+  }
+}
+
+static void
+clear_filters(struct rar_filters *filters)
+{
+  delete_filter(filters->stack);
+  delete_program_code(filters->progs);
+  free(filters->vm);
+}
+
+static void
+delete_program_code(struct rar_program_code *prog)
+{
+  while (prog)
+  {
+    struct rar_program_code *next = prog->next;
+    free(prog->staticdata);
+    free(prog->globalbackup);
+    free(prog);
+    prog = next;
+  }
+}
+
+static uint32_t
+membr_next_rarvm_number(struct memory_bit_reader *br)
+{
+  uint32_t val;
+  switch (membr_bits(br, 2))
+  {
+    case 0:
+      return membr_bits(br, 4);
+    case 1:
+      val = membr_bits(br, 8);
+      if (val >= 16)
+        return val;
+      return 0xFFFFFF00 | (val << 4) | membr_bits(br, 4);
+    case 2:
+      return membr_bits(br, 16);
+    default:
+      return membr_bits(br, 32);
+  }
+}
+
+static inline uint32_t
+membr_bits(struct memory_bit_reader *br, int bits)
+{
+  if (bits > br->available && (br->at_eof || !membr_fill(br, bits)))
+    return 0;
+  return (uint32_t)((br->bits >> (br->available -= bits)) & (((uint64_t)1 << bits) - 1));
+}
+
+static int
+membr_fill(struct memory_bit_reader *br, int bits)
+{
+  while (br->available < bits && br->offset < br->length)
+  {
+    br->bits = (br->bits << 8) | br->bytes[br->offset++];
+    br->available += 8;
+  }
+  if (bits > br->available)
+  {
+    br->at_eof = 1;
+    return 0;
+  }
+  return 1;
+}
+
+static int
+read_filter(struct archive_read *a, int64_t *end)
+{
+  struct rar *rar = (struct rar *)(a->format->data);
+  uint8_t flags, val, *code;
+  uint16_t length, i;
+
+  if (!rar_decode_byte(a, &flags))
+    return 0;
+  length = (flags & 0x07) + 1;
+  if (length == 7)
+  {
+    if (!rar_decode_byte(a, &val))
+      return 0;
+    length = val + 7;
+  }
+  else if (length == 8)
+  {
+    if (!rar_decode_byte(a, &val))
+      return 0;
+    length = val << 8;
+    if (!rar_decode_byte(a, &val))
+      return 0;
+    length |= val;
+  }
+
+  code = malloc(length);
+  if (!code)
+    return 0;
+  for (i = 0; i < length; i++)
+  {
+    if (!rar_decode_byte(a, &code[i]))
+    {
+      free(code);
+      return 0;
+    }
+  }
+  if (!parse_filter(a, code, length, flags))
+  {
+    free(code);
+    return 0;
+  }
+  free(code);
+
+  if (rar->filters.filterstart < *end)
+    *end = rar->filters.filterstart;
+
+  return 1;
+}
+
+static int
+execute_filter_delta(struct rar_filter *filter, struct rar_virtual_machine *vm)
+{
+  uint32_t length = filter->initialregisters[4];
+  uint32_t numchannels = filter->initialregisters[0];
+  uint8_t *src, *dst;
+  uint32_t i, idx;
+
+  if (length > PROGRAM_WORK_SIZE / 2)
+    return 0;
+
+  src = &vm->memory[0];
+  dst = &vm->memory[length];
+  for (i = 0; i < numchannels; i++)
+  {
+    uint8_t lastbyte = 0;
+    for (idx = i; idx < length; idx += numchannels)
+    {
+      /*
+       * The src block should not overlap with the dst block.
+       * If so it would be better to consider this archive is broken.
+       */
+      if (src >= dst)
+        return 0;
+      lastbyte = dst[idx] = lastbyte - *src++;
+    }
+  }
+
+  filter->filteredblockaddress = length;
+  filter->filteredblocklength = length;
+
+  return 1;
+}
+
+static int
+execute_filter_e8(struct rar_filter *filter, struct rar_virtual_machine *vm, size_t pos, int e9also)
+{
+  uint32_t length = filter->initialregisters[4];
+  uint32_t filesize = 0x1000000;
+  uint32_t i;
+
+  if (length > PROGRAM_WORK_SIZE || length <= 4)
+    return 0;
+
+  for (i = 0; i <= length - 5; i++)
+  {
+    if (vm->memory[i] == 0xE8 || (e9also && vm->memory[i] == 0xE9))
+    {
+      uint32_t currpos = (uint32_t)pos + i + 1;
+      int32_t address = (int32_t)vm_read_32(vm, i + 1);
+      if (address < 0 && currpos >= (~(uint32_t)address + 1))
+        vm_write_32(vm, i + 1, address + filesize);
+      else if (address >= 0 && (uint32_t)address < filesize)
+        vm_write_32(vm, i + 1, address - currpos);
+      i += 4;
+    }
+  }
+
+  filter->filteredblockaddress = 0;
+  filter->filteredblocklength = length;
+
+  return 1;
+}
+
+static int
+execute_filter_rgb(struct rar_filter *filter, struct rar_virtual_machine *vm)
+{
+  uint32_t stride = filter->initialregisters[0];
+  uint32_t byteoffset = filter->initialregisters[1];
+  uint32_t blocklength = filter->initialregisters[4];
+  uint8_t *src, *dst;
+  uint32_t i, j;
+
+  if (blocklength > PROGRAM_WORK_SIZE / 2 || stride > blocklength || blocklength < 3 || byteoffset > 2)
+    return 0;
+
+  src = &vm->memory[0];
+  dst = &vm->memory[blocklength];
+  for (i = 0; i < 3; i++) {
+    uint8_t byte = 0;
+    uint8_t *prev = dst + i - stride;
+    for (j = i; j < blocklength; j += 3)
+    {
+      /*
+       * The src block should not overlap with the dst block.
+       * If so it would be better to consider this archive is broken.
+       */
+      if (src >= dst)
+        return 0;
+
+      if (prev >= dst)
+      {
+        uint32_t delta1 = abs(prev[3] - prev[0]);
+        uint32_t delta2 = abs(byte - prev[0]);
+        uint32_t delta3 = abs(prev[3] - prev[0] + byte - prev[0]);
+        if (delta1 > delta2 || delta1 > delta3)
+          byte = delta2 <= delta3 ? prev[3] : prev[0];
+      }
+      byte -= *src++;
+      dst[j] = byte;
+      prev += 3;
+    }
+  }
+  for (i = byteoffset; i < blocklength - 2; i += 3)
+  {
+    dst[i] += dst[i + 1];
+    dst[i + 2] += dst[i + 1];
+  }
+
+  filter->filteredblockaddress = blocklength;
+  filter->filteredblocklength = blocklength;
+
+  return 1;
+}
+
+static int
+execute_filter_audio(struct rar_filter *filter, struct rar_virtual_machine *vm)
+{
+  uint32_t length = filter->initialregisters[4];
+  uint32_t numchannels = filter->initialregisters[0];
+  uint8_t *src, *dst;
+  uint32_t i, j;
+
+  if (length > PROGRAM_WORK_SIZE / 2)
+    return 0;
+
+  src = &vm->memory[0];
+  dst = &vm->memory[length];
+  for (i = 0; i < numchannels; i++)
+  {
+    struct audio_state state;
+    memset(&state, 0, sizeof(state));
+    for (j = i; j < length; j += numchannels)
+    {
+      /*
+       * The src block should not overlap with the dst block.
+       * If so it would be better to consider this archive is broken.
+       */
+      if (src >= dst)
+        return 0;
+
+      int8_t delta = (int8_t)*src++;
+      uint8_t predbyte, byte;
+      int prederror;
+      state.delta[2] = state.delta[1];
+      state.delta[1] = state.lastdelta - state.delta[0];
+      state.delta[0] = state.lastdelta;
+      predbyte = ((8 * state.lastbyte + state.weight[0] * state.delta[0] + state.weight[1] * state.delta[1] + state.weight[2] * state.delta[2]) >> 3) & 0xFF;
+      byte = (predbyte - delta) & 0xFF;
+      prederror = delta << 3;
+      state.error[0] += abs(prederror);
+      state.error[1] += abs(prederror - state.delta[0]); state.error[2] += abs(prederror + state.delta[0]);
+      state.error[3] += abs(prederror - state.delta[1]); state.error[4] += abs(prederror + state.delta[1]);
+      state.error[5] += abs(prederror - state.delta[2]); state.error[6] += abs(prederror + state.delta[2]);
+      state.lastdelta = (int8_t)(byte - state.lastbyte);
+      dst[j] = state.lastbyte = byte;
+      if (!(state.count++ & 0x1F))
+      {
+        uint8_t k, idx = 0;
+        for (k = 1; k < 7; k++)
+        {
+          if (state.error[k] < state.error[idx])
+            idx = k;
+        }
+        memset(state.error, 0, sizeof(state.error));
+        switch (idx)
+        {
+          case 1: if (state.weight[0] >= -16) state.weight[0]--; break;
+          case 2: if (state.weight[0] < 16) state.weight[0]++; break;
+          case 3: if (state.weight[1] >= -16) state.weight[1]--; break;
+          case 4: if (state.weight[1] < 16) state.weight[1]++; break;
+          case 5: if (state.weight[2] >= -16) state.weight[2]--; break;
+          case 6: if (state.weight[2] < 16) state.weight[2]++; break;
+        }
+      }
+    }
+  }
+
+  filter->filteredblockaddress = length;
+  filter->filteredblocklength = length;
+
+  return 1;
+}
+
+
+static int
+execute_filter(struct archive_read *a, struct rar_filter *filter, struct rar_virtual_machine *vm, size_t pos)
+{
+  if (filter->prog->fingerprint == 0x1D0E06077D)
+    return execute_filter_delta(filter, vm);
+  if (filter->prog->fingerprint == 0x35AD576887)
+    return execute_filter_e8(filter, vm, pos, 0);
+  if (filter->prog->fingerprint == 0x393CD7E57E)
+    return execute_filter_e8(filter, vm, pos, 1);
+  if (filter->prog->fingerprint == 0x951C2C5DC8)
+    return execute_filter_rgb(filter, vm);
+  if (filter->prog->fingerprint == 0xD8BC85E701)
+    return execute_filter_audio(filter, vm);
+
+  archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "No support for RAR VM program filter");
+  return 0;
+}
+
+static int
+rar_decode_byte(struct archive_read *a, uint8_t *byte)
+{
+  struct rar *rar = (struct rar *)(a->format->data);
+  struct rar_br *br = &(rar->br);
+  if (!rar_br_read_ahead(a, br, 8))
+    return 0;
+  *byte = (uint8_t)rar_br_bits(br, 8);
+  rar_br_consume(br, 8);
+  return 1;
+}
+
+static inline void
+vm_write_32(struct rar_virtual_machine* vm, size_t offset, uint32_t u32)
+{
+  archive_le32enc(vm->memory + offset, u32);
+}
+
+static inline uint32_t
+vm_read_32(struct rar_virtual_machine* vm, size_t offset)
+{
+  return archive_le32dec(vm->memory + offset);
+}
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,4411 @@
+/*-
+* 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"
+#include "archive_time_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. */
+	int64_t e_mtime;
+	int64_t e_ctime;
+	int64_t e_atime;
+	uint32_t e_mtime_ns;
+	uint32_t e_ctime_ns;
+	uint32_t e_atime_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;
+
+	/* Flag used to indicate that a previous file using this buffer was
+	   encrypted, meaning no data in the buffer can be trusted */
+	uint8_t data_encrypted : 1;
+
+	signed int notused : 3;
+
+	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. */
+	ssize_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;
+
+	/*
+	 * Custom field to denote that this archive contains encrypted entries
+	 */
+	int has_encrypted_entries;
+	int headers_are_encrypted;
+};
+
+/* 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);
+static void clear_data_ready_stack(struct rar5* rar);
+
+/* 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
+uint8_t bf_is_last_block(const struct compressed_block_header* hdr) {
+	return (hdr->block_flags_u8 >> 6) & 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 ssize_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 = calloc(1, sizeof(*f));
+
+	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) &
+			     (uint32_t)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);
+
+	clear_data_ready_stack(rar);
+	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",
+			    (unsigned int)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 ssize_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 archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint32_t* value)
+{
+	if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_PROGRAMMER,
+			"Premature end of stream during extraction of data (#1)");
+		return ARCHIVE_FATAL;
+	}
+
+	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 archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint16_t* value)
+{
+	if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_PROGRAMMER,
+			"Premature end of stream during extraction of data (#2)");
+		return ARCHIVE_FATAL;
+	}
+
+	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 archive_read* a, 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(a, 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 char 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);
+}
+
+static char 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);
+}
+
+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 bid_sfx(struct archive_read *a)
+{
+	const char *p;
+
+	if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+		return -1;
+
+	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0) {
+		/* This is a PE file */
+		char signature[sizeof(rar5_signature_xor)];
+		ssize_t offset = 0x10000;
+		ssize_t window = 4096;
+		ssize_t bytes_avail;
+
+		rar5_signature(signature);
+
+		while (offset + window <= (1024 * 512)) {
+			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 + 8 < buff + bytes_avail) {
+				if (memcmp(p, signature, sizeof(signature)) == 0)
+					return 30;
+				p += 0x10;
+			}
+			offset = p - buff;
+		}
+	}
+
+	return 0;
+}
+
+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;
+	}
+	my_bid = bid_sfx(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,
+    int64_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%jx)", (uintmax_t)hash_type);
+		return ARCHIVE_FATAL;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int parse_htime_item(struct archive_read* a, char unix_time,
+    int64_t* sec, uint32_t* nsec, int64_t* extra_data_size)
+{
+	if(unix_time) {
+		uint32_t time_val;
+		if(!read_u32(a, &time_val))
+			return ARCHIVE_EOF;
+
+		*extra_data_size -= 4;
+		*sec = (int64_t) time_val;
+	} else {
+		uint64_t windows_time;
+		if(!read_u64(a, &windows_time))
+			return ARCHIVE_EOF;
+
+		ntfs_to_unix(windows_time, sec, nsec);
+		*extra_data_size -= 8;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_version(struct archive_read* a,
+    struct archive_entry* e, int64_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, int64_t* extra_data_size)
+{
+	char unix_time, has_unix_ns, has_mtime, has_ctime, has_atime;
+	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;
+	has_unix_ns = unix_time && (flags & HAS_UNIX_NS);
+	has_mtime = flags & HAS_MTIME;
+	has_atime = flags & HAS_ATIME;
+	has_ctime = flags & HAS_CTIME;
+	rar->file.e_atime_ns = rar->file.e_ctime_ns = rar->file.e_mtime_ns = 0;
+
+	if(has_mtime) {
+		parse_htime_item(a, unix_time, &rar->file.e_mtime,
+		    &rar->file.e_mtime_ns, extra_data_size);
+	}
+
+	if(has_ctime) {
+		parse_htime_item(a, unix_time, &rar->file.e_ctime,
+		    &rar->file.e_ctime_ns, extra_data_size);
+	}
+
+	if(has_atime) {
+		parse_htime_item(a, unix_time, &rar->file.e_atime,
+		    &rar->file.e_atime_ns, extra_data_size);
+	}
+
+	if(has_mtime && has_unix_ns) {
+		if(!read_u32(a, &rar->file.e_mtime_ns))
+			return ARCHIVE_EOF;
+
+		*extra_data_size -= 4;
+	}
+
+	if(has_ctime && has_unix_ns) {
+		if(!read_u32(a, &rar->file.e_ctime_ns))
+			return ARCHIVE_EOF;
+
+		*extra_data_size -= 4;
+	}
+
+	if(has_atime && has_unix_ns) {
+		if(!read_u32(a, &rar->file.e_atime_ns))
+			return ARCHIVE_EOF;
+
+		*extra_data_size -= 4;
+	}
+
+	/* The seconds and nanoseconds are either together, or separated in two
+	 * fields so we parse them, then set the archive_entry's times. */
+	if(has_mtime) {
+		archive_entry_set_mtime(e, rar->file.e_mtime, rar->file.e_mtime_ns);
+	}
+
+	if(has_ctime) {
+		archive_entry_set_ctime(e, rar->file.e_ctime, rar->file.e_ctime_ns);
+	}
+
+	if(has_atime) {
+		archive_entry_set_atime(e, rar->file.e_atime, rar->file.e_atime_ns);
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int parse_file_extra_redir(struct archive_read* a,
+    struct archive_entry* e, struct rar5* rar, int64_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(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;
+	}
+
+	if(!read_ahead(a, target_size, &p))
+		return ARCHIVE_EOF;
+
+	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, int64_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, int64_t extra_data_size)
+{
+	uint64_t extra_field_size;
+	uint64_t extra_field_id = 0;
+	uint64_t var_size;
+
+	while(extra_data_size > 0) {
+		/* Make sure we won't fail if the file declares only unsupported
+		attributes. */
+		int ret = ARCHIVE_OK;
+
+		if(!read_var(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(a, &extra_field_id, &var_size))
+			return ARCHIVE_EOF;
+
+		extra_field_size -= var_size;
+		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:
+				/* Mark the entry as encrypted */
+				archive_entry_set_is_data_encrypted(e, 1);
+				rar->has_encrypted_entries = 1;
+				rar->cstate.data_encrypted = 1;
+				/* fallthrough */
+			case EX_SUBDATA:
+				/* fallthrough */
+			default:
+				/* Skip unsupported entry. */
+				extra_data_size -= extra_field_size;
+				if (ARCHIVE_OK != consume(a, extra_field_size)) {
+					return ARCHIVE_EOF;
+				}
+
+				/* Don't fail on unsupported attribute -- we've handled it
+				   by skipping over it. */
+				ret = ARCHIVE_OK;
+		}
+
+		if (ret != ARCHIVE_OK) {
+			/* Forward any errors signalled by the attribute parsing
+			   functions. */
+			return ret;
+		}
+	}
+
+	if (extra_data_size != 0) {
+		/* We didn't skip everything, or we skipped too much; either way,
+		   there's an error in this parsing function. */
+
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+				"unsupported structure of file header extra data");
+		return ARCHIVE_FATAL;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int file_entry_sanity_checks(struct archive_read* a,
+	size_t block_flags, uint8_t is_dir, uint64_t unpacked_size,
+	size_t packed_size)
+{
+	if (is_dir) {
+		const int declares_data_size =
+			(int) (unpacked_size != 0 || packed_size != 0);
+
+		/* FILE entries for directories still declare HFL_DATA in block flags,
+		   even though attaching data to such blocks doesn't make much sense. */
+		if (declares_data_size) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				"directory entries cannot have any data");
+			return ARCHIVE_FATAL;
+		}
+	} else {
+		const int declares_hfl_data = (int) ((block_flags & HFL_DATA) != 0);
+		if (!declares_hfl_data) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+					"no data found in file/service block");
+			return ARCHIVE_FATAL;
+		}
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int process_head_file(struct archive_read* a, struct rar5* rar,
+    struct archive_entry* entry, size_t block_flags)
+{
+	int64_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;
+	int sanity_ret;
+
+	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) {
+		uint64_t edata_size = 0;
+		if(!read_var(a, &edata_size, NULL))
+			return ARCHIVE_EOF;
+
+		/* Intentional type cast from unsigned to signed. */
+		extra_data_size = (int64_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;
+	}
+
+	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);
+
+	sanity_ret = file_entry_sanity_checks(a, block_flags, rar->file.dir,
+		unpacked_size, data_size);
+
+	if (sanity_ret != ARCHIVE_OK) {
+		return sanity_ret;
+	}
+
+	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, unless previous data was encrypted, in
+	 * which case we may never have had the chance */
+
+	if(rar->file.solid > 0 && rar->cstate.data_encrypted == 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;
+		}
+	}
+	else
+		rar->cstate.data_encrypted = 0; /* Reset for new buffer */
+
+	if(rar->cstate.window_size < (ssize_t) window_size &&
+	    rar->cstate.window_buf)
+	{
+		/* The `data_ready` stack contains pointers to the `window_buf` or
+		 * `filtered_buf` buffers.  Since we're about to reallocate the first
+		 * buffer, some of those pointers could become invalid. Therefore, we
+		 * need to dispose of all entries from the stack before attempting the
+		 * realloc. */
+		clear_data_ready_stack(rar);
+
+		/* If window_buf has been allocated before, reallocate it, so
+		 * that its size will match new window_size. */
+
+		uint8_t* new_window_buf =
+			realloc(rar->cstate.window_buf, (size_t) window_size);
+
+		if(!new_window_buf) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_PROGRAMMER,
+				"Not enough memory when trying to realloc the window "
+				"buffer.");
+			return ARCHIVE_FATAL;
+		}
+
+		rar->cstate.window_buf = new_window_buf;
+	}
+
+	/* 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(*fflags_text));
+			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) {
+					archive_entry_copy_fflags_text(entry,
+					    fflags_text + 1);
+				}
+				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%jx",
+				(uintmax_t)host_os);
+
+		return ARCHIVE_FATAL;
+	}
+
+	if(!read_var_sized(a, &name_size, NULL))
+		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;
+	}
+
+	if(!read_ahead(a, name_size, &p))
+		return ARCHIVE_EOF;
+
+	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;
+	uint64_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(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%jx)",
+			    (uintmax_t)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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Header CRC error");
+
+		return ARCHIVE_FATAL;
+#endif
+	}
+
+	/* 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_entry_set_is_metadata_encrypted(entry, 1);
+			archive_entry_set_is_data_encrypted(entry, 1);
+			rar->has_encrypted_entries = 1;
+			rar->headers_are_encrypted = 1;
+			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 try_skip_sfx(struct archive_read *a)
+{
+	const char *p;
+
+	if ((p = __archive_read_ahead(a, 7, NULL)) == NULL)
+		return ARCHIVE_EOF;
+
+	if ((p[0] == 'M' && p[1] == 'Z') || memcmp(p, "\x7F\x45LF", 4) == 0)
+	{
+		char signature[sizeof(rar5_signature_xor)];
+		const void *h;
+		const char *q;
+		size_t skip, total = 0;
+		ssize_t bytes, window = 4096;
+
+		rar5_signature(signature);
+
+		while (total + window <= (1024 * 512)) {
+			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 + 8 < q) {
+				if (memcmp(p, signature, sizeof(signature)) == 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;
+		}
+	}
+
+	return ARCHIVE_OK;
+fatal:
+	archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+			"Couldn't find out RAR header");
+	return (ARCHIVE_FATAL);
+}
+
+static int rar5_read_header(struct archive_read *a,
+    struct archive_entry *entry)
+{
+	struct rar5* rar = get_context(a);
+	int ret;
+
+	/*
+	 * It should be sufficient to call archive_read_next_header() for
+	 * a reader to determine if an entry is encrypted or not.
+	 */
+	if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+		rar->has_encrypted_entries = 0;
+	}
+
+	if(rar->header_initialized == 0) {
+		init_header(a);
+		if ((ret = try_skip_sfx(a)) < ARCHIVE_WARN)
+			return ret;
+		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;
+	}
+
+	clear_data_ready_stack(rar);
+
+	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, (unsigned int)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, ret;
+	uint16_t bitfield;
+	uint32_t pos;
+	struct rar5* rar = get_context(a);
+
+	if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &bitfield))) {
+		return ret;
+	}
+
+	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;
+
+		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 != (ret = read_bits_16(a, rar, p, &n)))
+				return ret;
+
+			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 != (ret = read_bits_16(a, rar, p, &n)))
+				return ret;
+
+			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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		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;
+#endif
+	}
+
+	return ARCHIVE_OK;
+}
+
+/* Convenience function used during filter processing. */
+static int parse_filter_data(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint32_t* filter_data)
+{
+	int i, bytes, ret;
+	uint32_t data = 0;
+
+	if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, p, 2, &bytes)))
+		return ret;
+
+	bytes++;
+
+	for(i = 0; i < bytes; i++) {
+		uint16_t byte;
+
+		if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &byte))) {
+			return ret;
+		}
+
+		/* 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);
+	int ret;
+
+	/* Read the parameters from the input stream. */
+	if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_start)))
+		return ret;
+
+	if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_length)))
+		return ret;
+
+	if(ARCHIVE_OK != (ret = read_bits_16(ar, rar, p, &filter_type)))
+		return ret;
+
+	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 != (ret = read_consume_bits(ar, rar, p, 5, &channels)))
+			return ret;
+
+		filt->channels = channels + 1;
+	}
+
+	return ARCHIVE_OK;
+}
+
+static int decode_code_length(struct archive_read* a, 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(a, 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 ssize_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
+		 *   transform 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(a, 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 != (ret = read_bits_32(
+						    a, rar, p, &add))) {
+							/* Return EOF if we
+							 * can't read more
+							 * data. */
+							return ret;
+						}
+
+						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 != (ret = read_consume_bits(a, rar,
+					     p, dbits, &add))) {
+						/* Return EOF if we can't read
+						 * more data. */
+						return ret;
+					}
+
+					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(a, rar, p, len_slot);
+			if (len == -1) {
+				return ARCHIVE_FATAL;
+			}
+
+			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;
+}
+
+static void clear_data_ready_stack(struct rar5* rar) {
+	memset(&rar->cstate.dready, 0, sizeof(rar->cstate.dready));
+}
+
+/* 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;
+	}
+
+	/* Don't allow extraction if window_size is invalid. */
+	if(rar->cstate.window_size == 0) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_FILE_FORMAT,
+			"Invalid window size declaration in this file");
+
+		/* This should never happen in valid files. */
+		return ARCHIVE_FATAL;
+	}
+
+	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 if this one
+				 * wasn't the last block in the file. */
+				if (bf_is_last_block(&rar->last_block_hdr)) {
+					return ARCHIVE_EOF;
+				}
+
+				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:
+				/* No data is returned here. But because a sparse-file aware
+				 * caller (like archive_read_data_into_fd) may treat zero-size
+				 * as a sparse file block, we need to update the offset
+				 * accordingly. At this point the decoder doesn't have any
+				 * pending uncompressed data blocks, so the current position in
+				 * the output file should be last_write_ptr. */
+				if (offset) *offset = rar->cstate.last_write_ptr;
+				return uncompress_file(a);
+			default:
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Compression method not supported: 0x%x",
+				    (unsigned int)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: %08" PRIx32 ", expected: %08" PRIx32 ")\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 "
+					    "(%08" PRIx32 "/%08" PRIx32 ")\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 (size)
+		*size = 0;
+
+	if (rar->has_encrypted_entries == ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW) {
+		rar->has_encrypted_entries = 0;
+	}
+
+	if (rar->headers_are_encrypted || rar->cstate.data_encrypted) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Reading encrypted data is not currently supported");
+		return ARCHIVE_FATAL;
+	}
+
+	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_FATAL;
+	}
+
+	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 && (rar->cstate.data_encrypted == 0)) {
+		/* 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. Note that
+		 * this is only possible when data withing this solid block is
+		 * not encrypted, in which case we'll skip and fail if the user
+		 * tries to read data. */
+
+		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);
+	clear_data_ready_stack(rar);
+
+	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 (ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_DATA
+			| ARCHIVE_READ_FORMAT_CAPS_ENCRYPT_METADATA);
+}
+
+static int rar5_has_encrypted_entries(struct archive_read *_a) {
+	if (_a && _a->format) {
+		struct rar5 *rar = (struct rar5 *)_a->format->data;
+		if (rar) {
+			return rar->has_encrypted_entries;
+		}
+	}
+
+	return ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+}
+
+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;
+
+	/*
+	 * Until enough data has been read, we cannot tell about
+	 * any encrypted entries yet.
+	 */
+	rar->has_encrypted_entries = ARCHIVE_READ_FORMAT_ENCRYPTION_DONT_KNOW;
+
+	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");
+		free(rar);
+		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,191 @@
+/*-
+ * 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"
+
+#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 = 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,3721 @@
+/*-
+ * Copyright (c) 2003-2023 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"
+
+#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	 entry_pathname;
+	/* For "GNU.sparse.name" and other similar path extensions. */
+	struct archive_string	 entry_pathname_override;
+	struct archive_string	 entry_uname;
+	struct archive_string	 entry_gname;
+	struct archive_string	 entry_linkpath;
+	struct archive_string	 line;
+	int			 pax_hdrcharset_utf8;
+	int64_t			 entry_bytes_remaining;
+	int64_t			 entry_offset;
+	int64_t			 entry_padding;
+	int64_t 		 entry_bytes_unconsumed;
+	int64_t			 disk_size;
+	int64_t			 GNU_sparse_realsize;
+	int64_t			 GNU_sparse_size;
+	int64_t			 SCHILY_sparse_realsize;
+	int64_t			 pax_size;
+	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_attributes_seen;
+	char			 filetype;
+	char			 size_fields; /* Bits defined below */
+
+	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;
+};
+
+/* Track which size fields were present in the headers */
+#define TAR_SIZE_PAX_SIZE 1
+#define TAR_SIZE_GNU_SPARSE_REALSIZE 2
+#define TAR_SIZE_GNU_SPARSE_SIZE 4
+#define TAR_SIZE_SCHILY_SPARSE_REALSIZE 8
+
+
+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, int64_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 *, size_t);
+static int64_t	gnu_sparse_10_read(struct archive_read *, struct tar *,
+		    int64_t *);
+static int	header_Solaris_ACL(struct archive_read *,  struct tar *,
+		    struct archive_entry *, const void *, int64_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_extension(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *, int64_t *);
+static int	header_pax_global(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, int64_t *);
+static int	header_gnu_longlink(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, int64_t *);
+static int	header_gnu_longname(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, int64_t *);
+static int	is_mac_metadata_entry(struct archive_entry *entry);
+static int	read_mac_metadata_blob(struct archive_read *,
+		    struct archive_entry *, int64_t *);
+static int	header_volume(struct archive_read *, struct tar *,
+		    struct archive_entry *, const void *h, int64_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, int64_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, size_t key_length,
+		    size_t value_length, int64_t *unconsumed);
+static int	pax_attribute_LIBARCHIVE_xattr(struct archive_entry *,
+		    const char *, size_t, const char *, size_t);
+static int	pax_attribute_SCHILY_acl(struct archive_read *, struct tar *,
+		    struct archive_entry *, size_t, int);
+static int	pax_attribute_SUN_holesdata(struct archive_read *, struct tar *,
+		    struct archive_entry *, const char *, size_t);
+static void	pax_time(const char *, size_t, int64_t *sec, long *nanos);
+static ssize_t	readline(struct archive_read *, struct tar *, const char **,
+		    ssize_t limit, int64_t *);
+static int	read_body_to_string(struct archive_read *, struct tar *,
+		    struct archive_string *, const void *h, int64_t *);
+static int	read_bytes_to_string(struct archive_read *,
+		    struct archive_string *, size_t, int64_t *);
+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 *, int64_t *);
+static int	tohex(int c);
+static char	*url_decode(const char *, size_t);
+static int	tar_flush_unconsumed(struct archive_read *, int64_t *);
+
+/* Sanity limits:  These numbers should be low enough to
+ * prevent a maliciously-crafted archive from forcing us to
+ * allocate extreme amounts of memory.  But of course, they
+ * need to be high enough for any correct value.  These
+ * will likely need some adjustment as we get more experience. */
+static const size_t guname_limit = 65536; /* Longest uname or gname: 64kiB */
+static const size_t pathname_limit = 1048576; /* Longest path name: 1MiB */
+static const size_t sparse_map_limit = 8 * 1048576; /* Longest sparse map: 8MiB */
+static const size_t xattr_limit = 16 * 1048576; /* Longest xattr: 16MiB */
+static const size_t fflags_limit = 512; /* Longest fflags */
+static const size_t acl_limit = 131072; /* Longest textual ACL: 128kiB */
+static const int64_t entry_limit = 0xfffffffffffffffLL; /* 2^60 bytes = 1 ExbiByte */
+
+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 = 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->entry_pathname);
+	archive_string_free(&tar->entry_pathname_override);
+	archive_string_free(&tar->entry_uname);
+	archive_string_free(&tar->entry_gname);
+	archive_string_free(&tar->entry_linkpath);
+	archive_string_free(&tar->line);
+	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 (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 int
+tar_flush_unconsumed(struct archive_read *a, int64_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);
+		}
+*/
+		int64_t consumed = __archive_read_consume(a, *unconsumed);
+		if (consumed != *unconsumed) {
+			return (ARCHIVE_FATAL);
+		}
+		*unconsumed = 0;
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * 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.
+	 */
+	/* TODO: Move this into `struct tar` to avoid conflicts
+	 * when reading multiple archives */
+	static int default_inode;
+	static int default_dev;
+	struct tar *tar;
+	const char *p;
+	const wchar_t *wp;
+	int r;
+	size_t l;
+	int64_t 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->size_fields = 0; /* We don't have any size info yet */
+
+	/* 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);
+				tar->entry_bytes_remaining = 0;
+				tar->entry_padding = 0;
+			}
+		} else if ((p = archive_entry_pathname(entry)) != NULL) {
+			l = strlen(p);
+			if (l > 0 && p[l - 1] == '/') {
+				archive_entry_set_filetype(entry, AE_IFDIR);
+				tar->entry_bytes_remaining = 0;
+				tar->entry_padding = 0;
+			}
+		}
+	}
+	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) {
+			int64_t request = tar->entry_bytes_remaining +
+			    tar->entry_padding;
+
+			if (__archive_read_consume(a, request) != request)
+				return (ARCHIVE_FATAL);
+			tar->entry_padding = 0;
+			*buff = NULL;
+			*size = 0;
+			*offset = tar->disk_size;
+			return (ARCHIVE_EOF);
+		}
+
+		*buff = __archive_read_ahead(a, 1, &bytes_read);
+		if (*buff == NULL) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "Truncated tar archive"
+			    " detected while reading data");
+			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 request;
+	struct tar* tar;
+
+	tar = (struct tar *)(a->format->data);
+
+	request = tar->entry_bytes_remaining + tar->entry_padding +
+	    tar->entry_bytes_unconsumed;
+
+	if (__archive_read_consume(a, request) != request)
+		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 resets the accumulated state while reading
+ * a header.
+ */
+static void
+tar_reset_header_state(struct tar *tar)
+{
+	tar->pax_hdrcharset_utf8 = 1;
+	tar->sparse_gnu_attributes_seen = 0;
+	archive_string_empty(&(tar->entry_gname));
+	archive_string_empty(&(tar->entry_pathname));
+	archive_string_empty(&(tar->entry_pathname_override));
+	archive_string_empty(&(tar->entry_uname));
+	archive_string_empty(&tar->entry_linkpath);
+}
+
+/*
+ * This function reads and 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, int64_t *unconsumed)
+{
+	ssize_t bytes;
+	int err = ARCHIVE_OK, err2;
+	int eof_fatal = 0; /* EOF is okay at some points... */
+	const char *h;
+	const struct archive_entry_header_ustar *header;
+	const struct archive_entry_header_gnutar *gnuheader;
+
+	/* Bitmask of what header types we've seen. */
+	int32_t seen_headers = 0;
+	static const int32_t seen_A_header = 1;
+	static const int32_t seen_g_header = 2;
+	static const int32_t seen_K_header = 4;
+	static const int32_t seen_L_header = 8;
+	static const int32_t seen_V_header = 16;
+	static const int32_t seen_x_header = 32; /* Also X */
+	static const int32_t seen_mac_metadata = 512;
+
+	tar_reset_header_state(tar);
+
+	/* Ensure format is set. */
+	if (a->archive.archive_format_name == NULL) {
+		a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+		a->archive.archive_format_name = "tar";
+	}
+
+	/*
+	 * TODO: Write global/default pax options into
+	 * 'entry' struct here before overwriting with
+	 * file-specific options.
+	 */
+
+	/* Loop over all the headers needed for the next entry */
+	for (;;) {
+
+		/* Find the next valid header record. */
+		while (1) {
+			if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+				return (ARCHIVE_FATAL);
+			}
+
+			/* Read 512-byte header record */
+			h = __archive_read_ahead(a, 512, &bytes);
+			if (bytes == 0) { /* EOF at a block boundary. */
+				if (eof_fatal) {
+					/* We've read a special header already;
+					 * if there's no regular header, then this is
+					 * a premature EOF. */
+					archive_set_error(&a->archive, EINVAL,
+							  "Damaged tar archive (end-of-archive within a sequence of headers)");
+					return (ARCHIVE_FATAL);
+				} else {
+					return (ARCHIVE_EOF);
+				}
+			}
+			if (h == NULL) {  /* Short block at EOF; this is bad. */
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_FILE_FORMAT,
+				    "Truncated tar archive"
+				    " detected while reading next header");
+				return (ARCHIVE_FATAL);
+			}
+			*unconsumed += 512;
+
+			if (h[0] == 0 && archive_block_is_null(h)) {
+				/* We found a NULL block which indicates end-of-archive */
+
+				if (tar->read_concatenated_archives) {
+					/* We're ignoring NULL blocks, so keep going. */
+					continue;
+				}
+
+				/* Try to consume a second all-null record, as well. */
+				/* If we can't, that's okay. */
+				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);
+			}
+
+			/* This is NOT a null block, so it must be a valid header. */
+			if (!checksum(a, h)) {
+				if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+					return (ARCHIVE_FATAL);
+				}
+				archive_set_error(&a->archive, EINVAL,
+						  "Damaged tar archive (bad header checksum)");
+				/* If we've read some critical information (pax headers, etc)
+				 * and _then_ see a bad header, we can't really recover. */
+				if (eof_fatal) {
+					return (ARCHIVE_FATAL);
+				} else {
+					return (ARCHIVE_RETRY);
+				}
+			}
+			break;
+		}
+
+		/* Determine the format variant. */
+		header = (const struct archive_entry_header_ustar *)h;
+		switch(header->typeflag[0]) {
+		case 'A': /* Solaris tar ACL */
+			if (seen_headers & seen_A_header) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Redundant 'A' header");
+				return (ARCHIVE_FATAL);
+			}
+			seen_headers |= seen_A_header;
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+			a->archive.archive_format_name = "Solaris tar";
+			err2 = header_Solaris_ACL(a, tar, entry, h, unconsumed);
+			break;
+		case 'g': /* POSIX-standard 'g' header. */
+			if (seen_headers & seen_g_header) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Redundant 'g' header");
+				return (ARCHIVE_FATAL);
+			}
+			seen_headers |= seen_g_header;
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+			a->archive.archive_format_name = "POSIX pax interchange format";
+			err2 = header_pax_global(a, tar, entry, h, unconsumed);
+			break;
+		case 'K': /* Long link name (GNU tar, others) */
+			if (seen_headers & seen_K_header) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Damaged archive: Redundant 'K' headers may cause linknames to be incorrect");
+				err = err_combine(err, ARCHIVE_WARN);
+			}
+			seen_headers |= seen_K_header;
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
+			a->archive.archive_format_name = "GNU tar format";
+			err2 = header_gnu_longlink(a, tar, entry, h, unconsumed);
+			break;
+		case 'L': /* Long filename (GNU tar, others) */
+			if (seen_headers & seen_L_header) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Damaged archive: Redundant 'L' headers may cause filenames to be incorrect");
+				err = err_combine(err, ARCHIVE_WARN);
+			}
+			seen_headers |= seen_L_header;
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
+			a->archive.archive_format_name = "GNU tar format";
+			err2 = header_gnu_longname(a, tar, entry, h, unconsumed);
+			break;
+		case 'V': /* GNU volume header */
+			if (seen_headers & seen_V_header) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Redundant 'V' header");
+				err = err_combine(err, ARCHIVE_WARN);
+			}
+			seen_headers |= seen_V_header;
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR_GNUTAR;
+			a->archive.archive_format_name = "GNU tar format";
+			err2 = header_volume(a, tar, entry, h, unconsumed);
+			break;
+		case 'X': /* Used by SUN tar; same as 'x'. */
+			if (seen_headers & seen_x_header) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Redundant 'X'/'x' header");
+				return (ARCHIVE_FATAL);
+			}
+			seen_headers |= seen_x_header;
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+			a->archive.archive_format_name =
+				"POSIX pax interchange format (Sun variant)";
+			err2 = header_pax_extension(a, tar, entry, h, unconsumed);
+			break;
+		case 'x': /* POSIX-standard 'x' header. */
+			if (seen_headers & seen_x_header) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Redundant 'x' header");
+				return (ARCHIVE_FATAL);
+			}
+			seen_headers |= seen_x_header;
+			a->archive.archive_format = ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE;
+			a->archive.archive_format_name = "POSIX pax interchange format";
+			err2 = header_pax_extension(a, tar, entry, h, unconsumed);
+			break;
+		default: /* Regular header: Legacy tar, GNU tar, or ustar */
+			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";
+				err2 = 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";
+				}
+				err2 = header_ustar(a, tar, entry, h);
+			} else {
+				a->archive.archive_format = ARCHIVE_FORMAT_TAR;
+				a->archive.archive_format_name = "tar (non-POSIX)";
+				err2 = header_old_tar(a, tar, entry, h);
+			}
+			err = err_combine(err, err2);
+			/* We return warnings or success as-is.  Anything else is fatal. */
+			if (err < ARCHIVE_WARN) {
+				return (ARCHIVE_FATAL);
+			}
+			/* Filename of the form `._filename` is an AppleDouble
+			 * extension entry.  The body is the macOS metadata blob;
+			 * this is followed by another entry with the actual
+			 * regular file data.
+			 * This design has two drawbacks:
+			 * = it's brittle; you might just have a file with such a name
+			 * = it duplicates any long pathname extensions
+			 *
+			 * TODO: This probably shouldn't be here at all.  Consider
+			 * just returning the contents as a regular entry here and
+			 * then dealing with it when we write data to disk.
+			 */
+			if (tar->process_mac_extensions
+			    && ((seen_headers & seen_mac_metadata) == 0)
+			    && is_mac_metadata_entry(entry)) {
+				err2 = read_mac_metadata_blob(a, entry, unconsumed);
+				if (err2 < ARCHIVE_WARN) {
+					return (ARCHIVE_FATAL);
+				}
+				err = err_combine(err, err2);
+				/* Note: Other headers can appear again. */
+				seen_headers = seen_mac_metadata;
+				tar_reset_header_state(tar);
+				break;
+			}
+
+			/* Reconcile GNU sparse attributes */
+			if (tar->sparse_gnu_attributes_seen) {
+				/* Only 'S' (GNU sparse) and ustar '0' regular files can be sparse */
+				if (tar->filetype != 'S' && tar->filetype != '0') {
+					archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+							  "Non-regular file cannot be sparse");
+					return (ARCHIVE_WARN);
+				} else if (tar->sparse_gnu_major == 0 &&
+				    tar->sparse_gnu_minor == 0) {
+					/* Sparse map already parsed from 'x' header */
+				} else if (tar->sparse_gnu_major == 0 &&
+				    tar->sparse_gnu_minor == 1) {
+					/* Sparse map already parsed from 'x' header */
+				} else if (tar->sparse_gnu_major == 1 &&
+				    tar->sparse_gnu_minor == 0) {
+					/* Sparse map is prepended to file contents */
+					ssize_t bytes_read;
+					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);
+				}
+			}
+			return (err);
+		}
+
+		/* We're between headers ... */
+		err = err_combine(err, err2);
+		if (err == ARCHIVE_FATAL)
+			return (err);
+
+		/* The GNU volume header and the pax `g` global header
+		 * are both allowed to be the only header in an
+		 * archive.  If we've seen any other header, a
+		 * following EOF is fatal. */
+		if ((seen_headers & ~seen_V_header & ~seen_g_header) != 0) {
+			eof_fatal = 1;
+		}
+	}
+}
+
+/*
+ * 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);
+
+#if DONT_FAIL_ON_CRC_ERROR
+	/* Speed up fuzzing by pretending the checksum is always right. */
+	return (1);
+#else
+	return (0);
+#endif
+}
+
+/*
+ * 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, int64_t *unconsumed)
+{
+	const struct archive_entry_header_ustar *header;
+	struct archive_string	 acl_text;
+	size_t size;
+	int err, acl_type;
+	uint64_t type;
+	char *acl, *p;
+
+	header = (const struct archive_entry_header_ustar *)h;
+	size = (size_t)tar_atol(header->size, sizeof(header->size));
+	archive_string_init(&acl_text);
+	err = read_body_to_string(a, tar, &acl_text, h, unconsumed);
+	if (err != ARCHIVE_OK) {
+		archive_string_free(&acl_text);
+		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 = 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)");
+			archive_string_free(&acl_text);
+			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)");
+			archive_string_free(&acl_text);
+			return (ARCHIVE_WARN);
+		}
+		p++;
+	}
+	switch (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 %llu)",
+		    (unsigned long long)type);
+		archive_string_free(&acl_text);
+		return (ARCHIVE_WARN);
+	}
+	p++;
+
+	if (p >= acl + size) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Malformed Solaris ACL attribute (body overflow)");
+		archive_string_free(&acl_text);
+		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) {
+			archive_string_free(&acl_text);
+			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);
+	/* Workaround: Force perm_is_set() to be correct */
+	/* If this bit were stored in the ACL, this wouldn't be needed */
+	archive_entry_set_perm(entry, archive_entry_perm(entry));
+	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)");
+	}
+	archive_string_free(&acl_text);
+	return (err);
+}
+
+/*
+ * Interpret 'K' long linkname header.
+ */
+static int
+header_gnu_longlink(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, int64_t *unconsumed)
+{
+	int err;
+
+	struct archive_string linkpath;
+	archive_string_init(&linkpath);
+	err = read_body_to_string(a, tar, &linkpath, h, unconsumed);
+	if (err == ARCHIVE_OK) {
+		archive_entry_set_link(entry, linkpath.s);
+	}
+	archive_string_free(&linkpath);
+	return (err);
+}
+
+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_gnu_longname(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, int64_t *unconsumed)
+{
+	int err;
+	struct archive_string longname;
+
+	archive_string_init(&longname);
+	err = read_body_to_string(a, tar, &longname, h, unconsumed);
+	if (err == ARCHIVE_OK) {
+		if (archive_entry_copy_pathname_l(entry, longname.s,
+		    archive_strlen(&longname), tar->sconv) != 0)
+			err = set_conversion_failed_error(a, tar->sconv, "Pathname");
+	}
+	archive_string_free(&longname);
+	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, int64_t *unconsumed)
+{
+	const struct archive_entry_header_ustar *header;
+	int64_t size, to_consume;
+
+	(void)a; /* UNUSED */
+	(void)tar; /* UNUSED */
+	(void)entry; /* UNUSED */
+
+	header = (const struct archive_entry_header_ustar *)h;
+	size = tar_atol(header->size, sizeof(header->size));
+	if (size < 0 || size > (int64_t)pathname_limit) {
+		return (ARCHIVE_FATAL);
+	}
+	to_consume = ((size + 511) & ~511);
+	*unconsumed += to_consume;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Read the next `size` bytes into the provided string.
+ * Null-terminate the string.
+ */
+static int
+read_bytes_to_string(struct archive_read *a,
+		     struct archive_string *as, size_t size,
+		     int64_t *unconsumed) {
+	const void *src;
+
+	/* Fail if we can't make our buffer big enough. */
+	if (archive_string_ensure(as, size + 1) == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+		    "No memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Read the body into the string. */
+	src = __archive_read_ahead(a, size, NULL);
+	if (src == NULL) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Truncated archive"
+		    " detected while reading metadata");
+		*unconsumed = 0;
+		return (ARCHIVE_FATAL);
+	}
+	memcpy(as->s, src, size);
+	as->s[size] = '\0';
+	as->length = size;
+	*unconsumed += size;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * 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, int64_t *unconsumed)
+{
+	int64_t size;
+	const struct archive_entry_header_ustar *header;
+	int r;
+
+	(void)tar; /* UNUSED */
+	header = (const struct archive_entry_header_ustar *)h;
+	size  = tar_atol(header->size, sizeof(header->size));
+	if (size < 0 || size > entry_limit) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Special header has invalid size: %lld",
+		    (long long)size);
+		return (ARCHIVE_FATAL);
+	}
+	if (size > (int64_t)pathname_limit) {
+		archive_string_empty(as);
+		int64_t to_consume = ((size + 511) & ~511);
+		if (to_consume != __archive_read_consume(a, to_consume)) {
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive, EINVAL,
+		    "Special header too large: %lld > 1MiB",
+		    (long long)size);
+		return (ARCHIVE_WARN);
+	}
+	r = read_bytes_to_string(a, as, size, unconsumed);
+	*unconsumed += 0x1ff & (-size);
+	return(r);
+}
+
+/*
+ * 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.
+ *
+ * This is called _after_ ustar, GNU tar, Schily, etc, special
+ * fields have already been parsed into the `tar` structure.
+ * So we can make final decisions here about how to reconcile
+ * size, mode, etc, information.
+ */
+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;
+	const char *existing_linkpath;
+	const wchar_t *existing_wcs_linkpath;
+	int     err = ARCHIVE_OK;
+
+	header = (const struct archive_entry_header_ustar *)h;
+
+	/* Parse out the numeric fields (all are octal) */
+
+	/* Split mode handling: Set filetype always, perm only if not already set */
+	archive_entry_set_filetype(entry,
+	    (mode_t)tar_atol(header->mode, sizeof(header->mode)));
+	if (!archive_entry_perm_is_set(entry)) {
+		archive_entry_set_perm(entry,
+			(mode_t)tar_atol(header->mode, sizeof(header->mode)));
+	}
+
+	/* Set uid, gid, mtime if not already set */
+	if (!archive_entry_uid_is_set(entry)) {
+		archive_entry_set_uid(entry, tar_atol(header->uid, sizeof(header->uid)));
+	}
+	if (!archive_entry_gid_is_set(entry)) {
+		archive_entry_set_gid(entry, tar_atol(header->gid, sizeof(header->gid)));
+	}
+	if (!archive_entry_mtime_is_set(entry)) {
+		archive_entry_set_mtime(entry, tar_atol(header->mtime, sizeof(header->mtime)), 0);
+	}
+
+	/* Reconcile the size info. */
+	/* First, how big is the file on disk? */
+	if ((tar->size_fields & TAR_SIZE_GNU_SPARSE_REALSIZE) != 0) {
+		/* GNU sparse format 1.0 uses `GNU.sparse.realsize`
+		 * to hold the size of the file on disk. */
+		tar->disk_size = tar->GNU_sparse_realsize;
+	} else if ((tar->size_fields & TAR_SIZE_GNU_SPARSE_SIZE) != 0
+		   && (tar->sparse_gnu_major == 0)) {
+		/* GNU sparse format 0.0 and 0.1 use `GNU.sparse.size`
+		 * to hold the size of the file on disk. */
+		tar->disk_size = tar->GNU_sparse_size;
+	} else if ((tar->size_fields & TAR_SIZE_SCHILY_SPARSE_REALSIZE) != 0) {
+		tar->disk_size = tar->SCHILY_sparse_realsize;
+	} else if ((tar->size_fields & TAR_SIZE_PAX_SIZE) != 0) {
+		tar->disk_size = tar->pax_size;
+	} else {
+		/* There wasn't a suitable pax header, so use the ustar info */
+		tar->disk_size = tar_atol(header->size, sizeof(header->size));
+	}
+
+	if (tar->disk_size < 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+				  "Tar entry has negative file size");
+		return (ARCHIVE_FATAL);
+	} else if (tar->disk_size > entry_limit) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+				  "Tar entry size overflow");
+		return (ARCHIVE_FATAL);
+	} else {
+		archive_entry_set_size(entry, tar->disk_size);
+	}
+
+	/* Second, how big is the data in the archive? */
+	if ((tar->size_fields & TAR_SIZE_GNU_SPARSE_SIZE) != 0
+	    && (tar->sparse_gnu_major == 1)) {
+		/* GNU sparse format 1.0 uses `GNU.sparse.size`
+		 * to hold the size of the data in the archive. */
+		tar->entry_bytes_remaining = tar->GNU_sparse_size;
+	} else if ((tar->size_fields & TAR_SIZE_PAX_SIZE) != 0) {
+		tar->entry_bytes_remaining = tar->pax_size;
+	} else {
+		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);
+	} else if (tar->entry_bytes_remaining > entry_limit) {
+		tar->entry_bytes_remaining = 0;
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+				  "Tar entry size overflow");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Handle the tar type flag appropriately. */
+	tar->filetype = header->typeflag[0];
+
+	/*
+	 * TODO: If the linkpath came from Pax extension header, then
+	 * we should obey the hdrcharset_utf8 flag when converting these.
+	 */
+	switch (tar->filetype) {
+	case '1': /* Hard link */
+		archive_entry_set_link_to_hardlink(entry);
+		existing_wcs_linkpath = archive_entry_hardlink_w(entry);
+		existing_linkpath = archive_entry_hardlink(entry);
+		if ((existing_linkpath == NULL || existing_linkpath[0] == '\0')
+		    && (existing_wcs_linkpath == NULL || existing_wcs_linkpath[0] == '\0')) {
+			struct archive_string linkpath;
+			archive_string_init(&linkpath);
+			archive_strncpy(&linkpath,
+					header->linkname, sizeof(header->linkname));
+			if (archive_entry_copy_hardlink_l(entry, linkpath.s,
+							  archive_strlen(&linkpath), tar->sconv) != 0) {
+				err = set_conversion_failed_error(a, tar->sconv,
+								  "Linkname");
+				if (err == ARCHIVE_FATAL) {
+					archive_string_free(&linkpath);
+					return (err);
+				}
+			}
+			archive_string_free(&linkpath);
+		}
+		/*
+		 * 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_link_to_symlink(entry);
+		existing_wcs_linkpath = archive_entry_symlink_w(entry);
+		existing_linkpath = archive_entry_symlink(entry);
+		if ((existing_linkpath == NULL || existing_linkpath[0] == '\0')
+		    && (existing_wcs_linkpath == NULL || existing_wcs_linkpath[0] == '\0')) {
+			struct archive_string linkpath;
+			archive_string_init(&linkpath);
+			archive_strncpy(&linkpath,
+					header->linkname, sizeof(header->linkname));
+			if (archive_entry_copy_symlink_l(entry, linkpath.s,
+			    archive_strlen(&linkpath), tar->sconv) != 0) {
+				err = set_conversion_failed_error(a, tar->sconv,
+				    "Linkname");
+				if (err == ARCHIVE_FATAL) {
+					archive_string_free(&linkpath);
+					return (err);
+				}
+			}
+			archive_string_free(&linkpath);
+		}
+		archive_entry_set_filetype(entry, AE_IFLNK);
+		archive_entry_set_size(entry, 0);
+		tar->entry_bytes_remaining = 0;
+		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': /* ustar "regular" file */
+		/* FALLTHROUGH */
+	default: /* Non-standard file 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).
+	 * Skip if pathname was already set e.g. by header_gnu_longname()
+	 */
+	header = (const struct archive_entry_header_ustar *)h;
+
+	const char *existing_pathname = archive_entry_pathname(entry);
+	const wchar_t *existing_wcs_pathname = archive_entry_pathname_w(entry);
+	if ((existing_pathname == NULL || existing_pathname[0] == '\0')
+	    && (existing_wcs_pathname == NULL || existing_wcs_pathname[0] == '\0') &&
+	    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);
+}
+
+/*
+ * Is this likely an AppleDouble extension?
+ */
+static int
+is_mac_metadata_entry(struct archive_entry *entry) {
+	const char *p, *name;
+	const wchar_t *wp, *wname;
+
+	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 1;
+	} 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 1;
+	}
+	/* Not a mac extension */
+	return 0;
+}
+
+/*
+ * Read a Mac AppleDouble-encoded blob of file metadata,
+ * if there is one.
+ *
+ * TODO: In Libarchive 4, we should consider ripping this
+ * out -- instead, return a file starting with `._` as
+ * a regular file and let the client (or archive_write logic)
+ * handle it.
+ */
+static int
+read_mac_metadata_blob(struct archive_read *a,
+    struct archive_entry *entry, int64_t *unconsumed)
+{
+	int64_t size;
+	size_t msize;
+	const void *data;
+
+ 	/* Read the body as a Mac OS metadata blob. */
+	size = archive_entry_size(entry);
+	msize = (size_t)size;
+	if (size < 0 || (uintmax_t)msize != (uintmax_t)size) {
+		*unconsumed = 0;
+		return (ARCHIVE_FATAL);
+	}
+
+	/* TODO: Should this merely skip the overlarge entry and
+	 * WARN?  Or is xattr_limit sufficiently large that we can
+	 * safely assume anything larger is malicious? */
+	if (size > (int64_t)xattr_limit) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Oversized AppleDouble extension has size %llu > %llu",
+		    (unsigned long long)size,
+		    (unsigned long long)xattr_limit);
+		return (ARCHIVE_FATAL);
+	}
+
+	/*
+	 * 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?
+	 */
+	if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+		return (ARCHIVE_FATAL);
+	}
+	data = __archive_read_ahead(a, msize, NULL);
+	if (data == NULL) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Truncated archive"
+		    " detected while reading macOS metadata");
+		*unconsumed = 0;
+		return (ARCHIVE_FATAL);
+	}
+	archive_entry_clear(entry);
+	archive_entry_copy_mac_metadata(entry, data, msize);
+	*unconsumed = (msize + 511) & ~ 511;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * 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, int64_t *unconsumed)
+{
+	const struct archive_entry_header_ustar *header;
+	int64_t size, to_consume;
+
+	(void)a; /* UNUSED */
+	(void)tar; /* UNUSED */
+	(void)entry; /* UNUSED */
+
+	header = (const struct archive_entry_header_ustar *)h;
+	size = tar_atol(header->size, sizeof(header->size));
+	if (size < 0 || size > entry_limit) {
+		archive_set_error(&a->archive, EINVAL,
+		    "Special header has invalid size: %lld",
+		    (long long)size);
+		return (ARCHIVE_FATAL);
+	}
+	to_consume = ((size + 511) & ~511);
+	*unconsumed += to_consume;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Parse a file header for a Posix "ustar" archive entry.  This also
+ * handles "pax" or "extended ustar" entries.
+ *
+ * In order to correctly handle pax attributes (which precede this),
+ * we have to skip parsing any field for which the entry already has
+ * contents.
+ */
+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. */
+	const char *existing_pathname = archive_entry_pathname(entry);
+	const wchar_t *existing_wcs_pathname = archive_entry_pathname_w(entry);
+	if ((existing_pathname == NULL || existing_pathname[0] == '\0')
+	    && (existing_wcs_pathname == NULL || existing_wcs_pathname[0] == '\0')) {
+		archive_string_init(&as);
+		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);
+		}
+		archive_string_free(&as);
+	}
+
+	/* 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. */
+	const char *existing_uname = archive_entry_uname(entry);
+	if (existing_uname == NULL || existing_uname[0] == '\0') {
+		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);
+		}
+	}
+
+	const char *existing_gname = archive_entry_gname(entry);
+	if (existing_gname == NULL || existing_gname[0] == '\0') {
+		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') {
+		if (!archive_entry_rdev_is_set(entry)) {
+			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);
+
+	return (err);
+}
+
+static int
+header_pax_extension(struct archive_read *a, struct tar *tar,
+    struct archive_entry *entry, const void *h, int64_t *unconsumed)
+{
+	/* Sanity checks: The largest `x` body I've ever heard of was
+	 * a little over 4MB.  So I doubt there has ever been a
+	 * well-formed archive with an `x` body over 1GiB.  Similarly,
+	 * it seems plausible that no single attribute has ever been
+	 * larger than 100MB.  So if we see a larger value here, it's
+	 * almost certainly a sign of a corrupted/malicious archive. */
+
+	/* Maximum sane size for extension body: 1 GiB */
+	/* This cannot be raised to larger than 8GiB without
+	 * exceeding the maximum size for a standard ustar
+	 * entry. */
+	const int64_t ext_size_limit = 1024 * 1024 * (int64_t)1024;
+	/* Maximum size for a single line/attr: 100 million characters */
+	/* This cannot be raised to more than 2GiB without exceeding
+	 * a `size_t` on 32-bit platforms. */
+	const size_t max_parsed_line_length = 99999999ULL;
+	/* Largest attribute prolog:  size + name. */
+	const size_t max_size_name = 512;
+
+	/* Size and padding of the full extension body */
+	int64_t ext_size, ext_padding;
+	size_t line_length, value_length, name_length;
+	ssize_t to_read, did_read;
+	const struct archive_entry_header_ustar *header;
+	const char *p, *attr_start, *name_start;
+	struct archive_string_conv *sconv;
+	struct archive_string *pas = NULL;
+	struct archive_string attr_name;
+	int err = ARCHIVE_OK, r;
+
+	header = (const struct archive_entry_header_ustar *)h;
+	ext_size  = tar_atol(header->size, sizeof(header->size));
+	if (ext_size > entry_limit) {
+		return (ARCHIVE_FATAL);
+	}
+	if (ext_size < 0) {
+	  archive_set_error(&a->archive, EINVAL,
+			    "pax extension header has invalid size: %lld",
+			    (long long)ext_size);
+	  return (ARCHIVE_FATAL);
+	}
+
+	ext_padding = 0x1ff & (-ext_size);
+	if (ext_size > ext_size_limit) {
+		/* Consume the pax extension body and return an error */
+		if (ext_size + ext_padding != __archive_read_consume(a, ext_size + ext_padding)) {
+			return (ARCHIVE_FATAL);
+		}
+		archive_set_error(&a->archive, EINVAL,
+		    "Ignoring oversized pax extensions: %lld > %lld",
+		    (long long)ext_size, (long long)ext_size_limit);
+		return (ARCHIVE_WARN);
+	}
+	if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Parse the size/name of each pax attribute in the body */
+	archive_string_init(&attr_name);
+	while (ext_size > 0) {
+		/* Read enough bytes to parse the size/name of the next attribute */
+		to_read = max_size_name;
+		if (to_read > ext_size) {
+			to_read = ext_size;
+		}
+		p = __archive_read_ahead(a, to_read, &did_read);
+		if (p == NULL) { /* EOF */
+			archive_set_error(&a->archive, EINVAL,
+					  "Truncated tar archive"
+					  " detected while reading pax attribute name");
+			return (ARCHIVE_FATAL);
+		}
+		if (did_read > ext_size) {
+			did_read = ext_size;
+		}
+
+		/* Parse size of attribute */
+		line_length = 0;
+		attr_start = p;
+		while (1) {
+			if (p >= attr_start + did_read) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Ignoring malformed pax attributes: overlarge attribute size field");
+				*unconsumed += ext_size + ext_padding;
+				return (ARCHIVE_WARN);
+			}
+			if (*p == ' ') {
+				p++;
+				break;
+			}
+			if (*p < '0' || *p > '9') {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Ignoring malformed pax attributes: malformed attribute size field");
+				*unconsumed += ext_size + ext_padding;
+				return (ARCHIVE_WARN);
+			}
+			line_length *= 10;
+			line_length += *p - '0';
+			if (line_length > max_parsed_line_length) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Ignoring malformed pax attribute: size > %lld",
+						  (long long)max_parsed_line_length);
+				*unconsumed += ext_size + ext_padding;
+				return (ARCHIVE_WARN);
+			}
+			p++;
+		}
+
+		if ((int64_t)line_length > ext_size) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Ignoring malformed pax attribute:  %lld > %lld",
+						  (long long)line_length, (long long)ext_size);
+				*unconsumed += ext_size + ext_padding;
+				return (ARCHIVE_WARN);
+		}
+
+		/* Parse name of attribute */
+		if (p >= attr_start + did_read
+		    || p >= attr_start + line_length
+		    || *p == '=') {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+					  "Ignoring malformed pax attributes: empty name found");
+			*unconsumed += ext_size + ext_padding;
+			return (ARCHIVE_WARN);
+		}
+		name_start = p;
+		while (1) {
+			if (p >= attr_start + did_read || p >= attr_start + line_length) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Ignoring malformed pax attributes: overlarge attribute name");
+				*unconsumed += ext_size + ext_padding;
+				return (ARCHIVE_WARN);
+			}
+			if (*p == '=') {
+				break;
+			}
+			p++;
+		}
+		name_length = p - name_start;
+		p++; // Skip '='
+
+		// Save the name before we consume it
+		archive_strncpy(&attr_name, name_start, name_length);
+
+		ext_size -= p - attr_start;
+		value_length = line_length - (p - attr_start);
+
+		/* Consume size, name, and `=` */
+		*unconsumed += p - attr_start;
+		if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+			return (ARCHIVE_FATAL);
+		}
+
+		if (value_length == 0) {
+			archive_set_error(&a->archive, EINVAL,
+					  "Malformed pax attributes");
+			*unconsumed += ext_size + ext_padding;
+			return (ARCHIVE_WARN);
+		}
+
+		/* pax_attribute will consume value_length - 1 */
+		r = pax_attribute(a, tar, entry, attr_name.s, archive_strlen(&attr_name), value_length - 1, unconsumed);
+		ext_size -= value_length - 1;
+
+		// Release the allocated attr_name (either here or before every return in this function)
+		archive_string_free(&attr_name);
+
+		if (r < ARCHIVE_WARN) {
+			*unconsumed += ext_size + ext_padding;
+			return (r);
+		}
+		err = err_combine(err, r);
+
+		/* Consume the `\n` that follows the pax attribute value. */
+		if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+			return (ARCHIVE_FATAL);
+		}
+		p = __archive_read_ahead(a, 1, &did_read);
+		if (p == NULL) {
+			archive_set_error(&a->archive, EINVAL,
+					  "Truncated tar archive"
+					  " detected while completing pax attribute");
+			return (ARCHIVE_FATAL);
+		}
+		if (p[0] != '\n') {
+			archive_set_error(&a->archive, EINVAL,
+					  "Malformed pax attributes");
+			*unconsumed += ext_size + ext_padding;
+			return (ARCHIVE_WARN);
+		}
+		ext_size -= 1;
+		*unconsumed += 1;
+		if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+			return (ARCHIVE_FATAL);
+		}
+	}
+	*unconsumed += ext_size + ext_padding;
+
+	/*
+	 * Some PAX values -- pathname, linkpath, uname, gname --
+	 * can't be copied into the entry until we know the character
+	 * set to use:
+	 */
+	if (!tar->pax_hdrcharset_utf8)
+		/* PAX specified "BINARY", so use the default charset */
+		sconv = tar->opt_sconv;
+	else {
+		/* PAX default UTF-8 */
+		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);
+	}
+
+	/* Pathname */
+	pas = NULL;
+	if (archive_strlen(&(tar->entry_pathname_override)) > 0) {
+		/* Prefer GNU.sparse.name attribute if present */
+		/* GNU sparse files store a fake name under the standard
+		 * "pathname" key. */
+		pas = &(tar->entry_pathname_override);
+	} else if (archive_strlen(&(tar->entry_pathname)) > 0) {
+		/* Use standard "pathname" PAX extension */
+		pas = &(tar->entry_pathname);
+	}
+	if (pas != NULL) {
+		if (archive_entry_copy_pathname_l(entry, pas->s,
+		    archive_strlen(pas), sconv) != 0) {
+			err = set_conversion_failed_error(a, sconv, "Pathname");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+			/* Use raw name without conversion */
+			archive_entry_copy_pathname(entry, pas->s);
+		}
+	}
+	/* Uname */
+	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 raw name without conversion */
+			archive_entry_copy_uname(entry, tar->entry_uname.s);
+		}
+	}
+	/* Gname */
+	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 raw name without conversion */
+			archive_entry_copy_gname(entry, tar->entry_gname.s);
+		}
+	}
+	/* Linkpath */
+	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, "Linkpath");
+			if (err == ARCHIVE_FATAL)
+				return (err);
+			/* Use raw name without conversion */
+			archive_entry_copy_link(entry, tar->entry_linkpath.s);
+		}
+	}
+
+	/* Extension may have given us a corrected `entry_bytes_remaining` for
+	 * the main entry; update the padding appropriately. */
+	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+	return (err);
+}
+
+static int
+pax_attribute_LIBARCHIVE_xattr(struct archive_entry *entry,
+	const char *name, size_t name_length, const char *value, size_t value_length)
+{
+	char *name_decoded;
+	void *value_decoded;
+	size_t value_len;
+
+	if (name_length < 1)
+		return 3;
+
+	/* URL-decode name */
+	name_decoded = url_decode(name, name_length);
+	if (name_decoded == NULL)
+		return 2;
+
+	/* Base-64 decode value */
+	value_decoded = base64_decode(value, value_length, &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, size_t name_length, const char *value, size_t value_length)
+{
+	if (name_length < 1 || name_length > 128) {
+		return 1;
+	}
+
+	char * null_terminated_name = malloc(name_length + 1);
+	if (null_terminated_name != NULL) {
+		memcpy(null_terminated_name, name, name_length);
+		null_terminated_name[name_length] = '\0';
+		archive_entry_xattr_add_entry(entry, null_terminated_name, value, value_length);
+		free(null_terminated_name);
+	}
+
+	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_SCHILY_acl(struct archive_read *a, struct tar *tar,
+	struct archive_entry *entry, size_t value_length, int type)
+{
+	int r;
+	const char *p;
+	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);
+	}
+
+	if (value_length > acl_limit) {
+		__archive_read_consume(a, value_length);
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+				  "Unreasonably large ACL: %llu > %llu",
+				  (unsigned long long)value_length,
+				  (unsigned long long)acl_limit);
+		return (ARCHIVE_WARN);
+	}
+
+	p = __archive_read_ahead(a, value_length, NULL);
+	if (p == NULL) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+				  "Truncated tar archive "
+				  "detected while reading ACL data");
+		return (ARCHIVE_FATAL);
+	}
+
+	r = archive_acl_from_text_nl(archive_entry_acl(entry), p, value_length,
+	    type, tar->sconv_acl);
+	__archive_read_consume(a, value_length);
+	/* Workaround: Force perm_is_set() to be correct */
+	/* If this bit were stored in the ACL, this wouldn't be needed */
+	archive_entry_set_perm(entry, archive_entry_perm(entry));
+	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);
+}
+
+static int
+pax_attribute_read_time(struct archive_read *a, size_t value_length, int64_t *ps, long *pn, int64_t *unconsumed) {
+	struct archive_string as;
+	int r;
+
+	if (value_length > 128) {
+		__archive_read_consume(a, value_length);
+		*ps = 0;
+		*pn = 0;
+		return (ARCHIVE_FATAL);
+	}
+
+	archive_string_init(&as);
+	r = read_bytes_to_string(a, &as, value_length, unconsumed);
+	if (r < ARCHIVE_OK) {
+		archive_string_free(&as);
+		*ps = 0;
+		*pn = 0;
+		return (r);
+	}
+
+	pax_time(as.s, archive_strlen(&as), ps, pn);
+	archive_string_free(&as);
+	if (*ps == INT64_MIN) {
+		*ps = 0;
+		*pn = 0;
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+pax_attribute_read_number(struct archive_read *a, size_t value_length, int64_t *result) {
+	struct archive_string as;
+	int64_t unconsumed = 0;
+	int r;
+
+	if (value_length > 64) {
+		__archive_read_consume(a, value_length);
+		*result = 0;
+		return (ARCHIVE_FATAL);
+	}
+
+	archive_string_init(&as);
+	r = read_bytes_to_string(a, &as, value_length, &unconsumed);
+	if (tar_flush_unconsumed(a, &unconsumed) != ARCHIVE_OK) {
+		return (ARCHIVE_FATAL);
+	}
+	if (r < ARCHIVE_OK) {
+		archive_string_free(&as);
+		*result = 0;
+		return (r);
+	}
+
+	*result = tar_atol10(as.s, archive_strlen(&as));
+	archive_string_free(&as);
+	if (*result < 0 || *result == INT64_MAX) {
+		*result = INT64_MAX;
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Parse a single key=value attribute.
+ *
+ * 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.
+ *
+ * TODO: 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, size_t key_length, size_t value_length, int64_t *unconsumed)
+{
+	int64_t t;
+	long n;
+	const char *p;
+	ssize_t bytes_read;
+	int err = ARCHIVE_OK;
+
+	switch (key[0]) {
+	case 'G':
+		/* GNU.* extensions */
+		if (key_length > 4 && memcmp(key, "GNU.", 4) == 0) {
+			key += 4;
+			key_length -= 4;
+
+			/* GNU.sparse marks the existence of GNU sparse information */
+			if (key_length == 6 && memcmp(key, "sparse", 6) == 0) {
+				tar->sparse_gnu_attributes_seen = 1;
+			}
+
+			/* GNU.sparse.* extensions */
+			else if (key_length > 7 && memcmp(key, "sparse.", 7) == 0) {
+				tar->sparse_gnu_attributes_seen = 1;
+				key += 7;
+				key_length -= 7;
+
+				/* GNU "0.0" sparse pax format. */
+				if (key_length == 9 && memcmp(key, "numblocks", 9) == 0) {
+					/* GNU.sparse.numblocks */
+					tar->sparse_offset = -1;
+					tar->sparse_numbytes = -1;
+					tar->sparse_gnu_major = 0;
+					tar->sparse_gnu_minor = 0;
+				}
+				else if (key_length == 6 && memcmp(key, "offset", 6) == 0) {
+					/* GNU.sparse.offset */
+					if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+						tar->sparse_offset = t;
+						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;
+						}
+					}
+					return (err);
+				}
+				else if (key_length == 8 && memcmp(key, "numbytes", 8) == 0) {
+					/* GNU.sparse.numbytes */
+					if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+						tar->sparse_numbytes = t;
+						if (tar->sparse_offset != -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;
+						}
+					}
+					return (err);
+				}
+				else if (key_length == 4 && memcmp(key, "size", 4) == 0) {
+					/* GNU.sparse.size */
+					/* This is either the size of stored entry OR the size of data on disk,
+					 * depending on which GNU sparse format version is in use.
+					 * Since pax attributes can be in any order, we may not actually
+					 * know at this point how to interpret this. */
+					if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+						tar->GNU_sparse_size = t;
+						tar->size_fields |= TAR_SIZE_GNU_SPARSE_SIZE;
+					}
+					return (err);
+				}
+
+				/* GNU "0.1" sparse pax format. */
+				else if (key_length == 3 && memcmp(key, "map", 3) == 0) {
+					/* GNU.sparse.map */
+					tar->sparse_gnu_major = 0;
+					tar->sparse_gnu_minor = 1;
+					if (value_length > sparse_map_limit) {
+						archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+								  "Unreasonably large sparse map: %llu > %llu",
+								  (unsigned long long)value_length,
+								  (unsigned long long)sparse_map_limit);
+						err = ARCHIVE_FAILED;
+					} else {
+						p = __archive_read_ahead(a, value_length, &bytes_read);
+						if (p == NULL) {
+							archive_set_error(&a->archive, EINVAL,
+									  "Truncated archive"
+									  " detected while reading GNU sparse data");
+							return (ARCHIVE_FATAL);
+						}
+						if (gnu_sparse_01_parse(a, tar, p, value_length) != ARCHIVE_OK) {
+							err = ARCHIVE_WARN;
+						}
+					}
+					__archive_read_consume(a, value_length);
+					return (err);
+				}
+
+				/* GNU "1.0" sparse pax format */
+				else if (key_length == 5 && memcmp(key, "major", 5) == 0) {
+					/* GNU.sparse.major */
+					if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK
+					    && t >= 0
+					    && t <= 10) {
+						tar->sparse_gnu_major = (int)t;
+					}
+					return (err);
+				}
+				else if (key_length == 5 && memcmp(key, "minor", 5) == 0) {
+					/* GNU.sparse.minor */
+					if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK
+					    && t >= 0
+					    && t <= 10) {
+						tar->sparse_gnu_minor = (int)t;
+					}
+					return (err);
+				}
+				else if (key_length == 4 && memcmp(key, "name", 4) == 0) {
+					/* GNU.sparse.name */
+					/*
+					 * The real filename; when storing sparse
+					 * files, GNU tar puts a synthesized name into
+					 * the regular 'path' attribute in an attempt
+					 * to limit confusion. ;-)
+					 */
+					if (value_length > pathname_limit) {
+						*unconsumed += value_length;
+						err = ARCHIVE_WARN;
+					} else {
+						err = read_bytes_to_string(a, &(tar->entry_pathname_override),
+									   value_length, unconsumed);
+					}
+					return (err);
+				}
+				else if (key_length == 8 && memcmp(key, "realsize", 8) == 0) {
+					/* GNU.sparse.realsize = size of file on disk */
+					if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+						tar->GNU_sparse_realsize = t;
+						tar->size_fields |= TAR_SIZE_GNU_SPARSE_REALSIZE;
+					}
+					return (err);
+				}
+			}
+		}
+		break;
+	case 'L':
+		/* LIBARCHIVE extensions */
+		if (key_length > 11 && memcmp(key, "LIBARCHIVE.", 11) == 0) {
+			key_length -= 11;
+			key += 11;
+
+			/* TODO: Handle arbitrary extended attributes... */
+			/*
+			  if (strcmp(key, "LIBARCHIVE.xxxxxxx") == 0)
+				  archive_entry_set_xxxxxx(entry, value);
+			*/
+			if (key_length == 12 && memcmp(key, "creationtime", 12) == 0) {
+				/* LIBARCHIVE.creationtime */
+				if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) {
+					archive_entry_set_birthtime(entry, t, n);
+				}
+				return (err);
+			}
+			else if (key_length == 11 && memcmp(key, "symlinktype", 11) == 0) {
+				/* LIBARCHIVE.symlinktype */
+				if (value_length < 16) {
+					p = __archive_read_ahead(a, value_length, &bytes_read);
+					if (p == NULL) {
+						archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+								  "Truncated tar archive "
+								  "detected while reading `symlinktype` attribute");
+						return (ARCHIVE_FATAL);
+					}
+					if (value_length == 4 && memcmp(p, "file", 4) == 0) {
+						archive_entry_set_symlink_type(entry,
+									       AE_SYMLINK_TYPE_FILE);
+					} else if (value_length == 3 && memcmp(p, "dir", 3) == 0) {
+							archive_entry_set_symlink_type(entry,
+										       AE_SYMLINK_TYPE_DIRECTORY);
+					} else {
+						archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+								  "Unrecognized symlink type");
+						err = ARCHIVE_WARN;
+					}
+				} else {
+					archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+							  "symlink type is very long"
+							  "(longest recognized value is 4 bytes, this is %llu)",
+							  (unsigned long long)value_length);
+					err = ARCHIVE_WARN;
+				}
+				__archive_read_consume(a, value_length);
+				return (err);
+			}
+			else if (key_length > 6 && memcmp(key, "xattr.", 6) == 0) {
+				key_length -= 6;
+				key += 6;
+				if (value_length > xattr_limit) {
+					err = ARCHIVE_WARN;
+				} else {
+					p = __archive_read_ahead(a, value_length, &bytes_read);
+					if (p == NULL) {
+						archive_set_error(&a->archive, EINVAL,
+								  "Truncated archive"
+								  " detected while reading xattr information");
+						return (ARCHIVE_FATAL);
+					}
+					if (pax_attribute_LIBARCHIVE_xattr(entry, key, key_length, p, value_length)) {
+						/* TODO: Unable to parse xattr */
+						err = ARCHIVE_WARN;
+					}
+				}
+				__archive_read_consume(a, value_length);
+				return (err);
+			}
+		}
+		break;
+	case 'R':
+		/* GNU tar uses RHT.security header to store SELinux xattrs
+		 * SCHILY.xattr.security.selinux == RHT.security.selinux */
+		if (key_length == 20 && memcmp(key, "RHT.security.selinux", 20) == 0) {
+			if (value_length > xattr_limit) {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+						  "Ignoring unreasonably large security.selinux attribute:"
+						  " %llu > %llu",
+						  (unsigned long long)value_length,
+						  (unsigned long long)xattr_limit);
+				/* TODO: Should this be FAILED instead? */
+				err = ARCHIVE_WARN;
+			} else {
+				p = __archive_read_ahead(a, value_length, &bytes_read);
+				if (p == NULL) {
+					archive_set_error(&a->archive, EINVAL,
+							  "Truncated archive"
+							  " detected while reading selinux data");
+					return (ARCHIVE_FATAL);
+				}
+				if (pax_attribute_RHT_security_selinux(entry, p, value_length)) {
+					/* TODO: Unable to parse xattr */
+					err = ARCHIVE_WARN;
+				}
+			}
+			__archive_read_consume(a, value_length);
+			return (err);
+		}
+		break;
+	case 'S':
+		/* SCHILY.* extensions used by "star" archiver */
+		if (key_length > 7 && memcmp(key, "SCHILY.", 7) == 0) {
+			key_length -= 7;
+			key += 7;
+
+			if (key_length == 10 && memcmp(key, "acl.access", 10) == 0) {
+				err = pax_attribute_SCHILY_acl(a, tar, entry, value_length,
+						      ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
+				// TODO: Mark mode as set
+				return (err);
+			}
+			else if (key_length == 11 && memcmp(key, "acl.default", 11) == 0) {
+				err = pax_attribute_SCHILY_acl(a, tar, entry, value_length,
+						      ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
+				return (err);
+			}
+			else if (key_length == 7 && memcmp(key, "acl.ace", 7) == 0) {
+				err = pax_attribute_SCHILY_acl(a, tar, entry, value_length,
+						      ARCHIVE_ENTRY_ACL_TYPE_NFS4);
+				// TODO: Mark mode as set
+				return (err);
+			}
+			else if (key_length == 8 && memcmp(key, "devmajor", 8) == 0) {
+				if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+					archive_entry_set_rdevmajor(entry, (dev_t)t);
+				}
+				return (err);
+			}
+			else if (key_length == 8 && memcmp(key, "devminor", 8) == 0) {
+				if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+					archive_entry_set_rdevminor(entry, (dev_t)t);
+				}
+				return (err);
+			}
+			else if (key_length == 6 && memcmp(key, "fflags", 6) == 0) {
+				if (value_length < fflags_limit) {
+					p = __archive_read_ahead(a, value_length, &bytes_read);
+					if (p == NULL) {
+						/* Truncated archive */
+						archive_set_error(&a->archive, EINVAL,
+								  "Truncated archive"
+								  " detected while reading SCHILY.fflags");
+						return (ARCHIVE_FATAL);
+					}
+					archive_entry_copy_fflags_text_len(entry, p, value_length);
+					err = ARCHIVE_OK;
+				} else {
+					/* Overlong fflags field */
+					err = ARCHIVE_WARN;
+				}
+				__archive_read_consume(a, value_length);
+				return (err);
+			}
+			else if (key_length == 3 && memcmp(key, "dev", 3) == 0) {
+				if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+					archive_entry_set_dev(entry, (dev_t)t);
+				}
+				return (err);
+			}
+			else if (key_length == 3 && memcmp(key, "ino", 3) == 0) {
+				if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+					archive_entry_set_ino(entry, t);
+				}
+				return (err);
+			}
+			else if (key_length == 5 && memcmp(key, "nlink", 5) == 0) {
+				if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+					archive_entry_set_nlink(entry, (unsigned int)t);
+				}
+				return (err);
+			}
+			else if (key_length == 8 && memcmp(key, "realsize", 8) == 0) {
+				if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+					tar->SCHILY_sparse_realsize = t;
+					tar->size_fields |= TAR_SIZE_SCHILY_SPARSE_REALSIZE;
+				}
+				return (err);
+			}
+			/* TODO: Is there a SCHILY.sparse.size similar to GNU.sparse.size ? */
+			else if (key_length > 6 && memcmp(key, "xattr.", 6) == 0) {
+				key_length -= 6;
+				key += 6;
+				if (value_length < xattr_limit) {
+					p = __archive_read_ahead(a, value_length, &bytes_read);
+					if (p == NULL) {
+						archive_set_error(&a->archive, EINVAL,
+								  "Truncated archive"
+								  " detected while reading SCHILY.xattr");
+						return (ARCHIVE_FATAL);
+					}
+					if (pax_attribute_SCHILY_xattr(entry, key, key_length, p, value_length)) {
+						/* TODO: Unable to parse xattr */
+						err = ARCHIVE_WARN;
+					}
+				} else {
+					archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+							  "Unreasonably large xattr: %llu > %llu",
+							  (unsigned long long)value_length,
+							  (unsigned long long)xattr_limit);
+					err = ARCHIVE_WARN;
+				}
+				__archive_read_consume(a, value_length);
+				return (err);
+			}
+		}
+		/* SUN.* extensions from Solaris tar */
+		if (key_length > 4 && memcmp(key, "SUN.", 4) == 0) {
+			key_length -= 4;
+			key += 4;
+
+			if (key_length == 9 && memcmp(key, "holesdata", 9) == 0) {
+				/* SUN.holesdata */
+				if (value_length < sparse_map_limit) {
+					p = __archive_read_ahead(a, value_length, &bytes_read);
+					if (p == NULL) {
+						archive_set_error(&a->archive, EINVAL,
+								  "Truncated archive"
+								  " detected while reading SUN.holesdata");
+						return (ARCHIVE_FATAL);
+					}
+					err = pax_attribute_SUN_holesdata(a, tar, entry, p, value_length);
+					if (err < ARCHIVE_OK) {
+						archive_set_error(&a->archive,
+								  ARCHIVE_ERRNO_MISC,
+								  "Parse error: SUN.holesdata");
+					}
+				} else {
+					archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+							  "Unreasonably large sparse map: %llu > %llu",
+							  (unsigned long long)value_length,
+							  (unsigned long long)sparse_map_limit);
+					err = ARCHIVE_FAILED;
+				}
+				__archive_read_consume(a, value_length);
+				return (err);
+			}
+		}
+		break;
+	case 'a':
+		if (key_length == 5 && memcmp(key, "atime", 5) == 0) {
+			if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) {
+				archive_entry_set_atime(entry, t, n);
+			}
+			return (err);
+		}
+		break;
+	case 'c':
+		if (key_length == 5 && memcmp(key, "ctime", 5) == 0) {
+			if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) {
+				archive_entry_set_ctime(entry, t, n);
+			}
+			return (err);
+		} else if (key_length == 7 && memcmp(key, "charset", 7) == 0) {
+			/* TODO: Publish charset information in entry. */
+		} else if (key_length == 7 && memcmp(key, "comment", 7) == 0) {
+			/* TODO: Publish comment in entry. */
+		}
+		break;
+	case 'g':
+		if (key_length == 3 && memcmp(key, "gid", 3) == 0) {
+			if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+				archive_entry_set_gid(entry, t);
+			}
+			return (err);
+		} else if (key_length == 5 && memcmp(key, "gname", 5) == 0) {
+			if (value_length > guname_limit) {
+				*unconsumed += value_length;
+				err = ARCHIVE_WARN;
+			} else {
+				err = read_bytes_to_string(a, &(tar->entry_gname), value_length, unconsumed);
+			}
+			return (err);
+		}
+		break;
+	case 'h':
+		if (key_length == 10 && memcmp(key, "hdrcharset", 10) == 0) {
+			if (value_length < 64) {
+				p = __archive_read_ahead(a, value_length, &bytes_read);
+				if (p == NULL) {
+					archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+							  "Truncated tar archive "
+							  "detected while reading hdrcharset attribute");
+					return (ARCHIVE_FATAL);
+				}
+				if (value_length == 6
+				    && memcmp(p, "BINARY", 6) == 0) {
+					/* Binary  mode. */
+					tar->pax_hdrcharset_utf8 = 0;
+					err = ARCHIVE_OK;
+				} else if (value_length == 23
+					   && memcmp(p, "ISO-IR 10646 2000 UTF-8", 23) == 0) {
+					tar->pax_hdrcharset_utf8 = 1;
+					err = ARCHIVE_OK;
+				} else {
+					/* TODO: Unrecognized character set */
+					err  = ARCHIVE_WARN;
+				}
+			} else {
+				archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+						  "hdrcharset attribute is unreasonably large (%llu bytes)",
+						  (unsigned long long)value_length);
+				err = ARCHIVE_WARN;
+			}
+			__archive_read_consume(a, value_length);
+			return (err);
+		}
+		break;
+	case 'l':
+		/* pax interchange doesn't distinguish hardlink vs. symlink. */
+		if (key_length == 8 && memcmp(key, "linkpath", 8) == 0) {
+			if (value_length > pathname_limit) {
+				*unconsumed += value_length;
+				err = ARCHIVE_WARN;
+			} else {
+				err = read_bytes_to_string(a, &tar->entry_linkpath, value_length, unconsumed);
+			}
+			return (err);
+		}
+		break;
+	case 'm':
+		if (key_length == 5 && memcmp(key, "mtime", 5) == 0) {
+			if ((err = pax_attribute_read_time(a, value_length, &t, &n, unconsumed)) == ARCHIVE_OK) {
+				archive_entry_set_mtime(entry, t, n);
+			}
+			return (err);
+		}
+		break;
+	case 'p':
+		if (key_length == 4 && memcmp(key, "path", 4) == 0) {
+			if (value_length > pathname_limit) {
+				*unconsumed += value_length;
+				err = ARCHIVE_WARN;
+			} else {
+				err = read_bytes_to_string(a, &(tar->entry_pathname), value_length, unconsumed);
+			}
+			return (err);
+		}
+		break;
+	case 'r':
+		/* POSIX has reserved 'realtime.*' */
+		break;
+	case 's':
+		/* POSIX has reserved 'security.*' */
+		/* Someday: if (strcmp(key, "security.acl") == 0) { ... } */
+		if (key_length == 4 && memcmp(key, "size", 4) == 0) {
+			/* "size" is the size of the data in the entry. */
+			if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+				tar->pax_size = t;
+				tar->size_fields |= TAR_SIZE_PAX_SIZE;
+			}
+			else if (t == INT64_MAX) {
+				/* Note: pax_attr_read_number returns INT64_MAX on overflow or < 0 */
+				tar->entry_bytes_remaining = 0;
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Tar size attribute overflow");
+				return (ARCHIVE_FATAL);
+			}
+			return (err);
+		}
+		break;
+	case 'u':
+		if (key_length == 3 && memcmp(key, "uid", 3) == 0) {
+			if ((err = pax_attribute_read_number(a, value_length, &t)) == ARCHIVE_OK) {
+				archive_entry_set_uid(entry, t);
+			}
+			return (err);
+		} else if (key_length == 5 && memcmp(key, "uname", 5) == 0) {
+			if (value_length > guname_limit) {
+				*unconsumed += value_length;
+				err = ARCHIVE_WARN;
+			} else {
+				err = read_bytes_to_string(a, &(tar->entry_uname), value_length, unconsumed);
+			}
+			return (err);
+		}
+		break;
+	}
+
+	/* Unrecognized key, just skip the entire value. */
+	__archive_read_consume(a, value_length);
+	return (err);
+}
+
+
+
+/*
+ * Parse a decimal time value, which may include a fractional portion
+ *
+ * Sets ps to INT64_MIN on error.
+ */
+static void
+pax_time(const char *p, size_t length, 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;
+
+	if (length <= 0) {
+		*ps = 0;
+		*pn = 0;
+		return;
+	}
+	s = 0;
+	sign = 1;
+	if (*p == '-') {
+		sign = -1;
+		p++;
+		length--;
+	}
+	while (length > 0 && *p >= '0' && *p <= '9') {
+		digit = *p - '0';
+		if (s > limit ||
+		    (s == limit && digit > last_digit_limit)) {
+			*ps = INT64_MIN;
+			*pn = 0;
+			return;
+		}
+		s = (s * 10) + digit;
+		++p;
+		--length;
+	}
+
+	*ps = s * sign;
+
+	/* Calculate nanoseconds. */
+	*pn = 0;
+
+	if (length <= 0 || *p != '.')
+		return;
+
+	l = 100000000UL;
+	do {
+		++p;
+		--length;
+		if (length > 0 && *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, int64_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.
+	 */
+
+	/* Copy filename over (to ensure null termination). */
+	header = (const struct archive_entry_header_gnutar *)h;
+	const char *existing_pathname = archive_entry_pathname(entry);
+	const wchar_t *existing_wcs_pathname = archive_entry_pathname_w(entry);
+	if ((existing_pathname == NULL || existing_pathname[0] == '\0')
+	    && (existing_wcs_pathname == NULL || existing_wcs_pathname[0] == L'\0')) {
+		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?  */
+	const char *existing_uname = archive_entry_uname(entry);
+	if (existing_uname == NULL || existing_uname[0] == '\0') {
+		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);
+		}
+	}
+
+	const char *existing_gname = archive_entry_gname(entry);
+	if (existing_gname == NULL || existing_gname[0] == '\0') {
+		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') {
+		if (!archive_entry_rdev_is_set(entry)) {
+			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);
+	}
+
+	/* Grab GNU-specific fields. */
+	if (!archive_entry_atime_is_set(entry)) {
+		t = tar_atol(header->atime, sizeof(header->atime));
+		if (t > 0)
+			archive_entry_set_atime(entry, t, 0);
+	}
+	if (!archive_entry_ctime_is_set(entry)) {
+		t = tar_atol(header->ctime, sizeof(header->ctime));
+		if (t > 0)
+			archive_entry_set_ctime(entry, t, 0);
+	}
+
+	if (header->realsize[0] != 0) {
+		/* Treat as a synonym for the pax GNU.sparse.realsize attr */
+		tar->GNU_sparse_realsize
+		    = tar_atol(header->realsize, sizeof(header->realsize));
+		tar->size_fields |= TAR_SIZE_GNU_SPARSE_REALSIZE;
+	}
+
+	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 */
+		}
+	}
+
+	/* Grab fields common to all tar variants. */
+	err = header_common(a, tar, entry, h);
+	if (err == ARCHIVE_FATAL)
+		return (err);
+
+	tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+
+	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 = 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, int64_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 {
+		if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+			return (ARCHIVE_FATAL);
+		}
+		data = __archive_read_ahead(a, 512, &bytes_read);
+		if (data == NULL) {
+			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, size_t length)
+{
+	const char *e;
+	int64_t offset = -1, size = -1;
+
+	for (;;) {
+		e = p;
+		while (length > 0 && *e != ',') {
+			if (*e < '0' || *e > '9')
+				return (ARCHIVE_WARN);
+			e++;
+			length--;
+		}
+		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 (length == 0)
+			return (ARCHIVE_OK);
+		p = e + 1;
+		length--;
+	}
+}
+
+/*
+ * 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 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, int64_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 int64_t
+gnu_sparse_10_read(struct archive_read *a, struct tar *tar, int64_t *unconsumed)
+{
+	int64_t bytes_read, entries, offset, size, to_skip, remaining;
+
+	/* Clear out the existing sparse list. */
+	gnu_clear_sparse_list(tar);
+
+	remaining = tar->entry_bytes_remaining;
+
+	/* Parse entries. */
+	entries = 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... */
+	if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+		return (ARCHIVE_FATAL);
+	}
+	bytes_read = 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 (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
+pax_attribute_SUN_holesdata(struct archive_read *a, struct tar *tar,
+	struct archive_entry *entry, const char *p, size_t length)
+{
+	const char *e;
+	int64_t start, end;
+	int hole = 1;
+
+	(void)entry; /* UNUSED */
+
+	end = 0;
+	if (length <= 0)
+		return (ARCHIVE_WARN);
+	if (*p == ' ') {
+		p++;
+		length--;
+	} else {
+		return (ARCHIVE_WARN);
+	}
+	for (;;) {
+		e = p;
+		while (length > 0 && *e != ' ') {
+			if (*e < '0' || *e > '9')
+				return (ARCHIVE_WARN);
+			e++;
+			length--;
+		}
+		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 (length == 0 || *e == '\n') {
+			if (length == 0 && *e == '\n') {
+				return (ARCHIVE_OK);
+			} else {
+				return (ARCHIVE_WARN);
+			}
+		}
+		p = e + 1;
+		length--;
+		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, int64_t *unconsumed)
+{
+	ssize_t bytes_read;
+	ssize_t total_size = 0;
+	const void *t;
+	const char *s;
+	void *p;
+
+	if (tar_flush_unconsumed(a, unconsumed) != ARCHIVE_OK) {
+		return (ARCHIVE_FATAL);
+	}
+
+	t = __archive_read_ahead(a, 1, &bytes_read);
+	if (bytes_read <= 0 || t == NULL)
+		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 || t == NULL)
+			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 = 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, size_t length)
+{
+	char *out, *d;
+	const char *s;
+
+	out = malloc(length + 1);
+	if (out == NULL)
+		return (NULL);
+	for (s = in, d = out; length > 0 && *s != '\0'; ) {
+		if (s[0] == '%' && length > 2) {
+			/* 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;
+				length -= 3;
+				/* Convert output */
+				*d++ = ((digit1 << 4) | digit2);
+				continue;
+			}
+			/* Else fall through and treat '%' as normal char */
+		}
+		*d++ = *s++;
+		--length;
+	}
+	*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,858 @@
+/*-
+ * 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"
+
+/**
+ * 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, 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;
+	char *tmp;
+	/* 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;
+			tmp = realloc(w->pool.str, w->pool.len);
+			if (tmp == NULL) {
+				archive_set_error(
+					&a->archive, ENOMEM,
+					"Out of memory");
+				return (ARCHIVE_FATAL);
+			}
+			w->pool.str = tmp;
+		}
+		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;
+	case WT_NONE:
+	case WT_INFO:
+	case WT_META:
+	case WT_REQ:
+	case WT_RVIS:
+	case WT_CONV:
+	case WT_CONT:
+	case LAST_WT:
+	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 */
+	case WT_NONE:
+	case WT_INFO:
+	case WT_META:
+	case WT_REQ:
+	case WT_RVIS:
+	case WT_CONV:
+	case WT_CONT:
+	case LAST_WT:
+	default:
+		/* consume the content and start over */
+		if (_warc_skip(a) < 0)
+			return (ARCHIVE_FATAL);
+		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;
+		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;
+
+	if (__archive_read_consume(a, w->cntlen) < 0 ||
+	    __archive_read_consume(a, 4U/*\r\n\r\n separator*/) < 0)
+		return (ARCHIVE_FATAL);
+	w->cntlen = 0U;
+	w->cntoff = 0U;
+	return (ARCHIVE_OK);
+}
+
+
+/* private routines */
+static void*
+deconst(const void *c)
+{
+	return (void *)(uintptr_t)c;
+}
+
+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__MKGMTIME
+        return _mkgmtime(t);
+#elif HAVE_TIMEGM
+        /* Use platform timegm() if available. */
+        return (timegm(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,3667 @@
+/*-
+ * 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
+#if HAVE_LIBXML_XMLREADER_H
+#include <libxml/xmlreader.h>
+#elif HAVE_BSDXML_H
+#include <bsdxml.h>
+#elif HAVE_EXPAT_H
+#include <expat.h>
+#elif HAVE_XMLLITE_H
+#include <objidl.h>
+#include <initguid.h>
+#include <xmllite.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_XMLLITE_H)) ||\
+	!defined(HAVE_ZLIB_H) || \
+	!defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
+/*
+ * xar needs several external libraries.
+ *   o libxml2, expat or (Windows only) xmllite --- 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 *, size_t);
+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 *);
+#elif defined(HAVE_XMLLITE_H)
+static int	xmllite_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 = 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);
+#elif defined(HAVE_XMLLITE_H)
+	r = xmllite_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;
+#ifndef DONT_FAIL_ON_CRC_ERROR
+		if (r != ARCHIVE_OK)
+			return (ARCHIVE_FATAL);
+#endif
+	}
+
+	/*
+	 * 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) {
+#ifndef DONT_FAIL_ON_CRC_ERROR
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+			    "Xattr checksum error");
+			r = ARCHIVE_WARN;
+			break;
+#endif
+		}
+		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 = (int64_t)xar->entry_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] <= 'f')
+			x = (p[0] - 'a' + 0x0a) << 4;
+		else if (p[0] >= 'A' && p[0] <= 'F')
+			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] <= 'f')
+			x |= p[1] - 'a' + 0x0a;
+		else if (p[1] >= 'A' && p[1] <= 'F')
+			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__MKGMTIME
+        return _mkgmtime(t);
+#elif HAVE_TIMEGM
+        /* Use platform timegm() if available. */
+        return (timegm(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 **)
+		    calloc(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 = (uInt)avail_in;
+		xar->stream.next_out = (unsigned char *)outbuff;
+		xar->stream.avail_out = (uInt)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 = (unsigned int)avail_in;
+		xar->bzstream.next_out = (char *)outbuff;
+		xar->bzstream.avail_out = (unsigned int)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));
+#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 (xar->file->hdnext != NULL || xar->file->link != 0 ||
+				    xar->file == xar->hdlink_orgs) {
+					archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+					    "File with multiple link attributes");
+					return (ARCHIVE_FATAL);
+				}
+				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, size_t 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->has & HAS_PATHNAME)
+			break;
+
+		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;
+	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) {
+				xmlFreeTextReader(reader);
+				xmlCleanupParser();
+				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;
+
+	if (ud->state != ARCHIVE_OK)
+		return;
+
+	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, (size_t)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) {
+			XML_ParserFree(parser);
+			return (r);
+		}
+		xar->toc_remaining -= used;
+		xar->offset += used;
+		xar->toc_total += outbytes;
+		PRINT_TOC(d, outbytes);
+
+		xr = XML_Parse(parser, d, (int)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);
+}
+
+#elif defined(HAVE_XMLLITE_H)
+
+struct ArchiveStreamAdapter {
+	const ISequentialStreamVtbl *lpVtbl; /* see asaStaticVtable */
+	struct archive_read *a;
+};
+
+static HRESULT STDMETHODCALLTYPE
+asaQueryInterface(ISequentialStream *this, REFIID riid, void **ppv)
+{
+	if (!IsEqualIID(riid, &IID_ISequentialStream)) {
+		*ppv = NULL;
+		return E_NOINTERFACE;
+	}
+	*ppv = this;
+	return S_OK;
+}
+
+/*
+ * We can dispense with reference counting as we tightly manage the lifetime
+ * of an ArchiveStreamAdapter.
+ */
+static ULONG STDMETHODCALLTYPE
+asaAddRef(ISequentialStream *this)
+{
+	(void)this; /* UNUSED */
+	return ULONG_MAX;
+}
+
+static ULONG STDMETHODCALLTYPE
+asaRelease(ISequentialStream *this)
+{
+	(void)this; /* UNUSED */
+	return ULONG_MAX;
+}
+
+static HRESULT STDMETHODCALLTYPE
+asaRead(ISequentialStream *this, void *pv, ULONG cb, ULONG *pcbRead)
+{
+	struct ArchiveStreamAdapter *asa = (struct ArchiveStreamAdapter *)this;
+	struct archive_read *a;
+	struct xar *xar;
+	const void *d = pv;
+	size_t outbytes = cb;
+	size_t used = 0;
+	int r;
+
+	a = asa->a;
+	xar = (struct xar *)(a->format->data);
+
+	*pcbRead = 0;
+
+	if (xar->toc_remaining <= 0)
+		return cb != 0 ? S_FALSE : S_OK;
+
+	r = rd_contents(a, &d, &outbytes, &used, xar->toc_remaining);
+	if (r != ARCHIVE_OK)
+		return E_FAIL;
+	__archive_read_consume(a, used);
+	xar->toc_remaining -= used;
+	xar->offset += used;
+	xar->toc_total += outbytes;
+	PRINT_TOC(pv, outbytes);
+
+	*pcbRead = (ULONG)outbytes;
+	return outbytes < cb ? S_FALSE : S_OK;
+}
+
+static HRESULT STDMETHODCALLTYPE
+asaWrite(ISequentialStream *this, const void *pv, ULONG cb, ULONG *pcbWritten)
+{
+	(void)this; /* UNUSED */
+	(void)pv; /* UNUSED */
+	(void)cb; /* UNUSED */
+	if (!pcbWritten) return E_INVALIDARG;
+	*pcbWritten = 0;
+	return E_NOTIMPL;
+}
+
+static const ISequentialStreamVtbl asaStaticVtable = {
+	.QueryInterface = asaQueryInterface,
+	.AddRef = asaAddRef,
+	.Release = asaRelease,
+	.Read = asaRead,
+	.Write = asaWrite,
+};
+
+static int
+xmllite_create_stream_adapter(struct archive_read *a,
+    struct ArchiveStreamAdapter **pasa)
+{
+	struct ArchiveStreamAdapter *asa =
+	    calloc(1, sizeof(struct ArchiveStreamAdapter));
+	if (!asa) {
+		archive_set_error(&(a->archive), ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+	asa->lpVtbl = &asaStaticVtable;
+	asa->a = a;
+	*pasa = asa;
+	return (ARCHIVE_OK);
+}
+
+typedef HRESULT(STDMETHODCALLTYPE *xmllite_wstr_func)(IXmlReader *, LPCWSTR *,
+    UINT *);
+
+/*
+ * Returns an narrow-char archive_string in *as after calling
+ * the wide-char COM API callee() on the XmlReader reader.
+ * Sets an appropriate error on the archive if it fails.
+ */
+static int
+xmllite_call_return_as(struct archive_read *a, struct archive_string *as,
+    IXmlReader *reader, xmllite_wstr_func callee)
+{
+	LPCWSTR wcs;
+	UINT wlen;
+
+	if (FAILED(callee(reader, &wcs, &wlen))) {
+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+		    "Failed to read XML data");
+		return (ARCHIVE_FATAL);
+	}
+
+	archive_string_init(as);
+	if (archive_string_append_from_wcs(as, wcs, (size_t)wlen) < 0) {
+		archive_string_free(as);
+		archive_set_error(&(a->archive), ENOMEM, "Out of memory");
+		return (ARCHIVE_FATAL);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static char *
+xmllite_call_return_mbs(struct archive_read *a, IXmlReader *reader,
+    xmllite_wstr_func callee)
+{
+	char *ret;
+	struct archive_string as;
+
+	if (xmllite_call_return_as(a, &as, reader, callee) < 0) {
+		return NULL;
+	}
+
+	ret = strdup(as.s);
+	archive_string_free(&as);
+	if (ret == NULL) {
+		archive_set_error(&(a->archive), ENOMEM, "Out of memory");
+		return NULL;
+	}
+	return ret;
+}
+
+static int
+xmllite_xmlattr_setup(struct archive_read *a,
+    struct xmlattr_list *list, IXmlReader *reader)
+{
+	struct xmlattr *attr;
+	HRESULT hr;
+
+	list->first = NULL;
+	list->last = &(list->first);
+	hr = reader->lpVtbl->MoveToFirstAttribute(reader);
+	/* Contrary to other checks, we're not using SUCCEEDED/FAILED
+	 * because MoveToNextAttribute returns *S_FALSE* (success!)
+	 * when it runs out of attributes.
+	 */
+	while (hr == S_OK) {
+		/* Attributes implied as being default by the DTD are ignored */
+		if (reader->lpVtbl->IsDefault(reader))
+			continue;
+
+		attr = malloc(sizeof*(attr));
+		if (attr == NULL) {
+			archive_set_error(&(a->archive), ENOMEM,
+			    "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+
+		attr->name = xmllite_call_return_mbs(a, reader,
+		    reader->lpVtbl->GetLocalName);
+		if (attr->name == NULL) {
+			free(attr);
+			/* xmllite_call_return_mbs sets an appropriate error */
+			return (ARCHIVE_FATAL);
+		}
+
+		attr->value = xmllite_call_return_mbs(a, reader,
+		    reader->lpVtbl->GetValue);
+		if (attr->value == NULL) {
+			free(attr->name);
+			free(attr);
+			/* xmllite_call_return_mbs sets an appropriate error */
+			return (ARCHIVE_FATAL);
+		}
+
+		attr->next = NULL;
+		*list->last = attr;
+		list->last = &(attr->next);
+		hr = reader->lpVtbl->MoveToNextAttribute(reader);
+	}
+
+	if (FAILED(hr)) {
+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_FILE_FORMAT,
+		    "Failed to parse XML document");
+		return (ARCHIVE_FAILED);
+	}
+
+	return (ARCHIVE_OK);
+}
+
+static int
+xmllite_read_toc(struct archive_read *a)
+{
+	struct ArchiveStreamAdapter *asa = NULL;
+	char *name;
+	struct archive_string as;
+	BOOL empty;
+	XmlNodeType type;
+	struct xmlattr_list list;
+	IXmlReader *reader = NULL;
+	int r = ARCHIVE_OK;
+
+	if ((r = xmllite_create_stream_adapter(a, &asa)) < 0) {
+		goto out;
+	}
+
+	if (FAILED(CreateXmlReader(&IID_IXmlReader, (void **)&reader, NULL))) {
+		r = ARCHIVE_FATAL;
+		goto out;
+	}
+
+	if (FAILED(reader->lpVtbl->SetInput(reader, (IUnknown *)asa))) {
+		archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+		    "Failed to prepare XML stream");
+		r = ARCHIVE_FATAL;
+		goto out;
+	}
+
+	while (!reader->lpVtbl->IsEOF(reader)) {
+		if (FAILED(reader->lpVtbl->Read(reader, &type))) {
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+			    "Failed to read XML stream");
+			r = ARCHIVE_FATAL;
+			goto out;
+		}
+
+		switch (type) {
+		case XmlNodeType_Element:
+			empty = reader->lpVtbl->IsEmptyElement(reader);
+
+			name = xmllite_call_return_mbs(a, reader,
+			    reader->lpVtbl->GetLocalName);
+			if (name == NULL) {
+				/* xmllite_call_return_mbs sets an appropriate error */
+				r = ARCHIVE_FATAL;
+				goto out;
+			}
+
+			r = xmllite_xmlattr_setup(a, &list, reader);
+			if (r == ARCHIVE_OK) {
+				r = xml_start(a, name, &list);
+			}
+			xmlattr_cleanup(&list);
+			if (r == ARCHIVE_OK && empty) {
+				xml_end(a, name);
+			}
+
+			free(name);
+			if (r != ARCHIVE_OK) {
+				goto out;
+			}
+
+			break;
+		case XmlNodeType_EndElement:
+			name = xmllite_call_return_mbs(a, reader,
+			    reader->lpVtbl->GetLocalName);
+			if (name == NULL) {
+				/* xmllite_call_return_mbs sets an appropriate error */
+				r = ARCHIVE_FATAL;
+				goto out;
+			}
+
+			xml_end(a, name);
+			free(name);
+			break;
+		case XmlNodeType_Text:
+			r = xmllite_call_return_as(a, &as, reader,
+			    reader->lpVtbl->GetValue);
+			if (r != ARCHIVE_OK) {
+				/* xmllite_call_return_as sets an appropriate error */
+				goto out;
+			}
+
+			xml_data(a, as.s, (int)archive_strlen(&as));
+			archive_string_free(&as);
+
+		case XmlNodeType_None:
+		case XmlNodeType_Attribute:
+		case XmlNodeType_CDATA:
+		case XmlNodeType_ProcessingInstruction:
+		case XmlNodeType_Comment:
+		case XmlNodeType_DocumentType:
+		case XmlNodeType_Whitespace:
+		case XmlNodeType_XmlDeclaration:
+		default:
+			break;
+		}
+	}
+
+out:
+	if (reader)
+		reader->lpVtbl->Release(reader);
+
+	free(asa);
+
+	return r;
+}
+#endif /* defined(XMLLITE) */
+
+#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,4406 @@
+/*-
+ * 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"
+
+/*
+ * 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
+#ifdef HAVE_ZSTD_H
+#include <zstd.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_time_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) /* Also called "Streaming bit" */
+#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;
+	int64_t			central_directory_offset_adjusted;
+	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 and decrypted data */
+	unsigned long		computed_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
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+	ZSTD_DStream	*zstdstream;
+	char            zstdstream_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))
+
+#ifdef HAVE_ZLIB_H
+static int
+zip_read_data_deflate(struct archive_read *a, const void **buff,
+	size_t *size, int64_t *offset);
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+static int
+zip_read_data_zipx_lzma_alone(struct archive_read *a, const void **buff,
+	size_t *size, int64_t *offset);
+#endif
+
+/* 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) */
+	{93, "zstd"},     /*  Zstandard (zstd) Compression */
+	{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 "??";
+}
+
+/*
+ * 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->computed_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 = dos_to_unix(archive_le32dec(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 == 0xffffffff) {
+			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 == 0xffffffff) {
+			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);
+
+		// take into account link compression if any
+		size_t linkname_full_length = linkname_length;
+		if (zip->entry->compression != 0)
+		{
+			// symlink target string appeared to be compressed
+			int status = ARCHIVE_FATAL;
+			const void *uncompressed_buffer = NULL;
+
+			switch (zip->entry->compression)
+			{
+#if HAVE_ZLIB_H
+				case 8: /* Deflate compression. */
+					zip->entry_bytes_remaining = zip_entry->compressed_size;
+					status = zip_read_data_deflate(a, &uncompressed_buffer,
+						&linkname_full_length, NULL);
+					break;
+#endif
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+				case 14: /* ZIPx LZMA compression. */
+					/*(see zip file format specification, section 4.4.5)*/
+					zip->entry_bytes_remaining = zip_entry->compressed_size;
+					status = zip_read_data_zipx_lzma_alone(a, &uncompressed_buffer,
+						&linkname_full_length, NULL);
+					break;
+#endif
+				default: /* Unsupported compression. */
+					break;
+			}
+			if (status == ARCHIVE_OK)
+			{
+				p = uncompressed_buffer;
+			}
+			else
+			{
+				archive_set_error(&a->archive,
+					ARCHIVE_ERRNO_FILE_FORMAT,
+					"Unsupported ZIP compression method "
+					"during decompression of link entry (%d: %s)",
+					zip->entry->compression,
+					compression_name(zip->entry->compression));
+				return ARCHIVE_FAILED;
+			}
+		}
+		else
+		{
+			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_full_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_full_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
+	       && zip_entry->uncompressed_size != 0xffffffff)) {
+		/* 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);
+}
+
+/*
+ * The Zip end-of-file marker is inherently ambiguous.  The specification
+ * in APPNOTE.TXT allows any of four possible formats, and there is no
+ * guaranteed-correct way for a reader to know a priori which one the writer
+ * will have used.  The four formats are:
+ * 1. 32-bit format with an initial PK78 marker
+ * 2. 32-bit format without that marker
+ * 3. 64-bit format with the marker
+ * 4. 64-bit format without the marker
+ *
+ * Mark Adler's `sunzip` streaming unzip program solved this ambiguity
+ * by just looking at every possible combination and accepting the
+ * longest one that matches the expected values.  His approach always
+ * consumes the longest possible matching EOF marker, based on an
+ * analysis of all the possible failures and how the values could
+ * overlap.
+ *
+ * For example, suppose both of the first two formats listed
+ * above match.  In that case, we know the next four
+ * 32-bit words match this pattern:
+ * ```
+ *  [PK\07\08] [CRC32]        [compressed size]   [uncompressed size]
+ * ```
+ * but we know they must also match this pattern:
+ * ```
+ *  [CRC32] [compressed size] [uncompressed size] [other PK marker]
+ * ```
+ *
+ * Since the first word here matches both the PK78 signature in the
+ * first form and the CRC32 in the second, we know those two values
+ * are equal, the CRC32 must be exactly 0x08074b50.  Similarly, the
+ * compressed and uncompressed size must also be exactly this value.
+ * So we know these four words are all 0x08074b50.  If we were to
+ * accept the shorter pattern, it would be immediately followed by
+ * another PK78 marker, which is not possible in a well-formed ZIP
+ * archive unless there is garbage between entries. This implies we
+ * should not accept the shorter form in such a case; we should accept
+ * the longer form.
+ *
+ * If the second and third possibilities above both match, we
+ * have a slightly different situation.  The following words
+ * must match both the 32-bit format
+ * ```
+ *  [CRC32] [compressed size] [uncompressed size] [other PK marker]
+ * ```
+ * and the 64-bit format
+ * ```
+ *  [CRC32] [compressed low] [compressed high] [uncompressed low] [uncompressed high] [other PK marker]
+ * ```
+ * Since the 32-bit and 64-bit compressed sizes both match, the
+ * actual size must fit in 32 bits, which implies the high-order
+ * word of the compressed size is zero.  So we know the uncompressed
+ * low word is zero, which again implies that if we accept the shorter
+ * format, there will not be a valid PK marker following it.
+ *
+ * Similar considerations rule out the shorter form in every other
+ * possibly-ambiguous pair.  So if two of the four possible formats
+ * match, we should accept the longer option.
+ *
+ * If none of the four formats matches, we know the archive must be
+ * corrupted in some fashion.  In particular, it's possible that the
+ * length-at-end bit was incorrect and we should not really be looking
+ * for an EOF marker at all.  To allow for this possibility, we
+ * evaluate the following words to collect data for a later error
+ * report but do not consume any bytes.  We instead rely on the later
+ * search for a new PK marker to re-sync to the next well-formed
+ * entry.
+ */
+static void
+consume_end_of_file_marker(struct archive_read *a, struct zip *zip)
+{
+	const char *marker;
+	const char *p;
+	uint64_t compressed32, uncompressed32;
+	uint64_t compressed64, uncompressed64;
+	uint64_t compressed_actual, uncompressed_actual;
+	uint32_t crc32_actual;
+	const uint32_t PK78 = 0x08074B50ULL;
+	uint8_t crc32_ignored, crc32_may_be_zero;
+
+	/* If there shouldn't be a marker, don't consume it. */
+	if ((zip->entry->zip_flags & ZIP_LENGTH_AT_END) == 0) {
+		return;
+	}
+
+	/* The longest Zip end-of-file record is 24 bytes.  Since an
+	 * end-of-file record can never appear at the end of the
+	 * archive, we know 24 bytes will be available unless
+	 * the archive is severely truncated. */
+	if (NULL == (marker = __archive_read_ahead(a, 24, NULL))) {
+		return;
+	}
+	p = marker;
+
+	/* The end-of-file record comprises:
+	 * = Optional PK\007\010 marker
+	 * = 4-byte CRC32
+	 * = Compressed size
+	 * = Uncompressed size
+	 *
+	 * The last two fields are either both 32 bits or both 64
+	 * bits.  We check all possible layouts and accept any one
+	 * that gives us a complete match, else we make a best-effort
+	 * attempt to parse out the pieces.
+	 */
+
+	/* CRC32 checking can be tricky:
+	 * * Test suites sometimes ignore the CRC32
+	 * * AES AE-2 always writes zero for the CRC32
+	 * * AES AE-1 sometimes writes zero for the CRC32
+	 */
+	crc32_ignored = zip->ignore_crc32;
+	crc32_may_be_zero = 0;
+	crc32_actual = zip->computed_crc32;
+	if (zip->hctx_valid) {
+	  switch (zip->entry->aes_extra.vendor) {
+	  case AES_VENDOR_AE_2:
+	    crc32_actual = 0;
+	    break;
+	  case AES_VENDOR_AE_1:
+	  default:
+	    crc32_may_be_zero = 1;
+	    break;
+	  }
+	}
+
+	/* Values computed from the actual data in the archive. */
+	compressed_actual = (uint64_t)zip->entry_compressed_bytes_read;
+	uncompressed_actual = (uint64_t)zip->entry_uncompressed_bytes_read;
+
+
+	/* Longest: PK78 marker, all 64-bit fields (24 bytes total) */
+	if (archive_le32dec(p) == PK78
+	    && ((archive_le32dec(p + 4) == crc32_actual)
+		|| (crc32_may_be_zero && (archive_le32dec(p + 4) == 0))
+		|| crc32_ignored)
+	    && (archive_le64dec(p + 8) == compressed_actual)
+	    && (archive_le64dec(p + 16) == uncompressed_actual)) {
+		if (!crc32_ignored) {
+			zip->entry->crc32 = crc32_actual;
+		}
+		zip->entry->compressed_size = compressed_actual;
+		zip->entry->uncompressed_size = uncompressed_actual;
+		zip->unconsumed += 24;
+		return;
+	}
+
+	/* No PK78 marker, 64-bit fields (20 bytes total) */
+	if (((archive_le32dec(p) == crc32_actual)
+	     || (crc32_may_be_zero && (archive_le32dec(p + 4) == 0))
+	     || crc32_ignored)
+	    && (archive_le64dec(p + 4) == compressed_actual)
+	    && (archive_le64dec(p + 12) == uncompressed_actual)) {
+	        if (!crc32_ignored) {
+			zip->entry->crc32 = crc32_actual;
+		}
+		zip->entry->compressed_size = compressed_actual;
+		zip->entry->uncompressed_size = uncompressed_actual;
+		zip->unconsumed += 20;
+		return;
+	}
+
+	/* PK78 marker and 32-bit fields (16 bytes total) */
+	if (archive_le32dec(p) == PK78
+	    && ((archive_le32dec(p + 4) == crc32_actual)
+		|| (crc32_may_be_zero && (archive_le32dec(p + 4) == 0))
+		|| crc32_ignored)
+	    && (archive_le32dec(p + 8) == compressed_actual)
+	    && (archive_le32dec(p + 12) == uncompressed_actual)) {
+		if (!crc32_ignored) {
+			zip->entry->crc32 = crc32_actual;
+		}
+		zip->entry->compressed_size = compressed_actual;
+		zip->entry->uncompressed_size = uncompressed_actual;
+		zip->unconsumed += 16;
+		return;
+	}
+
+	/* Shortest: No PK78 marker, all 32-bit fields (12 bytes total) */
+	if (((archive_le32dec(p) == crc32_actual)
+	     || (crc32_may_be_zero && (archive_le32dec(p + 4) == 0))
+	     || crc32_ignored)
+	    && (archive_le32dec(p + 4) == compressed_actual)
+	    && (archive_le32dec(p + 8) == uncompressed_actual)) {
+		if (!crc32_ignored) {
+			zip->entry->crc32 = crc32_actual;
+		}
+		zip->entry->compressed_size = compressed_actual;
+		zip->entry->uncompressed_size = uncompressed_actual;
+		zip->unconsumed += 12;
+		return;
+	}
+
+	/* If none of the above patterns gives us a full exact match,
+	 * then there's something definitely amiss.  The fallback code
+	 * below will parse out some plausible values for error
+	 * reporting purposes.  Note that this won't actually
+	 * consume anything:
+	 *
+	 * = If there really is a marker here, the logic to resync to
+	 *   the next entry will suffice to skip it.
+	 *
+	 * = There might not really be a marker: Corruption or bugs
+	 *   may have set the length-at-end bit without a marker ever
+	 *   having actually been written. In this case, we
+	 *   explicitly should not consume any bytes, since that would
+	 *   prevent us from correctly reading the next entry.
+	 */
+	if (archive_le32dec(p) == PK78) {
+		p += 4; /* Ignore PK78 if it appears to be present */
+	}
+	zip->entry->crc32 = archive_le32dec(p);  /* Parse CRC32 */
+	p += 4;
+
+	/* Consider both 32- and 64-bit interpretations */
+	compressed32 = archive_le32dec(p);
+	uncompressed32 = archive_le32dec(p + 4);
+	compressed64 = archive_le64dec(p);
+	uncompressed64 = archive_le64dec(p + 8);
+
+	/* The earlier patterns may have failed because of CRC32
+	 * mismatch, so it's still possible that both sizes match.
+	 * Try to match as many as we can...
+	 */
+	if (compressed32 == compressed_actual
+	    && uncompressed32 == uncompressed_actual) {
+		/* Both 32-bit fields match */
+		zip->entry->compressed_size = compressed32;
+		zip->entry->uncompressed_size = uncompressed32;
+	} else if (compressed64 == compressed_actual
+		   || uncompressed64 == uncompressed_actual) {
+		/* One or both 64-bit fields match */
+		zip->entry->compressed_size = compressed64;
+		zip->entry->uncompressed_size = uncompressed64;
+	} else {
+		/* Zero or one 32-bit fields match */
+		zip->entry->compressed_size = compressed32;
+		zip->entry->uncompressed_size = uncompressed32;
+	}
+}
+
+/*
+ * Read "uncompressed" data.
+ *
+ * This is straightforward if we know the size of the data.  This is
+ * always true for the seeking reader (we've examined the Central
+ * Directory already), and will often be true for the streaming reader
+ * (the writer was writing uncompressed so probably knows the size).
+ *
+ * If we don't know the size, then life is more interesting.  Note
+ * that a careful reading of the Zip specification says that a writer
+ * must use ZIP_LENGTH_AT_END if it cannot write the CRC into the
+ * local header.  And if it uses ZIP_LENGTH_AT_END, then it is
+ * prohibited from storing the sizes in the local header.  This
+ * prevents fully-compliant streaming writers from providing any size
+ * clues to a streaming reader.  In this case, we have to scan the
+ * data as we read to try to locate the end-of-file marker.
+ *
+ * We assume here that the end-of-file marker always has the
+ * PK\007\010 signature.  Although it's technically optional, newer
+ * writers seem to provide it pretty consistently, and it's not clear
+ * how to efficiently recognize an end-of-file marker that lacks it.
+ *
+ * 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;
+	ssize_t trailing_extra;
+	int r;
+
+	(void)offset; /* UNUSED */
+
+	zip = (struct zip *)(a->format->data);
+	trailing_extra = zip->hctx_valid ? AUTH_CODE_SIZE : 0;
+
+	if (zip->entry->zip_flags & ZIP_LENGTH_AT_END) {
+		const char *p;
+		ssize_t grabbing_bytes = 24 + trailing_extra;
+
+		/* 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 + trailing_extra;
+		if (p[0] == 'P' && p[1] == 'K'
+		    && p[2] == '\007' && p[3] == '\010'
+		    && (archive_le32dec(p + 4) == zip->computed_crc32
+			|| zip->ignore_crc32
+			|| (zip->hctx_valid
+			 && zip->entry->aes_extra.vendor == AES_VENDOR_AE_2))) {
+			zip->end_of_entry = 1;
+			if (zip->hctx_valid) {
+				r = check_authentication_code(a, buff);
+				if (r != ARCHIVE_OK)
+					return (r);
+			}
+			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') {
+				break;
+			} else { p += 4; }
+		}
+		p -= trailing_extra;
+		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;
+	}
+	zip->entry_bytes_remaining -= bytes_avail;
+	zip->entry_uncompressed_bytes_read += bytes_avail;
+	zip->entry_compressed_bytes_read += bytes_avail;
+	zip->unconsumed += bytes_avail;
+	*size = bytes_avail;
+	*_buff = buff;
+	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 = 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(zip->entry_bytes_remaining < 9 || (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 = 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 = (ssize_t)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 = (ssize_t)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 = (size_t)zip->zipx_lzma_stream.total_out;
+	*buff = zip->uncompressed_buffer;
+
+	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 = (ssize_t)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. */
+		(size_t)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:
+			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 = (ssize_t)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;
+	}
+
+	/* Free lzma decoder handle because we'll no longer need it. */
+	/* This cannot be folded into LZMA_STREAM_END handling above
+	 * because the stream end marker is not required in this format. */
+	if(zip->end_of_entry) {
+		lzma_end(&zip->zipx_lzma_stream);
+		zip->zipx_lzma_valid = 0;
+	}
+
+	/* Return values. */
+	*size = (size_t)zip->zipx_lzma_stream.total_out;
+	*buff = zip->uncompressed_buffer;
+
+	/* 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=%" PRIu32 ", "
+		    "restore=%" PRIu32 ")", 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: %" PRIu32 " 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 = 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 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;
+	}
+
+	/* Update pointers for libarchive. */
+	*buff = zip->uncompressed_buffer;
+	*size = consumed_bytes;
+
+	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 = 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 = (ssize_t)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 = (uint32_t)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 = (uint32_t)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 = (size_t)total_out;
+	*buff = zip->uncompressed_buffer;
+
+	return ARCHIVE_OK;
+}
+
+#endif
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+static int
+zipx_zstd_init(struct archive_read *a, struct zip *zip)
+{
+	size_t r;
+
+	/* Deallocate already existing Zstd decompression context if it
+	 * exists. */
+	if(zip->zstdstream_valid) {
+		ZSTD_freeDStream(zip->zstdstream);
+		zip->zstdstream_valid = 0;
+	}
+
+	/* Allocate a new Zstd decompression context. */
+	zip->zstdstream = ZSTD_createDStream();
+
+	r = ZSTD_initDStream(zip->zstdstream);
+	if (ZSTD_isError(r)) {
+		 archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"Error initializing zstd decompressor: %s",
+			ZSTD_getErrorName(r));
+
+		return ARCHIVE_FAILED;
+	}
+
+	/* Mark the zstdstream field to be released in cleanup phase. */
+	zip->zstdstream_valid = 1;
+
+	/* (Re)allocate the buffer that will contain decompressed bytes. */
+	free(zip->uncompressed_buffer);
+
+	zip->uncompressed_buffer_size = ZSTD_DStreamOutSize();
+	zip->uncompressed_buffer = malloc(zip->uncompressed_buffer_size);
+	if (zip->uncompressed_buffer == NULL) {
+		archive_set_error(&a->archive, ENOMEM,
+			"No memory for Zstd decompression");
+
+		return ARCHIVE_FATAL;
+	}
+
+	/* Initialization done. */
+	zip->decompress_init = 1;
+	return ARCHIVE_OK;
+}
+
+static int
+zip_read_data_zipx_zstd(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;
+	size_t ret;
+	uint64_t total_out;
+	ZSTD_outBuffer out;
+	ZSTD_inBuffer in;
+
+	(void) offset; /* UNUSED */
+
+	/* Initialize decompression context if we're here for the first time. */
+	if(!zip->decompress_init) {
+		r = zipx_zstd_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 zstd file body");
+		return (ARCHIVE_FATAL);
+	}
+
+	in_bytes = (ssize_t)zipmin(zip->entry_bytes_remaining, bytes_avail);
+	if(in_bytes < 1) {
+		/* zstd 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 zstd file body");
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Setup buffer boundaries */
+	in.src = compressed_buff;
+	in.size = in_bytes;
+	in.pos = 0;
+	out = (ZSTD_outBuffer) { zip->uncompressed_buffer, zip->uncompressed_buffer_size, 0 };
+
+	/* Perform the decompression. */
+	ret = ZSTD_decompressStream(zip->zstdstream, &out, &in);
+	if (ZSTD_isError(ret)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"Error during zstd decompression: %s",
+			ZSTD_getErrorName(ret));
+		return (ARCHIVE_FATAL);
+	}
+
+	/* Check end of the stream. */
+	if (ret == 0) {
+		if ((in.pos == in.size) && (out.pos < out.size)) {
+			zip->end_of_entry = 1;
+			ZSTD_freeDStream(zip->zstdstream);
+			zip->zstdstream_valid = 0;
+		}
+	}
+
+	/* Update the pointers so decompressor can continue decoding. */
+	to_consume = in.pos;
+	__archive_read_consume(a, to_consume);
+
+	total_out = out.pos;
+
+	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 = (size_t)total_out;
+	*buff = zip->uncompressed_buffer;
+
+	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, to_consume = 0;
+	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
+		    = 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. */
+	to_consume = zip->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->stream.total_out;
+
+	if (zip->tctx_valid || zip->cctx_valid) {
+		zip->decrypted_bytes_remaining -= to_consume;
+		if (zip->decrypted_bytes_remaining == 0)
+			zip->decrypted_ptr = zip->decrypted_buffer;
+		else
+			zip->decrypted_ptr += to_consume;
+	}
+	if (zip->hctx_valid)
+		archive_hmac_sha1_update(&zip->hctx, sp, to_consume);
+
+	if (zip->end_of_entry) {
+		if (zip->hctx_valid) {
+			r = check_authentication_code(a, NULL);
+			if (r != ARCHIVE_OK) {
+				return (r);
+			}
+		}
+	}
+
+	*size = zip->stream.total_out;
+	*buff = zip->uncompressed_buffer;
+
+	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,
+			    r == CRYPTOR_STUB_FUNCTION ? "Decryption is unsupported due "
+				"to lack of crypto library" : "Failed to process passphrase");
+			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
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+	case 93: /* ZIPx Zstd compression. */
+		r = zip_read_data_zipx_zstd(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);
+	}
+	if (r != ARCHIVE_OK)
+		return (r);
+	if (*size > 0) {
+		zip->computed_crc32 = zip->crc32func(zip->computed_crc32, *buff,
+						     (unsigned)*size);
+	}
+	/* If we hit the end, swallow any end-of-data marker and
+	 * verify the final check values. */
+	if (zip->end_of_entry) {
+		consume_end_of_file_marker(a, zip);
+
+		/* Check computed CRC against header */
+		if ((!zip->hctx_valid ||
+		      zip->entry->aes_extra.vendor != AES_VENDOR_AE_2) &&
+		   zip->entry->crc32 != zip->computed_crc32
+		    && !zip->ignore_crc32) {
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "ZIP bad CRC: 0x%lx should be 0x%lx",
+			    (unsigned long)zip->computed_crc32,
+			    (unsigned long)zip->entry->crc32);
+			return (ARCHIVE_FAILED);
+		}
+		/* Check file size against header. */
+		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_FAILED);
+		}
+		/* 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_FAILED);
+		}
+	}
+
+	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
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+	if (zip->zstdstream_valid) {
+		ZSTD_freeDStream(zip->zstdstream);
+	}
+#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 = 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)
+{
+	uint16_t disk_num;
+	uint32_t cd_size, cd_offset;
+	
+	disk_num = archive_le16dec(p + 4);
+	cd_size = archive_le32dec(p + 12);
+	cd_offset = archive_le32dec(p + 16);
+
+	/* Sanity-check the EOCD we've found. */
+
+	/* This must be the first volume. */
+	if (disk_num != 0)
+		return 0;
+	/* Central directory must be on this volume. */
+	if (disk_num != 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 ((int64_t)cd_offset + cd_size > current_offset)
+		return 0;
+
+	/* Save the central directory location for later use. */
+	zip->central_directory_offset = cd_offset;
+	zip->central_directory_offset_adjusted = current_offset - cd_size;
+
+	/* 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);
+	/* TODO: Needs scanning backwards to find the eocd64 instead of assuming */
+	zip->central_directory_offset_adjusted = zip->central_directory_offset;
+
+	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_adjusted, 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 = dos_to_unix(archive_le32dec(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. */
+
+				/* If this is an entry ending with slash,
+				 * make the resource for name slash-less
+				 * as the actual resource fork doesn't end with '/'.
+				 */
+				size_t tmp_length = filename_length;
+				if (tmp_length > 0 && name[tmp_length - 1] == '/') {
+					tmp_length--;
+					r = rsrc_basename(name, tmp_length);
+				}
+
+				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),
+				    tmp_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 = 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,4348 @@
+/*-
+ * 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"
+
+/*
+ * 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 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 *);
+
+#if defined __LITTLE_ENDIAN__
+  #define IS_BIG_ENDIAN 0
+#elif defined __BIG_ENDIAN__
+  #define IS_BIG_ENDIAN 1
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+  #define IS_BIG_ENDIAN 0
+#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+  #define IS_BIG_ENDIAN 1
+#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_ARM64))
+  #define IS_BIG_ENDIAN 0
+#else
+// Detect endianness at runtime.
+static int
+is_big_endian(void)
+{
+	uint16_t d = 1;
+
+	return (archive_be16dec(&d) == 1);
+}
+
+#define IS_BIG_ENDIAN is_big_endian()
+#endif
+
+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 = 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 ret = 0;
+	size_t count;
+	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 < 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 = utf16nbytes(s, length);
+		} else {
+			/*
+			 *  UTF-8 NFD ===> UTF-16 NFC
+			 *  UTF-8 NFC ===> UTF-16 NFD
+			 */
+			count = 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 = 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);
+			size_t 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);
+			size_t 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;
+
+		mbflag |= MB_ERR_INVALID_CHARS;
+
+		buffsize = dest->length + length + 1;
+		do {
+			int r;
+
+			/* MultiByteToWideChar is limited to int. */
+			if (length > (size_t)INT_MAX ||
+				(dest->buffer_length >> 1) > (size_t)INT_MAX)
+				return (-1);
+			/* Allocate memory for WCS. */
+			if (NULL == archive_wstring_ensure(dest, buffsize))
+				return (-1);
+			/* Convert MBS to WCS. */
+			r = MultiByteToWideChar(from_cp,
+			    mbflag, s, (int)length, dest->s + dest->length,
+			    (int)(dest->buffer_length >> 1) -1);
+			if (r == 0 &&
+			    GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
+				/* Expand the WCS buffer. */
+				buffsize = dest->buffer_length << 1;
+				continue;
+			}
+			if (r == 0 && length != 0)
+				ret = -1;
+			count = (size_t)r;
+			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 ret = 0;
+	UINT to_cp;
+	size_t count, wslen = 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 < len && *ws) {
+				archive_be16enc(u16+count, *ws);
+				ws++;
+				count++;
+			}
+		} else {
+			while (count < 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 {
+			int r;
+
+			defchar_used = 0;
+			if (to_cp == CP_UTF8 || sc == NULL)
+				dp = NULL;
+			else
+				dp = &defchar_used;
+			/* WideCharToMultiByte is limited to int. */
+			if (as->buffer_length - as->length - 1 > (size_t)INT_MAX ||
+				wslen > (size_t)INT_MAX)
+				return (-1);
+			r = WideCharToMultiByte(to_cp, 0, ws, (int)wslen,
+			    as->s + as->length,
+			    (int)(as->buffer_length - as->length - 1), NULL, dp);
+			if (r == 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 (r == 0)
+				ret = -1;
+			count = (size_t)r;
+			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__)
+# if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#  define GetOEMCP() CP_OEMCP
+# endif
+
+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) || (strcmp(p+1, "UTF-8") == 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) {
+		size_t 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 */
+	size_t 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 >= 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 (n > (size_t)INT_MAX)
+		return (-1); /* Invalid */
+	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 (n < (size_t)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 (n < (size_t)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, (uint16_t)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, (uint16_t)uc);
+		return (2);
+	}
+}
+
+/*
+ * Append new UTF-8 string to existing UTF-8 string.
+ * Existing string is assumed to already be in proper form;
+ * the new string will have invalid sequences replaced and
+ * surrogate pairs canonicalized.
+ */
+static int
+strncat_from_utf8_to_utf8(struct archive_string *as, const void *_src,
+    size_t len, struct archive_string_conv *sc)
+{
+	int ret = 0;
+	const char *src = _src;
+	(void)sc; /* UNUSED */
+
+	/* Pre-extend the destination */
+	if (archive_string_ensure(as, as->length + len + 1) == NULL)
+		return (-1);
+
+	/* Invariant: src points to the first UTF8 byte that hasn't
+	 * been copied to the destination `as`. */
+	for (;;) {
+		int n;
+		uint32_t uc;
+		const char *e = src;
+
+		/* Skip UTF-8 sequences until we reach end-of-string or
+		 * a code point that needs conversion. */
+		while ((n = utf8_to_unicode(&uc, e, len)) > 0) {
+			e += n;
+			len -= n;
+		}
+		/* Copy the part that doesn't need conversion */
+		if (e > src) {
+			if (archive_string_append(as, src, e - src) == NULL)
+				return (-1);
+			src = e;
+		}
+
+		if (n == 0) {
+			/* We reached end-of-string */
+			return (ret);
+		} else {
+			/* Next code point needs conversion */
+			char t[4];
+			size_t w;
+
+			/* Try decoding a surrogate pair */
+			if (n == -3 && IS_SURROGATE_PAIR_LA(uc)) {
+				n = cesu8_to_unicode(&uc, src, len);
+			}
+			/* Not a (valid) surrogate, so use a replacement char */
+			if (n < 0) {
+				ret = -1; /* Return -1 if we used any replacement */
+				n *= -1;
+			}
+			/* Consume converted code point */
+			src += n;
+			len -= n;
+			/* Convert and append new UTF-8 sequence. */
+			w = unicode_to_utf8(t, sizeof(t), uc);
+			if (archive_string_append(as, t, w) == NULL)
+				return (-1);
+		}
+	}
+}
+
+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;
+	size_t ts, tm;
+	int n, ret = 0;
+	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;
+	BOOL defchar;
+	char *mbs;
+	size_t mbs_size, b, ll;
+	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 {
+		int r;
+		defchar = 0;
+		/* WideCharToMultiByte is limited to int. */
+		if (bytes > (size_t)INT_MAX || mbs_size > (size_t)INT_MAX)
+			return (-1);
+		r = WideCharToMultiByte(sc->to_cp, 0,
+		    (LPCWSTR)u16, (int)bytes>>1, mbs, (int)mbs_size,
+			NULL, &defchar);
+		/* Exit loop if we succeeded */
+		if (r != 0 ||
+		    GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+			ll = (size_t)r;
+			break;
+		}
+		/* Else expand buffer and loop to try again. */
+		r = WideCharToMultiByte(sc->to_cp, 0,
+		    (LPCWSTR)u16, (int)bytes, NULL, 0, NULL, NULL);
+		ll = (size_t)r;
+		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));
+}
+
+/*
+ * 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 {
+		int r;
+		if (length > (size_t)INT_MAX || (avail >> 1) > (size_t)INT_MAX)
+			return (-1);
+		r = MultiByteToWideChar(sc->from_cp,
+		    MB_PRECOMPOSED, s, (int)length, (LPWSTR)u16, (int)avail>>1);
+		/* Exit loop if we succeeded */
+		if (r != 0 ||
+		    GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+			count = (size_t)r;
+			break;
+		}
+		/* Expand buffer and try again */
+		r = MultiByteToWideChar(sc->from_cp,
+		    MB_PRECOMPOSED, s, (int)length, NULL, 0);
+		count = (size_t)r;
+		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, (uint16_t)c);
+		else
+			archive_le16enc(utf16, (uint16_t)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 defined(_WIN32) && !defined(__CYGWIN__)
+	/*
+	 * On Windows, first try converting from WCS because (1) there's no
+	 * guarantee that the conversion to MBS will succeed, e.g. when using
+	 * CP_ACP, and (2) that's more efficient than converting to MBS, just to
+	 * convert back to WCS again before finally converting to UTF-8
+	 */
+	if ((aes->aes_set & AES_SET_WCS) != 0) {
+		sc = archive_string_conversion_to_charset(a, "UTF-8", 1);
+		if (sc == NULL)
+			return (-1);/* Couldn't allocate memory for sc. */
+		archive_string_empty(&(aes->aes_utf8));
+		r = archive_string_append_from_wcs_in_codepage(&(aes->aes_utf8),
+			aes->aes_wcs.s, aes->aes_wcs.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. */
+	}
+#endif
+	/* Try converting WCS to MBS first if MBS does not exist yet. */
+	if ((aes->aes_set & AES_SET_MBS) == 0) {
+		const char *pm; /* unused */
+		archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */
+	}
+	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)
+{
+	struct archive_string_conv *sc;
+	int r, ret = 0;
+
+	/* 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;
+	}
+
+	/* If there's a UTF-8 form, try converting with the native locale. */
+	if (aes->aes_set & AES_SET_UTF8) {
+		archive_string_empty(&(aes->aes_mbs));
+		sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
+		if (sc == NULL)
+			return (-1);/* Couldn't allocate memory for sc. */
+		r = archive_strncpy_l(&(aes->aes_mbs),
+			aes->aes_utf8.s, aes->aes_utf8.length, sc);
+		if (a == NULL)
+			free_sconv_object(sc);
+		*p = aes->aes_mbs.s;
+		if (r == 0) {
+			aes->aes_set |= AES_SET_MBS;
+			ret = 0;/* success; overwrite previous error. */
+		} else
+			ret = -1;/* failure. */
+	}
+	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;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/*
+	 * On Windows, prefer converting from UTF-8 directly to WCS because:
+	 * (1) there's no guarantee that the string can be represented in MBS (e.g.
+	 * with CP_ACP), and (2) in order to convert from UTF-8 to MBS, we're going
+	 * to need to convert from UTF-8 to WCS anyway and its wasteful to throw
+	 * away that intermediate result
+	 */
+	if (aes->aes_set & AES_SET_UTF8) {
+		struct archive_string_conv *sc;
+
+		sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
+		if (sc != NULL) {
+			archive_wstring_empty((&aes->aes_wcs));
+			r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs),
+			    aes->aes_utf8.s, aes->aes_utf8.length, sc);
+			if (a == NULL)
+				free_sconv_object(sc);
+			if (r == 0) {
+				aes->aes_set |= AES_SET_WCS;
+				*wp = aes->aes_wcs.s;
+				return (0);
+			}
+		}
+	}
+#endif
+	/* Try converting UTF8 to MBS first if MBS does not exist yet. */
+	if ((aes->aes_set & AES_SET_MBS) == 0) {
+		const char *p; /* unused */
+		archive_mstring_get_mbs(a, aes, &p); /* ignore errors, we'll handle it later */
+	}
+	/* 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 *a, struct archive_mstring *aes,
+    const char **p, size_t *length, struct archive_string_conv *sc)
+{
+	int ret = 0;
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	int r;
+
+	/*
+	 * 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 there is a WCS or UTF8 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) {
+		const char *pm; /* unused */
+		archive_mstring_get_mbs(a, aes, &pm); /* ignore errors, we'll handle it later */
+	}
+	/* 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. */
+
+	sc = archive_string_conversion_from_charset(a, "UTF-8", 1);
+	if (sc == NULL)
+		return (-1);/* Couldn't allocate memory for sc. */
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* On Windows, there's no good way to convert from UTF8 -> MBS directly, so
+	 * prefer to first convert to WCS as (1) it's wasteful to throw away the
+	 * intermediate result, and (2) WCS will still be set even if we fail to
+	 * convert to MBS (e.g. with ACP that can't represent the characters) */
+	r = archive_wstring_append_from_mbs_in_codepage(&(aes->aes_wcs),
+		aes->aes_utf8.s, aes->aes_utf8.length, sc);
+
+	if (a == NULL)
+		free_sconv_object(sc);
+	if (r != 0)
+		return (-1); /* This will guarantee we can't convert to MBS */
+	aes->aes_set = AES_SET_UTF8 | AES_SET_WCS; /* Both UTF8 and WCS set. */
+
+	/* Try converting WCS to MBS, return false on failure. */
+	if (archive_string_append_from_wcs(&(aes->aes_mbs), aes->aes_wcs.s,
+	    aes->aes_wcs.length))
+		return (-1);
+#else
+	/* Try converting UTF-8 to MBS, return false on failure. */
+	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);
+#endif
+
+	/* All conversions succeeded. */
+	aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
+
+	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,240 @@
+/*-
+ * 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.
+ */
+
+#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 *, 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,2289 @@
+/*-
+ * 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.
+ *
+ */
+
+/*
+ * 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,200 @@
+/*-
+ * 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"
+
+/*
+ * 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 'l':
+			if (p[1] == 'l') {
+				long_flag = 'L';
+				p += 2;
+				break;
+			}
+			__LA_FALLTHROUGH;
+		case 'j':
+		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 'L': s = va_arg(ap, long 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':
+			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 'L': u = va_arg(ap, unsigned long 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_time.c b/c/archive_time.c
new file mode 100644
--- /dev/null
+++ b/c/archive_time.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright © 2025 ARJANEN Loïc Jean David
+ * 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_private.h"
+#include "archive_time_private.h"
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NTFS_EPOC_TIME ARCHIVE_LITERAL_ULL(11644473600)
+#define NTFS_TICKS ARCHIVE_LITERAL_ULL(10000000)
+#define NTFS_EPOC_TICKS (NTFS_EPOC_TIME * NTFS_TICKS)
+#define DOS_MIN_TIME 0x00210000U
+#define DOS_MAX_TIME 0xff9fbf7dU
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <winnt.h>
+/* Windows FILETIME to NTFS time. */
+uint64_t
+FILETIME_to_ntfs(const FILETIME* filetime)
+{
+	ULARGE_INTEGER utc;
+	utc.HighPart = filetime->dwHighDateTime;
+	utc.LowPart  = filetime->dwLowDateTime;
+	return utc.QuadPart;
+}
+#endif
+
+/* Convert an MSDOS-style date/time into Unix-style time. */
+int64_t
+dos_to_unix(uint32_t dos_time)
+{
+	uint16_t msTime, msDate;
+	struct tm ts;
+	time_t t;
+
+	msTime = (0xFFFF & dos_time);
+	msDate = (dos_time >> 16);
+
+	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;
+	t = mktime(&ts);
+	return (int64_t)(t == (time_t)-1 ? INT32_MAX : t);
+}
+
+/* Convert into MSDOS-style date/time. */
+uint32_t
+unix_to_dos(int64_t unix_time)
+{
+	struct tm *t;
+	uint32_t dt;
+	time_t ut = unix_time;
+#if defined(HAVE_LOCALTIME_R) || defined(HAVE_LOCALTIME_S)
+	struct tm tmbuf;
+#endif
+
+	if (sizeof(time_t) < sizeof(int64_t) && (int64_t)ut != unix_time) {
+		ut = (time_t)(unix_time > 0 ? INT32_MAX : INT32_MIN);
+	}
+
+#if defined(HAVE_LOCALTIME_S)
+	t = localtime_s(&tmbuf, &ut) ? NULL : &tmbuf;
+#elif defined(HAVE_LOCALTIME_R)
+	t = localtime_r(&ut, &tmbuf);
+#else
+	t = localtime(&ut);
+#endif
+	dt = 0;
+	if (t != NULL && t->tm_year >= INT_MIN + 80) {
+		const int year = t->tm_year - 80;
+
+		if (year & ~0x7f) {
+			dt = year > 0 ? DOS_MAX_TIME : DOS_MIN_TIME;
+		}
+		else {
+			dt += (year & 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;
+			/* Only counting every 2 seconds. */
+			dt += (t->tm_sec & 0x3e) >> 1;
+		}
+	}
+	if (dt > DOS_MAX_TIME) {
+		dt = DOS_MAX_TIME;
+	}
+	else if (dt < DOS_MIN_TIME) {
+		dt = DOS_MIN_TIME;
+	}
+	return dt;
+}
+
+/* Convert NTFS time to Unix sec/nsec */
+void
+ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs)
+{
+	if (ntfs > INT64_MAX) {
+		ntfs -= NTFS_EPOC_TICKS;
+		*secs = ntfs / NTFS_TICKS;
+		*nsecs = 100 * (ntfs % NTFS_TICKS);
+	}
+	else {
+		lldiv_t tdiv;
+		int64_t value = (int64_t)ntfs - (int64_t)NTFS_EPOC_TICKS;
+
+		tdiv = lldiv(value, NTFS_TICKS);
+		*secs = tdiv.quot;
+		*nsecs = (uint32_t)(tdiv.rem * 100);
+	}
+}
+
+/* Convert Unix sec/nsec to NTFS time */
+uint64_t
+unix_to_ntfs(int64_t secs, uint32_t nsecs)
+{
+	uint64_t ntfs;
+
+	if (secs < -(int64_t)NTFS_EPOC_TIME)
+		return 0;
+
+	ntfs = secs + NTFS_EPOC_TIME;
+
+	if (ntfs > UINT64_MAX / NTFS_TICKS)
+		return UINT64_MAX;
+
+	ntfs *= NTFS_TICKS;
+
+	if (ntfs > UINT64_MAX - nsecs/100)
+		return UINT64_MAX;
+
+	return ntfs + nsecs/100;
+}
diff --git a/c/archive_time_private.h b/c/archive_time_private.h
new file mode 100644
--- /dev/null
+++ b/c/archive_time_private.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright © 2025 ARJANEN Loïc Jean David
+ * 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_TIME_PRIVATE_H_INCLUDED
+#define ARCHIVE_TIME_PRIVATE_H_INCLUDED
+
+#ifndef __LIBARCHIVE_BUILD
+#error This header is only to be used internally to libarchive.
+#endif
+#include <stdint.h>
+
+/* NTFS time to Unix sec/nsec. */
+void ntfs_to_unix(uint64_t ntfs, int64_t* secs, uint32_t* nsecs);
+/* DOS time to Unix sec. */
+int64_t dos_to_unix(uint32_t dos);
+/* Unix sec/nsec to NTFS time. */
+uint64_t unix_to_ntfs(int64_t secs, uint32_t nsecs);
+/* Unix sec to DOS time. */
+uint32_t unix_to_dos(int64_t secs);
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#include <windef.h>
+#include <winbase.h>
+/* Windows FILETIME to NTFS time. */
+uint64_t FILETIME_to_ntfs(const FILETIME* filetime);
+#endif
+#endif /* ARCHIVE_TIME_PRIVATE_H_INCLUDED */
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,686 @@
+/*-
+ * 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"
+
+#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(_WIN32) && !defined(__CYGWIN__)
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+/* don't use bcrypt when XP needs to be supported */
+#include <bcrypt.h>
+
+/* Common in other bcrypt implementations, but missing from VS2008. */
+#ifndef BCRYPT_SUCCESS
+#define BCRYPT_SUCCESS(r) ((NTSTATUS)(r) == STATUS_SUCCESS)
+#endif
+
+#elif defined(HAVE_WINCRYPT_H)
+#include <wincrypt.h>
+#endif
+#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
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+static int __LA_LIBC_CC archive_utility_string_sort_helper(const void *, const void *);
+#endif
+
+/* 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'
+	};
+	struct archive_wstring temp_name;
+	wchar_t *ws;
+	DWORD attr;
+	wchar_t *xp, *ep;
+	int fd;
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	BCRYPT_ALG_HANDLE hAlg = NULL;
+#else
+	HCRYPTPROV hProv = (HCRYPTPROV)NULL;
+#endif
+	fd = -1;
+	ws = NULL;
+	archive_string_init(&temp_name);
+
+	if (template == NULL) {
+		/* 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.length == 0 ||
+			    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 defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_RNG_ALGORITHM,
+		NULL, 0))) {
+		la_dosmaperr(GetLastError());
+		goto exit_tmpfile;
+	}
+#else
+	if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
+		CRYPT_VERIFYCONTEXT)) {
+		la_dosmaperr(GetLastError());
+		goto exit_tmpfile;
+	}
+#endif
+
+	for (;;) {
+		wchar_t *p;
+		HANDLE h;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+		/* Generate a random file name through CryptGenRandom(). */
+		p = xp;
+#if defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+		if (!BCRYPT_SUCCESS(BCryptGenRandom(hAlg, (PUCHAR)p,
+		    (DWORD)(ep - p)*sizeof(wchar_t), 0))) {
+			la_dosmaperr(GetLastError());
+			goto exit_tmpfile;
+		}
+#else
+		if (!CryptGenRandom(hProv, (DWORD)(ep - p)*sizeof(wchar_t),
+		    (BYTE*)p)) {
+			la_dosmaperr(GetLastError());
+			goto exit_tmpfile;
+		}
+#endif
+		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;
+		}
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		ZeroMemory(&createExParams, sizeof(createExParams));
+		createExParams.dwSize = sizeof(createExParams);
+		createExParams.dwFileAttributes = attr & 0xFFFF;
+		createExParams.dwFileFlags = attr & 0xFFF00000;
+		h = CreateFile2(ws,
+		    GENERIC_READ | GENERIC_WRITE | DELETE,
+		    0,/* Not share */
+			CREATE_NEW,
+			&createExParams);
+#else
+		h = CreateFileW(ws,
+		    GENERIC_READ | GENERIC_WRITE | DELETE,
+		    0,/* Not share */
+		    NULL,
+		    CREATE_NEW,/* Create a new file only */
+		    attr,
+		    NULL);
+#endif
+		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 defined(HAVE_BCRYPT_H) && _WIN32_WINNT >= _WIN32_WINNT_VISTA
+	if (hAlg != NULL)
+		BCryptCloseAlgorithmProvider(hAlg, 0);
+#else
+	if (hProv != (HCRYPTPROV)NULL)
+		CryptReleaseContext(hProv, 0);
+#endif
+	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
+__archive_issetugid(void)
+{
+#ifdef HAVE_ISSETUGID
+	return (issetugid());
+#elif HAVE_GETRESUID
+	uid_t ruid, euid, suid;
+	gid_t rgid, egid, sgid;
+	if (getresuid(&ruid, &euid, &suid) != 0)
+		return (-1);
+	if (ruid != euid || ruid != suid)
+		return (1);
+	if (getresgid(&ruid, &egid, &sgid) != 0)
+		return (-1);
+	if (rgid != egid || rgid != sgid)
+		return (1);
+#elif HAVE_GETEUID
+	if (geteuid() != getuid())
+		return (1);
+#if HAVE_GETEGID
+	if (getegid() != getgid())
+		return (1);
+#endif
+#endif
+	return (0);
+}
+
+int
+__archive_get_tempdir(struct archive_string *temppath)
+{
+	const char *tmp = NULL;
+
+	if (__archive_issetugid() == 0)
+		tmp = getenv("TMPDIR");
+	if (tmp == NULL)
+#ifdef _PATH_TMP
+		tmp = _PATH_TMP;
+#else
+                tmp = "/tmp";
+#endif
+	archive_strcpy(temppath, tmp);
+	if (temppath->length == 0 || 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 (__archive_get_tempdir(&temp_name) != ARCHIVE_OK)
+			goto exit_tmpfile;
+	} else {
+		archive_strcpy(&temp_name, tmpdir);
+		if (temp_name.length == 0 ||
+		    temp_name.s[temp_name.length-1] != '/')
+			archive_strappend_char(&temp_name, '/');
+	}
+#ifdef O_TMPFILE
+	fd = open(temp_name.s, O_RDWR|O_CLOEXEC|O_TMPFILE|O_EXCL, 0600); 
+	if(fd >= 0)
+		goto exit_tmpfile;
+#endif
+	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 (__archive_get_tempdir(&temp_name) != ARCHIVE_OK)
+				goto exit_tmpfile;
+		} else
+			archive_strcpy(&temp_name, tmpdir);
+		if (temp_name.length > 0 && 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
+}
+
+#if ARCHIVE_VERSION_NUMBER < 4000000
+/*
+ * Utility functions to sort a group of strings using quicksort.
+ */
+static int
+__LA_LIBC_CC
+archive_utility_string_sort_helper(const void *p1, const void *p2)
+{
+	const char * const * const s1 = p1;
+	const char * const * const s2 = p2;
+
+	return strcmp(*s1, *s2);
+}
+
+int
+archive_utility_string_sort(char **strings)
+{
+	size_t size = 0;
+	while (strings[size] != NULL)
+		size++;
+	qsort(strings, size, sizeof(char *),
+	      archive_utility_string_sort_helper);
+	return (ARCHIVE_OK);
+}
+#endif
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,539 @@
+/*-
+ * 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"
+
+#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>
+#include <stdio.h>
+#endif
+#ifdef HAVE_LZO_LZOCONF_H
+#include <lzo/lzoconf.h>
+#endif
+#if HAVE_LIBXML_XMLVERSION_H
+#include <libxml/xmlversion.h>
+#elif HAVE_BSDXML_H
+#include <bsdxml.h>
+#elif HAVE_EXPAT_H
+#include <expat.h>
+#endif
+#if HAVE_MBEDTLS_VERSION_H
+#include <mbedtls/version.h>
+#endif
+#if HAVE_NETTLE_VERSION_H
+#include <nettle/version.h>
+#include <stdio.h>
+#endif
+#if HAVE_OPENSSL_OPENSSLV_H
+#include <openssl/opensslv.h>
+#include <stdio.h>
+#endif
+#if HAVE_ICONV_H
+#include <iconv.h>
+#endif
+#if HAVE_PCRE_H
+#include <pcre.h>
+#endif
+#if HAVE_PCRE2_H
+#include <pcre2.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_cryptor_private.h"
+#include "archive_digest_private.h"
+
+static void
+archive_regex_version(struct archive_string* str)
+{
+#if HAVE_LIBPCREPOSIX && HAVE_PCRE_H
+	archive_strcat(str, " libpcre/");
+	archive_strcat(str, archive_libpcre_version());
+#elif HAVE_LIBPCRE2POSIX && HAVE_PCRE2_H
+	archive_strcat(str, " libpcre2/");
+	archive_strcat(str, archive_libpcre2_version());
+#else
+	(void)str; /* UNUSED */
+#endif
+}
+
+static void
+archive_xml_version(struct archive_string* str)
+{
+#if HAVE_LIBXML_XMLVERSION_H && HAVE_LIBXML2
+	archive_strcat(str, " libxml2/");
+	archive_strcat(str, archive_libxml2_version());
+#elif HAVE_BSDXML_H && HAVE_LIBBSDXML
+	archive_strcat(str, " bsdxml/");
+	archive_strcat(str, archive_libbsdxml_version());
+#elif HAVE_EXPAT_H && HAVE_LIBEXPAT
+	archive_strcat(str, " expat/");
+	archive_strcat(str, archive_libexpat_version());
+#else
+	(void)str; /* UNUSED */
+#endif
+}
+
+static void
+archive_libb2_version(struct archive_string* str)
+{
+	archive_strcat(str, " libb2/");
+#if HAVE_BLAKE2_H && HAVE_LIBB2
+#if defined(LIBB2_PKGCONFIG_VERSION)
+	archive_strcat(str, LIBB2_PKGCONFIG_VERSION);
+#else
+	archive_strcat(str, "system");
+#endif
+#else
+	archive_strcat(str, "bundled");
+#endif
+}
+
+static void
+archive_crypto_version(struct archive_string* str)
+{
+#if defined(ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto)
+	archive_strcat(str, " CommonCrypto/");
+	archive_strcat(str, archive_commoncrypto_version());
+#endif
+#if defined(ARCHIVE_CRYPTOR_USE_CNG)
+	archive_strcat(str, " cng/");
+	archive_strcat(str, archive_cng_version());
+#endif
+#if defined(ARCHIVE_CRYPTOR_USE_MBED)
+	archive_strcat(str, " mbedtls/");
+	archive_strcat(str, archive_mbedtls_version());
+#endif
+#if defined(ARCHIVE_CRYPTOR_USE_NETTLE)
+	archive_strcat(str, " nettle/");
+	archive_strcat(str, archive_nettle_version());
+#endif
+#if defined(ARCHIVE_CRYPTOR_USE_OPENSSL)
+	archive_strcat(str, " openssl/");
+	archive_strcat(str, archive_openssl_version());
+#endif
+#if defined(ARCHIVE_CRYPTOR_USE_LIBMD)
+	archive_strcat(str, " libmd/");
+	archive_strcat(str, archive_libmd_version());
+#endif
+#if defined(ARCHIVE_CRYPTOR_USE_WINCRYPT)
+	archive_strcat(str, " WinCrypt/");
+	archive_strcat(str, archive_wincrypt_version());
+#endif
+	// Just in case
+	(void)str; /* UNUSED */
+}
+
+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();
+	const char *liblzo = archive_liblzo2_version();
+	const char *libiconv = archive_libiconv_version();
+	const char *libacl = archive_libacl_version();
+	const char *librichacl = archive_librichacl_version();
+	const char *libattr = archive_libacl_version();
+
+	if (!init) {
+		archive_string_init(&str);
+
+		archive_strcat(&str, ARCHIVE_VERSION_STRING);
+		if (zlib) {
+			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);
+		}
+		if (liblzo) {
+			archive_strcat(&str, " liblzo2/");
+			archive_strcat(&str, liblzo);
+		}
+		archive_xml_version(&str);
+		archive_regex_version(&str);
+		archive_crypto_version(&str);
+		archive_libb2_version(&str);
+		if (librichacl) {
+			archive_strcat(&str, " librichacl/");
+			archive_strcat(&str, librichacl);
+		}
+		if (libacl) {
+			archive_strcat(&str, " libacl/");
+			archive_strcat(&str, libacl);
+		}
+		if (libattr) {
+			archive_strcat(&str, " libattr/");
+			archive_strcat(&str, libattr);
+		}
+		if (libiconv) {
+			archive_strcat(&str, " libiconv/");
+			archive_strcat(&str, libiconv);
+		}
+	}
+	return str.s;
+}
+
+const char *
+archive_zlib_version(void)
+{
+#if HAVE_ZLIB_H && HAVE_LIBZ
+	return zlibVersion();
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_liblzma_version(void)
+{
+#if HAVE_LZMA_H && HAVE_LIBLZMA
+	return lzma_version_string();
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_bzlib_version(void)
+{
+#if HAVE_BZLIB_H && HAVE_LIBBZ2
+	return BZ2_bzlibVersion();
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_liblz4_version(void)
+{
+#if HAVE_LZ4_H && HAVE_LIBLZ4
+#if LZ4_VERSION_NUMBER > 10705
+	return LZ4_versionString();
+#elif LZ4_VERSION_NUMBER > 10300
+	div_t major = div(LZ4_versionNumber(), 10000);
+	div_t minor = div(major.rem, 100);
+	static char lz4_version[9];
+	snprintf(lz4_version, 9, "%d.%d.%d", major.quot, minor.quot, minor.rem);
+	return lz4_version;
+#else
+#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
+#endif
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libzstd_version(void)
+{
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+#if ZSTD_VERSION_NUMBER > 10300
+	return ZSTD_versionString();
+#else
+	div_t major = div(ZSTD_versionNumber(), 10000);
+	div_t minor = div(major.rem, 100);
+	static char zstd_version[9];
+	snprintf(zstd_version, 9, "%d.%d.%d", major.quot, minor.quot, minor.rem);
+	return zstd_version;
+#endif
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_liblzo2_version(void)
+{
+#if HAVE_LZO_LZOCONF_H && HAVE_LIBLZO2
+	return LZO_VERSION_STRING;
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libbsdxml_version(void)
+{
+#if HAVE_BSDXML_H && HAVE_LIBBSDXML
+	return XML_ExpatVersion();
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libxml2_version(void)
+{
+#if HAVE_LIBXML_XMLREADER_H && HAVE_LIBXML2
+	return LIBXML_DOTTED_VERSION;
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libexpat_version(void)
+{
+#if HAVE_EXPAT_H && HAVE_LIBEXPAT
+	return XML_ExpatVersion();
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_mbedtls_version(void)
+{
+#if defined(ARCHIVE_CRYPTOR_USE_MBED) || defined(ARCHIVE_CRYPTO_MBED)
+	static char mbed_version[9];
+	mbedtls_version_get_string(mbed_version);
+	return mbed_version;
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_nettle_version(void)
+{
+#if defined(ARCHIVE_CRYPTOR_USE_NETTLE) || defined(ARCHIVE_CRYPTO_NETTLE)
+	static char nettle_version[6];
+	snprintf(nettle_version, 6, "%d.%d", nettle_version_major(), nettle_version_minor());
+	return nettle_version;
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_openssl_version(void)
+{
+#if defined(ARCHIVE_CRYPTOR_USE_OPENSSL) || defined(ARCHIVE_CRYPTO_OPENSSL)
+#ifdef OPENSSL_VERSION_STR
+	return OPENSSL_VERSION_STR;
+#else
+#define OPENSSL_MAJOR (OPENSSL_VERSION_NUMBER >> 28)
+#define OPENSSL_MINOR ((OPENSSL_VERSION_NUMBER >> 20) & 0xFF)
+	static char openssl_version[6];
+	snprintf(openssl_version, 6, "%ld.%ld", OPENSSL_MAJOR, OPENSSL_MINOR);
+	return openssl_version;
+#undef OPENSSL_MAJOR
+#undef OPENSSL_MINOR
+#endif
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libmd_version(void)
+{
+#if defined(ARCHIVE_CRYPTOR_USE_LIBMD) || defined(ARCHIVE_CRYPTO_LIBMD)
+	return "system";
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_commoncrypto_version(void)
+{
+#if defined(ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto) || defined(ARCHIVE_CRYPTO_CommonCrypto)
+	return "system";
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_cng_version(void)
+{
+#if defined(ARCHIVE_CRYPTOR_USE_CNG) || defined(ARCHIVE_CRYPTO_CNG)
+#ifdef BCRYPT_HASH_INTERFACE_MAJORVERSION_2
+	return "2.0";
+#else
+	return "1.0";
+#endif
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_wincrypt_version(void)
+{
+#if defined(ARCHIVE_CRYPTOR_USE_WINCRYPT) || defined(ARCHIVE_CRYPTO_WINCRYPT)
+	HCRYPTPROV prov;
+	if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
+		if (GetLastError() != (DWORD)NTE_BAD_KEYSET)
+			return NULL;
+		if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET))
+			return NULL;
+	}
+	DWORD version, length = sizeof(version);
+	if (!CryptGetProvParam(prov, PP_VERSION, (BYTE *)&version, &length, 0)) {
+		return NULL;
+	} else {
+		char major = (version >> 8) & 0xFF;
+		char minor = version & 0xFF;
+		static char wincrypt_version[6];
+		snprintf(wincrypt_version, 6, "%hhd.%hhd", major, minor);
+		return wincrypt_version;
+	}
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_librichacl_version(void)
+{
+#if HAVE_LIBRICHACL
+#if defined(LIBRICHACL_PKGCONFIG_VERSION)
+	return LIBRICHACL_PKGCONFIG_VERSION;
+#else
+	return "system";
+#endif
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libacl_version(void)
+{
+#if HAVE_LIBACL
+#if defined(LIBACL_PKGCONFIG_VERSION)
+	return LIBACL_PKGCONFIG_VERSION;
+#else
+	return "system";
+#endif
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libattr_version(void)
+{
+#if HAVE_LIBATTR
+#if defined(LIBATTR_PKGCONFIG_VERSION)
+	return LIBATTR_PKGCONFIG_VERSION;
+#else
+	return "system";
+#endif
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libiconv_version(void)
+{
+#if HAVE_LIBCHARSET && HAVE_ICONV_H
+	char major = _libiconv_version >> 8;
+	char minor = _libiconv_version & 0xFF;
+	static char charset_version[6];
+	snprintf(charset_version, 6, "%hhd.%hhd", major, minor);
+	return charset_version;
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libpcre_version(void)
+{
+#if HAVE_LIBPCREPOSIX && HAVE_PCRE_H
+#define str(s) #s
+#define NUMBER(x) str(x)
+	return NUMBER(PCRE_MAJOR) "." NUMBER(PCRE_MINOR);
+#undef NUMBER
+#undef str
+#else
+	return NULL;
+#endif
+}
+
+const char *
+archive_libpcre2_version(void)
+{
+#if HAVE_LIBPCRE2POSIX && HAVE_PCRE2_H
+#define str(s) #s
+#define NUMBER(x) str(x)
+	return NUMBER(PCRE2_MAJOR) "." NUMBER(PCRE2_MINOR);
+#undef NUMBER
+#undef str
+#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,162 @@
+/*-
+ * 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"
+
+#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,1051 @@
+/*-
+ * 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.
+ */
+
+/*
+ * 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_platform_stat.h"
+#include "archive_private.h"
+#include "archive_entry.h"
+#include "archive_time_private.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>
+
+#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;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+	handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
+	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+	    hTemplateFile);
+	if (handle != INVALID_HANDLE_VALUE)
+		return (handle);
+	if (GetLastError() != ERROR_PATH_NOT_FOUND)
+		return (handle);
+#endif
+	wpath = __la_win_permissive_name(path);
+	if (wpath == NULL)
+		return INVALID_HANDLE_VALUE;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	ZeroMemory(&createExParams, sizeof(createExParams));
+	createExParams.dwSize = sizeof(createExParams);
+	createExParams.dwFileAttributes = dwFlagsAndAttributes & 0xFFFF;
+	createExParams.dwFileFlags = dwFlagsAndAttributes & 0xFFF00000;
+	createExParams.dwSecurityQosFlags = dwFlagsAndAttributes & 0x000F00000;
+	createExParams.lpSecurityAttributes = lpSecurityAttributes;
+	createExParams.hTemplateFile = hTemplateFile;
+	handle = CreateFile2(wpath, dwDesiredAccess, dwShareMode,
+	    dwCreationDisposition, &createExParams);
+#else /* !WINAPI_PARTITION_DESKTOP */
+	handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
+	    lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+	    hTemplateFile);
+#endif /* !WINAPI_PARTITION_DESKTOP */
+	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;
+
+	/* _(w)sopen_s fails if we provide any other modes */
+	pmode = pmode & (_S_IREAD | _S_IWRITE);
+
+	if ((flags & ~O_BINARY) == O_RDONLY) {
+		/*
+		 * When we open a directory, _open function returns
+		 * "Permission denied" error.
+		 */
+		attr = GetFileAttributesA(path);
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+		if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND)
+#endif
+		{
+			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 !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+			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);
+#else /* !WINAPI_PARTITION_DESKTOP */
+			CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+			ZeroMemory(&createExParams, sizeof(createExParams));
+			createExParams.dwSize = sizeof(createExParams);
+			createExParams.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
+			createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+			handle = CreateFile2(ws, 0, 0,
+				OPEN_EXISTING, &createExParams);
+#endif /* !WINAPI_PARTITION_DESKTOP */
+			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
+		_sopen_s(&r, path, flags, _SH_DENYNO, 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);
+		}
+	}
+	_wsopen_s(&r, ws, flags, _SH_DENYNO, 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);
+}
+
+int
+__la_wopen(const wchar_t *path, int flags, ...)
+{
+	va_list ap;
+	wchar_t *fullpath;
+	int r, pmode;
+	DWORD attr;
+
+	va_start(ap, flags);
+	pmode = va_arg(ap, int);
+	va_end(ap);
+	fullpath = NULL;
+
+	/* _(w)sopen_s fails if we provide any other modes */
+	pmode = pmode & (_S_IREAD | _S_IWRITE);
+
+	if ((flags & ~O_BINARY) == O_RDONLY) {
+		/*
+		 * When we open a directory, _open function returns
+		 * "Permission denied" error.
+		 */
+		attr = GetFileAttributesW(path);
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+		if (attr == (DWORD)-1 && GetLastError() == ERROR_PATH_NOT_FOUND)
+#endif
+		{
+			fullpath = __la_win_permissive_name_w(path);
+			if (fullpath == NULL) {
+				errno = EINVAL;
+				return (-1);
+			}
+			path = fullpath;
+			attr = GetFileAttributesW(fullpath);
+		}
+		if (attr == (DWORD)-1) {
+			la_dosmaperr(GetLastError());
+			free(fullpath);
+			return (-1);
+		}
+		if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+			HANDLE handle;
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP)
+			if (fullpath != NULL)
+				handle = CreateFileW(fullpath, 0, 0, NULL,
+				    OPEN_EXISTING,
+				    FILE_FLAG_BACKUP_SEMANTICS |
+				    FILE_ATTRIBUTE_READONLY,
+					NULL);
+			else
+				handle = CreateFileW(path, 0, 0, NULL,
+				    OPEN_EXISTING,
+				    FILE_FLAG_BACKUP_SEMANTICS |
+				    FILE_ATTRIBUTE_READONLY,
+					NULL);
+#else /* !WINAPI_PARTITION_DESKTOP */
+			CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+			ZeroMemory(&createExParams, sizeof(createExParams));
+			createExParams.dwSize = sizeof(createExParams);
+			createExParams.dwFileAttributes = FILE_ATTRIBUTE_READONLY;
+			createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+			handle = CreateFile2(fullpath, 0, 0,
+				OPEN_EXISTING, &createExParams);
+#endif /* !WINAPI_PARTITION_DESKTOP */
+			free(fullpath);
+			if (handle == INVALID_HANDLE_VALUE) {
+				la_dosmaperr(GetLastError());
+				return (-1);
+			}
+			r = _open_osfhandle((intptr_t)handle, _O_RDONLY);
+			return (r);
+		}
+	}
+	_wsopen_s(&r, path, flags, _SH_DENYNO, pmode);
+	if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
+		/* Simulate other POSIX system action to pass our test suite. */
+		attr = GetFileAttributesW(path);
+		if (attr == (DWORD)-1)
+			la_dosmaperr(GetLastError());
+		else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+			errno = EISDIR;
+		else
+			errno = EACCES;
+	}
+	free(fullpath);
+	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);
+}
+
+/* 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;
+
+	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;
+
+	ntfs_to_unix(FILETIME_to_ntfs(&info.ftLastAccessTime), &st->st_atime, &st->st_atime_nsec);
+	ntfs_to_unix(FILETIME_to_ntfs(&info.ftLastWriteTime), &st->st_mtime, &st->st_mtime_nsec);
+	ntfs_to_unix(FILETIME_to_ntfs(&info.ftCreationTime), &st->st_ctime, &st->st_ctime_nsec);
+	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;
+	if (st->st_size < 0 || (uint64_t)st->st_size != us->st_size)
+		st->st_size = 0;
+	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);
+}
+
+static void
+copy_seek_stat(la_seek_stat_t *st, struct ustat *us)
+{
+	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 = (la_seek_t)us->st_size;
+	if (st->st_size < 0 || (uint64_t)st->st_size != us->st_size)
+		st->st_size = -1;
+	st->st_uid = us->st_uid;
+	st->st_dev = us->st_dev;
+	st->st_rdev = us->st_rdev;
+}
+
+int
+__la_seek_fstat(int fd, la_seek_stat_t *st)
+{
+	struct ustat u;
+	int ret;
+
+	ret = __hstat((HANDLE)_get_osfhandle(fd), &u);
+	copy_seek_stat(st, &u);
+	return (ret);
+}
+
+int
+__la_seek_stat(const char *path, la_seek_stat_t *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);
+	copy_seek_stat(st, &u);
+	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) {
+			la_dosmaperr(GetLastError());
+			CloseHandle(child);
+			*status = 0;
+			return (-1);
+		}
+	} while (cs == STILL_ACTIVE);
+
+	CloseHandle(child);
+	*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,325 @@
+/*-
+ * 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.
+ */
+
+/*
+ * 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
+
+#include "archive_platform_stat.h"
+
+#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	_wopen		__la_wopen
+#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
+
+/* Functions to circumvent off_t limitations */
+int __la_seek_fstat(int fd, la_seek_stat_t *st);
+int __la_seek_stat(const char *path, la_seek_stat_t *st);
+
+/* 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 int	 __la_wopen(const wchar_t *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 !WINAPI_FAMILY_PARTITION (WINAPI_PARTITION_DESKTOP) && NTDDI_VERSION < NTDDI_WIN10_VB
+// not supported in UWP SDK before 20H1
+#define GetVolumePathNameW(f, v, c)   (0)
+#elif defined(_MSC_VER) && _MSC_VER < 1300
+WINBASEAPI BOOL WINAPI GetVolumePathNameW(
+       LPCWSTR lpszFileName,
+       LPWSTR lpszVolumePathName,
+       DWORD cchBufferLength
+       );
+#endif
+#if defined(_MSC_VER) && _MSC_VER < 1300
+# 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,873 @@
+/*-
+ * 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"
+
+/*
+ * 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 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 const struct archive_vtable
+archive_write_vtable = {
+	.archive_close = _archive_write_close,
+	.archive_filter_bytes = _archive_filter_bytes,
+	.archive_filter_code = _archive_filter_code,
+	.archive_filter_name = _archive_filter_name,
+	.archive_filter_count = _archive_write_filter_count,
+	.archive_free = _archive_write_free,
+	.archive_write_header = _archive_write_header,
+	.archive_write_finish_entry = _archive_write_finish_entry,
+	.archive_write_data = _archive_write_data,
+};
+
+/*
+ * Allocate, initialize and return an archive object.
+ */
+struct archive *
+archive_write_new(void)
+{
+	struct archive_write *a;
+	unsigned char *nulls;
+
+	a = 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 = calloc(a->null_length, sizeof(unsigned char));
+	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");
+
+	if (bytes_per_block < 0) {
+		// Do nothing if the bytes_per_block is negative
+		return 0;
+	}
+	a->bytes_per_block = bytes_per_block;
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Get the current block size.
+ */
+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");
+	if (a->bytes_per_block < 0) {
+		// Don't return a negative value
+		return 1;
+	}
+	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));
+
+	if (f == NULL)
+		return (NULL);
+
+	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 filters
+ */
+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));
+}
+
+static int
+__archive_write_filters_flush(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) {
+		if (f->flush != NULL && f->bytes_written > 0) {
+			ret1 = (f->flush)(f);
+			if (ret1 < ret)
+				ret = ret1;
+			if (ret1 < ARCHIVE_WARN)
+				f->state = ARCHIVE_WRITE_FILTER_STATE_FATAL;
+		}
+	}
+	return (ret);
+}
+
+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;
+
+	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 = calloc(1, sizeof(*state));
+	buffer = 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);
+	return (a->client_opener(f->archive, a->client_data));
+}
+
+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_free(struct archive_write_filter *f)
+{
+	struct archive_write *a = (struct archive_write *)f->archive;
+	struct archive_none *state = (struct archive_none *)f->data;
+
+	if (a->client_freer)
+		(*a->client_freer)(&a->archive, a->client_data);
+	a->client_data = NULL;
+
+	/* Clear passphrase. */
+	if (a->passphrase != NULL) {
+		memset(a->passphrase, 0, strlen(a->passphrase));
+		free(a->passphrase);
+		a->passphrase = NULL;
+	}
+
+	/* Free state. */
+	if (state != NULL) {
+		free(state->buffer);
+		free(state);
+		f->data = NULL;
+	}
+
+	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;
+	size_t to_write;
+	char *p;
+	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;
+		}
+		p = state->buffer;
+		to_write = block_length;
+		while (to_write > 0) {
+			bytes_written = (a->client_writer)(&a->archive,
+			    a->client_data, p, to_write);
+			if (bytes_written <= 0) {
+				ret = ARCHIVE_FATAL;
+				break;
+			}
+			if ((size_t)bytes_written > to_write) {
+				archive_set_error(&(a->archive),
+						  -1, "write overrun");
+				ret = ARCHIVE_FATAL;
+				break;
+			}
+			p += bytes_written;
+			to_write -= bytes_written;
+		}
+	}
+	if (a->client_closer)
+		(*a->client_closer)(&a->archive, a->client_data);
+
+	/* 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_open2(struct archive *_a, void *client_data,
+    archive_open_callback *opener, archive_write_callback *writer,
+    archive_close_callback *closer, archive_free_callback *freer)
+{
+	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_freer = freer;
+	a->client_data = client_data;
+
+	client_filter = __archive_write_allocate_filter(_a);
+
+	if (client_filter == NULL)
+		return (ARCHIVE_FATAL);
+
+	client_filter->open = archive_write_client_open;
+	client_filter->write = archive_write_client_write;
+	client_filter->close = archive_write_client_close;
+	client_filter->free = archive_write_client_free;
+
+	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);
+}
+
+int
+archive_write_open(struct archive *_a, void *client_data,
+    archive_open_callback *opener, archive_write_callback *writer,
+    archive_close_callback *closer)
+{
+	return archive_write_open2(_a, client_data, opener, writer,
+	    closer, NULL);
+}
+
+/*
+ * 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);
+	}
+
+	/* Flush filters at boundary. */
+	r2 = __archive_write_filters_flush(a);
+	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;
+
+	/* 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);
+	if (ret == ARCHIVE_FATAL)
+		a->archive.state = ARCHIVE_STATE_FATAL;
+	else
+		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;
+	int ret;
+
+	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);
+	ret = (a->format_write_data)(a, buff, s);
+	if (ret == ARCHIVE_FATAL)
+		a->archive.state = ARCHIVE_STATE_FATAL;
+	return (ret);
+}
+
+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,71 @@
+/*-
+ * 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"
+
+#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,302 @@
+/*-
+ * 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"
+
+#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_b64encode");
+
+	state = 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 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",
+	    (unsigned int)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,76 @@
+/*-
+ * 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"
+
+#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,402 @@
+/*-
+ * 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"
+
+#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 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 = 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 = (uint32_t)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 = (uint32_t)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;
+
+	/* May already have been called, but not necessarily. */
+	(void)BZ2_bzCompressEnd(&(data->stream));
+
+	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 = (uint32_t)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,445 @@
+/*-
+ * 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"
+
+#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 = 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 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,133 @@
+/*-
+ * 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"
+
+#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,479 @@
+/*-
+ * 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"
+
+#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;
+	char	*original_filename;
+#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";
+
+	data->original_filename = NULL;
+#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((void*)data->original_filename);
+	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);
+	}
+	if (strcmp(key, "original-filename") == 0) {
+		free((void*)data->original_filename);
+		data->original_filename = NULL;
+		if (value)
+			data->original_filename = strdup(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);
+}
+
+#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 = ARCHIVE_OK;
+	int init_success;
+
+	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 = 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] = 0x00; /* Flags */
+	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;
+
+	if (data->original_filename != NULL) {
+		/* Limit "original filename" to 32k or the
+		 * remaining space in the buffer, whichever is smaller.
+		 */
+		int ofn_length = strlen(data->original_filename);
+		int ofn_max_length = 32768;
+		int ofn_space_available = data->compressed
+			+ data->compressed_buffer_size
+			- data->stream.next_out
+			- 1;
+		if (ofn_max_length > ofn_space_available) {
+			ofn_max_length = ofn_space_available;
+		}
+		if (ofn_length < ofn_max_length) {
+			data->compressed[3] |= 0x8;
+			strcpy((char*)data->compressed + 10,
+			       data->original_filename);
+			data->stream.next_out += ofn_length + 1;
+			data->stream.avail_out -= ofn_length + 1;
+		} else {
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+					  "Gzip 'Original Filename' ignored because it is too long");
+			ret = ARCHIVE_WARN;
+		}
+	}
+
+	f->write = archive_compressor_gzip_write;
+
+	/* Initialize compression library. */
+	init_success = deflateInit2(&(data->stream),
+	    data->compression_level,
+	    Z_DEFLATED,
+	    -15 /* < 0 to suppress zlib header */,
+	    8,
+	    Z_DEFAULT_STRATEGY);
+
+	if (init_success == Z_OK) {
+		f->data = data;
+		return (ret);
+	}
+
+	/* 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 (init_success) {
+	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,195 @@
+/*-
+ * 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"
+
+#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,698 @@
+/*-
+ * 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_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, (uint32_t)(length | 0x80000000));
+		data->out += 4;
+		memcpy(data->out, p, length);
+		outsize = (uint32_t)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, (uint32_t)(length | 0x80000000));
+		data->out += 4;
+		memcpy(data->out, p, length);
+		outsize = (uint32_t)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,477 @@
+/*-
+ * 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"
+
+//#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,42 @@
+/*-
+ * 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"
+
+#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,389 @@
+/*-
+ * 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"
+
+#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;
+	pid_t pid;
+	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);
+	do {
+		pid = waitpid(data->child, &status, 0);
+	} while (pid == -1 && errno == EINTR);
+	data->child = 0;
+
+	if (pid < 0 || 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,293 @@
+/*-
+ * 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"
+
+#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 = 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",
+	    (unsigned int)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,542 @@
+/*-
+ * 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"
+
+#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 << 29)) {
+			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+			    "Unacceptable dictionary size for lzip: %d",
+			    dict_size);
+			return (ARCHIVE_FATAL);
+		}
+		for (log2dic = 29; 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 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 = 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 > 9)
+			data->compression_level = 9;
+		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,572 @@
+/*-
+ * Copyright (c) 2017 Sean Purcell
+ * Copyright (c) 2023-2024 Klara, Inc.
+ * 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_STDINT_H
+#include <stdint.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_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;
+	int		 threads;
+	int		 long_distance;
+#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
+	enum {
+		running,
+		finishing,
+		resetting,
+	} state;
+	int		 frame_per_file;
+	size_t		 min_frame_in;
+	size_t		 max_frame_in;
+	size_t		 min_frame_out;
+	size_t		 max_frame_out;
+	size_t		 cur_frame;
+	size_t		 cur_frame_in;
+	size_t		 cur_frame_out;
+	size_t		 total_in;
+	ZSTD_CStream	*cstream;
+	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 LONG_STD 27
+
+#define MINVER_NEGCLEVEL 10304
+#define MINVER_MINCLEVEL 10306
+#define MINVER_LONG 10302
+
+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_flush(struct archive_write_filter *);
+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_ZSTD_compressStream
+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->flush = &archive_compressor_zstd_flush;
+	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;
+	data->threads = 0;
+	data->long_distance = 0;
+#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
+	data->frame_per_file = 0;
+	data->min_frame_in = 0;
+	data->max_frame_in = SIZE_MAX;
+	data->min_frame_out = 0;
+	data->max_frame_out = SIZE_MAX;
+	data->cur_frame_in = 0;
+	data->cur_frame_out = 0;
+	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_ZSTD_compressStream
+	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_to_number(const char *string, intmax_t *numberp)
+{
+	char *end;
+
+	if (string == NULL || *string == '\0')
+		return (ARCHIVE_WARN);
+	*numberp = strtoimax(string, &end, 10);
+	if (end == string || *end != '\0' || errno == EOVERFLOW) {
+		*numberp = 0;
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
+static int
+string_to_size(const char *string, size_t *numberp)
+{
+	uintmax_t number;
+	char *end;
+	unsigned int shift = 0;
+
+	if (string == NULL || *string == '\0' || *string == '-')
+		return (ARCHIVE_WARN);
+	number = strtoumax(string, &end, 10);
+	if (end > string) {
+		if (*end == 'K' || *end == 'k') {
+			shift = 10;
+			end++;
+		} else if (*end == 'M' || *end == 'm') {
+			shift = 20;
+			end++;
+		} else if (*end == 'G' || *end == 'g') {
+			shift = 30;
+			end++;
+		}
+		if (*end == 'B' || *end == 'b') {
+			end++;
+		}
+	}
+	if (end == string || *end != '\0' || errno == EOVERFLOW) {
+		return (ARCHIVE_WARN);
+	}
+	if (number > (uintmax_t)SIZE_MAX >> shift) {
+		return (ARCHIVE_WARN);
+	}
+	*numberp = (size_t)(number << shift);
+	return (ARCHIVE_OK);
+}
+#endif
+
+/*
+ * 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) {
+		intmax_t level;
+		if (string_to_number(value, &level) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+		/* If we don't have the library, hard-code the max level */
+		int minimum = CLEVEL_MIN;
+		int maximum = CLEVEL_MAX;
+#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
+		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 = (int)level;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "threads") == 0) {
+		intmax_t threads;
+		if (string_to_number(value, &threads) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+
+#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
+		if (threads == 0) {
+			threads = sysconf(_SC_NPROCESSORS_ONLN);
+		}
+#elif !defined(__CYGWIN__) && defined(_WIN32_WINNT) && \
+    _WIN32_WINNT >= 0x0601 /* _WIN32_WINNT_WIN7 */
+		if (threads == 0) {
+			DWORD winCores = GetActiveProcessorCount(
+			    ALL_PROCESSOR_GROUPS);
+			threads = (intmax_t)winCores;
+		}
+#endif
+		if (threads < 0 || threads > INT_MAX) {
+			return (ARCHIVE_WARN);
+		}
+		data->threads = (int)threads;
+		return (ARCHIVE_OK);
+#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
+	} else if (strcmp(key, "frame-per-file") == 0) {
+		data->frame_per_file = 1;
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "min-frame-in") == 0) {
+		if (string_to_size(value, &data->min_frame_in) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "min-frame-out") == 0 ||
+	    strcmp(key, "min-frame-size") == 0) {
+		if (string_to_size(value, &data->min_frame_out) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "max-frame-in") == 0 ||
+	    strcmp(key, "max-frame-size") == 0) {
+		if (string_to_size(value, &data->max_frame_in) != ARCHIVE_OK ||
+		    data->max_frame_in < 1024) {
+			return (ARCHIVE_WARN);
+		}
+		return (ARCHIVE_OK);
+	} else if (strcmp(key, "max-frame-out") == 0) {
+		if (string_to_size(value, &data->max_frame_out) != ARCHIVE_OK ||
+		    data->max_frame_out < 1024) {
+			return (ARCHIVE_WARN);
+		}
+		return (ARCHIVE_OK);
+#endif
+	}
+	else if (strcmp(key, "long") == 0) {
+		intmax_t long_distance;
+		if (string_to_number(value, &long_distance) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream && ZSTD_VERSION_NUMBER >= MINVER_LONG
+		ZSTD_bounds bounds = ZSTD_cParam_getBounds(ZSTD_c_windowLog);
+		if (ZSTD_isError(bounds.error)) {
+			int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31));
+			if (((int)long_distance) < 10 || (int)long_distance > max_distance)
+				return (ARCHIVE_WARN);
+		} else {
+			if ((int)long_distance < bounds.lowerBound || (int)long_distance > bounds.upperBound)
+				return (ARCHIVE_WARN);
+		}
+#else
+		int max_distance = ((int)(sizeof(size_t) == 4 ? 30 : 31));
+		if (((int)long_distance) < 10 || (int)long_distance > max_distance)
+		    return (ARCHIVE_WARN);
+#endif
+		data->long_distance = (int)long_distance;
+		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_ZSTD_compressStream
+/*
+ * 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 = 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);
+	}
+
+	ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_nbWorkers, data->threads);
+
+	ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_checksumFlag, 1);
+
+#if ZSTD_VERSION_NUMBER >= MINVER_LONG
+	ZSTD_CCtx_setParameter(data->cstream, ZSTD_c_windowLog, data->long_distance);
+#endif
+
+	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;
+
+	return (drive_compressor(f, data, 0, buff, length));
+}
+
+/*
+ * Flush the compressed stream.
+ */
+static int
+archive_compressor_zstd_flush(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (data->frame_per_file && data->state == running) {
+		if (data->cur_frame_in > data->min_frame_in &&
+		    data->cur_frame_out > data->min_frame_out) {
+			data->state = finishing;
+		}
+	}
+	return (drive_compressor(f, data, 1, NULL, 0));
+}
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_zstd_close(struct archive_write_filter *f)
+{
+	struct private_data *data = (struct private_data *)f->data;
+
+	if (data->state == running)
+		data->state = finishing;
+	return (drive_compressor(f, data, 1, NULL, 0));
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+    struct private_data *data, int flush, const void *src, size_t length)
+{
+	ZSTD_inBuffer in = { .src = src, .size = length, .pos = 0 };
+	size_t ipos, opos, zstdret = 0;
+	int ret;
+
+	for (;;) {
+		ipos = in.pos;
+		opos = data->out.pos;
+		switch (data->state) {
+		case running:
+			if (in.pos == in.size)
+				return (ARCHIVE_OK);
+			zstdret = ZSTD_compressStream(data->cstream,
+			    &data->out, &in);
+			if (ZSTD_isError(zstdret))
+				goto zstd_fatal;
+			break;
+		case finishing:
+			zstdret = ZSTD_endStream(data->cstream, &data->out);
+			if (ZSTD_isError(zstdret))
+				goto zstd_fatal;
+			if (zstdret == 0)
+				data->state = resetting;
+			break;
+		case resetting:
+			ZSTD_CCtx_reset(data->cstream, ZSTD_reset_session_only);
+			data->cur_frame++;
+			data->cur_frame_in = 0;
+			data->cur_frame_out = 0;
+			data->state = running;
+			break;
+		}
+		data->total_in += in.pos - ipos;
+		data->cur_frame_in += in.pos - ipos;
+		data->cur_frame_out += data->out.pos - opos;
+		if (data->state == running) {
+			if (data->cur_frame_in >= data->max_frame_in ||
+			    data->cur_frame_out >= data->max_frame_out) {
+				data->state = finishing;
+			}
+		}
+		if (data->out.pos == data->out.size ||
+		    (flush && data->out.pos > 0)) {
+			ret = __archive_write_filter(f->next_filter,
+			    data->out.dst, data->out.pos);
+			if (ret != ARCHIVE_OK)
+				goto fatal;
+			data->out.pos = 0;
+		}
+	}
+zstd_fatal:
+	archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+	    "Zstd compression failed: %s",
+	    ZSTD_getErrorName(zstdret));
+fatal:
+	return (ARCHIVE_FATAL);
+}
+
+#else /* HAVE_ZSTD_H && HAVE_ZSTD_compressStream */
+
+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) {
+		archive_string_sprintf(&as, " --fast=%d", -data->compression_level);
+	} else {
+		archive_string_sprintf(&as, " -%d", data->compression_level);
+	}
+
+	if (data->compression_level > CLEVEL_STD_MAX) {
+		archive_strcat(&as, " --ultra");
+	}
+
+	if (data->threads != 0) {
+		archive_string_sprintf(&as, " --threads=%d", data->threads);
+	}
+
+	if (data->long_distance != 0) {
+		archive_string_sprintf(&as, " --long=%d", data->long_distance);
+	}
+
+	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_flush(struct archive_write_filter *f)
+{
+	(void)f; /* UNUSED */
+
+	return (ARCHIVE_OK);
+}
+
+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_ZSTD_compressStream */
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,4764 @@
+/*-
+ * 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"
+
+#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;
+	__LA_MODE_T		 filetype;
+	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 int	la_verify_filetype(mode_t, __LA_MODE_T);
+static void	fsobj_error(int *, struct archive_string *, int, const char *,
+		    const char *);
+static int	check_symlinks_fsobj(char *, int *, struct archive_string *,
+		    int, 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 void close_file_descriptor(struct archive_write_disk *);
+
+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
+la_verify_filetype(mode_t mode, __LA_MODE_T filetype) {
+	int ret = 0;
+
+	switch (filetype) {
+	case AE_IFREG:
+		ret = (S_ISREG(mode));
+		break;
+	case AE_IFDIR:
+		ret = (S_ISDIR(mode));
+		break;
+	case AE_IFLNK:
+		ret = (S_ISLNK(mode));
+		break;
+#ifdef S_ISSOCK
+	case AE_IFSOCK:
+		ret = (S_ISSOCK(mode));
+		break;
+#endif
+	case AE_IFCHR:
+		ret = (S_ISCHR(mode));
+		break;
+	case AE_IFBLK:
+		ret = (S_ISBLK(mode));
+		break;
+	case AE_IFIFO:
+		ret = (S_ISFIFO(mode));
+		break;
+	default:
+		break;
+	}
+
+	return (ret);
+}
+
+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?
+	 */
+#ifdef HAVE_LSTAT
+	if (lstat(a->name, &a->st) == 0)
+#else
+	if (la_stat(a->name, &a->st) == 0)
+#endif
+	{
+		a->pst = &a->st;
+		return (ARCHIVE_OK);
+	}
+	archive_set_error(&a->archive, errno, "Couldn't stat file");
+	return (ARCHIVE_WARN);
+}
+
+static const struct archive_vtable
+archive_write_disk_vtable = {
+	.archive_close = _archive_write_disk_close,
+	.archive_filter_bytes = _archive_write_disk_filter_bytes,
+	.archive_free = _archive_write_disk_free,
+	.archive_write_header = _archive_write_disk_header,
+	.archive_write_finish_entry = _archive_write_disk_finish_entry,
+	.archive_write_data = _archive_write_disk_data,
+	.archive_write_data_block = _archive_write_disk_data_block,
+};
+
+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;
+	const char *linkname;
+	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);
+
+	/*
+	 * Check if we have a hardlink that points to itself.
+	 */
+	linkname = archive_entry_hardlink(a->entry);
+	if (linkname != NULL && strcmp(a->name, linkname) == 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Skipping hardlink pointing to itself: %s",
+		    a->name);
+		return (ARCHIVE_WARN);
+	}
+
+	/*
+	 * 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->filetype = archive_entry_filetype(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(entry));
+		if (fe == NULL)
+			return (ARCHIVE_FATAL);
+		fe->filetype = archive_entry_filetype(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(entry));
+		if (fe == NULL)
+			return (ARCHIVE_FATAL);
+		fe->filetype = archive_entry_filetype(entry);
+		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->filetype = archive_entry_filetype(entry);
+			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->filetype = archive_entry_filetype(entry);
+		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) {
+			uint64_t skip = a->offset - a->fd_offset;
+			char nullblock[1024];
+
+			memset(nullblock, 0, sizeof(nullblock));
+			while (skip > 0) {
+				if (skip > 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) {
+				close_file_descriptor(a);
+				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");
+			close_file_descriptor(a);
+			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) {
+            close_file_descriptor(a);
+            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");
+				close_file_descriptor(a);
+				return (ARCHIVE_FATAL);
+			}
+			if (write(a->fd, &nul, 1) < 0) {
+				archive_set_error(&a->archive, errno,
+				    "Write to restore size failed");
+				close_file_descriptor(a);
+				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 = 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);
+	}
+	a->path_safe.s[0] = 0;
+
+#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))
+#ifdef HAVE_LSTAT
+			r = lstat(a->name, &a->st);
+#else
+			r = la_stat(a->name, &a->st);
+#endif
+		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->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, 1);
+		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);
+#ifdef HAVE_LINKAT
+		r = linkat(AT_FDCWD, linkname, AT_FDCWD, a->name,
+		    0) ? errno : 0;
+#else
+		r = link(linkname, a->name) ? errno : 0;
+#endif
+		/*
+		 * 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;
+	struct stat st;
+	char *c;
+	int fd, ret, openflags;
+
+	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. */
+
+		/* We must strip trailing slashes from the path to avoid
+		   dereferencing symbolic links to directories */
+		c = p->name;
+		while (*c != '\0')
+			c++;
+		while (c != p->name && *(c - 1) == '/') {
+			c--;
+			*c = '\0';
+		}
+
+		if (p->fixup == 0)
+			goto skip_fixup_entry;
+		else {
+			/*
+			 * We need to verify if the type of the file
+			 * we are going to open matches the file type
+			 * of the fixup entry.
+			 */
+			openflags = O_BINARY | O_NOFOLLOW | O_RDONLY
+			    | O_CLOEXEC;
+#if defined(O_DIRECTORY)
+			if (p->filetype == AE_IFDIR)
+				openflags |= O_DIRECTORY;
+#endif
+			fd = open(p->name, openflags);
+
+#if defined(O_DIRECTORY)
+			/*
+			 * If we support O_DIRECTORY and open was
+			 * successful we can skip the file type check
+			 * for directories. For other file types
+			 * we need to verify via fstat() or lstat()
+			 */
+			if (fd < 0 || p->filetype != AE_IFDIR) {
+#if HAVE_FSTAT
+				if (fd >= 0 && (
+				    fstat(fd, &st) != 0 ||
+				    la_verify_filetype(st.st_mode,
+				    p->filetype) == 0)) {
+					goto skip_fixup_entry;
+				} else
+#endif
+				if (
+#ifdef HAVE_LSTAT
+					lstat(p->name, &st) != 0 ||
+#else
+					la_stat(p->name, &st) != 0 ||
+#endif
+				    la_verify_filetype(st.st_mode,
+				    p->filetype) == 0) {
+					goto skip_fixup_entry;
+				}
+			}
+#else
+#if HAVE_FSTAT
+			if (fd > 0 && (
+			    fstat(fd, &st) != 0 ||
+			    la_verify_filetype(st.st_mode,
+			    p->filetype) == 0)) {
+				goto skip_fixup_entry;
+			} else
+#endif
+			if (
+#ifdef HAVE_LSTAT
+				lstat(p->name, &st) != 0 ||
+#else
+				la_stat(p->name, &st) != 0 ||
+#endif
+			    la_verify_filetype(st.st_mode,
+			    p->filetype) == 0) {
+				goto skip_fixup_entry;
+			}
+#endif
+		}
+		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 & 07777);
+			else
+#endif
+#ifdef HAVE_LCHMOD
+			lchmod(p->name, p->mode & 07777);
+#else
+			chmod(p->name, p->mode & 07777);
+#endif
+		}
+		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);
+skip_fixup_entry:
+		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 = 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->filetype = 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, int checking_linkname)
+{
+#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)a_eno; /* UNUSED */
+	(void)a_estr; /* UNUSED */
+	(void)flags; /* UNUSED */
+	(void)checking_linkname; /* UNUSED */
+	return (ARCHIVE_OK);
+#else
+	int res = ARCHIVE_OK;
+	char *tail;
+	char *head;
+	int last;
+	char c = '\0';
+	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);
+#elif defined(HAVE_LSTAT)
+		r = lstat(head, &st);
+#else
+		r = la_stat(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 && checking_linkname) {
+#ifdef HAVE_LINKAT
+				/*
+				 * Hardlinks to symlinks are safe to write
+				 * if linkat() is supported as it does not
+				 * follow symlinks.
+				 */
+				res = ARCHIVE_OK;
+#else
+				/*
+				 * We return ARCHIVE_FAILED here as we are
+				 * not able to safely write hardlinks
+				 * to symlinks.
+				 */
+				tail[0] = c;
+				fsobj_error(a_eno, a_estr, errno,
+				    "Cannot write hardlink to symlink ",
+				    path);
+				res = ARCHIVE_FAILED;
+#endif
+				break;
+			} else
+			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, 0);
+	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; /* UNUSED */
+	(void)atime_nsec; /* UNUSED */
+	(void)mtime; /* 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) || defined (__hppa) )
+	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_t)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",
+				    (unsigned 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_t)mode);
+		else
+#endif
+		/* If this platform lacks fchmod(), then
+		 * we'll just use chmod(). */
+		r2 = chmod(a->name, (mode_t)mode);
+
+		if (r2 != 0) {
+			archive_set_error(&a->archive, errno,
+			    "Can't set permissions to 0%o", (unsigned 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->filetype = archive_entry_filetype(a->entry);
+			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
+#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
+	;
+
+	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 | O_NOFOLLOW);
+		__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 *p;
+		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;
+		}
+		p = realloc(xattr_val, s);
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Failed to get metadata(xattr)");
+			ret = ARCHIVE_WARN;
+			goto exit_xattr;
+		}
+		xattr_val = p;
+		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, "tar.mmd.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 (
+#ifdef HAVE_LSTAT
+		lstat(datafork.s, &st) == -1 ||
+#else
+		la_stat(datafork.s, &st) == -1 ||
+#endif
+	    (((st.st_mode & AE_IFMT) != AE_IFREG) &&
+		((st.st_mode & AE_IFMT) != AE_IFDIR)))
+		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 < 0) {
+		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;
+
+			namespace = EXTATTR_NAMESPACE_USER;
+
+			if (strncmp(name, "user.", 5) == 0) {
+				/* "user." attributes go to user namespace */
+				name += 5;
+				namespace = EXTATTR_NAMESPACE_USER;
+			} else if (strncmp(name, "system.", 7) == 0) {
+				name += 7;
+				namespace = EXTATTR_NAMESPACE_SYSTEM;
+				if (!strcmp(name, "nfs4.acl") ||
+				    !strcmp(name, "posix1e.acl_access") ||
+				    !strcmp(name, "posix1e.acl_default"))
+					continue;
+			} else {
+				/* Other namespaces are unsupported */
+				archive_strcat(&errlist, name);
+				archive_strappend_char(&errlist, ' ');
+				fail = 1;
+				ret = ARCHIVE_WARN;
+				continue;
+			}
+
+			if (a->fd >= 0) {
+				/*
+				 * On FreeBSD, extattr_set_fd does not
+				 * return the same as
+				 * extattr_set_file. It returns zero
+				 * on success, non-zero on failure.
+				 *
+				 * We can detect the failure by
+				 * manually setting errno prior to the
+				 * call and checking after.
+				 *
+				 * If errno remains zero, fake the
+				 * return value by setting e to size.
+				 *
+				 * This is a hack for now until I
+				 * (Shawn Webb) get FreeBSD to fix the
+				 * issue, if that's even possible.
+				 */
+				errno = 0;
+				e = extattr_set_fd(a->fd, namespace, name,
+				    value, size);
+				if (e == 0 && errno == 0) {
+					e = 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
+
+/*
+ * Close the file descriptor if one is open.
+ */
+static void close_file_descriptor(struct archive_write_disk* a)
+{
+	if (a->fd >= 0) {
+		close(a->fd);
+		a->fd = -1;
+	}
+}
+
+
+#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,43 @@
+/*-
+ * Copyright (c) 2003-2007 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer
+ *    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.
+ */
+
+#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,262 @@
+/*-
+ * 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"
+
+#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,2921 @@
+/*-
+ * 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"
+
+#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"
+#include "archive_time_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 *, wchar_t *);
+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 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 _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+	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;
+
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+	ZeroMemory(&createExParams, sizeof(createExParams));
+	createExParams.dwSize = sizeof(createExParams);
+	createExParams.dwFileFlags = flag;
+	h = CreateFile2(a->name, 0, 0,
+		OPEN_EXISTING, &createExParams);
+#else
+	h = CreateFileW(a->name, 0, 0, NULL,
+	    OPEN_EXISTING, flag, NULL);
+#endif
+	if (h == INVALID_HANDLE_VALUE &&
+	    GetLastError() == ERROR_INVALID_NAME) {
+		wchar_t *full;
+		full = __la_win_permissive_name_w(path);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		h = CreateFile2(full, 0, 0,
+			OPEN_EXISTING, &createExParams);
+#else
+		h = CreateFileW(full, 0, 0, NULL,
+		    OPEN_EXISTING, flag, NULL);
+#endif
+		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);
+		if (archive_wstring_ensure(&(a->_name_data),
+			4 + wcslen(wn) + 1) == NULL) {
+			free(wn);
+			return (-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);
+				if (archive_wstring_ensure(&(a->_name_data),
+					8 + wcslen(wn) + 1) == NULL) {
+					free(wn);
+					return (-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) {
+			free(wsp);
+			return (-1);
+		}
+		if (archive_wstring_ensure(&(a->_name_data),
+			4 + 2 + wcslen(wn) + 1) == NULL) {
+			free(wsp);
+			free(wn);
+			return (-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) {
+		free(wsp);
+		return (-1);
+	}
+	if (archive_wstring_ensure(&(a->_name_data),
+		4 + l + 1 + wcslen(wn) + 1) == NULL) {
+		free(wsp);
+		free(wn);
+		return (-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);
+}
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+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);
+}
+#endif
+
+static int
+la_CreateHardLinkW(wchar_t *linkname, wchar_t *target)
+{
+	static BOOL (WINAPI *f)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
+	BOOL ret;
+
+#if _WIN32_WINNT < _WIN32_WINNT_XP
+	static int set;
+/* CreateHardLinkW is available since XP and always loaded */
+	if (!set) {
+		set = 1;
+		f = la_GetFunctionKernel32("CreateHardLinkW");
+	}
+#else
+	f = CreateHardLinkW;
+#endif
+	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 symbolic 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);
+	wchar_t *ttarget, *p;
+	size_t len;
+	DWORD attrs = 0;
+	DWORD flags = 0;
+	DWORD newflags = 0;
+	BOOL ret = 0;
+
+#if _WIN32_WINNT < _WIN32_WINNT_VISTA
+/* CreateSymbolicLinkW is available since Vista and always loaded */
+	static int set;
+	if (!set) {
+		set = 1;
+		f = la_GetFunctionKernel32("CreateSymbolicLinkW");
+	}
+#else
+# if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+	f = CreateSymbolicLinkW;
+# else
+	f = NULL;
+# endif
+#endif
+	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 const struct archive_vtable
+archive_write_disk_vtable = {
+	.archive_close = _archive_write_disk_close,
+	.archive_filter_bytes = _archive_write_disk_filter_bytes,
+	.archive_free = _archive_write_disk_free,
+	.archive_write_header = _archive_write_disk_header,
+	.archive_write_finish_entry = _archive_write_disk_finish_entry,
+	.archive_write_data = _archive_write_disk_data,
+	.archive_write_data_block = _archive_write_disk_data_block,
+};
+
+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, a->name);
+	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");
+			CloseHandle(a->fh);
+			a->fh = INVALID_HANDLE_VALUE;
+			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 = 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);
+	}
+	a->path_safe.s[0] = 0;
+	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(a->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;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+	/* 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 *linksanitized, *linkfull, *namefull;
+		size_t l = (wcslen(linkname) + 1) * sizeof(wchar_t);
+		linksanitized = malloc(l);
+		if (linksanitized == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory for hardlink target");
+			return (-1);
+		}
+		memcpy(linksanitized, linkname, l);
+		r = cleanup_pathname(a, linksanitized);
+		if (r != ARCHIVE_OK) {
+			free(linksanitized);
+			return (r);
+		}
+		linkfull = __la_win_permissive_name_w(linksanitized);
+		free(linksanitized);
+		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) {
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+			ZeroMemory(&createExParams, sizeof(createExParams));
+			createExParams.dwSize = sizeof(createExParams);
+			createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+			a->fh = CreateFile2(namefull, GENERIC_WRITE, 0,
+			    TRUNCATE_EXISTING, &createExParams);
+#else
+			a->fh = CreateFileW(namefull, GENERIC_WRITE, 0, NULL,
+			    TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+			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 */
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		ZeroMemory(&createExParams, sizeof(createExParams));
+		createExParams.dwSize = sizeof(createExParams);
+		createExParams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+		a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
+		    CREATE_NEW, &createExParams);
+#else
+		a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
+		    CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+		if (a->fh == INVALID_HANDLE_VALUE &&
+		    GetLastError() == ERROR_INVALID_NAME &&
+		    fullname == a->name) {
+			fullname = __la_win_permissive_name_w(a->name);
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+			a->fh = CreateFile2(fullname, GENERIC_WRITE, 0,
+			    CREATE_NEW, &createExParams);
+#else
+			a->fh = CreateFileW(fullname, GENERIC_WRITE, 0, NULL,
+			    CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+#endif
+		}
+		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 = 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);
+			}
+		}
+		if (!c)
+			break;
+		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 or (if ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS)
+ * if the path is absolute.
+ */
+static int
+cleanup_pathname(struct archive_write_disk *a, wchar_t *name)
+{
+	wchar_t *dest, *src, *p, *top;
+	wchar_t separator = L'\0';
+	BOOL absolute_path = 0;
+
+	p = 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 = 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'\\')
+	{
+		absolute_path = 1;
+
+		/* 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;
+	/* Network drive path like "\\<server-name>\<share-name>\file" */
+	} else if (p[0] == L'\\' && p[1] == L'\\') {
+		absolute_path = 1;
+		p += 2;
+	}
+
+	/* 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);
+		}
+
+		absolute_path = 1;
+
+		if (p[2] == L'\\')
+			p += 2;
+	}
+
+	if (absolute_path && (a->flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS)) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Path is absolute");
+		return (ARCHIVE_FAILED);
+	}
+
+	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)
+{
+	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 _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		CREATEFILE2_EXTENDED_PARAMETERS createExParams;
+#endif
+
+		if (S_ISLNK(mode))
+			return (ARCHIVE_OK);
+		ws = __la_win_permissive_name_w(name);
+		if (ws == NULL)
+			goto settimes_failed;
+# if _WIN32_WINNT >= 0x0602 /* _WIN32_WINNT_WIN8 */
+		ZeroMemory(&createExParams, sizeof(createExParams));
+		createExParams.dwSize = sizeof(createExParams);
+		createExParams.dwFileFlags = FILE_FLAG_BACKUP_SEMANTICS;
+		hw = CreateFile2(ws, FILE_WRITE_ATTRIBUTES, 0,
+		    OPEN_EXISTING, &createExParams);
+#else
+		hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES,
+		    0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+#endif
+		free(ws);
+		if (hw == INVALID_HANDLE_VALUE)
+			goto settimes_failed;
+		h = hw;
+	}
+
+	wintm.QuadPart = unix_to_ntfs(atime, atime_nanos);
+	fatime.dwLowDateTime = wintm.LowPart;
+	fatime.dwHighDateTime = wintm.HighPart;
+	wintm.QuadPart = unix_to_ntfs(mtime, mtime_nanos);
+	fmtime.dwLowDateTime = wintm.LowPart;
+	fmtime.dwHighDateTime = wintm.HighPart;
+	/*
+	 * SetFileTime() supports birthtime.
+	 */
+	if (birthtime > 0 || birthtime_nanos > 0) {
+		wintm.QuadPart = unix_to_ntfs(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);
+}
+
+/*
+ * Test if file on disk is older than entry.
+ */
+static int
+older(BY_HANDLE_FILE_INFORMATION *st, struct archive_entry *entry)
+{
+	int64_t sec;
+	uint32_t nsec;
+
+	ntfs_to_unix(FILETIME_to_ntfs(&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 ((long)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,145 @@
+/*-
+ * 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"
+
+#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_free(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 = 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_open2(a, mine,
+		    file_open, file_write, NULL, file_free));
+}
+
+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_free(struct archive *a, void *client_data)
+{
+	struct write_fd_data	*mine = (struct write_fd_data *)client_data;
+
+	(void)a; /* UNUSED */
+	if (mine == NULL)
+		return (ARCHIVE_OK);
+	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,106 @@
+/*-
+ * 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"
+
+#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_free(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 = malloc(sizeof(*mine));
+	if (mine == NULL) {
+		archive_set_error(a, ENOMEM, "No memory");
+		return (ARCHIVE_FATAL);
+	}
+	mine->f = f;
+	return (archive_write_open2(a, mine, file_open, file_write,
+	    NULL, file_free));
+}
+
+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;
+	bytesWritten = fwrite(buff, 1, length, mine->f);
+	if (bytesWritten != length) {
+		archive_set_error(a, errno, "Write error");
+		return (-1);
+	}
+	return (bytesWritten);
+}
+
+static int
+file_free(struct archive *a, void *client_data)
+{
+	struct write_FILE_data	*mine = client_data;
+
+	(void)a; /* UNUSED */
+	if (mine == NULL)
+		return (ARCHIVE_OK);
+	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,270 @@
+/*-
+ * 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"
+
+#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_free(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 = 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) {
+		free(mine);
+		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 '%ls' to MBS",
+			    (const wchar_t *)filename);
+		return (ARCHIVE_FAILED);
+	}
+	mine->fd = -1;
+	return (archive_write_open2(a, mine,
+		    file_open, file_write, file_close, file_free));
+}
+
+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 '%ls' 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 '%ls'", 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 '%ls'", 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 == NULL)
+		return (ARCHIVE_FATAL);
+
+	if (mine->fd >= 0)
+		close(mine->fd);
+
+	return (ARCHIVE_OK);
+}
+
+static int
+file_free(struct archive *a, void *client_data)
+{
+	struct write_file_data	*mine = (struct write_file_data *)client_data;
+
+	(void)a; /* UNUSED */
+
+	if (mine == NULL)
+		return (ARCHIVE_OK);
+
+	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,114 @@
+/*-
+ * 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"
+
+#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_free(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 = 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_open2(a, mine,
+		    memory_write_open, memory_write, NULL, memory_write_free));
+}
+
+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_free(struct archive *a, void *client_data)
+{
+	struct write_memory_data *mine;
+	(void)a; /* UNUSED */
+	mine = client_data;
+	if (mine == NULL)
+		return (ARCHIVE_OK);
+	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.
+ */
+
+#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	(*flush)(struct archive_write_filter *);
+	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;
+	archive_free_callback	*client_freer;
+	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 an 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,124 @@
+/*-
+ * 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"
+
+#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_BIN_LE,	archive_write_set_format_cpio_bin },
+	{ ARCHIVE_FORMAT_CPIO_PWB,	archive_write_set_format_cpio_pwb },
+	{ ARCHIVE_FORMAT_CPIO_POSIX,	archive_write_set_format_cpio_odc },
+	{ 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,2584 @@
+/*-
+ * 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"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.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
+#ifdef HAVE_ZSTD_H
+#include <zstd.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_time_private.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
+
+#define _7Z_ZSTD	0x4F71101 /* Copied from https://github.com/mcmilk/7-Zip-zstd.git */
+
+/*
+ * 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
+
+// Check that some windows file attribute constants are defined.
+// Reference: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
+#ifndef FILE_ATTRIBUTE_READONLY
+#define FILE_ATTRIBUTE_READONLY 0x00000001
+#endif
+
+#ifndef FILE_ATTRIBUTE_DIRECTORY
+#define FILE_ATTRIBUTE_DIRECTORY 0x00000010
+#endif
+
+#ifndef FILE_ATTRIBUTE_ARCHIVE
+#define FILE_ATTRIBUTE_ARCHIVE 0x00000020
+#endif
+
+// This value is defined in 7zip with the comment "trick for Unix".
+//
+// 7z archives created on unix have this bit set in the high 16 bits of
+// the attr field along with the unix permissions.
+#define FILE_ATTRIBUTE_UNIX_EXTENSION 0x8000
+
+// Many systems define min or MIN, but not all.
+#define sevenzipmin(a,b) ((a) < (b) ? (a) : (b))
+
+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;
+
+	unsigned		 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;
+	int			 opt_zstd_compression_level; // This requires a different default value.
+
+	int			 opt_threads;
+
+	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 *, uint8_t, 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_init_encoder_zstd(struct archive *,
+		    struct la_zstream *, int, int);
+#if defined(HAVE_ZSTD_H)
+static int	compression_code_zstd(struct archive *,
+		    struct la_zstream *, enum la_zaction);
+static int	compression_end_zstd(struct archive *, struct la_zstream *);
+#endif
+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);
+
+static int
+string_to_number(const char *string, intmax_t *numberp)
+{
+	char *end;
+
+	if (string == NULL || *string == '\0')
+		return (ARCHIVE_WARN);
+	*numberp = strtoimax(string, &end, 10);
+	if (end == string || *end != '\0' || errno == EOVERFLOW) {
+		*numberp = 0;
+		return (ARCHIVE_WARN);
+	}
+	return (ARCHIVE_OK);
+}
+
+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;
+#elif HAVE_ZSTD_H
+	zip->opt_compression = _7Z_ZSTD;
+#else
+	zip->opt_compression = _7Z_COPY;
+#endif
+
+	zip->opt_compression_level = 6;
+
+#ifdef ZSTD_CLEVEL_DEFAULT
+	// Zstandard compression needs a different default
+	// value than other encoders.
+	zip->opt_zstd_compression_level = ZSTD_CLEVEL_DEFAULT;
+#else
+	zip->opt_zstd_compression_level = 3;
+#endif
+
+	zip->opt_threads = 1;
+
+	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, "zstd") == 0 ||
+		    strcmp(value, "ZSTD") == 0)
+#if HAVE_ZSTD_H
+			zip->opt_compression = _7Z_ZSTD;
+#else
+			name = "zstd";
+#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') {
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+				"Invalid compression-level option value `%s'", value);
+			return (ARCHIVE_FAILED);
+		}
+
+		char *end = NULL;
+		long lvl = strtol(value, &end, 10);
+		if (end == NULL || *end != '\0') {
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+				"parsing compression-level option value failed `%s'", value);
+			return (ARCHIVE_FAILED);
+		}
+
+#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream && HAVE_ZSTD_minCLevel
+		int min_level = sevenzipmin(0, ZSTD_minCLevel());
+#else
+		const int min_level = 0;
+#endif
+
+#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
+		int max_level = ZSTD_maxCLevel();
+#else
+		const int max_level = 9;
+#endif
+
+		if (lvl < min_level || lvl > max_level) {
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+				"compression-level option value `%ld' out of range", lvl);
+			return (ARCHIVE_FAILED);
+		}
+
+		// Note: we don't know here if this value is for zstd (negative to ~22),
+		// or zlib-style 0-9. If zstd is enabled but not in use, we will need to
+		// validate opt_compression_level before use.
+		zip->opt_compression_level = (int)lvl;
+
+		zip->opt_zstd_compression_level = (int)lvl;
+		return (ARCHIVE_OK);
+	}
+	if (strcmp(key, "threads") == 0) {
+		intmax_t threads;
+		if (string_to_number(value, &threads) != ARCHIVE_OK) {
+			return (ARCHIVE_WARN);
+		}
+		if (threads < 0) {
+			return (ARCHIVE_WARN);
+		}
+		if (threads == 0) {
+#if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
+			threads = sysconf(_SC_NPROCESSORS_ONLN);
+#elif !defined(__CYGWIN__) && defined(_WIN32_WINNT) && \
+	_WIN32_WINNT >= 0x0601 /* _WIN32_WINNT_WIN7 */
+			DWORD winCores = GetActiveProcessorCount(
+				ALL_PROCESSOR_GROUPS);
+			threads = (intmax_t)winCores;
+#else
+			threads = 1;
+#endif
+		}
+
+		zip->opt_threads = (int)threads;
+		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) {
+
+		int level = zip->opt_compression_level;
+#if HAVE_ZSTD_H
+		if (zip->opt_compression == _7Z_ZSTD) {
+			level = zip->opt_zstd_compression_level;
+		} else if (level < 0 || level > 9) {
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+				"compression-level option value `%d' out of range 0-9", level);
+			file_free(file);
+			return (ARCHIVE_FATAL);
+		}
+#endif
+
+		r = _7z_compression_init_encoder(a, 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_utf8(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,
+			    "write 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(zip->opt_compression == _7Z_LZMA2 ||
+		   zip->opt_compression == _7Z_COPY)
+			header_compression = zip->opt_compression;
+
+		/* 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
+
+		int level = zip->opt_compression_level;
+		if (level < 0) level = 0;
+		else if (level > 9) level = 9;
+
+		r = _7z_compression_init_encoder(a, header_compression, level);
+		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);
+}
+
+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, unix_to_ntfs(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 = 0;
+
+		if (file->dir)
+			attr |= FILE_ATTRIBUTE_DIRECTORY;
+		else
+			attr |= FILE_ATTRIBUTE_ARCHIVE;
+
+		if ((file->mode & 0222) == 0)
+			attr |= FILE_ATTRIBUTE_READONLY;
+
+		attr |= FILE_ATTRIBUTE_UNIX_EXTENSION;
+		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) {
+		const char* linkpath;
+		linkpath = archive_entry_symlink_utf8(entry);
+		if (linkpath == NULL) {
+			free(file);
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "symlink path could not be converted to UTF-8");
+			return (ARCHIVE_FAILED);
+		}
+		else
+			file->size = strlen(linkpath);
+	}
+	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) ||\
+	 !(HAVE_ZSTD_H && HAVE_ZSTD_compressStream)
+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 = (uint32_t)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 = (uint32_t)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 = (uint32_t)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 = (uint32_t)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 > 9)
+		level = 9;
+	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, uint8_t 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);
+}
+
+#if HAVE_ZSTD_H && HAVE_ZSTD_compressStream
+static int
+compression_init_encoder_zstd(struct archive *a, struct la_zstream *lastrm, int level, int threads)
+{
+	if (lastrm->valid)
+		compression_end(a, lastrm);
+
+	ZSTD_CStream *strm = ZSTD_createCStream();
+	if (strm == NULL) {
+		archive_set_error(a, ENOMEM,
+			"Can't allocate memory for zstd stream");
+		return (ARCHIVE_FATAL);
+	}
+
+	if (ZSTD_isError(ZSTD_initCStream(strm, level))) {
+		ZSTD_freeCStream(strm);
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			"Internal error initializing zstd compressor object");
+		return (ARCHIVE_FATAL);
+	}
+
+	ZSTD_CCtx_setParameter(strm, ZSTD_c_nbWorkers, threads);
+
+	// p7zip-zstd fails to unpack archives that don't have prop_size 5.
+	// 7-Zip-zstd fails to unpack archives that don't have prop_size 3 or 5.
+	// So let's use 5...
+	lastrm->prop_size = 5;
+	lastrm->props = calloc(5, 1);
+	if (lastrm->props == NULL) {
+		ZSTD_freeCStream(strm);
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			"Internal error initializing zstd compressor properties");
+		return (ARCHIVE_FATAL);
+	}
+
+	// Refer to the DProps struct in 7-Zip-zstd's ZstdDecoder.h:
+	// https://github.com/mcmilk/7-Zip-zstd/blob/79b2c78e9e7735ddf90147129b75cf2797ff6522/CPP/7zip/Compress/ZstdDecoder.h#L34S
+	lastrm->props[0] = ZSTD_VERSION_MAJOR;
+	lastrm->props[1] = ZSTD_VERSION_MINOR;
+	lastrm->props[2] = level;
+	// lastrm->props[3] and lastrm->props[4] are reserved. Leave them as 0.
+
+	lastrm->real_stream = strm;
+	lastrm->valid = 1;
+	lastrm->code = compression_code_zstd;
+	lastrm->end = compression_end_zstd;
+
+	return (ARCHIVE_OK);
+}
+
+static int
+compression_code_zstd(struct archive *a,
+    struct la_zstream *lastrm, enum la_zaction action)
+{
+	ZSTD_CStream *strm = (ZSTD_CStream *)lastrm->real_stream;
+
+	ZSTD_outBuffer out = { .dst = lastrm->next_out, .size = lastrm->avail_out, .pos = 0 };
+	ZSTD_inBuffer  in  = { .src = lastrm->next_in,  .size = lastrm->avail_in,  .pos = 0 };
+
+	size_t zret;
+
+	ZSTD_EndDirective mode = (action == ARCHIVE_Z_RUN) ? ZSTD_e_continue : ZSTD_e_end;
+
+	zret = ZSTD_compressStream2(strm, &out, &in, mode);
+	if (ZSTD_isError(zret)) {
+		archive_set_error(a, ARCHIVE_ERRNO_MISC,
+			"zstd compression failed, ZSTD_compressStream2 returned: %s",
+			ZSTD_getErrorName(zret));
+		return (ARCHIVE_FATAL);
+	}
+
+	lastrm->next_in += in.pos;
+	lastrm->avail_in -= in.pos;
+	lastrm->total_in += in.pos;
+
+	lastrm->next_out += out.pos;
+	lastrm->avail_out -= out.pos;
+	lastrm->total_out += out.pos;
+
+	if (action == ARCHIVE_Z_FINISH && zret == 0)
+		return (ARCHIVE_EOF); // All done.
+
+	return (ARCHIVE_OK); // More work to do.
+}
+
+static int
+compression_end_zstd(struct archive *a, struct la_zstream *lastrm)
+{
+	ZSTD_CStream *strm;
+
+	(void)a; /* UNUSED */
+	strm = (ZSTD_CStream *)lastrm->real_stream;
+	ZSTD_freeCStream(strm);
+	lastrm->valid = 0;
+	lastrm->real_stream = NULL;
+	return (ARCHIVE_OK);
+}
+
+#else
+
+static int
+compression_init_encoder_zstd(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, "zstd"));
+}
+#endif
+
+/*
+ * 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_ZSTD:
+		r = compression_init_encoder_zstd(
+		    &(a->archive), &(zip->stream),
+		    compression_level, zip->opt_threads);
+		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,569 @@
+/*-
+ * 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"
+
+#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 = 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 = 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 = 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,93 @@
+/*-
+ * 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"
+
+#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 },
+	{ "bin",	archive_write_set_format_cpio_bin },
+	{ "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_odc },
+	{ "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 },
+	{ "pwb",	archive_write_set_format_cpio_pwb },
+	{ "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,11 @@
+#include "archive_platform.h"
+#include "archive.h"
+
+/*
+ * Set output format to the default 'cpio' format.
+ */
+int
+archive_write_set_format_cpio(struct archive *_a)
+{
+	return archive_write_set_format_cpio_odc(_a);
+}
diff --git a/c/archive_write_set_format_cpio_binary.c b/c/archive_write_set_format_cpio_binary.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_cpio_binary.c
@@ -0,0 +1,612 @@
+/*-
+ * 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"
+
+#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_binary_data(struct archive_write *,
+		    const void *buff, size_t s);
+static int	archive_write_binary_close(struct archive_write *);
+static int	archive_write_binary_free(struct archive_write *);
+static int	archive_write_binary_finish_entry(struct archive_write *);
+static int	archive_write_binary_header(struct archive_write *,
+		    struct archive_entry *);
+static int	archive_write_binary_options(struct archive_write *,
+		    const char *, const char *);
+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;
+};
+
+/* This struct needs to be packed to get the header right */
+
+#if defined(__GNUC__)
+#define PACKED(x) x __attribute__((packed))
+#elif defined(_MSC_VER)
+#define PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
+#else
+#define PACKED(x) x
+#endif
+
+#define HSIZE 26
+
+PACKED(struct cpio_binary_header {
+	uint16_t	h_magic;
+	uint16_t	h_dev;
+	uint16_t	h_ino;
+	uint16_t	h_mode;
+	uint16_t	h_uid;
+	uint16_t	h_gid;
+	uint16_t	h_nlink;
+	uint16_t	h_majmin;
+	uint32_t	h_mtime;
+	uint16_t	h_namesize;
+	uint32_t	h_filesize;
+});
+
+/* Back in the day, the 7th Edition cpio.c had this, to
+ * adapt to, as the comment said, "VAX, Interdata, ...":
+ *
+ * union { long l; short s[2]; char c[4]; } U;
+ * #define MKSHORT(v,lv) {U.l=1L;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,v[0]=U.s[0],v[1]=U.s[1];}
+ * long mklong(v)
+ * short v[];
+ * {
+ *         U.l = 1;
+ *         if(U.c[0])
+ *                 U.s[0] = v[1], U.s[1] = v[0];
+ *         else
+ *                 U.s[0] = v[0], U.s[1] = v[1];
+ *         return U.l;
+ * }
+ *
+ * Of course, that assumes that all machines have little-endian shorts,
+ * and just adapts the others to the special endianness of the PDP-11.
+ *
+ * Now, we could do this:
+ *
+ * union { uint32_t l; uint16_t s[2]; uint8_t c[4]; } U;
+ * #define PUTI16(v,sv) {U.s[0]=1;if(U.c[0]) v=sv; else U.s[0]=sv,U.c[2]=U.c[1],U.c[3]=U.c[0],v=U.s[1];}
+ * #define PUTI32(v,lv) {char_t Ut;U.l=1;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,Ut=U.c[0],U.c[0]=U.c[1],U.c[1]=Ut,Ut=U.c[2],U.c[2]=U.c[3],U.c[3]=Ut,v[0]=U.s[0],v[1]=U.s[1];}
+ *
+ * ...but it feels a little better to do it like this:
+ */
+
+static uint16_t la_swap16(uint16_t in) {
+	union {
+		uint16_t s[2];
+		uint8_t c[4];
+	} U;
+	U.s[0] = 1;
+	if (U.c[0])
+		return in;
+	else {
+		U.s[0] = in;
+		U.c[2] = U.c[1];
+		U.c[3] = U.c[0];
+		return U.s[1];
+	}
+	/* NOTREACHED */
+}
+
+static uint32_t la_swap32(uint32_t in) {
+	union {
+		uint32_t l;
+		uint16_t s[2];
+		uint8_t c[4];
+	} U;
+	U.l = 1;
+	if (U.c[0]) {		/* Little-endian */
+		uint16_t t;
+		U.l = in;
+		t = U.s[0];
+		U.s[0] = U.s[1];
+		U.s[1] = t;
+	} else if (U.c[3]) {	/* Big-endian */
+		U.l = in;
+		U.s[0] = la_swap16(U.s[0]);
+		U.s[1] = la_swap16(U.s[1]);
+	} else {		/* PDP-endian */
+		U.l = in;
+	}
+	return U.l;
+}
+
+/*
+ * Set output format to the selected binary variant
+ */
+static int
+archive_write_set_format_cpio_binary(struct archive *_a, int format)
+{
+	struct archive_write *a = (struct archive_write *)_a;
+	struct cpio *cpio;
+
+	if (sizeof(struct cpio_binary_header) != HSIZE) {
+		archive_set_error(&a->archive, EINVAL,
+				  "Binary cpio format not supported on this platform");
+		return (ARCHIVE_FATAL);
+	}
+
+	archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
+	    ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_binary");
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	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_binary_options;
+	a->format_write_header = archive_write_binary_header;
+	a->format_write_data = archive_write_binary_data;
+	a->format_finish_entry = archive_write_binary_finish_entry;
+	a->format_close = archive_write_binary_close;
+	a->format_free = archive_write_binary_free;
+	a->archive.archive_format = format;
+	switch (format) {
+	case ARCHIVE_FORMAT_CPIO_PWB:
+		a->archive.archive_format_name = "PWB cpio";
+		break;
+	case ARCHIVE_FORMAT_CPIO_BIN_LE:
+		a->archive.archive_format_name = "7th Edition cpio";
+		break;
+	default:
+		archive_set_error(&a->archive, EINVAL, "binary format must be 'pwb' or 'bin'");
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * Set output format to PWB (6th Edition) binary format
+ */
+int
+archive_write_set_format_cpio_pwb(struct archive *_a)
+{
+	return archive_write_set_format_cpio_binary(_a, ARCHIVE_FORMAT_CPIO_PWB);
+}
+
+/*
+ * Set output format to 7th Edition binary format
+ */
+int
+archive_write_set_format_cpio_bin(struct archive *_a)
+{
+	return archive_write_set_format_cpio_binary(_a, ARCHIVE_FORMAT_CPIO_BIN_LE);
+}
+
+static int
+archive_write_binary_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 16 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_binary_header(struct archive_write *a, struct archive_entry *entry)
+{
+	const char *path;
+	size_t len;
+
+	if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
+		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;
+	struct cpio_binary_header h;
+	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;
+
+	h.h_magic = la_swap16(070707);
+	h.h_dev = la_swap16(archive_entry_dev(entry));
+
+	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 > 077777) {
+		archive_set_error(&a->archive, ERANGE,
+		    "Too many files for this cpio format");
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
+	h.h_ino = la_swap16((uint16_t)ino);
+
+	h.h_mode = archive_entry_mode(entry);
+	if (((h.h_mode & AE_IFMT) == AE_IFSOCK) || ((h.h_mode & AE_IFMT) == AE_IFIFO)) {
+		archive_set_error(&a->archive, EINVAL,
+				  "sockets and fifos cannot be represented in the binary cpio formats");
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
+	if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) {
+		if ((h.h_mode & AE_IFMT) == AE_IFLNK) {
+			archive_set_error(&a->archive, EINVAL,
+					  "symbolic links cannot be represented in the PWB cpio format");
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		/* we could turn off AE_IFREG here, but it does no harm, */
+		/* and allows v7 cpio to read the entry without confusion */
+	}
+	h.h_mode = la_swap16(h.h_mode);
+
+	h.h_uid = la_swap16((uint16_t)archive_entry_uid(entry));
+	h.h_gid = la_swap16((uint16_t)archive_entry_gid(entry));
+	h.h_nlink = la_swap16((uint16_t)archive_entry_nlink(entry));
+
+	if (archive_entry_filetype(entry) == AE_IFBLK
+	    || archive_entry_filetype(entry) == AE_IFCHR)
+		h.h_majmin = la_swap16(archive_entry_rdev(entry));
+	else
+		h.h_majmin = 0;
+
+	h.h_mtime = la_swap32((uint32_t)archive_entry_mtime(entry));
+	h.h_namesize = la_swap16(pathlength);
+
+	/* 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') {
+		if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) {
+			archive_set_error(&a->archive, EINVAL,
+					  "symlinks are not supported by UNIX V6 or by PWB cpio");
+			ret_final = ARCHIVE_FATAL;
+			goto exit_write_header;
+		}
+		h.h_filesize = la_swap32((uint32_t)strlen(p)); /* symlink */
+	} else {
+		if ((a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) &&
+		    (archive_entry_size(entry) > 256*256*256-1)) {
+			archive_set_error(&a->archive, ERANGE,
+					  "File is too large for PWB binary cpio format.");
+			ret_final = ARCHIVE_FAILED;
+			goto exit_write_header;
+		} else if (archive_entry_size(entry) > INT32_MAX) {
+			archive_set_error(&a->archive, ERANGE,
+					  "File is too large for binary cpio format.");
+			ret_final = ARCHIVE_FAILED;
+			goto exit_write_header;
+		}
+		h.h_filesize = la_swap32((uint32_t)archive_entry_size(entry)); /* file */
+	}
+
+	ret = __archive_write_output(a, &h, HSIZE);
+	if (ret != ARCHIVE_OK) {
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
+
+	ret = __archive_write_output(a, path, pathlength);
+	if ((ret == ARCHIVE_OK) && ((pathlength % 2) != 0))
+		ret = __archive_write_nulls(a, 1);
+	if (ret != ARCHIVE_OK) {
+		ret_final = ARCHIVE_FATAL;
+		goto exit_write_header;
+	}
+
+	cpio->entry_bytes_remaining = archive_entry_size(entry);
+	if ((cpio->entry_bytes_remaining % 2) != 0)
+		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) && ((strlen(p) % 2) != 0))
+			ret = __archive_write_nulls(a, 1);
+		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_binary_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);
+}
+
+static int
+archive_write_binary_close(struct archive_write *a)
+{
+	int er;
+	struct archive_entry *trailer;
+
+	trailer = archive_entry_new2(NULL);
+	if (trailer == NULL) {
+		return ARCHIVE_FATAL;
+	}
+	/* 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_binary_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_binary_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,456 @@
+/*-
+ * 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"
+
+#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 = 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_entry_hardlink(entry) == NULL) {
+		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_cpio_odc.c b/c/archive_write_set_format_cpio_odc.c
new file mode 100644
--- /dev/null
+++ b/c/archive_write_set_format_cpio_odc.c
@@ -0,0 +1,502 @@
+/*-
+ * 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"
+
+#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_odc_data(struct archive_write *,
+		    const void *buff, size_t s);
+static int	archive_write_odc_close(struct archive_write *);
+static int	archive_write_odc_free(struct archive_write *);
+static int	archive_write_odc_finish_entry(struct archive_write *);
+static int	archive_write_odc_header(struct archive_write *,
+		    struct archive_entry *);
+static int	archive_write_odc_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_odc(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_odc");
+
+	/* If someone else was already registered, unregister them. */
+	if (a->format_free != NULL)
+		(a->format_free)(a);
+
+	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_odc_options;
+	a->format_write_header = archive_write_odc_header;
+	a->format_write_data = archive_write_odc_data;
+	a->format_finish_entry = archive_write_odc_finish_entry;
+	a->format_close = archive_write_odc_close;
+	a->format_free = archive_write_odc_free;
+	a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
+	a->archive.archive_format_name = "POSIX cpio";
+	return (ARCHIVE_OK);
+}
+
+static int
+archive_write_odc_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_odc_header(struct archive_write *a, struct archive_entry *entry)
+{
+	const char *path;
+	size_t len;
+
+	if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
+		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_rdev(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_odc_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_odc_close(struct archive_write *a)
+{
+	int er;
+	struct archive_entry *trailer;
+
+	trailer = archive_entry_new2(NULL);
+	if (trailer == NULL) {
+		return ARCHIVE_FATAL;
+	}
+	/* 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_odc_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_odc_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_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,141 @@
+/*-
+ * 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"
+
+#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,753 @@
+/*-
+ * 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"
+
+#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 *, char 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 = 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;
+	char 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 pathname");
+			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, "././@LongName");
+		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_is_set(entry)) {
+		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, char 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 for gnutar format",
+		    (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 for gnutar format",
+		    (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 for gnutar format");
+			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 for gnutar format");
+			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,8172 @@
+/*-
+ * 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;
+
+	unsigned 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".
+	 */
+	unsigned 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 {
+		unsigned int	 detect_magic:1;
+		unsigned int	 making:1;
+		unsigned 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_identifier(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));
+	if (archive_string_ensure(&(iso9660->cur_dirstr), 1) == NULL) {
+		free(iso9660);
+		archive_set_error(&a->archive, ENOMEM,
+		    "Can't allocate memory");
+		return (ARCHIVE_FATAL);
+	}
+	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_identifier(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
+	strncpy(system_id, "Unknown", size-1);
+	system_id[size-1] = '\0';
+#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 = "\0\0";
+	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_S
+	localtime_s(tm, t);
+#elif HAVE_LOCALTIME_R
+	tzset();
+	localtime_r(t, tm);
+#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_identifier(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, (unsigned int)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_CTIME_S)
+	ctime_s(buf, sizeof(buf), &(iso9660->birth_time));
+#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));
+		if (archive_string_ensure(&(iso9660->cur_dirstr),
+		    archive_strlen(&(dent->file->parentdir)) +
+		    archive_strlen(&(dent->file->basename)) + 2) == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+			_isoent_free(isoent);
+			*isoentpp = NULL;
+			return (ARCHIVE_FATAL);
+		}
+		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
+__LA_LIBC_CC
+_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
+__LA_LIBC_CC
+_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;
+
+	unsigned int	 initialized:1;
+	unsigned 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,2296 @@
+/*-
+ * 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"
+
+#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_entry_private.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 attr_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;
+	uint_least32_t mset_digest;
+	struct ae_digest digest;
+};
+
+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 attr_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 attr_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 attr_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 attr_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 void
+mtree_copy_ae_digests(struct reg_info *reg, struct archive_entry *entry, int compute_sum)
+{
+	reg->compute_sum = compute_sum;
+	reg->mset_digest = entry->mset_digest;
+
+	if ((reg->compute_sum & F_MD5)
+		&& (reg->mset_digest & AE_MSET_DIGEST_MD5)) {
+
+		memcpy(&reg->digest.md5, entry->digest.md5,
+			sizeof(reg->digest.md5));
+	}
+	if ((reg->compute_sum & F_RMD160)
+		&& (reg->mset_digest & AE_MSET_DIGEST_RMD160)) {
+
+		memcpy(&reg->digest.rmd160, entry->digest.rmd160,
+			sizeof(reg->digest.rmd160));
+	}
+	if ((reg->compute_sum & F_SHA1)
+		&& (reg->mset_digest & AE_MSET_DIGEST_SHA1)) {
+
+		memcpy(&reg->digest.sha1, entry->digest.sha1,
+			sizeof(reg->digest.sha1));
+	}
+	if ((reg->compute_sum & F_SHA256)
+		&& (reg->mset_digest & AE_MSET_DIGEST_SHA256)) {
+
+		memcpy(&reg->digest.sha256, entry->digest.sha256,
+			sizeof(reg->digest.sha256));
+	}
+	if ((reg->compute_sum & F_SHA384)
+		&& (reg->mset_digest & AE_MSET_DIGEST_SHA384)) {
+
+		memcpy(&reg->digest.sha384, entry->digest.sha384,
+			sizeof(reg->digest.sha384));
+	}
+	if ((reg->compute_sum & F_SHA512)
+		&& (reg->mset_digest & AE_MSET_DIGEST_SHA512)) {
+
+		memcpy(&reg->digest.sha512, entry->digest.sha512,
+			sizeof(reg->digest.sha512));
+	}
+}
+
+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);
+		/* honor archive_entry_set_digest() calls. These values will be
+		 * overwritten if archive_write_mtree_data() is called */
+		mtree_copy_ae_digests(mtree_entry->reg_info, entry, mtree->compute_sum);
+	}
+
+	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);
+		mtree->mtree_entry->reg_info->mset_digest &=
+			~AE_MSET_DIGEST_MD5;
+	}
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if (mtree->compute_sum & F_RMD160) {
+		archive_rmd160_update(&mtree->rmd160ctx, buff, n);
+		mtree->mtree_entry->reg_info->mset_digest &=
+			~AE_MSET_DIGEST_RMD160;
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if (mtree->compute_sum & F_SHA1) {
+		archive_sha1_update(&mtree->sha1ctx, buff, n);
+		mtree->mtree_entry->reg_info->mset_digest &=
+			~AE_MSET_DIGEST_SHA1;
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if (mtree->compute_sum & F_SHA256) {
+		archive_sha256_update(&mtree->sha256ctx, buff, n);
+		mtree->mtree_entry->reg_info->mset_digest &=
+			~AE_MSET_DIGEST_SHA256;
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if (mtree->compute_sum & F_SHA384) {
+		archive_sha384_update(&mtree->sha384ctx, buff, n);
+		mtree->mtree_entry->reg_info->mset_digest &=
+			~AE_MSET_DIGEST_SHA384;
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if (mtree->compute_sum & F_SHA512) {
+		archive_sha512_update(&mtree->sha512ctx, buff, n);
+		mtree->mtree_entry->reg_info->mset_digest &=
+			~AE_MSET_DIGEST_SHA512;
+	}
+#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)
+		&& !(reg->mset_digest & AE_MSET_DIGEST_MD5))
+
+		archive_md5_final(&mtree->md5ctx, reg->digest.md5);
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if ((mtree->compute_sum & F_RMD160)
+		&& !(reg->mset_digest & AE_MSET_DIGEST_RMD160))
+
+		archive_rmd160_final(&mtree->rmd160ctx, reg->digest.rmd160);
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if ((mtree->compute_sum & F_SHA1)
+		&& !(reg->mset_digest & AE_MSET_DIGEST_SHA1))
+
+		archive_sha1_final(&mtree->sha1ctx, reg->digest.sha1);
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if ((mtree->compute_sum & F_SHA256)
+		&& !(reg->mset_digest & AE_MSET_DIGEST_SHA256))
+
+		archive_sha256_final(&mtree->sha256ctx, reg->digest.sha256);
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if ((mtree->compute_sum & F_SHA384)
+		&& !(reg->mset_digest & AE_MSET_DIGEST_SHA384))
+
+		archive_sha384_final(&mtree->sha384ctx, reg->digest.sha384);
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if ((mtree->compute_sum & F_SHA512)
+		&& !(reg->mset_digest & AE_MSET_DIGEST_SHA512))
+
+		archive_sha512_final(&mtree->sha512ctx, reg->digest.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);
+	}
+
+#define append_digest(_s, _r, _t) \
+	strappend_bin(_s, _r->digest._t, sizeof(_r->digest._t))
+
+#ifdef ARCHIVE_HAS_MD5
+	if (reg->compute_sum & F_MD5) {
+		archive_strcat(str, " md5digest=");
+		append_digest(str, reg, md5);
+	}
+#endif
+#ifdef ARCHIVE_HAS_RMD160
+	if (reg->compute_sum & F_RMD160) {
+		archive_strcat(str, " rmd160digest=");
+		append_digest(str, reg, rmd160);
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA1
+	if (reg->compute_sum & F_SHA1) {
+		archive_strcat(str, " sha1digest=");
+		append_digest(str, reg, sha1);
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA256
+	if (reg->compute_sum & F_SHA256) {
+		archive_strcat(str, " sha256digest=");
+		append_digest(str, reg, sha256);
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA384
+	if (reg->compute_sum & F_SHA384) {
+		archive_strcat(str, " sha384digest=");
+		append_digest(str, reg, sha384);
+	}
+#endif
+#ifdef ARCHIVE_HAS_SHA512
+	if (reg->compute_sum & F_SHA512) {
+		archive_strcat(str, " sha512digest=");
+		append_digest(str, reg, sha512);
+	}
+#endif
+#undef append_digest
+}
+
+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 to 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 an entry from a parent entry with given 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 a directory! */
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "`%s' is not a 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));
+		if (archive_string_ensure(&(mtree->cur_dirstr),
+		    archive_strlen(&(dent->parentdir)) +
+		    archive_strlen(&(dent->basename)) + 2) == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't allocate memory");
+			return (ARCHIVE_FATAL);
+		}
+		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 already have an entry with same filename. */
+	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 for `%s' with "
+		    "differing file types.",
+		    np->pathname.s);
+		return (ARCHIVE_FAILED);
+	}
+
+	/* Update the existing 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,2093 @@
+/*-
+ * 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"
+
+#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);
+static time_t		 get_ustar_max_mtime(void);
+
+/*
+ * 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 = 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 (encoded_name == NULL)
+		return;
+
+	if (pax->flags & WRITE_LIBARCHIVE_XATTR) {
+		encoded_value = base64_encode((const char *)value, value_len);
+		if (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)
+			goto malloc_error;
+		else {
+			/* 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 (r == -1)
+				goto malloc_error;
+			else {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "Error encoding pax extended attribute");
+				return (ARCHIVE_FAILED);
+			}
+		}
+
+		archive_write_pax_header_xattr(pax, encoded_name,
+		    value, size);
+
+	}
+	return (ARCHIVE_OK);
+malloc_error:
+	archive_set_error(&a->archive, ENOMEM, "Can't allocate memory");
+	return (ARCHIVE_FATAL);
+}
+
+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;
+
+	const time_t ustar_max_mtime = get_ustar_max_mtime();
+
+	/* Sanity check. */
+#if defined(_WIN32) && !defined(__CYGWIN__)
+	/* NOTE: If the caller supplied a pathname that fails WCS conversion (e.g.
+	 * if it is invalid UTF-8), we are expected to return ARCHIVE_WARN later on
+	 * in execution, hence the check for both pointers */
+	if ((archive_entry_pathname_w(entry_original) == NULL) &&
+	    (archive_entry_pathname(entry_original) == NULL)) {
+#else
+	if (archive_entry_pathname(entry_original) == NULL) {
+#endif
+		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");
+			}
+			else {
+				/* Otherwise, has non-ASCII characters; update the paths to
+				 * however they got decoded above */
+				if (hardlink != NULL) 
+					archive_entry_set_hardlink(entry_main, linkpath);
+				else
+					archive_entry_set_symlink(entry_main, linkpath);
+			}
+			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, we need pax extended attrs. */
+	if (archive_entry_size(entry_main) >= (((int64_t)1) << 33)) {
+		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;
+		}
+	}
+
+	/*
+	 * 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) >= ustar_max_mtime)))
+		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) >= ustar_max_mtime  ||
+		    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);
+	}
+
+	/* 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));
+	}
+
+	/* 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;
+		__LA_MODE_T 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 > ustar_max_mtime) { s = ustar_max_mtime; }
+		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 = 98; /* 99 - 1 for trailing slash */
+	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 + 154 /* 155 - 1 for trailing / */;
+	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. */
+	snprintf(buff, sizeof(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;
+	size_t out_len = 0;
+	char *out;
+
+	for (s = in; *s != '\0'; s++) {
+		if (*s < 33 || *s > 126 || *s == '%' || *s == '=') {
+			if (SIZE_MAX - out_len < 4)
+				return (NULL);
+			out_len += 3;
+		} else {
+			if (SIZE_MAX - out_len < 2)
+				return (NULL);
+			out_len++;
+		}
+	}
+
+	out = 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 = 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 = 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));
+}
+
+static time_t
+get_ustar_max_mtime(void)
+{
+	/*
+	 * Technically, the mtime field in the ustar header can
+	 * support 33 bits. We are using all of them to keep
+	 * tar/test/test_option_C_mtree.c simple and passing after 2038.
+	 * For platforms that use signed 32-bit time values we
+	 * use the 32-bit maximum.
+	 */
+	if (sizeof(time_t) > sizeof(int32_t))
+		return (time_t)0x1ffffffff;
+	else
+		return (time_t)0x7fffffff;
+}
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,40 @@
+/*-
+ * 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.
+ */
+
+#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 = 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,648 @@
+/*-
+ * 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"
+
+#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 = 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);
+		if (p == NULL) {
+			archive_set_error(&a->archive, ENOMEM, "Out of memory");
+			return (ARCHIVE_FATAL);
+		}
+		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);
+			if (shar->last_dir == NULL) {
+				archive_set_error(&a->archive, ENOMEM, "Out of memory");
+				return (ARCHIVE_FATAL);
+			}
+			/* 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,760 @@
+/*-
+ * 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"
+
+#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 = 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 defined(_WIN32) && !defined(__CYGWIN__)
+	if (archive_entry_pathname_w(entry) == NULL) {
+#else
+	if (archive_entry_pathname(entry) == NULL) {
+#endif
+		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,636 @@
+/*-
+ * 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"
+
+#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 = 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,443 @@
+/*-
+ * 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"
+
+#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_GMTIME_S)
+	struct tm timeHere;
+#endif
+	char strtime[100];
+	size_t len;
+
+#if defined(HAVE_GMTIME_S)
+	rt = gmtime_s(&timeHere, &t) ? NULL : &timeHere;
+#elif defined(HAVE_GMTIME_R)
+	rt = gmtime_r(&t, &timeHere);
+#else
+	rt = gmtime(&t);
+#endif
+	if (!rt)
+		return;
+	/* 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,3698 @@
+/*-
+ * 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
+#include <stdlib.h>
+#if HAVE_LIBXML_XMLWRITER_H
+#include <libxml/xmlwriter.h>
+#if defined(LIBXML_VERSION) && LIBXML_VERSION >= 20703
+#define XAR_WRITER_HAS_XML
+#endif /* LIBXML_VERSION */
+#elif HAVE_XMLLITE_H
+#include <objidl.h>
+#include <initguid.h>
+#include <xmllite.h>
+#define XAR_WRITER_HAS_XML
+#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(XAR_WRITER_HAS_XML) ||\
+	!defined(HAVE_ZLIB_H) || \
+	!defined(ARCHIVE_HAS_MD5) || !defined(ARCHIVE_HAS_SHA1)
+/*
+ * xar needs several external libraries.
+ *   o libxml2 or xmllite (on Windows)
+ *   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 */
+
+struct xml_writer;
+static int xml_writer_create(struct xml_writer **pctx);
+static int xml_writer_start_document(struct xml_writer *ctx);
+static int xml_writer_end_document(struct xml_writer *ctx);
+static int xml_writer_set_indent(struct xml_writer *ctx, unsigned int indent);
+static int xml_writer_start_element(struct xml_writer *ctx,
+    const char *localName);
+static int xml_writer_write_attribute(struct xml_writer *ctx, const char *key,
+    const char *value);
+static int xml_writer_write_attributef(struct xml_writer *ctx, const char *key,
+    const char *format, ...);
+static int xml_writer_write_string(struct xml_writer *ctx, const char *string);
+static int xml_writer_write_base64(struct xml_writer* ctx,
+    const char *data, size_t start, size_t len);
+static int xml_writer_end_element(struct xml_writer *ctx);
+static int xml_writer_get_final_content_and_length(struct xml_writer *ctx,
+    const char **out, size_t *size);
+static int xml_writer_destroy(struct xml_writer *ctx);
+
+/*#define DEBUG_PRINT_TOC		1 */
+
+#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;
+
+	unsigned int		 virtual:1;
+	unsigned 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,
+			    "write 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 ((int)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, struct xml_writer *writer,
+	const char *key, const char *value,
+	const char *attrkey, const char *attrvalue)
+{
+	int r;
+
+	r = xml_writer_start_element(writer, key);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_start_element() failed: %d", r);
+		return (ARCHIVE_FATAL);
+	}
+	if (attrkey != NULL && attrvalue != NULL) {
+		r = xml_writer_write_attribute(writer, attrkey, attrvalue);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_write_attribute() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+	if (value != NULL) {
+		r = xml_writer_write_string(writer, value);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_write_string() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+	r = xml_writer_end_element(writer);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_end_element() failed: %d", r);
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_string(struct archive_write *a, struct xml_writer *writer,
+	const char *key, const char *value)
+{
+	int r;
+
+	if (value == NULL)
+		return (ARCHIVE_OK);
+
+	r = xml_writer_start_element(writer, key);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_start_element() failed: %d", r);
+		return (ARCHIVE_FATAL);
+	}
+	if (value != NULL) {
+		r = xml_writer_write_string(writer, value);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_write_string() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+	r = xml_writer_end_element(writer);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_end_element() failed: %d", r);
+		return (ARCHIVE_FATAL);
+	}
+	return (ARCHIVE_OK);
+}
+
+static int
+xmlwrite_fstring(struct archive_write *a, struct xml_writer *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, struct xml_writer *writer,
+	const char *key, time_t t, int z)
+{
+	char timestr[100];
+	struct tm tm;
+
+#if defined(HAVE_GMTIME_S)
+	gmtime_s(&tm, &t);
+#elif defined(HAVE_GMTIME_R)
+	gmtime_r(&t, &tm);
+#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, struct xml_writer *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, struct xml_writer *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, struct xml_writer *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, struct xml_writer *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 = xml_writer_start_element(writer, element);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_start_element() 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 = xml_writer_end_element(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_end_element() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+	return (ARCHIVE_OK);
+}
+
+/*
+ * This function determines whether a UTF-8 string contains
+ * only codepoints that are convertible to Latin-1. Strings
+ * beyond Latin-1 are stored base64-encoded in the XAR TOC.
+ */
+static int
+is_u8_zstring_latin1(const char *in)
+{
+	unsigned int c;
+	while (*in) {
+		c = *in++;
+		if (c < 0x80) continue;
+		/*
+		 * Filter out non-continuation, any continuation of 2-3
+		 * bytes, and any continuation of 1 byte whose high 3 bits
+		 * are non-zero. Recall, 1-byte continuations can store 11
+		 * bits whereas Latin-1 codepoints are only 8 bits wide.
+		 */
+		if ((c & 0xFC) != 0xC0)
+			return (0);
+		c = *in++;
+		/*
+		 * If we get any non-continuation byte (including 0x00!),
+		 * the string is not valid UTF-8.
+		 */
+		if ((c & 0xC0) != 0x80)
+			return (0); /* invalid unicode */
+	}
+	return (1);
+}
+
+static int
+make_file_entry(struct archive_write *a, struct xml_writer *writer,
+    struct file *file)
+{
+	struct xar *xar;
+	const char *filetype, *filelink, *fflags;
+	struct archive_string linkto;
+	struct heap_data *heap;
+	const char *p;
+	size_t len;
+	int r, r2;
+
+	xar = (struct xar *)a->format_data;
+	r2 = ARCHIVE_OK;
+
+	/*
+	 * Make a file name entry, "<name>".
+	 */
+	if (!is_u8_zstring_latin1(file->basename.s)) {
+		r = xml_writer_start_element(writer, "name");
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_start_element() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		r = xml_writer_write_attribute(writer,
+		    "enctype", "base64");
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_write_attribute() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		r = xml_writer_write_base64(writer, file->basename.s,
+		    0, (int)archive_strlen(&(file->basename)));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_write_base64() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		r = xml_writer_end_element(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_end_element() 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 = xml_writer_start_element(writer, "device");
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_start_element() 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 = xml_writer_end_element(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_end_element() 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 = xml_writer_start_element(writer, "ea");
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_start_element() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+		r = xml_writer_write_attributef(writer,
+		    "id", "%d", heap->id);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_write_attributef() 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 = xml_writer_end_element(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_end_element() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	/*
+	 * Make a file data entry, "<data>".
+	 */
+	if (file->data.length > 0) {
+		r = xml_writer_start_element(writer, "data");
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_start_element() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+
+		r = xmlwrite_heap(a, writer, &(file->data));
+		if (r < 0)
+			return (ARCHIVE_FATAL);
+
+		r = xml_writer_end_element(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_end_element() failed: %d", r);
+			return (ARCHIVE_FATAL);
+		}
+	}
+
+	if (archive_strlen(&file->script) > 0) {
+		r = xml_writer_start_element(writer, "content");
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_start_element() 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 = xml_writer_end_element(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_end_element() 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;
+	struct xml_writer *writer;
+	const char* content;
+	size_t use;
+	int algsize;
+	int r, ret;
+
+	xar = (struct xar *)a->format_data;
+
+	ret = ARCHIVE_FATAL;
+
+	/*
+	 * Initialize xml writer.
+	 */
+	writer = NULL;
+	r = xml_writer_create(&writer);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_create() failed: %d", r);
+		goto exit_toc;
+	}
+	r = xml_writer_set_indent(writer, 4);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_set_indent() failed: %d", r);
+		goto exit_toc;
+	}
+	r = xml_writer_start_document(writer);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_start_document() failed: %d", r);
+		goto exit_toc;
+	}
+
+	/*
+	 * Start recording TOC
+	 */
+	r = xml_writer_start_element(writer, "xar");
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_start_element() failed: %d", r);
+		goto exit_toc;
+	}
+	r = xml_writer_start_element(writer, "toc");
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_start_element() 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 = xml_writer_start_element(writer, "checksum");
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_start_element() failed: %d", r);
+			goto exit_toc;
+		}
+		r = xml_writer_write_attribute(writer, "style",
+		    getalgname(xar->opt_toc_sumalg));
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_write_attribute() 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 = xml_writer_end_element(writer);
+		if (r < 0) {
+			archive_set_error(&a->archive,
+			    ARCHIVE_ERRNO_MISC,
+			    "xml_writer_end_element() 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 = xml_writer_start_element(writer,
+			    "file");
+			if (r < 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "xml_writer_start_element() "
+				    "failed: %d", r);
+				goto exit_toc;
+			}
+			r = xml_writer_write_attributef(
+			    writer, "id", "%d", np->id);
+			if (r < 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "xml_writer_write_attributef() "
+				    "failed: %d", r);
+				goto exit_toc;
+			}
+			continue;
+		}
+		while (np != np->parent) {
+			r = xml_writer_end_element(writer);
+			if (r < 0) {
+				archive_set_error(&a->archive,
+				    ARCHIVE_ERRNO_MISC,
+				    "xml_writer_end_element() "
+				    "failed: %d", r);
+				goto exit_toc;
+			}
+			if (np->chnext == NULL) {
+				/* Return to the parent directory. */
+				np = np->parent;
+			} else {
+				np = np->chnext;
+				r = xml_writer_start_element(writer,
+				    "file");
+				if (r < 0) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_MISC,
+					    "xml_writer_start_element() "
+					    "failed: %d", r);
+					goto exit_toc;
+				}
+				r = xml_writer_write_attributef(
+				    writer, "id", "%d", np->id);
+				if (r < 0) {
+					archive_set_error(&a->archive,
+					    ARCHIVE_ERRNO_MISC,
+					    "xml_writer_write_attributef() "
+					    "failed: %d", r);
+					goto exit_toc;
+				}
+				break;
+			}
+		}
+	} while (np != np->parent);
+
+	r = xml_writer_end_document(writer);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_end_document() failed: %d", r);
+		goto exit_toc;
+	}
+
+	r = xml_writer_get_final_content_and_length(writer, &content, &use);
+	if (r < 0) {
+		archive_set_error(&a->archive,
+		    ARCHIVE_ERRNO_MISC,
+		    "xml_writer_get_final_content_and_length() failed: %d", r);
+		goto exit_toc;
+	}
+
+#if DEBUG_PRINT_TOC
+	fprintf(stderr, "\n---TOC-- %d bytes --\n%s\n",
+	    (int)strlen(content), content);
+#endif
+
+	/*
+	 * Compress the TOC and calculate the sum of the TOC.
+	 */
+	xar->toc.temp_offset = xar->temp_offset;
+	xar->toc.size = (uint64_t)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 = (const unsigned char *)content;
+	xar->stream.avail_in = 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)
+		xml_writer_destroy(writer);
+
+	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 = (int)strlen(fn)) == 0)
+			return (0);
+	} else
+		l = (int)(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 = (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_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 = (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_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 = (unsigned int)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 = (unsigned int)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 = (unsigned int)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 = (unsigned int)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 > 9)
+		level = 9;
+	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);
+	}
+}
+
+#if HAVE_LIBXML_XMLWRITER_H
+
+#define BAD_CAST_CONST (const xmlChar *)
+
+struct xml_writer {
+	xmlTextWriterPtr writer;
+	xmlBufferPtr bp;
+	unsigned int indent;
+};
+
+static int
+xml_writer_create(struct xml_writer **pctx)
+{
+	struct xml_writer *ctx = calloc(1, sizeof(struct xml_writer));
+	if (ctx == NULL) {
+		return (-1);
+	}
+
+	ctx->bp = xmlBufferCreate();
+	if (ctx->bp == NULL) {
+		free(ctx);
+		return (-1);
+	}
+
+	ctx->writer = xmlNewTextWriterMemory(ctx->bp, 0);
+	if (ctx->writer == NULL) {
+		xmlBufferFree(ctx->bp);
+		free(ctx);
+		return (-1);
+	}
+
+	*pctx = ctx;
+	return (0);
+}
+
+static int
+xml_writer_destroy(struct xml_writer *ctx)
+{
+	xmlFreeTextWriter(ctx->writer);
+	xmlBufferFree(ctx->bp);
+	free(ctx);
+	return (0);
+}
+
+static int
+xml_writer_start_document(struct xml_writer *ctx)
+{
+	int r;
+	r = xmlTextWriterStartDocument(ctx->writer, "1.0", "UTF-8", NULL);
+	if (r < 0) {
+		return (r);
+	}
+
+	r = xmlTextWriterSetIndent(ctx->writer, (int)ctx->indent);
+	return (r);
+}
+
+static int
+xml_writer_end_document(struct xml_writer *ctx)
+{
+	return (xmlTextWriterEndDocument(ctx->writer));
+}
+
+static int
+xml_writer_set_indent(struct xml_writer *ctx, unsigned int indent)
+{
+	/* libxml2 only lets you set the indent after starting the document */
+	ctx->indent = indent;
+	return (0);
+}
+
+static int
+xml_writer_start_element(struct xml_writer *ctx, const char *localName)
+{
+	return (xmlTextWriterStartElement(ctx->writer,
+	    BAD_CAST_CONST(localName)));
+}
+
+static int
+xml_writer_write_attribute(struct xml_writer *ctx,
+    const char *key, const char *value)
+{
+	return (xmlTextWriterWriteAttribute(ctx->writer,
+	    BAD_CAST_CONST(key), BAD_CAST_CONST(value)));
+}
+
+static int
+xml_writer_write_attributef(struct xml_writer *ctx,
+    const char *key, const char *format, ...)
+{
+	va_list ap;
+	int ret;
+	va_start(ap, format);
+	ret = xmlTextWriterWriteVFormatAttribute(ctx->writer,
+	    BAD_CAST_CONST(key), format, ap);
+	va_end(ap);
+	return (ret);
+}
+
+static int
+xml_writer_write_string(struct xml_writer *ctx, const char *string)
+{
+	return (xmlTextWriterWriteString(ctx->writer, BAD_CAST_CONST(string)));
+}
+
+static int
+xml_writer_write_base64(struct xml_writer* ctx,
+    const char *data, size_t start, size_t len)
+{
+	return (xmlTextWriterWriteBase64(ctx->writer, data,
+	    (int)start, (int)len));
+}
+
+static int
+xml_writer_end_element(struct xml_writer *ctx)
+{
+	return (xmlTextWriterEndElement(ctx->writer));
+}
+
+static int
+xml_writer_get_final_content_and_length(struct xml_writer *ctx,
+    const char **out, size_t *size)
+{
+	*out = (const char*)xmlBufferContent(ctx->bp);
+	*size = (size_t)xmlBufferLength(ctx->bp);
+	return (0);
+}
+
+#elif HAVE_XMLLITE_H
+
+struct xml_writer {
+	IXmlWriter *writer;
+	IStream *stream;
+	HGLOBAL global;
+};
+
+static int
+xml_writer_create(struct xml_writer **pctx)
+{
+	struct xml_writer *ctx;
+	HRESULT hr;
+
+	ctx = calloc(1, sizeof(struct xml_writer));
+	if (ctx == NULL) {
+		return (E_OUTOFMEMORY);
+	}
+
+	hr = CreateStreamOnHGlobal(NULL, TRUE, &ctx->stream);
+	if (FAILED(hr)) {
+		free(ctx);
+		return (hr);
+	}
+
+	hr = CreateXmlWriter(&IID_IXmlWriter, (void **)&ctx->writer, NULL);
+	if (FAILED(hr)) {
+		ctx->stream->lpVtbl->Release(ctx->stream);
+		free(ctx);
+		return (hr);
+	}
+
+	hr = ctx->writer->lpVtbl->SetOutput(ctx->writer,
+	    (IUnknown *)ctx->stream);
+	if (FAILED(hr)) {
+		ctx->writer->lpVtbl->Release(ctx->writer);
+		ctx->stream->lpVtbl->Release(ctx->stream);
+		free(ctx);
+		return (hr);
+	}
+
+	*pctx = ctx;
+	return (S_OK);
+}
+
+static int
+xml_writer_destroy(struct xml_writer *ctx)
+{
+	if (ctx->global)
+		GlobalUnlock(ctx->global);
+	ctx->writer->lpVtbl->Release(ctx->writer); /* Destroys only writer */
+	ctx->stream->lpVtbl->Release(ctx->stream); /* Destroys stream, global */
+	free(ctx);
+	return (S_OK);
+}
+
+static int
+xml_writer_start_document(struct xml_writer *ctx)
+{
+	return ctx->writer->lpVtbl->WriteStartDocument(ctx->writer,
+	    XmlStandalone_Omit);
+}
+
+static int
+xml_writer_end_document(struct xml_writer *ctx)
+{
+	return ctx->writer->lpVtbl->WriteEndDocument(ctx->writer);
+}
+
+static int
+xml_writer_set_indent(struct xml_writer *ctx, unsigned int indent)
+{
+	/* Windows' xmllite does not support indent sizes; will always be 2 */
+	(void)indent;
+	return ctx->writer->lpVtbl->SetProperty(ctx->writer,
+	    XmlWriterProperty_Indent, (LONG_PTR)TRUE);
+}
+
+static int
+xml_writer_start_element(struct xml_writer *ctx, const char *localName)
+{
+	struct archive_wstring as;
+	HRESULT hr;
+	archive_string_init(&as);
+	if (archive_wstring_append_from_mbs(&as, localName,
+	    strlen(localName))) {
+		hr = E_OUTOFMEMORY;
+		goto exit_hr;
+	}
+	hr = ctx->writer->lpVtbl->WriteStartElement(ctx->writer, NULL,
+	    as.s, NULL);
+
+exit_hr:
+	archive_wstring_free(&as);
+	return hr;
+}
+
+static int
+xml_writer_write_attribute(struct xml_writer *ctx,
+    const char *key, const char *value)
+{
+	struct archive_wstring ask, asv;
+	HRESULT hr;
+	archive_string_init(&ask);
+	archive_string_init(&asv);
+	if (archive_wstring_append_from_mbs(&ask, key, strlen(key))) {
+		hr = E_OUTOFMEMORY;
+		goto exit_hr;
+	}
+	if (archive_wstring_append_from_mbs(&asv, value, strlen(value))) {
+		hr = E_OUTOFMEMORY;
+		goto exit_hr;
+	}
+	hr = ctx->writer->lpVtbl->WriteAttributeString(ctx->writer, NULL,
+	    ask.s, NULL, asv.s);
+
+exit_hr:
+	archive_wstring_free(&asv);
+	archive_wstring_free(&ask);
+	return hr;
+}
+
+static int
+xml_writer_write_attributef(struct xml_writer *ctx,
+    const char *key, const char *format, ...)
+{
+	struct archive_wstring ask, asv;
+	struct archive_string asf;
+	HRESULT hr;
+	va_list ap;
+
+	va_start(ap, format);
+	archive_string_init(&ask);
+	archive_string_init(&asv);
+	archive_string_init(&asf);
+
+	if (archive_wstring_append_from_mbs(&ask, key, strlen(key))) {
+		hr = E_OUTOFMEMORY;
+		goto exit_hr;
+	}
+
+	archive_string_vsprintf(&asf, format, ap);
+	if (archive_wstring_append_from_mbs(&asv, asf.s, asf.length)) {
+		hr = E_OUTOFMEMORY;
+		goto exit_hr;
+	}
+
+	hr = ctx->writer->lpVtbl->WriteAttributeString(ctx->writer, NULL,
+	    ask.s, NULL, asv.s);
+
+exit_hr:
+	archive_string_free(&asf);
+	archive_wstring_free(&asv);
+	archive_wstring_free(&ask);
+	va_end(ap);
+
+	return hr;
+}
+
+static int
+xml_writer_write_string(struct xml_writer *ctx, const char *string)
+{
+	struct archive_wstring as;
+	HRESULT hr;
+	archive_string_init(&as);
+	if (archive_wstring_append_from_mbs(&as, string, strlen(string))) {
+		hr = E_OUTOFMEMORY;
+		goto exit_hr;
+	}
+	hr = ctx->writer->lpVtbl->WriteString(ctx->writer, as.s);
+
+exit_hr:
+	archive_wstring_free(&as);
+	return hr;
+}
+
+static const wchar_t base64[] = {
+	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', L'0', L'1', L'2', L'3',
+	L'4', L'5', L'6', L'7', L'8', L'9', L'+', L'/'
+};
+
+static void
+la_b64_wencode(struct archive_wstring *as, const unsigned char *p, size_t len)
+{
+	int c;
+
+	for (; len >= 3; p += 3, len -= 3) {
+		c = p[0] >> 2;
+		archive_wstrappend_wchar(as, base64[c]);
+		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
+		archive_wstrappend_wchar(as, base64[c]);
+		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
+		archive_wstrappend_wchar(as, base64[c]);
+		c = p[2] & 0x3f;
+		archive_wstrappend_wchar(as, base64[c]);
+	}
+	if (len > 0) {
+		c = p[0] >> 2;
+		archive_wstrappend_wchar(as, base64[c]);
+		c = (p[0] & 0x03) << 4;
+		if (len == 1) {
+			archive_wstrappend_wchar(as, base64[c]);
+			archive_wstrappend_wchar(as, '=');
+			archive_wstrappend_wchar(as, '=');
+		} else {
+			c |= (p[1] & 0xf0) >> 4;
+			archive_wstrappend_wchar(as, base64[c]);
+			c = (p[1] & 0x0f) << 2;
+			archive_wstrappend_wchar(as, base64[c]);
+			archive_wstrappend_wchar(as, '=');
+		}
+	}
+}
+
+static int
+xml_writer_write_base64(struct xml_writer* ctx,
+    const char *data, size_t start, size_t len)
+{
+	struct archive_wstring as;
+	HRESULT hr;
+	archive_string_init(&as);
+	la_b64_wencode(&as, (const unsigned char *)data + start, len - start);
+	hr = ctx->writer->lpVtbl->WriteString(ctx->writer, as.s);
+	archive_wstring_free(&as);
+	return hr;
+}
+
+static int
+xml_writer_end_element(struct xml_writer *ctx)
+{
+	return ctx->writer->lpVtbl->WriteEndElement(ctx->writer);
+}
+
+static int
+xml_writer_get_final_content_and_length(struct xml_writer *ctx,
+    const char **out, size_t *size)
+{
+	HGLOBAL gbl;
+	HRESULT hr;
+
+	hr = ctx->writer->lpVtbl->Flush(ctx->writer);
+	if (FAILED(hr)) {
+		return (hr);
+	}
+
+	hr = GetHGlobalFromStream(ctx->stream, &gbl);
+	if (FAILED(hr)) {
+		return (hr);
+	}
+
+	*out = (const char *)GlobalLock(gbl);
+	if (*out == NULL) {
+		hr = HRESULT_FROM_WIN32(GetLastError());
+		return (hr);
+	}
+
+	/* GlobalUnlock is called in
+	 * xml_writer_destroy.
+	 */
+	*size = (size_t)GlobalSize(gbl);
+	ctx->global = gbl;
+	return (hr);
+}
+
+#endif /* HAVE_LIBXML_XMLWRITER_H */
+
+#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,2523 @@
+/*-
+ * 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"
+
+#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_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.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_ZSTD_H
+#include <zstd.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_time_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_LZMA_EOPM	(1 << 1)
+#define ZIP_ENTRY_FLAG_DEFLATE_MAX	(1 << 1) /* i.e. compression levels 8 & 9 */
+#define ZIP_ENTRY_FLAG_DEFLATE_FAST	(1 << 2) /* i.e. compression levels 3 & 4 */
+#define ZIP_ENTRY_FLAG_DEFLATE_SUPER_FAST	(1 << 1) | (1 << 2) /* i.e. compression levels 1 & 2 */
+#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,
+	COMPRESSION_BZIP2 = 12,
+	COMPRESSION_LZMA = 14,
+	COMPRESSION_ZSTD = 93,
+	COMPRESSION_XZ = 95
+};
+
+#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 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;
+	short compression_level;
+	int init_default_conversion;
+	enum encryption encryption_type;
+	short threads;
+
+#define ZIP_FLAG_AVOID_ZIP64 1
+#define ZIP_FLAG_FORCE_ZIP64 2
+#define ZIP_FLAG_EXPERIMENT_xl 4
+	int flags;
+#if defined(HAVE_LZMA_H) || defined(HAVE_ZLIB_H) || defined(HAVE_BZLIB_H) || defined(HAVE_ZSTD_H)
+	union {
+#ifdef HAVE_LZMA_H
+		/* ZIP's XZ format (id 95) is easy enough: copy Deflate, mutatis
+		 * mutandis the library changes. ZIP's LZMA format (id 14),
+		 * however, is rather more involved, starting here: it being a
+		 * modified LZMA Alone format requires a bit more
+		 * book-keeping. */
+		struct {
+			char headers_to_write;
+			lzma_options_lzma options;
+			lzma_stream context;
+		} lzma;
+#endif
+#ifdef HAVE_ZLIB_H
+		z_stream deflate;
+#endif
+#ifdef HAVE_BZLIB_H
+		bz_stream bzip2;
+#endif
+#if defined(HAVE_ZSTD_H) && HAVE_ZSTD_compressStream
+		struct {
+			/* Libzstd's init function gives a pointer to a memory area
+			 * it manages rather than asking for memory to initialise. */
+			ZSTD_CStream* context;
+			ZSTD_inBuffer in;
+			ZSTD_outBuffer out;
+		} zstd;
+#endif
+	} 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 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);
+
+#ifdef HAVE_LZMA_H
+/* ZIP's LZMA format requires the use of a alas not exposed in LibLZMA
+ * function to write the ZIP header. Given our internal version never
+ * fails, no need for a non-void return type. */
+static void
+lzma_lzma_props_encode(const lzma_options_lzma* options, uint8_t* out)
+{
+	out[0] = (options->pb * 5 + options->lp) * 9 + options->lc;
+	archive_le32enc(out + 1, options->dict_size);
+}
+#endif
+
+#if defined(HAVE_LZMA_H) && !defined(HAVE_LZMA_STREAM_ENCODER_MT)
+/* Dummy mt declarations, to avoid spaghetti includes below. Defined with
+ * macros being renamed afterwards to shadow liblzma's types in order to
+ * avoid some compiler errors. */
+#define lzma_stream_encoder_mt(str, opt) dummy_mt(str, opt)
+#define lzma_mt dummy_options
+
+typedef struct {
+	void* filters;
+	uint32_t preset;
+	lzma_check check;
+	short threads;
+	char flags;
+	char block_size;
+	char timeout;
+} dummy_options;
+
+static inline lzma_ret
+dummy_mt(lzma_stream* stream, const lzma_mt* options)
+{
+	(void)stream; /* UNUSED */
+	(void)options; /* UNUSED */
+	return LZMA_PROG_ERROR;
+}
+#endif
+
+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;
+		} else if (strcmp(val, "bzip2") == 0) {
+#ifdef HAVE_BZLIB_H
+			zip->requested_compression = COMPRESSION_BZIP2;
+			ret = ARCHIVE_OK;
+#else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "bzip2 compression not supported");
+#endif
+		} else if (strcmp(val, "lzma") == 0) {
+#ifdef HAVE_LZMA_H
+			zip->requested_compression = COMPRESSION_LZMA;
+			ret = ARCHIVE_OK;
+#else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "lzma compression not supported");
+#endif
+		} else if (strcmp(val, "xz") == 0) {
+#ifdef HAVE_LZMA_H
+			zip->requested_compression = COMPRESSION_XZ;
+			ret = ARCHIVE_OK;
+#else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "xz compression not supported");
+#endif
+		} else if (strcmp(val, "zstd") == 0) {
+#if defined(HAVE_ZSTD_H) && HAVE_ZSTD_compressStream
+			zip->requested_compression = COMPRESSION_ZSTD;
+			ret = ARCHIVE_OK;
+#else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "zstd compression not supported");
+#endif
+		}
+		return (ret);
+	} else if (strcmp(key, "compression-level") == 0) {
+		char *endptr;
+
+		if (val == NULL)
+			return (ARCHIVE_WARN);
+		errno = 0;
+		zip->compression_level = (short)strtoul(val, &endptr, 10);
+		if (errno != 0 || *endptr != '\0' || zip->compression_level < 0 ||
+			zip->compression_level > 9) {
+			zip->compression_level = 6; // set to default
+			return (ARCHIVE_WARN);
+		}
+
+		if (zip->compression_level == 0) {
+			zip->requested_compression = COMPRESSION_STORE;
+			return ARCHIVE_OK;
+		} else {
+#if defined(HAVE_ZLIB_H) || defined(HAVE_LZMA_H) || defined(HAVE_BZLIB_H) || (defined(HAVE_ZSTD_H) && HAVE_ZSTD_compressStream)
+			// Not forcing an already specified compression algorithm
+			if (zip->requested_compression == COMPRESSION_UNSPECIFIED) {
+#ifdef HAVE_ZLIB_H
+				zip->requested_compression = COMPRESSION_DEFLATE;
+#elif defined(HAVE_BZLIB_H)
+				zip->requested_compression = COMPRESSION_BZIP2;
+#elif defined(HAVE_LZMA_H)
+				// Arbitrarily choosing LZMA of the two LZMA methods
+				zip->requested_compression = COMPRESSION_LZMA;
+#else
+				zip->requested_compression = COMPRESSION_ZSTD;
+#endif
+			}
+			return ARCHIVE_OK;
+#else
+			archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			    "compression not supported");
+#endif
+		}
+	} else if (strcmp(key, "threads") == 0) {
+		char *endptr;
+
+		if (val == NULL)
+			return (ARCHIVE_FAILED);
+		errno = 0;
+		zip->threads = (short)strtoul(val, &endptr, 10);
+		if (errno != 0 || *endptr != '\0') {
+			zip->threads = 1;
+			archive_set_error(&(a->archive), ARCHIVE_ERRNO_MISC,
+			    "Illegal value `%s'", val);
+			return (ARCHIVE_FAILED);
+		}
+		if (zip->threads == 0) {
+#ifdef HAVE_LZMA_STREAM_ENCODER_MT
+			zip->threads = lzma_cputhreads();
+#elif defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_ONLN)
+			zip->threads = sysconf(_SC_NPROCESSORS_ONLN);
+#elif !defined(__CYGWIN__) && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0601
+			/* Windows 7 and up */
+			DWORD activeProcs = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS);
+			zip->threads = activeProcs <= SHRT_MAX ? (short)activeProcs : SHRT_MAX;
+#else
+			zip->threads = 1;
+#endif
+		}
+		return (ARCHIVE_OK);
+	} 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_bzip2(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_bzip2");
+	if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		"Can only use archive_write_zip_set_compression_bzip2"
+		" with zip format");
+		ret = ARCHIVE_FATAL;
+	} else {
+#ifdef HAVE_BZLIB_H
+		struct zip *zip = a->format_data;
+		zip->requested_compression = COMPRESSION_BZIP2;
+		ret = ARCHIVE_OK;
+#else
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"bzip2 compression not supported");
+		ret = ARCHIVE_FAILED;
+#endif
+	}
+	return (ret);
+}
+
+int
+archive_write_zip_set_compression_zstd(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_zstd");
+	if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		"Can only use archive_write_zip_set_compression_zstd"
+		" with zip format");
+		ret = ARCHIVE_FATAL;
+	} else {
+#if defined(HAVE_ZSTD_H) && HAVE_ZSTD_compressStream
+		struct zip *zip = a->format_data;
+		zip->requested_compression = COMPRESSION_ZSTD;
+		ret = ARCHIVE_OK;
+#else
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"zstd compression not supported");
+		ret = ARCHIVE_FAILED;
+#endif
+	}
+	return (ret);
+}
+
+int
+archive_write_zip_set_compression_lzma(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_lzma");
+	if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		"Can only use archive_write_zip_set_compression_lzma"
+		" with zip format");
+		ret = ARCHIVE_FATAL;
+	} else {
+#ifdef HAVE_LZMA_H
+		struct zip *zip = a->format_data;
+		zip->requested_compression = COMPRESSION_LZMA;
+		ret = ARCHIVE_OK;
+#else
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"lzma compression not supported");
+		ret = ARCHIVE_FAILED;
+#endif
+	}
+	return (ret);
+}
+
+int
+archive_write_zip_set_compression_xz(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_xz");
+	if (a->archive.archive_format != ARCHIVE_FORMAT_ZIP) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		"Can only use archive_write_zip_set_compression_xz"
+		" with zip format");
+		ret = ARCHIVE_FATAL;
+	} else {
+#ifdef HAVE_LZMA_H
+		struct zip *zip = a->format_data;
+		zip->requested_compression = COMPRESSION_XZ;
+		ret = ARCHIVE_OK;
+#else
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+			"xz 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_store");
+	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 = 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;
+	/* Following the 7-zip write support's lead, setting the default
+	 * compression level explicitly to 6 no matter what. */
+	zip->compression_level = 6;
+	/* Following the xar write support's lead, the default number of
+	 * threads is 1 (i.e. the xz compression, the only one caring about
+	 * that, not being multi-threaded even if the multi-threaded encoder
+	 * were available) */
+	zip->threads = 1;
+	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;
+#define MIN_VERSION_NEEDED(x) do { if (version_needed < x) { version_needed = x; } } while (0)
+
+	/* 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;
+	/* Zero size values implies that we're using a trailing data descriptor */
+	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_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;
+		case ENCRYPTION_NONE:
+		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(zip->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(zip->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(zip->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;
+		MIN_VERSION_NEEDED(20);
+	} else if (type != AE_IFREG) {
+		zip->entry_compression = COMPRESSION_STORE;
+		zip->entry_uncompressed_limit = 0;
+		MIN_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;
+		}
+		switch (zip->entry_compression) {
+		case COMPRESSION_STORE:
+			zip->entry_compressed_size = size;
+			zip->entry_uncompressed_size = size;
+			MIN_VERSION_NEEDED(10);
+			break;
+		case COMPRESSION_ZSTD:
+			zip->entry_uncompressed_size = size;
+			MIN_VERSION_NEEDED(63);
+			break;
+		case COMPRESSION_LZMA:
+			zip->entry_uncompressed_size = size;
+			zip->entry_flags |= ZIP_ENTRY_FLAG_LZMA_EOPM;
+			MIN_VERSION_NEEDED(63);
+			break;
+		case COMPRESSION_XZ:
+			zip->entry_uncompressed_size = size;
+			MIN_VERSION_NEEDED(63);
+			break;
+		case COMPRESSION_BZIP2:
+			zip->entry_uncompressed_size = size;
+			MIN_VERSION_NEEDED(46);
+			break;
+		default: // i.e. deflate compression
+			zip->entry_uncompressed_size = size;
+			switch (zip->compression_level) {
+			case 1:
+			case 2:
+				zip->entry_flags |= ZIP_ENTRY_FLAG_DEFLATE_SUPER_FAST;
+				break;
+			case 3:
+			case 4:
+				zip->entry_flags |= ZIP_ENTRY_FLAG_DEFLATE_FAST;
+				break;
+			case 8:
+			case 9:
+				zip->entry_flags |= ZIP_ENTRY_FLAG_DEFLATE_MAX;
+				break;
+			default:
+				break;
+			}
+			MIN_VERSION_NEEDED(20);
+			break;
+		}
+
+		if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+			switch (zip->entry_encryption) {
+			case ENCRYPTION_TRADITIONAL:
+				additional_size = TRAD_HEADER_SIZE;
+				MIN_VERSION_NEEDED(20);
+				break;
+			case ENCRYPTION_WINZIP_AES128:
+				additional_size = WINZIP_AES128_HEADER_SIZE
+				    + AUTH_CODE_SIZE;
+				MIN_VERSION_NEEDED(20);
+				break;
+			case ENCRYPTION_WINZIP_AES256:
+				additional_size = WINZIP_AES256_HEADER_SIZE
+				    + AUTH_CODE_SIZE;
+				MIN_VERSION_NEEDED(20);
+				break;
+			case ENCRYPTION_NONE:
+			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)) {
+			MIN_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. Use the default
+		 * compression unless specified otherwise.
+		 */
+		zip->entry_compression = zip->requested_compression;
+		if (zip->entry_compression == COMPRESSION_UNSPECIFIED) {
+			zip->entry_compression = COMPRESSION_DEFAULT;
+		}
+
+		zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END;
+		if ((zip->flags & ZIP_FLAG_AVOID_ZIP64) == 0) {
+			/* We might use zip64 extensions, so require 4.5 */
+			MIN_VERSION_NEEDED(45);
+		}
+		switch (zip->entry_compression) {
+		case COMPRESSION_STORE:
+			MIN_VERSION_NEEDED(10);
+			break;
+		case COMPRESSION_ZSTD:
+			MIN_VERSION_NEEDED(63);
+			break;
+		case COMPRESSION_LZMA:
+			zip->entry_flags |= ZIP_ENTRY_FLAG_LZMA_EOPM;
+			MIN_VERSION_NEEDED(63);
+			break;
+		case COMPRESSION_XZ:
+			MIN_VERSION_NEEDED(63);
+			break;
+		case COMPRESSION_BZIP2:
+			MIN_VERSION_NEEDED(46);
+			break;
+		default: // i.e. deflate compression
+			switch (zip->compression_level) {
+			case 1:
+			case 2:
+				zip->entry_flags |= ZIP_ENTRY_FLAG_DEFLATE_SUPER_FAST;
+				break;
+			case 3:
+			case 4:
+				zip->entry_flags |= ZIP_ENTRY_FLAG_DEFLATE_FAST;
+				break;
+			case 8:
+			case 9:
+				zip->entry_flags |= ZIP_ENTRY_FLAG_DEFLATE_MAX;
+				break;
+			default:
+				break;
+			}
+			MIN_VERSION_NEEDED(20);
+			break;
+		}
+
+		if (zip->entry_flags & ZIP_ENTRY_FLAG_ENCRYPTED) {
+			switch (zip->entry_encryption) {
+			case ENCRYPTION_TRADITIONAL:
+			case ENCRYPTION_WINZIP_AES128:
+			case ENCRYPTION_WINZIP_AES256:
+				MIN_VERSION_NEEDED(20);
+				break;
+			case ENCRYPTION_NONE:
+			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,
+		unix_to_dos(archive_entry_mtime(zip->entry)));
+	if ((zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END) == 0) {
+		archive_le32enc(local_header + 14, zip->entry_crc32);
+		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,
+		unix_to_dos(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. */
+
+	/* ux Unix extra data, length 11, version 1 */
+	if (archive_entry_uid_is_set(entry) || archive_entry_gid_is_set(entry)) {
+		/* 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 ux, 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.
+	 */
+
+	/* UT timestamp: length depends on what timestamps are set.
+	 * This header appears in the Central Directory also, but
+	 * according to Info-Zip specification, the CD form
+	 * only holds mtime, so we format it separately. */
+	if (archive_entry_mtime_is_set(entry)
+	    || archive_entry_atime_is_set(entry)
+	    || archive_entry_ctime_is_set(entry)) {
+		unsigned char *ut = e;
+		memcpy(e, "UT\000\000", 4);
+		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;
+		}
+		archive_le16enc(ut + 2, (uint16_t)(e - ut - 4));
+	}
+
+	/*
+	 * Note about Zip64 Extended Information Extra Field:
+	 * Because libarchive always writes in a streaming
+	 * fashion, we never know the CRC when we're writing
+	 * the local header.  So we have to use length-at-end, which
+	 * prevents us from putting size information into a Zip64
+	 * extra field.  However, apparently some readers find it
+	 * a helpful clue to have an empty such field so they
+	 * can expect a 64-bit length-at-end marker.
+	 */
+	if (archive_entry_size_is_set(zip->entry)
+	    && (zip->entry_uncompressed_size > ZIP_4GB_MAX
+		|| zip->entry_compressed_size > ZIP_4GB_MAX)) {
+		/* Header ID 0x0001, size 0 */
+		memcpy(e, "\001\000\000\000", 4);
+		e += 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;
+	}
+
+	switch (zip->entry_compression) {
+#ifdef HAVE_ZLIB_H
+	case COMPRESSION_DEFLATE:
+		zip->stream.deflate.zalloc = Z_NULL;
+		zip->stream.deflate.zfree = Z_NULL;
+		zip->stream.deflate.opaque = Z_NULL;
+		zip->stream.deflate.next_out = zip->buf;
+		zip->stream.deflate.avail_out = (uInt)zip->len_buf;
+		if (deflateInit2(&zip->stream.deflate, zip->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);
+		}
+		break;
+#endif
+#ifdef HAVE_BZLIB_H
+	case COMPRESSION_BZIP2:
+		memset(&zip->stream.bzip2, 0, sizeof(bz_stream));
+		zip->stream.bzip2.next_out = (char*)zip->buf;
+		zip->stream.bzip2.avail_out = (unsigned int)zip->len_buf;
+		if (BZ2_bzCompressInit(&zip->stream.bzip2, zip->compression_level, 0, 0) != BZ_OK) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't init bzip2 compressor");
+			return (ARCHIVE_FATAL);
+		}
+		break;
+#endif
+#if defined(HAVE_ZSTD_H) && HAVE_ZSTD_compressStream
+	case COMPRESSION_ZSTD:
+		{/* Libzstd, contrary to many compression libraries, doesn't use
+		 * zlib's 0 to 9 scale and its negative scale is way bigger than
+		 * its positive one. So setting 1 as the lowest allowed compression
+		 * level and rescaling to 2 to 9 to libzstd's positive scale. */
+		int zstd_compression_level = zip->compression_level == 1
+			? ZSTD_minCLevel() // ZSTD_minCLevel is negative !
+			: (zip->compression_level - 1) * ZSTD_maxCLevel() / 8;
+		zip->stream.zstd.context = ZSTD_createCStream();
+		size_t zret = ZSTD_initCStream(zip->stream.zstd.context, zstd_compression_level);
+		if (ZSTD_isError(zret)) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't init zstd compressor");
+			return (ARCHIVE_FATAL);
+		}
+		/* Asking for the multi-threaded compressor is a no-op in zstd if
+		 * it's not supported, so no need to explicitly check for it */
+		ZSTD_CCtx_setParameter(zip->stream.zstd.context, ZSTD_c_nbWorkers, zip->threads);
+		zip->stream.zstd.out.dst = zip->buf;
+		zip->stream.zstd.out.size = zip->len_buf;
+		zip->stream.zstd.out.pos = 0;
+		break;}
+#endif
+#ifdef HAVE_LZMA_H
+	case COMPRESSION_LZMA:
+		{/* Set compression level 9 as the no-holds barred one */
+		uint32_t lzma_compression_level = zip->compression_level == 9
+			? LZMA_PRESET_EXTREME | zip->compression_level
+			: (uint32_t)zip->compression_level;
+		/* Forcibly setting up the encoder to use the LZMA1 variant, as
+		 * it is the one LZMA Alone uses. */
+		lzma_filter filters[2] = {
+			{
+				.id = LZMA_FILTER_LZMA1,
+				.options = &zip->stream.lzma.options
+			},
+			{
+				.id = LZMA_VLI_UNKNOWN
+			}
+		};
+		memset(&zip->stream.lzma.context, 0, sizeof(lzma_stream));
+		lzma_lzma_preset(&zip->stream.lzma.options, lzma_compression_level);
+		zip->stream.lzma.headers_to_write = 1;
+		/* We'll be writing the headers ourselves, so using the raw
+		 * encoder */
+		if (lzma_raw_encoder(&zip->stream.lzma.context, filters) != LZMA_OK) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't init lzma compressor");
+			return (ARCHIVE_FATAL);
+		}
+		zip->stream.lzma.context.next_out = zip->buf;
+		zip->stream.lzma.context.avail_out = (unsigned int)zip->len_buf;
+		break;}
+	case COMPRESSION_XZ:
+		{/* Set compression level 9 as the no-holds barred one */
+		uint32_t lzma_compression_level = zip->compression_level == 9
+			? LZMA_PRESET_EXTREME | zip->compression_level
+			: (uint32_t)zip->compression_level;
+		lzma_ret retval;
+#ifndef HAVE_LZMA_STREAM_ENCODER_MT
+		/* Force the number of threads to one, and thus to a mono-threaded
+		 * encoder in case we don't have the multi-threaded one */
+		zip->threads = 1;
+#endif
+		memset(&zip->stream.lzma.context, 0, sizeof(lzma_stream));
+		/* The XZ check will be arbitrarily set to none: ZIP already has
+		 * a CRC-32 check of its own */
+		if (zip->threads == 1) {
+			/* XZ uses LZMA2. */
+			lzma_filter filters[2] = {
+				{
+					.id = LZMA_FILTER_LZMA2,
+					.options = &zip->stream.lzma.options
+				},
+				{
+					.id = LZMA_VLI_UNKNOWN
+				}
+			};
+			/* Might as well use the lzma_options we already allocated,
+			 * even if we'll never use it after the initialisation */
+			lzma_lzma_preset(&zip->stream.lzma.options, lzma_compression_level);
+			/* 1 thread requested, so non multi-threaded encoder */
+			retval = lzma_stream_encoder(&zip->stream.lzma.context,
+				filters, LZMA_CHECK_NONE);
+		}
+		else {
+			lzma_mt options = {
+				.flags = 0,
+				.block_size = 0,
+				.timeout = 0,
+				.filters = NULL,
+				.check = LZMA_CHECK_NONE,
+				.preset = lzma_compression_level,
+				.threads = zip->threads
+			};
+			/* More than 1 thread requested, so multi-threaded encoder
+			 * which always outputs XZ */
+			retval = lzma_stream_encoder_mt(&zip->stream.lzma.context,
+				&options);
+		}
+		if (retval != LZMA_OK) {
+			archive_set_error(&a->archive, ENOMEM,
+			    "Can't init xz compressor");
+			return (ARCHIVE_FATAL);
+		}
+		zip->stream.lzma.context.next_out = zip->buf;
+		zip->stream.lzma.context.avail_out = (unsigned int)zip->len_buf;
+		break;}
+#endif
+	default:
+		break;
+	}
+
+	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;
+		case ENCRYPTION_NONE:
+		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;
+#ifdef HAVE_ZLIB_H
+	case COMPRESSION_DEFLATE:
+		zip->stream.deflate.next_in = (unsigned char*)(uintptr_t)buff;
+		zip->stream.deflate.avail_in = (uInt)s;
+		do {
+			ret = deflate(&zip->stream.deflate, Z_NO_FLUSH);
+			if (ret == Z_STREAM_ERROR)
+				return (ARCHIVE_FATAL);
+			if (zip->stream.deflate.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.deflate.next_out = zip->buf;
+				zip->stream.deflate.avail_out = (uInt)zip->len_buf;
+			}
+		} while (zip->stream.deflate.avail_in != 0);
+		break;
+#endif
+#if defined(HAVE_ZSTD_H) && HAVE_ZSTD_compressStream
+	case COMPRESSION_ZSTD:
+		zip->stream.zstd.in.src = buff;
+		zip->stream.zstd.in.size = s;
+		zip->stream.zstd.in.pos = 0;
+		do {
+			size_t zret = ZSTD_compressStream(zip->stream.zstd.context,
+				&zip->stream.zstd.out, &zip->stream.zstd.in);
+			if (ZSTD_isError(zret))
+				return (ARCHIVE_FATAL);
+			if (zip->stream.zstd.out.pos == zip->stream.zstd.out.size) {
+				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.zstd.out.dst = zip->buf;
+				zip->stream.zstd.out.size = zip->len_buf;
+				zip->stream.zstd.out.pos = 0;
+			}
+		} while (zip->stream.zstd.in.pos != zip->stream.zstd.in.size);
+		break;
+#endif
+#ifdef HAVE_BZLIB_H
+	case COMPRESSION_BZIP2:
+		zip->stream.bzip2.next_in = (char*)(uintptr_t)buff;
+		zip->stream.bzip2.avail_in = (unsigned int)s;
+		do {
+			ret = BZ2_bzCompress(&zip->stream.bzip2, BZ_RUN);
+			if (ret != BZ_RUN_OK)
+				return (ARCHIVE_FATAL);
+			if (zip->stream.bzip2.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.bzip2.next_out = (char*)zip->buf;
+				zip->stream.bzip2.avail_out = (unsigned int)zip->len_buf;
+			}
+		} while (zip->stream.bzip2.avail_in != 0);
+		break;
+#endif
+#ifdef HAVE_LZMA_H
+	case COMPRESSION_LZMA:
+		if (zip->stream.lzma.headers_to_write) {
+			/* LZMA Alone and ZIP's LZMA format (i.e. id 14) are almost
+			 * the same. Here's an example of a structure of LZMA Alone:
+			 *
+			 * $ 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 ZIP's LZMA format:
+			 *
+			 * $ 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 ZIP's LZMA format contains an additional
+			 * magic1 and magic2 headers, the lzma_params field contains
+			 * the same parameter set as in LZMA Alone, and the <data...>
+			 * field is the same as in LZMA Alone as well. However, note
+			 * that ZIP's format is missing the uncompressed_size field.
+			 *
+			 * So we need to write a raw LZMA stream, set up for LZMA1
+			 * (i.e. the algorithm variant LZMA Alone uses), which was
+			 * done above in the initialisation but first we need to
+			 * write ZIP's LZMA header, as if it were Stored data. Then
+			 * we can use the raw stream as if it were any other. magic1
+			 * being version numbers and magic2 being lzma_params's size,
+			 * they get written in without further ado but lzma_params
+			 * requires to use other functions than the usual lzma_stream
+			 * manipulating ones, hence the additional book-keeping
+			 * required alongside the lzma_stream.
+			 */
+			uint8_t buf[9] = { LZMA_VERSION_MAJOR, LZMA_VERSION_MINOR, 5, 0 };
+			lzma_lzma_props_encode(&zip->stream.lzma.options, buf + 4);
+			const size_t sh = 9;
+			if (zip->tctx_valid || zip->cctx_valid) {
+				uint8_t* header = buf;
+				const uint8_t * const rh = header + sh;
+
+				while (header < rh) {
+					size_t l;
+
+					if (zip->tctx_valid) {
+						l = trad_enc_encrypt_update(&zip->tctx,
+							header, rh - header,
+							zip->buf, zip->len_buf);
+					} else {
+						l = zip->len_buf;
+						ret = archive_encrypto_aes_ctr_update(
+							&zip->cctx,
+							header, rh - header, 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;
+					header += l;
+				}
+			} else {
+				ret = __archive_write_output(a, buf, sh);
+				if (ret != ARCHIVE_OK)
+					return (ret);
+				zip->written_bytes += sh;
+				zip->entry_compressed_written += sh;
+			}
+			zip->stream.lzma.headers_to_write = 0;
+		}
+		/* FALLTHROUGH */
+	case COMPRESSION_XZ:
+		zip->stream.lzma.context.next_in = (unsigned char*)(uintptr_t)buff;
+		zip->stream.lzma.context.avail_in = (unsigned int)s;
+		do {
+			ret = lzma_code(&zip->stream.lzma.context, LZMA_RUN);
+			if (ret == LZMA_MEM_ERROR)
+				return (ARCHIVE_FATAL);
+			if (zip->stream.lzma.context.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.lzma.context.next_out = zip->buf;
+				zip->stream.lzma.context.avail_out = (unsigned int)zip->len_buf;
+			}
+		} while (zip->stream.lzma.context.avail_in != 0);
+		break;
+#endif
+	case COMPRESSION_UNSPECIFIED:
+	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 defined(HAVE_BZLIB_H) || (defined(HAVE_ZSTD_H) && HAVE_ZSTD_compressStream) || HAVE_LZMA_H
+	char finishing;
+#endif
+
+	switch (zip->entry_compression) {
+#ifdef HAVE_ZLIB_H
+	case COMPRESSION_DEFLATE:
+		for (;;) {
+			size_t remainder;
+
+			ret = deflate(&zip->stream.deflate, Z_FINISH);
+			if (ret == Z_STREAM_ERROR)
+				return (ARCHIVE_FATAL);
+			remainder = zip->len_buf - zip->stream.deflate.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)
+			{
+				deflateEnd(&zip->stream.deflate);
+				return (ret);
+			}
+			zip->entry_compressed_written += remainder;
+			zip->written_bytes += remainder;
+			zip->stream.deflate.next_out = zip->buf;
+			if (zip->stream.deflate.avail_out != 0)
+				break;
+			zip->stream.deflate.avail_out = (uInt)zip->len_buf;
+		}
+		deflateEnd(&zip->stream.deflate);
+		break;
+#endif
+#ifdef HAVE_BZLIB_H
+	case COMPRESSION_BZIP2:
+		finishing = 1;
+		do {
+			size_t remainder;
+
+			ret = BZ2_bzCompress(&zip->stream.bzip2, BZ_FINISH);
+			if (ret == BZ_STREAM_END)
+				finishing = 0;
+			else if (ret != BZ_RUN_OK && ret != BZ_FINISH_OK)
+				return (ARCHIVE_FATAL);
+			remainder = zip->len_buf - zip->stream.bzip2.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)
+			{
+				BZ2_bzCompressEnd(&zip->stream.bzip2);
+				return (ret);
+			}
+			zip->entry_compressed_written += remainder;
+			zip->written_bytes += remainder;
+			zip->stream.bzip2.next_out = (char*)zip->buf;
+			if (zip->stream.bzip2.avail_out != 0)
+				finishing = 0;
+			zip->stream.bzip2.avail_out = (unsigned int)zip->len_buf;
+		} while (finishing);
+		BZ2_bzCompressEnd(&zip->stream.bzip2);
+		break;
+#endif
+#if defined(HAVE_ZSTD_H) && HAVE_ZSTD_compressStream
+	case COMPRESSION_ZSTD:
+		finishing = 1;
+		do {
+			size_t remainder;
+
+			size_t zret = ZSTD_endStream(zip->stream.zstd.context, &zip->stream.zstd.out);
+			if (zret == 0)
+				finishing = 0;
+			else if (ZSTD_isError(zret))
+				return (ARCHIVE_FATAL);
+			remainder = zip->len_buf - (zip->stream.zstd.out.size - zip->stream.zstd.out.pos);
+			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)
+			{
+				ZSTD_freeCStream(zip->stream.zstd.context);
+				return (ret);
+			}
+			zip->entry_compressed_written += remainder;
+			zip->written_bytes += remainder;
+			if (zip->stream.zstd.out.pos != zip->stream.zstd.out.size)
+				finishing = 0;
+			zip->stream.zstd.out.dst = zip->buf;
+			zip->stream.zstd.out.size = zip->len_buf;
+			zip->stream.zstd.out.pos = 0;
+		} while (finishing);
+		ZSTD_freeCStream(zip->stream.zstd.context);
+		break;
+#endif
+#ifdef HAVE_LZMA_H
+	/* XZ and LZMA share clean-up code */
+	case COMPRESSION_LZMA:
+	case COMPRESSION_XZ:
+		finishing = 1;
+		do {
+			size_t remainder;
+
+			ret = lzma_code(&zip->stream.lzma.context, LZMA_FINISH);
+			if (ret == LZMA_STREAM_END)
+				finishing = 0;
+			else if (ret == LZMA_MEM_ERROR)
+				return (ARCHIVE_FATAL);
+			remainder = zip->len_buf - zip->stream.lzma.context.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)
+			{
+				lzma_end(&zip->stream.lzma.context);
+				return (ret);
+			}
+			zip->entry_compressed_written += remainder;
+			zip->written_bytes += remainder;
+			zip->stream.lzma.context.next_out = zip->buf;
+			if (zip->stream.lzma.context.avail_out != 0)
+				finishing = 0;
+			zip->stream.lzma.context.avail_out = (unsigned int)zip->len_buf;
+		} while (finishing);
+		lzma_end(&zip->stream.lzma.context);
+		break;
+#endif
+	default:
+		break;
+	}
+	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_compressed_written > ZIP_4GB_MAX
+		    || zip->entry_uncompressed_written > ZIP_4GB_MAX
+		    || zip->flags & ZIP_FLAG_FORCE_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);
+	}
+
+	/* UT timestamp: Info-Zip specifies that _only_ the mtime should
+	 * be recorded here; ctime and atime are also included in the
+	 * local file descriptor. */
+	if (archive_entry_mtime_is_set(zip->entry)) {
+		unsigned char ut[9];
+		unsigned char *u = ut, *ud;
+		memcpy(u, "UT\005\000\001", 5);
+		u += 5;
+		archive_le32enc(u, (uint32_t)archive_entry_mtime(zip->entry));
+		u += 4;
+		ud = cd_alloc(zip, u - ut);
+		if (ud == NULL) {
+			archive_set_error(&a->archive, ENOMEM,
+					  "Can't allocate zip data");
+			return (ARCHIVE_FATAL);
+		}
+		memcpy(ud, ut, u - ut);
+	}
+
+	/* Fill in size information in the central directory entry. */
+	/* 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);
+	/* Truncate to 32 bits; we'll fix up below. */
+	archive_le32enc(zip->file_header + 20, (uint32_t)zip->entry_compressed_written);
+	archive_le32enc(zip->file_header + 24, (uint32_t)zip->entry_uncompressed_written);
+	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)zip->entry_offset);
+
+	/* If any of the values immediately above are too large, we'll
+	 * need to put the corresponding value in a Zip64 extra field
+	 * and set the central directory value to 0xffffffff as a flag. */
+	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_le32enc(zip->file_header + 24, ZIP_4GB_MAX);
+			archive_le64enc(z, zip->entry_uncompressed_written);
+			z += 8;
+		}
+		if (zip->entry_compressed_written >= ZIP_4GB_MAX) {
+			archive_le32enc(zip->file_header + 20, ZIP_4GB_MAX);
+			archive_le64enc(z, zip->entry_compressed_written);
+			z += 8;
+		}
+		if (zip->entry_offset >= ZIP_4GB_MAX) {
+			archive_le32enc(zip->file_header + 42, 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);
+}
+
+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);
+	}
+	ret = archive_pbkdf2_sha1(passphrase, strlen(passphrase),
+	    salt, salt_len, 1000, derived_key, key_len * 2 + 2);
+	if (ret != 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    ret == CRYPTOR_STUB_FUNCTION ? "Encryption is unsupported due to "
+			"lack of crypto library" : "Failed to process passphrase");
+		return (ARCHIVE_FAILED);
+	}
+
+	ret = archive_encrypto_aes_ctr_init(&zip->cctx, derived_key, key_len);
+	if (ret != 0) {
+		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+		    "Failed to initialize AES CTR mode");
+		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,129 @@
+/*-
+ * 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"
+
+#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"
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "archive_write_private.h"
+
+static int
+set_passphrase(struct archive_write *a, const char *p)
+{
+	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(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");
+
+	return (set_passphrase(a, p));
+}
+
+
+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);
+		set_passphrase(a, p);
+		a->passphrase_callback = NULL;
+		a->passphrase_client_data = NULL;
+	}
+	return (a->passphrase);
+}
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/config.h.in b/c/config.h.in
new file mode 100644
--- /dev/null
+++ b/c/config.h.in
@@ -0,0 +1,884 @@
+/* c/config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Version number of bsdcat */
+#undef BSDCAT_VERSION_STRING
+
+/* Version number of bsdcpio */
+#undef BSDCPIO_VERSION_STRING
+
+/* Version number of bsdtar */
+#undef BSDTAR_VERSION_STRING
+
+/* Define to 1 if you have the <acl/libacl.h> header file. */
+#undef HAVE_ACL_LIBACL_H
+
+/* 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 `chflags' function. */
+#undef HAVE_CHFLAGS
+
+/* Define to 1 if you have the `chown' function. */
+#undef HAVE_CHOWN
+
+/* Define to 1 if you have the `chroot' function. */
+#undef HAVE_CHROOT
+
+/* cmtime_s function */
+#undef HAVE_CMTIME_S
+
+/* 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. */
+#undef HAVE_CTIME_R
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#undef HAVE_CTYPE_H
+
+/* 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 `INT32_MAX', and to 0 if you
+   don't. */
+#undef HAVE_DECL_INT32_MAX
+
+/* Define to 1 if you have the declaration of `INT32_MIN', and to 0 if you
+   don't. */
+#undef HAVE_DECL_INT32_MIN
+
+/* Define to 1 if you have the declaration of `INT64_MAX', and to 0 if you
+   don't. */
+#undef HAVE_DECL_INT64_MAX
+
+/* Define to 1 if you have the declaration of `INT64_MIN', and to 0 if you
+   don't. */
+#undef HAVE_DECL_INT64_MIN
+
+/* Define to 1 if you have the declaration of `INTMAX_MAX', and to 0 if you
+   don't. */
+#undef HAVE_DECL_INTMAX_MAX
+
+/* Define to 1 if you have the declaration of `INTMAX_MIN', and to 0 if you
+   don't. */
+#undef HAVE_DECL_INTMAX_MIN
+
+/* Define to 1 if you have the declaration of `SIZE_MAX', and to 0 if you
+   don't. */
+#undef HAVE_DECL_SIZE_MAX
+
+/* Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you
+   don't. */
+#undef HAVE_DECL_SSIZE_MAX
+
+/* Define to 1 if you have the declaration of `strerror_r', and to 0 if you
+   don't. */
+#undef HAVE_DECL_STRERROR_R
+
+/* Define to 1 if you have the declaration of `UINT32_MAX', and to 0 if you
+   don't. */
+#undef HAVE_DECL_UINT32_MAX
+
+/* Define to 1 if you have the declaration of `UINT64_MAX', and to 0 if you
+   don't. */
+#undef HAVE_DECL_UINT64_MAX
+
+/* Define to 1 if you have the declaration of `UINTMAX_MAX', and to 0 if you
+   don't. */
+#undef HAVE_DECL_UINTMAX_MAX
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+   */
+#undef HAVE_DIRENT_H
+
+/* Define to 1 if you have a dirfd function or macro */
+#undef HAVE_DIRFD
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* 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 */
+#undef HAVE_EILSEQ
+
+/* Define to 1 if you have the <errno.h> header file. */
+#undef HAVE_ERRNO_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 `fchdir' function. */
+#undef HAVE_FCHDIR
+
+/* Define to 1 if you have the `fchflags' function. */
+#undef HAVE_FCHFLAGS
+
+/* Define to 1 if you have the `fchmod' function. */
+#undef HAVE_FCHMOD
+
+/* Define to 1 if you have the `fchown' function. */
+#undef HAVE_FCHOWN
+
+/* Define to 1 if you have the `fcntl' function. */
+#undef HAVE_FCNTL
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the `fdopendir' function. */
+#undef HAVE_FDOPENDIR
+
+/* Define to 1 if you have the `fnmatch' function. */
+#undef HAVE_FNMATCH
+
+/* Define to 1 if you have the <fnmatch.h> header file. */
+#undef HAVE_FNMATCH_H
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#undef HAVE_FSEEKO
+
+/* Define to 1 if you have the `fstat' function. */
+#undef HAVE_FSTAT
+
+/* Define to 1 if you have the `fstatat' function. */
+#undef HAVE_FSTATAT
+
+/* Define to 1 if you have the `fstatfs' function. */
+#undef HAVE_FSTATFS
+
+/* Define to 1 if you have the `fstatvfs' function. */
+#undef HAVE_FSTATVFS
+
+/* Define to 1 if you have the `ftruncate' function. */
+#undef HAVE_FTRUNCATE
+
+/* Define to 1 if you have the `futimens' function. */
+#undef HAVE_FUTIMENS
+
+/* Define to 1 if you have the `futimes' function. */
+#undef HAVE_FUTIMES
+
+/* Define to 1 if you have the `futimesat' function. */
+#undef HAVE_FUTIMESAT
+
+/* Define to 1 if you have the `geteuid' function. */
+#undef HAVE_GETEUID
+
+/* Define to 1 if you have the `getgrgid_r' function. */
+#undef HAVE_GETGRGID_R
+
+/* Define to 1 if you have the `getgrnam_r' function. */
+#undef HAVE_GETGRNAM_R
+
+/* Define to 1 if you have the `getline' function. */
+#undef HAVE_GETLINE
+
+/* Define to 1 if you have the `getpid' function. */
+#undef HAVE_GETPID
+
+/* Define to 1 if you have the `getpwnam_r' function. */
+#undef HAVE_GETPWNAM_R
+
+/* Define to 1 if you have the `getpwuid_r' function. */
+#undef HAVE_GETPWUID_R
+
+/* Define to 1 if you have the `getvfsbyname' function. */
+#undef HAVE_GETVFSBYNAME
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#undef HAVE_GMTIME_R
+
+/* gmtime_s function */
+#undef HAVE_GMTIME_S
+
+/* Define to 1 if you have the <grp.h> header file. */
+#undef HAVE_GRP_H
+
+/* Define to 1 if the system has the type `intmax_t'. */
+#undef HAVE_INTMAX_T
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* 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. */
+#undef HAVE_LANGINFO_H
+
+/* 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. */
+#undef HAVE_LCHOWN
+
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
+/* Define to 1 if you have the `link' function. */
+#undef HAVE_LINK
+
+/* Define to 1 if you have the `linkat' function. */
+#undef HAVE_LINKAT
+
+/* 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 <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define to 1 if you have the `localtime_r' function. */
+#undef HAVE_LOCALTIME_R
+
+/* localtime_s function */
+#undef HAVE_LOCALTIME_S
+
+/* Define to 1 if the system has the type `long long int'. */
+#undef HAVE_LONG_LONG_INT
+
+/* Define to 1 if you have the `lstat' function. */
+#undef HAVE_LSTAT
+
+/* 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. */
+#undef HAVE_LUTIMES
+
+/* Define to 1 if you have the `mbrtowc' function. */
+#undef HAVE_MBRTOWC
+
+/* Define to 1 if you have the <membership.h> header file. */
+#undef HAVE_MEMBERSHIP_H
+
+/* Define to 1 if you have the `memmove' function. */
+#undef HAVE_MEMMOVE
+
+/* Define to 1 if you have the `memset' function. */
+#undef HAVE_MEMSET
+
+/* Define to 1 if you have the <minix/config.h> header file. */
+#undef HAVE_MINIX_CONFIG_H
+
+/* Define to 1 if you have the `mkdir' function. */
+#undef HAVE_MKDIR
+
+/* Define to 1 if you have the `mkfifo' function. */
+#undef HAVE_MKFIFO
+
+/* Define to 1 if you have the `mknod' function. */
+#undef HAVE_MKNOD
+
+/* Define to 1 if you have the `mkstemp' function. */
+#undef HAVE_MKSTEMP
+
+/* 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 `nl_langinfo' function. */
+#undef HAVE_NL_LANGINFO
+
+/* Define to 1 if you have the `openat' function. */
+#undef HAVE_OPENAT
+
+/* Define to 1 if you have the <paths.h> header file. */
+#undef HAVE_PATHS_H
+
+/* Define to 1 if you have the `pipe' function. */
+#undef HAVE_PIPE
+
+/* Define to 1 if you have the `poll' function. */
+#undef HAVE_POLL
+
+/* Define to 1 if you have the <poll.h> header file. */
+#undef HAVE_POLL_H
+
+/* Define to 1 if you have the `posix_spawnp' function. */
+#undef HAVE_POSIX_SPAWNP
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#undef HAVE_PTHREAD_H
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#undef HAVE_PWD_H
+
+/* Define to 1 if you have a POSIX compatible readdir_r */
+#undef HAVE_READDIR_R
+
+/* Define to 1 if you have the `readlink' function. */
+#undef HAVE_READLINK
+
+/* Define to 1 if you have the `readlinkat' function. */
+#undef HAVE_READLINKAT
+
+/* 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 `select' function. */
+#undef HAVE_SELECT
+
+/* Define to 1 if you have the `setenv' function. */
+#undef HAVE_SETENV
+
+/* Define to 1 if you have the `setlocale' function. */
+#undef HAVE_SETLOCALE
+
+/* Define to 1 if you have the `sigaction' function. */
+#undef HAVE_SIGACTION
+
+/* Define to 1 if you have the <signal.h> header file. */
+#undef HAVE_SIGNAL_H
+
+/* Define to 1 if you have the <spawn.h> header file. */
+#undef HAVE_SPAWN_H
+
+/* Define to 1 if you have the `statfs' function. */
+#undef HAVE_STATFS
+
+/* Define to 1 if you have the `statvfs' function. */
+#undef HAVE_STATVFS
+
+/* 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. */
+#undef HAVE_STDARG_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strchr' function. */
+#undef HAVE_STRCHR
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strerror' function. */
+#undef HAVE_STRERROR
+
+/* Define if you have `strerror_r'. */
+#undef HAVE_STRERROR_R
+
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strncpy_s' function. */
+#undef HAVE_STRNCPY_S
+
+/* Define to 1 if you have the `strnlen' function. */
+#undef HAVE_STRNLEN
+
+/* Define to 1 if you have the `strrchr' function. */
+#undef HAVE_STRRCHR
+
+/* Define to 1 if the system has the type `struct statfs'. */
+#undef HAVE_STRUCT_STATFS
+
+/* Define to 1 if `f_iosize' is a member of `struct statfs'. */
+#undef HAVE_STRUCT_STATFS_F_IOSIZE
+
+/* 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'. */
+#undef HAVE_STRUCT_STAT_ST_BLKSIZE
+
+/* 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'. */
+#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'. */
+#undef HAVE_STRUCT_TM_TM_GMTOFF
+
+/* 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. */
+#undef HAVE_SYMLINK
+
+/* Define to 1 if you have the <sys/acl.h> header file. */
+#undef HAVE_SYS_ACL_H
+
+/* Define to 1 if you have the <sys/cdefs.h> header file. */
+#undef HAVE_SYS_CDEFS_H
+
+/* 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. */
+#undef HAVE_SYS_IOCTL_H
+
+/* 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. */
+#undef HAVE_SYS_MOUNT_H
+
+/* 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. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#undef HAVE_SYS_POLL_H
+
+/* Define to 1 if you have the <sys/queue.h> header file. */
+#undef HAVE_SYS_QUEUE_H
+
+/* 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. */
+#undef HAVE_SYS_SELECT_H
+
+/* 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. */
+#undef HAVE_SYS_STATVFS_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* 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. */
+#undef HAVE_SYS_TIME_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* 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. */
+#undef HAVE_SYS_UTSNAME_H
+
+/* 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. */
+#undef HAVE_SYS_WAIT_H
+
+/* 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. */
+#undef HAVE_TIMEGM
+
+/* Define to 1 if you have the <time.h> header file. */
+#undef HAVE_TIME_H
+
+/* Define to 1 if you have the `tzset' function. */
+#undef HAVE_TZSET
+
+/* Define to 1 if the system has the type `uintmax_t'. */
+#undef HAVE_UINTMAX_T
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `unlinkat' function. */
+#undef HAVE_UNLINKAT
+
+/* Define to 1 if you have the `unsetenv' function. */
+#undef HAVE_UNSETENV
+
+/* Define to 1 if the system has the type `unsigned long long'. */
+#undef HAVE_UNSIGNED_LONG_LONG
+
+/* Define to 1 if the system has the type `unsigned long long int'. */
+#undef HAVE_UNSIGNED_LONG_LONG_INT
+
+/* Define to 1 if you have the `utime' function. */
+#undef HAVE_UTIME
+
+/* Define to 1 if you have the `utimensat' function. */
+#undef HAVE_UTIMENSAT
+
+/* Define to 1 if you have the `utimes' function. */
+#undef HAVE_UTIMES
+
+/* Define to 1 if you have the <utime.h> header file. */
+#undef HAVE_UTIME_H
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* Define to 1 if you have the `vprintf' function. */
+#undef HAVE_VPRINTF
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#undef HAVE_WCHAR_H
+
+/* Define to 1 if the system has the type `wchar_t'. */
+#undef HAVE_WCHAR_T
+
+/* Define to 1 if you have the `wcrtomb' function. */
+#undef HAVE_WCRTOMB
+
+/* Define to 1 if you have the `wcscmp' function. */
+#undef HAVE_WCSCMP
+
+/* Define to 1 if you have the `wcscpy' function. */
+#undef HAVE_WCSCPY
+
+/* Define to 1 if you have the `wcslen' function. */
+#undef HAVE_WCSLEN
+
+/* Define to 1 if you have the `wctomb' function. */
+#undef HAVE_WCTOMB
+
+/* Define to 1 if you have the <wctype.h> header file. */
+#undef HAVE_WCTYPE_H
+
+/* 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. */
+#undef HAVE_WMEMCMP
+
+/* Define to 1 if you have the `wmemcpy' function. */
+#undef HAVE_WMEMCPY
+
+/* Define to 1 if you have the `wmemmove' function. */
+#undef HAVE_WMEMMOVE
+
+/* 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 `_fseeki64' function. */
+#undef HAVE__FSEEKI64
+
+/* Define to 1 if you have the `_get_timezone' function. */
+#undef HAVE__GET_TIMEZONE
+
+/* _mkgmtime function */
+#undef HAVE__MKGMTIME
+
+/* Version number of libarchive as a single integer */
+#undef LIBARCHIVE_VERSION_NUMBER
+
+/* Version number of libarchive */
+#undef LIBARCHIVE_VERSION_STRING
+
+/* 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. */
+#undef LT_OBJDIR
+
+/* 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
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* The size of `wchar_t', as computed by sizeof. */
+#undef SIZEOF_WCHAR_T
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+   required in a freestanding environment). This macro is provided for
+   backward compatibility; new code need not use it. */
+#undef STDC_HEADERS
+
+/* Define to 1 if strerror_r returns char *. */
+#undef STRERROR_R_CHAR_P
+
+/* Enable extensions on AIX 3, Interix.  */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable general extensions on macOS.  */
+#ifndef _DARWIN_C_SOURCE
+# undef _DARWIN_C_SOURCE
+#endif
+/* Enable general extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+/* Enable GNU extensions on systems that have them.  */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable X/Open compliant socket functions that do not require linking
+   with -lxnet on HP-UX 11.11.  */
+#ifndef _HPUX_ALT_XOPEN_SOCKET_API
+# undef _HPUX_ALT_XOPEN_SOCKET_API
+#endif
+/* Identify the host operating system as Minix.
+   This macro does not affect the system headers' behavior.
+   A future release of Autoconf may stop defining this macro.  */
+#ifndef _MINIX
+# undef _MINIX
+#endif
+/* Enable general extensions on NetBSD.
+   Enable NetBSD compatibility extensions on Minix.  */
+#ifndef _NETBSD_SOURCE
+# undef _NETBSD_SOURCE
+#endif
+/* Enable OpenBSD compatibility extensions on NetBSD.
+   Oddly enough, this does nothing on OpenBSD.  */
+#ifndef _OPENBSD_SOURCE
+# undef _OPENBSD_SOURCE
+#endif
+/* Define to 1 if needed for POSIX-compatible behavior.  */
+#ifndef _POSIX_SOURCE
+# undef _POSIX_SOURCE
+#endif
+/* Define to 2 if needed for POSIX-compatible behavior.  */
+#ifndef _POSIX_1_SOURCE
+# undef _POSIX_1_SOURCE
+#endif
+/* Enable POSIX-compatible threading on Solaris.  */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-5:2014.  */
+#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-1:2014.  */
+#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
+# undef __STDC_WANT_IEC_60559_BFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-2:2015.  */
+#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
+# undef __STDC_WANT_IEC_60559_DFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-4:2015.  */
+#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
+# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-3:2015.  */
+#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
+# undef __STDC_WANT_IEC_60559_TYPES_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TR 24731-2:2010.  */
+#ifndef __STDC_WANT_LIB_EXT2__
+# undef __STDC_WANT_LIB_EXT2__
+#endif
+/* Enable extensions specified by ISO/IEC 24747:2009.  */
+#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
+# undef __STDC_WANT_MATH_SPEC_FUNCS__
+#endif
+/* Enable extensions on HP NonStop.  */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable X/Open extensions.  Define to 500 only if necessary
+   to make mbstate_t available.  */
+#ifndef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
+#endif
+
+
+/* Define to '0x0502' for Windows Server 2003 APIs. */
+#undef WINVER
+
+/* 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 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
+
+/* Internal macro for sanity checks */
+#undef __LIBARCHIVE_CONFIG_H_INCLUDED
+
+/* 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,275 @@
+/*-
+ * 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.
+ */
+#define __LIBARCHIVE_CONFIG_H_INCLUDED 1
+
+#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_FNMATCH 1
+#define HAVE_FNMATCH_H 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_GETLINE 1
+#define HAVE_GETOPT_OPTRESET 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_LINKAT 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_STRNLEN 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_TCGETATTR 1
+#define HAVE_TCSETATTR 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 HAVE_SYS_TIME_H 1
+
+#if __FreeBSD_version >= 800505
+#define HAVE_LIBLZMA 1
+#define HAVE_LZMA_H 1
+#if __FreeBSD_version >= 1002504
+#define HAVE_LZMA_STREAM_ENCODER_MT 1
+#endif
+#endif
+
+#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,44 @@
+/*-
+ * 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.
+ */
+
+#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,269 @@
+/*-
+ * 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))
+
+#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 = -1;
+	int stdin_pipe[2], stdout_pipe[2], tmp;
+
+#if !defined(POSIX_SPAWN_CLOEXEC_DEFAULT) && \
+    (HAVE_FORK || HAVE_VFORK) && \
+    (HAVE_CLOSEFROM || HAVE_CLOSE_RANGE || defined(_SC_OPEN_MAX))
+#undef HAVE_POSIX_SPAWNP
+#endif
+
+#if HAVE_POSIX_SPAWNP
+	posix_spawnattr_t attr;
+	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_spawnattr_init(&attr);
+	if (r != 0) {
+		errno = r;
+		goto stdout_opened;
+	}
+	r = posix_spawn_file_actions_init(&actions);
+	if (r != 0) {
+		errno = r;
+		goto attr_inited;
+	}
+#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
+	r = posix_spawnattr_setflags(&attr, POSIX_SPAWN_CLOEXEC_DEFAULT);
+	if (r != 0)
+		goto actions_inited;
+#endif
+	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, &attr,
+		cmdline->argv, NULL);
+	if (r != 0)
+		goto actions_inited;
+	posix_spawn_file_actions_destroy(&actions);
+	posix_spawnattr_destroy(&attr);
+
+#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]);
+
+#if HAVE_CLOSEFROM
+		closefrom(3);
+#elif HAVE_CLOSE_RANGE
+		close_range(3, ~0U, 0);
+#elif defined(_SC_OPEN_MAX)
+		for (int i = sysconf(_SC_OPEN_MAX); i > 3;)
+			close(--i);
+#endif
+
+		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);
+attr_inited:
+	posix_spawnattr_destroy(&attr);
+#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,245 @@
+/*-
+ * 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"
+
+#if !defined(WINAPI_FAMILY_PARTITION) || WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+/* There are some editions of Windows ("nano server," for example) that
+ * do not host user32.dll. If we want to keep running on those editions,
+ * we need to delay-load WaitForInputIdle. */
+static void *
+la_GetFunctionUser32(const char *name)
+{
+	static HINSTANCE lib;
+	static int set;
+	if (!set) {
+		set = 1;
+		lib = LoadLibrary(TEXT("user32.dll"));
+	}
+	if (lib == NULL) {
+		return NULL;
+	}
+	return (void *)GetProcAddress(lib, name);
+}
+
+static int
+la_WaitForInputIdle(HANDLE hProcess, DWORD dwMilliseconds)
+{
+	static DWORD (WINAPI *f)(HANDLE, DWORD);
+	static int set;
+
+	if (!set) {
+		set = 1;
+		f = la_GetFunctionUser32("WaitForInputIdle");
+	}
+
+	if (!f) {
+		/* An inability to wait for input idle is
+		 * not _good_, but it is not catastrophic. */
+		return WAIT_FAILED;
+	}
+	return (*f)(hProcess, dwMilliseconds);
+}
+
+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;
+	la_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;
+}
+#else /* !WINAPI_PARTITION_DESKTOP */
+int
+__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout, HANDLE *out_child)
+{
+	(void)cmd; (void)child_stdin; (void) child_stdout; (void) out_child;
+	return ARCHIVE_FAILED;
+}
+#endif /* !WINAPI_PARTITION_DESKTOP */
+
+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,529 @@
+/*
+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__))
+#else
+#  if defined(__clang__)
+__attribute__((no_sanitize("undefined")))
+#  endif
+#endif
+#if defined(_MSC_VER)
+static __inline U32 A32(const void * x)
+#else
+static inline U32 A32(const void* x)
+#endif
+{
+    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/configure b/configure
new file mode 100644
--- /dev/null
+++ b/configure
@@ -0,0 +1,17995 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.71 for Haskell libarchive-clib package 3.7.2.
+#
+# Report bugs to <vamchale@gmail.com>.
+#
+#
+# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
+# Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else $as_nop
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
+as_nl='
+'
+export as_nl
+IFS=" ""	$as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh).  This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2)            ; then :; else exec 2>/dev/null; fi
+
+# The user is always right.
+if ${PATH_SEPARATOR+false} :; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    test -r "$as_dir$0" && as_myself=$as_dir$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="as_nop=:
+if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else \$as_nop
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" )
+then :
+
+else \$as_nop
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+blah=\$(echo \$(echo blah))
+test x\"\$blah\" = xblah || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
+
+  test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || (
+    ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+    ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+    ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO
+    PATH=/empty FPATH=/empty; export PATH FPATH
+    test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\
+      || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1
+test \$(( 1 + 1 )) = 2 || exit 1"
+  if (eval "$as_required") 2>/dev/null
+then :
+  as_have_required=yes
+else $as_nop
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null
+then :
+
+else $as_nop
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+  as_found=:
+  case $as_dir in #(
+	 /*)
+	   for as_base in sh bash ksh sh5; do
+	     # Try only shells that exist, to save several forks.
+	     as_shell=$as_dir$as_base
+	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+		    as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+		   if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null
+then :
+  break 2
+fi
+fi
+	   done;;
+       esac
+  as_found=false
+done
+IFS=$as_save_IFS
+if $as_found
+then :
+
+else $as_nop
+  if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+	      as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi
+fi
+
+
+      if test "x$CONFIG_SHELL" != x
+then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno
+then :
+  printf "%s\n" "$0: This script requires a shell more modern than all"
+  printf "%s\n" "$0: the shells that I found on your system."
+  if test ${ZSH_VERSION+y} ; then
+    printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and vamchale@gmail.com
+$0: about your system, including any error possibly output
+$0: before this message. Then install a modern shell, or
+$0: manually run the script under such a shell if you do
+$0: have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+# as_fn_nop
+# ---------
+# Do nothing but, unlike ":", preserve the value of $?.
+as_fn_nop ()
+{
+  return $?
+}
+as_nop=as_fn_nop
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else $as_nop
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else $as_nop
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+# as_fn_nop
+# ---------
+# Do nothing but, unlike ":", preserve the value of $?.
+as_fn_nop ()
+{
+  return $?
+}
+as_nop=as_fn_nop
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  printf "%s\n" "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n.  New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='Haskell libarchive-clib package'
+PACKAGE_TARNAME='libarchive-clib'
+PACKAGE_VERSION='3.7.2'
+PACKAGE_STRING='Haskell libarchive-clib package 3.7.2'
+PACKAGE_BUGREPORT='vamchale@gmail.com'
+PACKAGE_URL=''
+
+ac_unique_file="c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stddef.h>
+#ifdef HAVE_STDIO_H
+# include <stdio.h>
+#endif
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+#ifdef HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_header_c_list=
+ac_func_c_list=
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+INC_BLAKE2_FALSE
+INC_BLAKE2_TRUE
+MKDIR_P
+LT_SYS_LIBRARY_PATH
+OTOOL64
+OTOOL
+LIPO
+NMEDIT
+DSYMUTIL
+MANIFEST_TOOL
+AWK
+RANLIB
+STRIP
+ac_ct_AR
+AR
+FILECMD
+LN_S
+NM
+ac_ct_DUMPBIN
+DUMPBIN
+LD
+FGREP
+EGREP
+GREP
+SED
+LIBTOOL
+OBJDUMP
+DLLTOOL
+AS
+CPP
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+INC_CYGWIN_FILES_FALSE
+INC_CYGWIN_FILES_TRUE
+INC_WINDOWS_FILES_FALSE
+INC_WINDOWS_FILES_TRUE
+host_os
+host_vendor
+host_cpu
+host
+build_os
+build_vendor
+build_cpu
+build
+LIBARCHIVE_VERSION_NUMBER
+LIBARCHIVE_VERSION_STRING
+BSDCAT_VERSION_STRING
+BSDTAR_VERSION_STRING
+BSDCPIO_VERSION_STRING
+ARCHIVE_LIBTOOL_VERSION
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+runstatedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+enable_shared
+enable_static
+with_pic
+enable_fast_install
+with_aix_soname
+with_gnu_ld
+with_sysroot
+enable_libtool_lock
+enable_largefile
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP
+LT_SYS_LIBRARY_PATH'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+runstatedir='${localstatedir}/run'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -runstatedir | --runstatedir | --runstatedi | --runstated \
+  | --runstate | --runstat | --runsta | --runst | --runs \
+  | --run | --ru | --r)
+    ac_prev=runstatedir ;;
+  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
+  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
+  | --run=* | --ru=* | --r=*)
+    runstatedir=$ac_optarg ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: \`$ac_useropt'"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
+		datadir sysconfdir sharedstatedir localstatedir includedir \
+		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+		libdir localedir mandir runstatedir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_myself" : 'X\(//\)[^/]' \| \
+	 X"$as_myself" : 'X\(//\)$' \| \
+	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+	pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures Haskell libarchive-clib package 3.7.2 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/libarchive-clib]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of Haskell libarchive-clib package 3.7.2:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-option-checking  ignore unrecognized --enable/--with options
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-shared[=PKGS]  build shared libraries [default=yes]
+  --enable-static[=PKGS]  build static libraries [default=yes]
+  --enable-fast-install[=PKGS]
+                          optimize for fast installation [default=yes]
+  --disable-libtool-lock  avoid locking (might break parallel builds)
+  --disable-largefile     omit support for large files
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-pic[=PKGS]       try to use only PIC/non-PIC objects [default=use
+                          both]
+  --with-aix-soname=aix|svr4|both
+                          shared library versioning (aka "SONAME") variant to
+                          provide on AIX, [default=aix].
+  --with-gnu-ld           assume the C compiler uses GNU ld [default=no]
+  --with-sysroot[=DIR]    Search for dependent libraries within DIR (or the
+                          compiler's sysroot if not specified).
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+  LT_SYS_LIBRARY_PATH
+              User-defined run-time library search path.
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <vamchale@gmail.com>.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for configure.gnu first; this name is used for a wrapper for
+    # Metaconfig's "Configure" on case-insensitive file systems.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+Haskell libarchive-clib package configure 3.7.2
+generated by GNU Autoconf 2.71
+
+Copyright (C) 2021 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest.beam
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext
+then :
+  ac_retval=0
+else $as_nop
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+	 test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       }
+then :
+  ac_retval=0
+else $as_nop
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
+# -------------------------------------------------------
+# Tests whether HEADER exists and can be compiled using the include files in
+# INCLUDES, setting the cache variable VAR accordingly.
+ac_fn_c_check_header_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+#include <$2>
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  eval "$3=yes"
+else $as_nop
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_header_compile
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 test -x conftest$ac_exeext
+       }
+then :
+  ac_retval=0
+else $as_nop
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+   which can conflict with char $2 (); below.  */
+
+#include <limits.h>
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main (void)
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  eval "$3=yes"
+else $as_nop
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+if (sizeof ($2))
+	 return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+if (sizeof (($2)))
+	    return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
+
+# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
+# ----------------------------------------------------
+# Tries to find if the field MEMBER exists in type AGGR, after including
+# INCLUDES, setting cache variable VAR accordingly.
+ac_fn_c_check_member ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
+printf %s "checking for $2.$3... " >&6; }
+if eval test \${$4+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$5
+int
+main (void)
+{
+static $2 ac_aggr;
+if (ac_aggr.$3)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  eval "$4=yes"
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$5
+int
+main (void)
+{
+static $2 ac_aggr;
+if (sizeof ac_aggr.$3)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  eval "$4=yes"
+else $as_nop
+  eval "$4=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$4
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_member
+
+# ac_fn_c_try_run LINENO
+# ----------------------
+# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that
+# executables *can* be run.
+ac_fn_c_try_run ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+then :
+  ac_retval=0
+else $as_nop
+  printf "%s\n" "$as_me: program exited with status $ac_status" >&5
+       printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=$ac_status
+fi
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_run
+
+# ac_fn_c_find_intX_t LINENO BITS VAR
+# -----------------------------------
+# Finds a signed integer type with width BITS, setting cache variable VAR
+# accordingly.
+ac_fn_c_find_intX_t ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5
+printf %s "checking for int$2_t... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  eval "$3=no"
+     # Order is important - never check a type that is potentially smaller
+     # than half of the expected target width.
+     for ac_type in int$2_t 'int' 'long int' \
+	 'long long int' 'short int' 'signed char'; do
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+	     enum { N = $2 / 2 - 1 };
+int
+main (void)
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+	        enum { N = $2 / 2 - 1 };
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1)
+		 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+  case $ac_type in #(
+  int$2_t) :
+    eval "$3=yes" ;; #(
+  *) :
+    eval "$3=\$ac_type" ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+       if eval test \"x\$"$3"\" = x"no"
+then :
+
+else $as_nop
+  break
+fi
+     done
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_find_intX_t
+
+# ac_fn_c_find_uintX_t LINENO BITS VAR
+# ------------------------------------
+# Finds an unsigned integer type with width BITS, setting cache variable VAR
+# accordingly.
+ac_fn_c_find_uintX_t ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5
+printf %s "checking for uint$2_t... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  eval "$3=no"
+     # Order is important - never check a type that is potentially smaller
+     # than half of the expected target width.
+     for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \
+	 'unsigned long long int' 'unsigned short int' 'unsigned char'; do
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main (void)
+{
+static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  case $ac_type in #(
+  uint$2_t) :
+    eval "$3=yes" ;; #(
+  *) :
+    eval "$3=\$ac_type" ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+       if eval test \"x\$"$3"\" = x"no"
+then :
+
+else $as_nop
+  break
+fi
+     done
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_find_uintX_t
+
+# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR
+# ------------------------------------------------------------------
+# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
+# accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR.
+ac_fn_check_decl ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  as_decl_name=`echo $2|sed 's/ *(.*//'`
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5
+printf %s "checking whether $as_decl_name is declared... " >&6; }
+if eval test \${$3+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
+  eval ac_save_FLAGS=\$$6
+  as_fn_append $6 " $5"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+#ifndef $as_decl_name
+#ifdef __cplusplus
+  (void) $as_decl_use;
+#else
+  (void) $as_decl_name;
+#endif
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  eval "$3=yes"
+else $as_nop
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  eval $6=\$ac_save_FLAGS
+
+fi
+eval ac_res=\$$3
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_check_decl
+
+# ac_fn_c_compute_int LINENO EXPR VAR INCLUDES
+# --------------------------------------------
+# Tries to find the compile-time value of EXPR in a program that includes
+# INCLUDES, setting VAR accordingly. Returns whether the value could be
+# computed
+ac_fn_c_compute_int ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if test "$cross_compiling" = yes; then
+    # Depending upon the size, compute the lo and hi bounds.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) >= 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_lo=0 ac_mid=0
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_hi=$ac_mid; break
+else $as_nop
+  as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+			if test $ac_lo -le $ac_mid; then
+			  ac_lo= ac_hi=
+			  break
+			fi
+			as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  done
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) < 0)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_hi=-1 ac_mid=-1
+  while :; do
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) >= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_lo=$ac_mid; break
+else $as_nop
+  as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+			if test $ac_mid -le $ac_hi; then
+			  ac_lo= ac_hi=
+			  break
+			fi
+			as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  done
+else $as_nop
+  ac_lo= ac_hi=
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+# Binary search between lo and hi bounds.
+while test "x$ac_lo" != "x$ac_hi"; do
+  as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main (void)
+{
+static int test_array [1 - 2 * !(($2) <= $ac_mid)];
+test_array [0] = 0;
+return test_array [0];
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_hi=$ac_mid
+else $as_nop
+  as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+done
+case $ac_lo in #((
+?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
+'') ac_retval=1 ;;
+esac
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+static long int longval (void) { return $2; }
+static unsigned long int ulongval (void) { return $2; }
+#include <stdio.h>
+#include <stdlib.h>
+int
+main (void)
+{
+
+  FILE *f = fopen ("conftest.val", "w");
+  if (! f)
+    return 1;
+  if (($2) < 0)
+    {
+      long int i = longval ();
+      if (i != ($2))
+	return 1;
+      fprintf (f, "%ld", i);
+    }
+  else
+    {
+      unsigned long int i = ulongval ();
+      if (i != ($2))
+	return 1;
+      fprintf (f, "%lu", i);
+    }
+  /* Do not output a trailing newline, as this causes \r\n confusion
+     on some platforms.  */
+  return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+  echo >>conftest.val; read $3 <conftest.val; ac_retval=0
+else $as_nop
+  ac_retval=1
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f conftest.val
+
+  fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_compute_int
+ac_configure_args_raw=
+for ac_arg
+do
+  case $ac_arg in
+  *\'*)
+    ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+  esac
+  as_fn_append ac_configure_args_raw " '$ac_arg'"
+done
+
+case $ac_configure_args_raw in
+  *$as_nl*)
+    ac_safe_unquote= ;;
+  *)
+    ac_unsafe_z='|&;<>()$`\\"*?[ ''	' # This string ends in space, tab.
+    ac_unsafe_a="$ac_unsafe_z#~"
+    ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g"
+    ac_configure_args_raw=`      printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;;
+esac
+
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by Haskell libarchive-clib package $as_me 3.7.2, which was
+generated by GNU Autoconf 2.71.  Invocation command line was
+
+  $ $0$ac_configure_args_raw
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    printf "%s\n" "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Sanitize IFS.
+  IFS=" ""	$as_nl"
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    printf "%s\n" "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+	"s/'\''/'\''\\\\'\'''\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    printf "%s\n" "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      printf "%s\n" "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      printf "%s\n" "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=\$$ac_var
+	case $ac_val in
+	*\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+	esac
+	printf "%s\n" "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      printf "%s\n" "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      printf "%s\n" "$as_me: caught signal $ac_signal"
+    printf "%s\n" "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+printf "%s\n" "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h
+
+printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+if test -n "$CONFIG_SITE"; then
+  ac_site_files="$CONFIG_SITE"
+elif test "x$prefix" != xNONE; then
+  ac_site_files="$prefix/share/config.site $prefix/etc/config.site"
+else
+  ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+fi
+
+for ac_site_file in $ac_site_files
+do
+  case $ac_site_file in #(
+  */*) :
+     ;; #(
+  *) :
+    ac_site_file=./$ac_site_file ;;
+esac
+  if test -f "$ac_site_file" && test -r "$ac_site_file"; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+printf "%s\n" "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+printf "%s\n" "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Test code for whether the C compiler supports C89 (global declarations)
+ac_c_conftest_c89_globals='
+/* Does the compiler advertise C89 conformance?
+   Do not test the value of __STDC__, because some compilers set it to 0
+   while being otherwise adequately conformant. */
+#if !defined __STDC__
+# error "Compiler does not advertise C89 conformance"
+#endif
+
+#include <stddef.h>
+#include <stdarg.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7 src/conf.sh.  */
+struct buf { int x; };
+struct buf * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not \xHH hex character constants.
+   These do not provoke an error unfortunately, instead are silently treated
+   as an "x".  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously \x00 != x always comes out true, for an
+   array size at least.  It is necessary to write \x00 == 0 to get something
+   that is true only with -std.  */
+int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) '\''x'\''
+int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int),
+               int, int);'
+
+# Test code for whether the C compiler supports C89 (body of main).
+ac_c_conftest_c89_main='
+ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]);
+'
+
+# Test code for whether the C compiler supports C99 (global declarations)
+ac_c_conftest_c99_globals='
+// Does the compiler advertise C99 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# error "Compiler does not advertise C99 conformance"
+#endif
+
+#include <stdbool.h>
+extern int puts (const char *);
+extern int printf (const char *, ...);
+extern int dprintf (int, const char *, ...);
+extern void *malloc (size_t);
+
+// Check varargs macros.  These examples are taken from C99 6.10.3.5.
+// dprintf is used instead of fprintf to avoid needing to declare
+// FILE and stderr.
+#define debug(...) dprintf (2, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+  int x = 1234;
+  int y = 5678;
+  debug ("Flag");
+  debug ("X = %d\n", x);
+  showlist (The first, second, and third items.);
+  report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+  #error "your preprocessor is broken"
+#endif
+#if BIG_OK
+#else
+  #error "your preprocessor is broken"
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+  int datasize;
+  double data[];
+};
+
+struct named_init {
+  int number;
+  const wchar_t *name;
+  double average;
+};
+
+typedef const char *ccp;
+
+static inline int
+test_restrict (ccp restrict text)
+{
+  // See if C++-style comments work.
+  // Iterate through items via the restricted pointer.
+  // Also check for declarations in for loops.
+  for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i)
+    continue;
+  return 0;
+}
+
+// Check varargs and va_copy.
+static bool
+test_varargs (const char *format, ...)
+{
+  va_list args;
+  va_start (args, format);
+  va_list args_copy;
+  va_copy (args_copy, args);
+
+  const char *str = "";
+  int number = 0;
+  float fnumber = 0;
+
+  while (*format)
+    {
+      switch (*format++)
+	{
+	case '\''s'\'': // string
+	  str = va_arg (args_copy, const char *);
+	  break;
+	case '\''d'\'': // int
+	  number = va_arg (args_copy, int);
+	  break;
+	case '\''f'\'': // float
+	  fnumber = va_arg (args_copy, double);
+	  break;
+	default:
+	  break;
+	}
+    }
+  va_end (args_copy);
+  va_end (args);
+
+  return *str && number && fnumber;
+}
+'
+
+# Test code for whether the C compiler supports C99 (body of main).
+ac_c_conftest_c99_main='
+  // Check bool.
+  _Bool success = false;
+  success |= (argc != 0);
+
+  // Check restrict.
+  if (test_restrict ("String literal") == 0)
+    success = true;
+  char *restrict newvar = "Another string";
+
+  // Check varargs.
+  success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234);
+  test_varargs_macros ();
+
+  // Check flexible array members.
+  struct incomplete_array *ia =
+    malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+  ia->datasize = 10;
+  for (int i = 0; i < ia->datasize; ++i)
+    ia->data[i] = i * 1.234;
+
+  // Check named initializers.
+  struct named_init ni = {
+    .number = 34,
+    .name = L"Test wide string",
+    .average = 543.34343,
+  };
+
+  ni.number = 58;
+
+  int dynamic_array[ni.number];
+  dynamic_array[0] = argv[0][0];
+  dynamic_array[ni.number - 1] = 543;
+
+  // work around unused variable warnings
+  ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\''
+	 || dynamic_array[ni.number - 1] != 543);
+'
+
+# Test code for whether the C compiler supports C11 (global declarations)
+ac_c_conftest_c11_globals='
+// Does the compiler advertise C11 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L
+# error "Compiler does not advertise C11 conformance"
+#endif
+
+// Check _Alignas.
+char _Alignas (double) aligned_as_double;
+char _Alignas (0) no_special_alignment;
+extern char aligned_as_int;
+char _Alignas (0) _Alignas (int) aligned_as_int;
+
+// Check _Alignof.
+enum
+{
+  int_alignment = _Alignof (int),
+  int_array_alignment = _Alignof (int[100]),
+  char_alignment = _Alignof (char)
+};
+_Static_assert (0 < -_Alignof (int), "_Alignof is signed");
+
+// Check _Noreturn.
+int _Noreturn does_not_return (void) { for (;;) continue; }
+
+// Check _Static_assert.
+struct test_static_assert
+{
+  int x;
+  _Static_assert (sizeof (int) <= sizeof (long int),
+                  "_Static_assert does not work in struct");
+  long int y;
+};
+
+// Check UTF-8 literals.
+#define u8 syntax error!
+char const utf8_literal[] = u8"happens to be ASCII" "another string";
+
+// Check duplicate typedefs.
+typedef long *long_ptr;
+typedef long int *long_ptr;
+typedef long_ptr long_ptr;
+
+// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1.
+struct anonymous
+{
+  union {
+    struct { int i; int j; };
+    struct { int k; long int l; } w;
+  };
+  int m;
+} v1;
+'
+
+# Test code for whether the C compiler supports C11 (body of main).
+ac_c_conftest_c11_main='
+  _Static_assert ((offsetof (struct anonymous, i)
+		   == offsetof (struct anonymous, w.k)),
+		  "Anonymous union alignment botch");
+  v1.i = 2;
+  v1.w.k = 5;
+  ok |= v1.i != 5;
+'
+
+# Test code for whether the C compiler supports C11 (complete).
+ac_c_conftest_c11_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+${ac_c_conftest_c11_globals}
+
+int
+main (int argc, char **argv)
+{
+  int ok = 0;
+  ${ac_c_conftest_c89_main}
+  ${ac_c_conftest_c99_main}
+  ${ac_c_conftest_c11_main}
+  return ok;
+}
+"
+
+# Test code for whether the C compiler supports C99 (complete).
+ac_c_conftest_c99_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+
+int
+main (int argc, char **argv)
+{
+  int ok = 0;
+  ${ac_c_conftest_c89_main}
+  ${ac_c_conftest_c99_main}
+  return ok;
+}
+"
+
+# Test code for whether the C compiler supports C89 (complete).
+ac_c_conftest_c89_program="${ac_c_conftest_c89_globals}
+
+int
+main (int argc, char **argv)
+{
+  int ok = 0;
+  ${ac_c_conftest_c89_main}
+  return ok;
+}
+"
+
+as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H"
+as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H"
+as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H"
+as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H"
+as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H"
+as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H"
+as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H"
+as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H"
+as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H"
+as_fn_append ac_header_c_list " wchar.h wchar_h HAVE_WCHAR_H"
+as_fn_append ac_header_c_list " minix/config.h minix_config_h HAVE_MINIX_CONFIG_H"
+as_fn_append ac_header_c_list " sys/time.h sys_time_h HAVE_SYS_TIME_H"
+as_fn_append ac_func_c_list " vprintf HAVE_VPRINTF"
+
+# Auxiliary files required by this configure script.
+ac_aux_files="install-sh ltmain.sh config.guess config.sub"
+
+# Locations in which to look for auxiliary files.
+ac_aux_dir_candidates="${srcdir}/build/autoconf"
+
+# Search for a directory containing all of the required auxiliary files,
+# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates.
+# If we don't find one directory that contains all the files we need,
+# we report the set of missing files from the *first* directory in
+# $ac_aux_dir_candidates and give up.
+ac_missing_aux_files=""
+ac_first_candidate=:
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in $ac_aux_dir_candidates
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+  as_found=:
+
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}:  trying $as_dir" >&5
+  ac_aux_dir_found=yes
+  ac_install_sh=
+  for ac_aux in $ac_aux_files
+  do
+    # As a special case, if "install-sh" is required, that requirement
+    # can be satisfied by any of "install-sh", "install.sh", or "shtool",
+    # and $ac_install_sh is set appropriately for whichever one is found.
+    if test x"$ac_aux" = x"install-sh"
+    then
+      if test -f "${as_dir}install-sh"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}install-sh found" >&5
+        ac_install_sh="${as_dir}install-sh -c"
+      elif test -f "${as_dir}install.sh"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}install.sh found" >&5
+        ac_install_sh="${as_dir}install.sh -c"
+      elif test -f "${as_dir}shtool"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}shtool found" >&5
+        ac_install_sh="${as_dir}shtool install -c"
+      else
+        ac_aux_dir_found=no
+        if $ac_first_candidate; then
+          ac_missing_aux_files="${ac_missing_aux_files} install-sh"
+        else
+          break
+        fi
+      fi
+    else
+      if test -f "${as_dir}${ac_aux}"; then
+        printf "%s\n" "$as_me:${as_lineno-$LINENO}:   ${as_dir}${ac_aux} found" >&5
+      else
+        ac_aux_dir_found=no
+        if $ac_first_candidate; then
+          ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}"
+        else
+          break
+        fi
+      fi
+    fi
+  done
+  if test "$ac_aux_dir_found" = yes; then
+    ac_aux_dir="$as_dir"
+    break
+  fi
+  ac_first_candidate=false
+
+  as_found=false
+done
+IFS=$as_save_IFS
+if $as_found
+then :
+
+else $as_nop
+  as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5
+fi
+
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+if test -f "${ac_aux_dir}config.guess"; then
+  ac_config_guess="$SHELL ${ac_aux_dir}config.guess"
+fi
+if test -f "${ac_aux_dir}config.sub"; then
+  ac_config_sub="$SHELL ${ac_aux_dir}config.sub"
+fi
+if test -f "$ac_aux_dir/configure"; then
+  ac_configure="$SHELL ${ac_aux_dir}configure"
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	# differences in whitespace do not lead to failure.
+	ac_old_val_w=`echo x $ac_old_val`
+	ac_new_val_w=`echo x $ac_new_val`
+	if test "$ac_old_val_w" != "$ac_new_val_w"; then
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  ac_cache_corrupted=:
+	else
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  eval $ac_var=\$ac_old_val
+	fi
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+printf "%s\n" "$as_me:   former value:  \`$ac_old_val'" >&2;}
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+printf "%s\n" "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file'
+	    and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+#
+# Now starts the "real" configure script.
+#
+
+# Make sure the srcdir contains "libarchive" directory
+
+# Use auxiliary subscripts from this subdirectory (cleans up root)
+
+# M4 scripts
+
+
+# Libtool's "interface version" can be computed from the libarchive version.
+
+# Libtool interface version bumps on any API change, so increments
+# whenever libarchive minor version does.
+ARCHIVE_MINOR=$(( (3007002 / 1000) % 1000 ))
+# Libarchive 2.7 == libtool interface 9 = 2 + 7
+# Libarchive 2.8 == libtool interface 10 = 2 + 8
+# Libarchive 2.9 == libtool interface 11 = 2 + 8
+# Libarchive 3.0 == libtool interface 12
+# Libarchive 3.1 == libtool interface 13
+ARCHIVE_INTERFACE=`echo $((13 + ${ARCHIVE_MINOR}))`
+# Libarchive revision is bumped on any source change === libtool revision
+ARCHIVE_REVISION=$(( 3007002 % 1000 ))
+# Libarchive minor is bumped on any interface addition === libtool age
+ARCHIVE_LIBTOOL_VERSION=$ARCHIVE_INTERFACE:$ARCHIVE_REVISION:$ARCHIVE_MINOR
+
+# Stick the version numbers into config.h
+
+printf "%s\n" "#define __LIBARCHIVE_CONFIG_H_INCLUDED 1" >>confdefs.h
+
+
+printf "%s\n" "#define LIBARCHIVE_VERSION_STRING \"3.7.2\"" >>confdefs.h
+
+
+printf "%s\n" "#define LIBARCHIVE_VERSION_NUMBER \"3007002\"" >>confdefs.h
+
+
+printf "%s\n" "#define BSDCPIO_VERSION_STRING \"3.7.2\"" >>confdefs.h
+
+
+printf "%s\n" "#define BSDTAR_VERSION_STRING \"3.7.2\"" >>confdefs.h
+
+
+printf "%s\n" "#define BSDCAT_VERSION_STRING \"3.7.2\"" >>confdefs.h
+
+
+# The shell variables here must be the same as the AC_SUBST() variables
+# below, but the shell variable names apparently cannot be the same as
+# the m4 macro names above.  Why?  Ask autoconf.
+BSDCPIO_VERSION_STRING=3.7.2
+BSDTAR_VERSION_STRING=3.7.2
+BSDCAT_VERSION_STRING=3.7.2
+LIBARCHIVE_VERSION_STRING=3.7.2
+LIBARCHIVE_VERSION_NUMBER=3007002
+
+# Substitute the above version numbers into the various files below.
+# Yes, I believe this is the fourth time we define what are essentially
+# the same symbols.  Why? Ask autoconf.
+
+
+
+
+
+
+
+ac_config_headers="$ac_config_headers c/config.h"
+
+
+# Check for host type
+
+
+
+  # Make sure we can run config.sub.
+$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 ||
+  as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+printf %s "checking build system type... " >&6; }
+if test ${ac_cv_build+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_build_alias=$build_alias
+test "x$ac_build_alias" = x &&
+  ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"`
+test "x$ac_build_alias" = x &&
+  as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
+ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` ||
+  as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+printf "%s\n" "$ac_cv_build" >&6; }
+case $ac_cv_build in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
+esac
+build=$ac_cv_build
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_build
+shift
+build_cpu=$1
+build_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+build_os=$*
+IFS=$ac_save_IFS
+case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+printf %s "checking host system type... " >&6; }
+if test ${ac_cv_host+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test "x$host_alias" = x; then
+  ac_cv_host=$ac_cv_build
+else
+  ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` ||
+    as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+printf "%s\n" "$ac_cv_host" >&6; }
+case $ac_cv_host in
+*-*-*) ;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
+esac
+host=$ac_cv_host
+ac_save_IFS=$IFS; IFS='-'
+set x $ac_cv_host
+shift
+host_cpu=$1
+host_vendor=$2
+shift; shift
+# Remember, the first character of IFS is used to create $*,
+# except with old shells:
+host_os=$*
+IFS=$ac_save_IFS
+case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
+
+
+
+inc_windows_files=no
+inc_cygwin_files=no
+case "$host_os" in
+  *mingw* ) inc_windows_files=yes ;;
+  *cygwin* | *msys*) inc_cygwin_files=yes ;;
+esac
+ if test $inc_windows_files = yes; then
+  INC_WINDOWS_FILES_TRUE=
+  INC_WINDOWS_FILES_FALSE='#'
+else
+  INC_WINDOWS_FILES_TRUE='#'
+  INC_WINDOWS_FILES_FALSE=
+fi
+
+ if test $inc_cygwin_files = yes; then
+  INC_CYGWIN_FILES_TRUE=
+  INC_CYGWIN_FILES_FALSE='#'
+else
+  INC_CYGWIN_FILES_TRUE='#'
+  INC_CYGWIN_FILES_FALSE=
+fi
+
+
+# Checks for programs.
+
+
+
+
+
+
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+          if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args.
+set dummy ${ac_tool_prefix}clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}clang"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "clang", so it can be a program name with args.
+set dummy clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="clang"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+
+
+test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion -version; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+printf %s "checking whether the C compiler works... " >&6; }
+ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no;
+	then :; else
+	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	fi
+	# We set ac_cv_exeext here because the later test for it is not
+	# safe: cross compilers may not add the suffix if given an `-o'
+	# argument, so we may need to know it at that point already.
+	# Even if this section looks crufty: it has the advantage of
+	# actually working.
+	break;;
+    * )
+	break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else $as_nop
+  ac_file=''
+fi
+if test -z "$ac_file"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+printf %s "checking for C compiler default output file name... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+printf "%s\n" "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+printf %s "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  break;;
+    * ) break;;
+  esac
+done
+else $as_nop
+  { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+printf "%s\n" "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main (void)
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+printf %s "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+printf "%s\n" "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+printf %s "checking for suffix of object files... " >&6; }
+if test ${ac_cv_objext+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else $as_nop
+  printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+printf "%s\n" "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5
+printf %s "checking whether the compiler supports GNU C... " >&6; }
+if test ${ac_cv_c_compiler_gnu+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_compiler_gnu=yes
+else $as_nop
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+y}
+ac_save_CFLAGS=$CFLAGS
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+printf %s "checking whether $CC accepts -g... " >&6; }
+if test ${ac_cv_prog_cc_g+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_g=yes
+else $as_nop
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+  ac_c_werror_flag=$ac_save_c_werror_flag
+	 CFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
+if test $ac_test_CFLAGS; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+ac_prog_cc_stdc=no
+if test x$ac_prog_cc_stdc = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
+printf %s "checking for $CC option to enable C11 features... " >&6; }
+if test ${ac_cv_prog_cc_c11+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_prog_cc_c11=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_c_conftest_c11_program
+_ACEOF
+for ac_arg in '' -std=gnu11
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_c11=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+  test "x$ac_cv_prog_cc_c11" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c11" = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+  if test "x$ac_cv_prog_cc_c11" = x
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
+     CC="$CC $ac_cv_prog_cc_c11"
+fi
+  ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
+  ac_prog_cc_stdc=c11
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
+printf %s "checking for $CC option to enable C99 features... " >&6; }
+if test ${ac_cv_prog_cc_c99+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_prog_cc_c99=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_c_conftest_c99_program
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99=
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_c99=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+  test "x$ac_cv_prog_cc_c99" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c99" = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+  if test "x$ac_cv_prog_cc_c99" = x
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
+     CC="$CC $ac_cv_prog_cc_c99"
+fi
+  ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+  ac_prog_cc_stdc=c99
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
+printf %s "checking for $CC option to enable C89 features... " >&6; }
+if test ${ac_cv_prog_cc_c89+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_c_conftest_c89_program
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+fi
+
+if test "x$ac_cv_prog_cc_c89" = xno
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+printf "%s\n" "unsupported" >&6; }
+else $as_nop
+  if test "x$ac_cv_prog_cc_c89" = x
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+printf "%s\n" "none needed" >&6; }
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
+     CC="$CC $ac_cv_prog_cc_c89"
+fi
+  ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+  ac_prog_cc_stdc=c89
+fi
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+printf %s "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test ${ac_cv_prog_CPP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+      # Double quotes because $CC needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+else $as_nop
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+  # Broken: success on invalid input.
+continue
+else $as_nop
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok
+then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+printf "%s\n" "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+		     Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+else $as_nop
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+  # Broken: success on invalid input.
+continue
+else $as_nop
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok
+then :
+
+else $as_nop
+  { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ac_header= ac_cache=
+for ac_item in $ac_header_c_list
+do
+  if test $ac_cache; then
+    ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
+    if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
+      printf "%s\n" "#define $ac_item 1" >> confdefs.h
+    fi
+    ac_header= ac_cache=
+  elif test $ac_header; then
+    ac_cache=$ac_item
+  else
+    ac_header=$ac_item
+  fi
+done
+
+
+
+
+
+
+
+
+if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
+then :
+
+printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
+
+fi
+
+
+
+
+
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5
+printf %s "checking whether it is safe to define __EXTENSIONS__... " >&6; }
+if test ${ac_cv_safe_to_define___extensions__+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#         define __EXTENSIONS__ 1
+          $ac_includes_default
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_safe_to_define___extensions__=yes
+else $as_nop
+  ac_cv_safe_to_define___extensions__=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5
+printf "%s\n" "$ac_cv_safe_to_define___extensions__" >&6; }
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether _XOPEN_SOURCE should be defined" >&5
+printf %s "checking whether _XOPEN_SOURCE should be defined... " >&6; }
+if test ${ac_cv_should_define__xopen_source+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_should_define__xopen_source=no
+    if test $ac_cv_header_wchar_h = yes
+then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+          #include <wchar.h>
+          mbstate_t x;
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+            #define _XOPEN_SOURCE 500
+            #include <wchar.h>
+            mbstate_t x;
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_should_define__xopen_source=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_should_define__xopen_source" >&5
+printf "%s\n" "$ac_cv_should_define__xopen_source" >&6; }
+
+  printf "%s\n" "#define _ALL_SOURCE 1" >>confdefs.h
+
+  printf "%s\n" "#define _DARWIN_C_SOURCE 1" >>confdefs.h
+
+  printf "%s\n" "#define _GNU_SOURCE 1" >>confdefs.h
+
+  printf "%s\n" "#define _HPUX_ALT_XOPEN_SOCKET_API 1" >>confdefs.h
+
+  printf "%s\n" "#define _NETBSD_SOURCE 1" >>confdefs.h
+
+  printf "%s\n" "#define _OPENBSD_SOURCE 1" >>confdefs.h
+
+  printf "%s\n" "#define _POSIX_PTHREAD_SEMANTICS 1" >>confdefs.h
+
+  printf "%s\n" "#define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1" >>confdefs.h
+
+  printf "%s\n" "#define __STDC_WANT_IEC_60559_BFP_EXT__ 1" >>confdefs.h
+
+  printf "%s\n" "#define __STDC_WANT_IEC_60559_DFP_EXT__ 1" >>confdefs.h
+
+  printf "%s\n" "#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1" >>confdefs.h
+
+  printf "%s\n" "#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1" >>confdefs.h
+
+  printf "%s\n" "#define __STDC_WANT_LIB_EXT2__ 1" >>confdefs.h
+
+  printf "%s\n" "#define __STDC_WANT_MATH_SPEC_FUNCS__ 1" >>confdefs.h
+
+  printf "%s\n" "#define _TANDEM_SOURCE 1" >>confdefs.h
+
+  if test $ac_cv_header_minix_config_h = yes
+then :
+  MINIX=yes
+    printf "%s\n" "#define _MINIX 1" >>confdefs.h
+
+    printf "%s\n" "#define _POSIX_SOURCE 1" >>confdefs.h
+
+    printf "%s\n" "#define _POSIX_1_SOURCE 2" >>confdefs.h
+
+else $as_nop
+  MINIX=
+fi
+  if test $ac_cv_safe_to_define___extensions__ = yes
+then :
+  printf "%s\n" "#define __EXTENSIONS__ 1" >>confdefs.h
+
+fi
+  if test $ac_cv_should_define__xopen_source = yes
+then :
+  printf "%s\n" "#define _XOPEN_SOURCE 500" >>confdefs.h
+
+fi
+
+enable_win32_dll=yes
+
+case $host in
+*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*)
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args.
+set dummy ${ac_tool_prefix}as; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_AS+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$AS"; then
+  ac_cv_prog_AS="$AS" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AS="${ac_tool_prefix}as"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AS=$ac_cv_prog_AS
+if test -n "$AS"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AS" >&5
+printf "%s\n" "$AS" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_AS"; then
+  ac_ct_AS=$AS
+  # Extract the first word of "as", so it can be a program name with args.
+set dummy as; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_AS+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_AS"; then
+  ac_cv_prog_ac_ct_AS="$ac_ct_AS" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_AS="as"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AS=$ac_cv_prog_ac_ct_AS
+if test -n "$ac_ct_AS"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AS" >&5
+printf "%s\n" "$ac_ct_AS" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_AS" = x; then
+    AS="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    AS=$ac_ct_AS
+  fi
+else
+  AS="$ac_cv_prog_AS"
+fi
+
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_DLLTOOL+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$DLLTOOL"; then
+  ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DLLTOOL=$ac_cv_prog_DLLTOOL
+if test -n "$DLLTOOL"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
+printf "%s\n" "$DLLTOOL" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DLLTOOL"; then
+  ac_ct_DLLTOOL=$DLLTOOL
+  # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_DLLTOOL+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_DLLTOOL"; then
+  ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DLLTOOL="dlltool"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
+if test -n "$ac_ct_DLLTOOL"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
+printf "%s\n" "$ac_ct_DLLTOOL" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_DLLTOOL" = x; then
+    DLLTOOL="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DLLTOOL=$ac_ct_DLLTOOL
+  fi
+else
+  DLLTOOL="$ac_cv_prog_DLLTOOL"
+fi
+
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_OBJDUMP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$OBJDUMP"; then
+  ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+printf "%s\n" "$OBJDUMP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+  ac_ct_OBJDUMP=$OBJDUMP
+  # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_OBJDUMP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_OBJDUMP"; then
+  ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OBJDUMP="objdump"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+printf "%s\n" "$ac_ct_OBJDUMP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_OBJDUMP" = x; then
+    OBJDUMP="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OBJDUMP=$ac_ct_OBJDUMP
+  fi
+else
+  OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+  ;;
+esac
+
+test -z "$AS" && AS=as
+
+
+
+
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+
+
+
+
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+
+
+case `pwd` in
+  *\ * | *\	*)
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
+printf "%s\n" "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;;
+esac
+
+
+
+macro_version='2.4.7'
+macro_revision='2.4.7'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ltmain=$ac_aux_dir/ltmain.sh
+
+# Backslashify metacharacters that are still active within
+# double-quoted strings.
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+
+# Same as above, but do not quote variable references.
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+
+# Sed substitution to delay expansion of an escaped shell variable in a
+# double_quote_subst'ed string.
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+
+# Sed substitution to delay expansion of an escaped single quote.
+delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g'
+
+# Sed substitution to avoid accidental globbing in evaled expressions
+no_glob_subst='s/\*/\\\*/g'
+
+ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
+ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5
+printf %s "checking how to print strings... " >&6; }
+# Test print first, because it will be a builtin if present.
+if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \
+   test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='print -r --'
+elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then
+  ECHO='printf %s\n'
+else
+  # Use this function as a fallback that always works.
+  func_fallback_echo ()
+  {
+    eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+  }
+  ECHO='func_fallback_echo'
+fi
+
+# func_echo_all arg...
+# Invoke $ECHO with all args, space-separated.
+func_echo_all ()
+{
+    $ECHO ""
+}
+
+case $ECHO in
+  printf*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: printf" >&5
+printf "%s\n" "printf" >&6; } ;;
+  print*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
+printf "%s\n" "print -r" >&6; } ;;
+  *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cat" >&5
+printf "%s\n" "cat" >&6; } ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
+printf %s "checking for a sed that does not truncate output... " >&6; }
+if test ${ac_cv_path_SED+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+            ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+     for ac_i in 1 2 3 4 5 6 7; do
+       ac_script="$ac_script$as_nl$ac_script"
+     done
+     echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed
+     { ac_script=; unset ac_script;}
+     if test -z "$SED"; then
+  ac_path_SED_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_prog in sed gsed
+   do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_SED="$as_dir$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_SED" || continue
+# Check for GNU ac_path_SED and select it if it is found.
+  # Check for GNU $ac_path_SED
+case `"$ac_path_SED" --version 2>&1` in
+*GNU*)
+  ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+*)
+  ac_count=0
+  printf %s 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    printf "%s\n" '' >> "conftest.nl"
+    "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_SED_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_SED="$ac_path_SED"
+      ac_path_SED_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_SED_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_SED"; then
+    as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
+  fi
+else
+  ac_cv_path_SED=$SED
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
+printf "%s\n" "$ac_cv_path_SED" >&6; }
+ SED="$ac_cv_path_SED"
+  rm -f conftest.sed
+
+test -z "$SED" && SED=sed
+Xsed="$SED -e 1s/^X//"
+
+
+
+
+
+
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
+printf %s "checking for grep that handles long lines and -e... " >&6; }
+if test ${ac_cv_path_GREP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -z "$GREP"; then
+  ac_path_GREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_prog in grep ggrep
+   do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_GREP="$as_dir$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_GREP" || continue
+# Check for GNU ac_path_GREP and select it if it is found.
+  # Check for GNU $ac_path_GREP
+case `"$ac_path_GREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+*)
+  ac_count=0
+  printf %s 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    printf "%s\n" 'GREP' >> "conftest.nl"
+    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_GREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_GREP="$ac_path_GREP"
+      ac_path_GREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_GREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_GREP"; then
+    as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_GREP=$GREP
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
+printf "%s\n" "$ac_cv_path_GREP" >&6; }
+ GREP="$ac_cv_path_GREP"
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
+printf %s "checking for egrep... " >&6; }
+if test ${ac_cv_path_EGREP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+   then ac_cv_path_EGREP="$GREP -E"
+   else
+     if test -z "$EGREP"; then
+  ac_path_EGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_prog in egrep
+   do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_EGREP="$as_dir$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_EGREP" || continue
+# Check for GNU ac_path_EGREP and select it if it is found.
+  # Check for GNU $ac_path_EGREP
+case `"$ac_path_EGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+*)
+  ac_count=0
+  printf %s 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    printf "%s\n" 'EGREP' >> "conftest.nl"
+    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_EGREP="$ac_path_EGREP"
+      ac_path_EGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_EGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_EGREP"; then
+    as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_EGREP=$EGREP
+fi
+
+   fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
+printf "%s\n" "$ac_cv_path_EGREP" >&6; }
+ EGREP="$ac_cv_path_EGREP"
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
+printf %s "checking for fgrep... " >&6; }
+if test ${ac_cv_path_FGREP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+   then ac_cv_path_FGREP="$GREP -F"
+   else
+     if test -z "$FGREP"; then
+  ac_path_FGREP_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_prog in fgrep
+   do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_FGREP="$as_dir$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_FGREP" || continue
+# Check for GNU ac_path_FGREP and select it if it is found.
+  # Check for GNU $ac_path_FGREP
+case `"$ac_path_FGREP" --version 2>&1` in
+*GNU*)
+  ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+*)
+  ac_count=0
+  printf %s 0123456789 >"conftest.in"
+  while :
+  do
+    cat "conftest.in" "conftest.in" >"conftest.tmp"
+    mv "conftest.tmp" "conftest.in"
+    cp "conftest.in" "conftest.nl"
+    printf "%s\n" 'FGREP' >> "conftest.nl"
+    "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break
+    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+    as_fn_arith $ac_count + 1 && ac_count=$as_val
+    if test $ac_count -gt ${ac_path_FGREP_max-0}; then
+      # Best one so far, save it but keep looking for a better one
+      ac_cv_path_FGREP="$ac_path_FGREP"
+      ac_path_FGREP_max=$ac_count
+    fi
+    # 10*(2^10) chars as input seems more than enough
+    test $ac_count -gt 10 && break
+  done
+  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+      $ac_path_FGREP_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_FGREP"; then
+    as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+  fi
+else
+  ac_cv_path_FGREP=$FGREP
+fi
+
+   fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
+printf "%s\n" "$ac_cv_path_FGREP" >&6; }
+ FGREP="$ac_cv_path_FGREP"
+
+
+test -z "$GREP" && GREP=grep
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-gnu-ld was given.
+if test ${with_gnu_ld+y}
+then :
+  withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes
+else $as_nop
+  with_gnu_ld=no
+fi
+
+ac_prog=ld
+if test yes = "$GCC"; then
+  # Check if gcc -print-prog-name=ld gives a path.
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
+printf %s "checking for ld used by $CC... " >&6; }
+  case $host in
+  *-*-mingw*)
+    # gcc leaves a trailing carriage return, which upsets mingw
+    ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
+  *)
+    ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
+  esac
+  case $ac_prog in
+    # Accept absolute paths.
+    [\\/]* | ?:[\\/]*)
+      re_direlt='/[^/][^/]*/\.\./'
+      # Canonicalize the pathname of ld
+      ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'`
+      while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
+	ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
+      done
+      test -z "$LD" && LD=$ac_prog
+      ;;
+  "")
+    # If it fails, then pretend we aren't using GCC.
+    ac_prog=ld
+    ;;
+  *)
+    # If it is relative, then search for the first ld in PATH.
+    with_gnu_ld=unknown
+    ;;
+  esac
+elif test yes = "$with_gnu_ld"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
+printf %s "checking for GNU ld... " >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
+printf %s "checking for non-GNU ld... " >&6; }
+fi
+if test ${lt_cv_path_LD+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -z "$LD"; then
+  lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+  for ac_dir in $PATH; do
+    IFS=$lt_save_ifs
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
+      lt_cv_path_LD=$ac_dir/$ac_prog
+      # Check to see if the program is GNU ld.  I'd rather use --version,
+      # but apparently some variants of GNU ld only accept -v.
+      # Break only if it was the GNU/non-GNU ld that we prefer.
+      case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
+      *GNU* | *'with BFD'*)
+	test no != "$with_gnu_ld" && break
+	;;
+      *)
+	test yes != "$with_gnu_ld" && break
+	;;
+      esac
+    fi
+  done
+  IFS=$lt_save_ifs
+else
+  lt_cv_path_LD=$LD # Let the user override the test with a path.
+fi
+fi
+
+LD=$lt_cv_path_LD
+if test -n "$LD"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+printf "%s\n" "$LD" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
+printf %s "checking if the linker ($LD) is GNU ld... " >&6; }
+if test ${lt_cv_prog_gnu_ld+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  # I'd rather use --version here, but apparently some GNU lds only accept -v.
+case `$LD -v 2>&1 </dev/null` in
+*GNU* | *'with BFD'*)
+  lt_cv_prog_gnu_ld=yes
+  ;;
+*)
+  lt_cv_prog_gnu_ld=no
+  ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
+printf "%s\n" "$lt_cv_prog_gnu_ld" >&6; }
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+
+
+
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
+printf %s "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
+if test ${lt_cv_path_NM+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$NM"; then
+  # Let the user override the test.
+  lt_cv_path_NM=$NM
+else
+  lt_nm_to_check=${ac_tool_prefix}nm
+  if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
+    lt_nm_to_check="$lt_nm_to_check nm"
+  fi
+  for lt_tmp_nm in $lt_nm_to_check; do
+    lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+    for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
+      IFS=$lt_save_ifs
+      test -z "$ac_dir" && ac_dir=.
+      tmp_nm=$ac_dir/$lt_tmp_nm
+      if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
+	# Check to see if the nm accepts a BSD-compat flag.
+	# Adding the 'sed 1q' prevents false positives on HP-UX, which says:
+	#   nm: unknown option "B" ignored
+	# Tru64's nm complains that /dev/null is an invalid object file
+	# MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+	case $build_os in
+	mingw*) lt_bad_file=conftest.nm/nofile ;;
+	*) lt_bad_file=/dev/null ;;
+	esac
+	case `"$tmp_nm" -B $lt_bad_file 2>&1 | $SED '1q'` in
+	*$lt_bad_file* | *'Invalid file or object type'*)
+	  lt_cv_path_NM="$tmp_nm -B"
+	  break 2
+	  ;;
+	*)
+	  case `"$tmp_nm" -p /dev/null 2>&1 | $SED '1q'` in
+	  */dev/null*)
+	    lt_cv_path_NM="$tmp_nm -p"
+	    break 2
+	    ;;
+	  *)
+	    lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
+	    continue # so that we can try to find one that supports BSD flags
+	    ;;
+	  esac
+	  ;;
+	esac
+      fi
+    done
+    IFS=$lt_save_ifs
+  done
+  : ${lt_cv_path_NM=no}
+fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
+printf "%s\n" "$lt_cv_path_NM" >&6; }
+if test no != "$lt_cv_path_NM"; then
+  NM=$lt_cv_path_NM
+else
+  # Didn't find any BSD compatible name lister, look for dumpbin.
+  if test -n "$DUMPBIN"; then :
+    # Let the user override the test.
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in dumpbin "link -dump"
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_DUMPBIN+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$DUMPBIN"; then
+  ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DUMPBIN=$ac_cv_prog_DUMPBIN
+if test -n "$DUMPBIN"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5
+printf "%s\n" "$DUMPBIN" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+    test -n "$DUMPBIN" && break
+  done
+fi
+if test -z "$DUMPBIN"; then
+  ac_ct_DUMPBIN=$DUMPBIN
+  for ac_prog in dumpbin "link -dump"
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_DUMPBIN+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_DUMPBIN"; then
+  ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
+if test -n "$ac_ct_DUMPBIN"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5
+printf "%s\n" "$ac_ct_DUMPBIN" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_DUMPBIN" && break
+done
+
+  if test "x$ac_ct_DUMPBIN" = x; then
+    DUMPBIN=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DUMPBIN=$ac_ct_DUMPBIN
+  fi
+fi
+
+    case `$DUMPBIN -symbols -headers /dev/null 2>&1 | $SED '1q'` in
+    *COFF*)
+      DUMPBIN="$DUMPBIN -symbols -headers"
+      ;;
+    *)
+      DUMPBIN=:
+      ;;
+    esac
+  fi
+
+  if test : != "$DUMPBIN"; then
+    NM=$DUMPBIN
+  fi
+fi
+test -z "$NM" && NM=nm
+
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
+printf %s "checking the name lister ($NM) interface... " >&6; }
+if test ${lt_cv_nm_interface+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_nm_interface="BSD nm"
+  echo "int some_variable = 0;" > conftest.$ac_ext
+  (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5)
+  (eval "$ac_compile" 2>conftest.err)
+  cat conftest.err >&5
+  (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
+  (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
+  cat conftest.err >&5
+  (eval echo "\"\$as_me:$LINENO: output\"" >&5)
+  cat conftest.out >&5
+  if $GREP 'External.*some_variable' conftest.out > /dev/null; then
+    lt_cv_nm_interface="MS dumpbin"
+  fi
+  rm -f conftest*
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
+printf "%s\n" "$lt_cv_nm_interface" >&6; }
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5
+printf %s "checking whether ln -s works... " >&6; }
+LN_S=$as_ln_s
+if test "$LN_S" = "ln -s"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5
+printf "%s\n" "no, using $LN_S" >&6; }
+fi
+
+# find the maximum length of command line arguments
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
+printf %s "checking the maximum length of command line arguments... " >&6; }
+if test ${lt_cv_sys_max_cmd_len+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+    i=0
+  teststring=ABCD
+
+  case $build_os in
+  msdosdjgpp*)
+    # On DJGPP, this test can blow up pretty badly due to problems in libc
+    # (any single argument exceeding 2000 bytes causes a buffer overrun
+    # during glob expansion).  Even if it were fixed, the result of this
+    # check would be larger than it should be.
+    lt_cv_sys_max_cmd_len=12288;    # 12K is about right
+    ;;
+
+  gnu*)
+    # Under GNU Hurd, this test is not required because there is
+    # no limit to the length of command line arguments.
+    # Libtool will interpret -1 as no limit whatsoever
+    lt_cv_sys_max_cmd_len=-1;
+    ;;
+
+  cygwin* | mingw* | cegcc*)
+    # On Win9x/ME, this test blows up -- it succeeds, but takes
+    # about 5 minutes as the teststring grows exponentially.
+    # Worse, since 9x/ME are not pre-emptively multitasking,
+    # you end up with a "frozen" computer, even though with patience
+    # the test eventually succeeds (with a max line length of 256k).
+    # Instead, let's just punt: use the minimum linelength reported by
+    # all of the supported platforms: 8192 (on NT/2K/XP).
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  mint*)
+    # On MiNT this can take a long time and run out of memory.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  amigaos*)
+    # On AmigaOS with pdksh, this test takes hours, literally.
+    # So we just punt and use a minimum line length of 8192.
+    lt_cv_sys_max_cmd_len=8192;
+    ;;
+
+  bitrig* | darwin* | dragonfly* | freebsd* | midnightbsd* | netbsd* | openbsd*)
+    # This has been around since 386BSD, at least.  Likely further.
+    if test -x /sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
+    elif test -x /usr/sbin/sysctl; then
+      lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax`
+    else
+      lt_cv_sys_max_cmd_len=65536	# usable default for all BSDs
+    fi
+    # And add a safety zone
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+    lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    ;;
+
+  interix*)
+    # We know the value 262144 and hardcode it with a safety zone (like BSD)
+    lt_cv_sys_max_cmd_len=196608
+    ;;
+
+  os2*)
+    # The test takes a long time on OS/2.
+    lt_cv_sys_max_cmd_len=8192
+    ;;
+
+  osf*)
+    # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
+    # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
+    # nice to cause kernel panics so lets avoid the loop below.
+    # First set a reasonable default.
+    lt_cv_sys_max_cmd_len=16384
+    #
+    if test -x /sbin/sysconfig; then
+      case `/sbin/sysconfig -q proc exec_disable_arg_limit` in
+        *1*) lt_cv_sys_max_cmd_len=-1 ;;
+      esac
+    fi
+    ;;
+  sco3.2v5*)
+    lt_cv_sys_max_cmd_len=102400
+    ;;
+  sysv5* | sco5v6* | sysv4.2uw2*)
+    kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null`
+    if test -n "$kargmax"; then
+      lt_cv_sys_max_cmd_len=`echo $kargmax | $SED 's/.*[	 ]//'`
+    else
+      lt_cv_sys_max_cmd_len=32768
+    fi
+    ;;
+  *)
+    lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
+    if test -n "$lt_cv_sys_max_cmd_len" && \
+       test undefined != "$lt_cv_sys_max_cmd_len"; then
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
+    else
+      # Make teststring a little bigger before we do anything with it.
+      # a 1K string should be a reasonable start.
+      for i in 1 2 3 4 5 6 7 8; do
+        teststring=$teststring$teststring
+      done
+      SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
+      # If test is not a shell built-in, we'll probably end up computing a
+      # maximum length that is only half of the actual maximum length, but
+      # we can't tell.
+      while { test X`env echo "$teststring$teststring" 2>/dev/null` \
+	         = "X$teststring$teststring"; } >/dev/null 2>&1 &&
+	      test 17 != "$i" # 1/2 MB should be enough
+      do
+        i=`expr $i + 1`
+        teststring=$teststring$teststring
+      done
+      # Only check the string length outside the loop.
+      lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1`
+      teststring=
+      # Add a significant safety factor because C++ compilers can tack on
+      # massive amounts of additional arguments before passing them to the
+      # linker.  It appears as though 1/2 is a usable value.
+      lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2`
+    fi
+    ;;
+  esac
+
+fi
+
+if test -n "$lt_cv_sys_max_cmd_len"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
+printf "%s\n" "$lt_cv_sys_max_cmd_len" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none" >&5
+printf "%s\n" "none" >&6; }
+fi
+max_cmd_len=$lt_cv_sys_max_cmd_len
+
+
+
+
+
+
+: ${CP="cp -f"}
+: ${MV="mv -f"}
+: ${RM="rm -f"}
+
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  lt_unset=unset
+else
+  lt_unset=false
+fi
+
+
+
+
+
+# test EBCDIC or ASCII
+case `echo X|tr X '\101'` in
+ A) # ASCII based system
+    # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr
+  lt_SP2NL='tr \040 \012'
+  lt_NL2SP='tr \015\012 \040\040'
+  ;;
+ *) # EBCDIC based system
+  lt_SP2NL='tr \100 \n'
+  lt_NL2SP='tr \r\n \100\100'
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5
+printf %s "checking how to convert $build file names to $host format... " >&6; }
+if test ${lt_cv_to_host_file_cmd+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32
+        ;;
+    esac
+    ;;
+  *-*-cygwin* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin
+        ;;
+      *-*-cygwin* )
+        lt_cv_to_host_file_cmd=func_convert_file_noop
+        ;;
+      * ) # otherwise, assume *nix
+        lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin
+        ;;
+    esac
+    ;;
+  * ) # unhandled hosts (and "normal" native builds)
+    lt_cv_to_host_file_cmd=func_convert_file_noop
+    ;;
+esac
+
+fi
+
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5
+printf "%s\n" "$lt_cv_to_host_file_cmd" >&6; }
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5
+printf %s "checking how to convert $build file names to toolchain format... " >&6; }
+if test ${lt_cv_to_tool_file_cmd+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  #assume ordinary cross tools, or native build.
+lt_cv_to_tool_file_cmd=func_convert_file_noop
+case $host in
+  *-*-mingw* )
+    case $build in
+      *-*-mingw* ) # actually msys
+        lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32
+        ;;
+    esac
+    ;;
+esac
+
+fi
+
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5
+printf "%s\n" "$lt_cv_to_tool_file_cmd" >&6; }
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
+printf %s "checking for $LD option to reload object files... " >&6; }
+if test ${lt_cv_ld_reload_flag+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_ld_reload_flag='-r'
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
+printf "%s\n" "$lt_cv_ld_reload_flag" >&6; }
+reload_flag=$lt_cv_ld_reload_flag
+case $reload_flag in
+"" | " "*) ;;
+*) reload_flag=" $reload_flag" ;;
+esac
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    if test yes != "$GCC"; then
+      reload_cmds=false
+    fi
+    ;;
+  darwin*)
+    if test yes = "$GCC"; then
+      reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
+    else
+      reload_cmds='$LD$reload_flag -o $output$reload_objs'
+    fi
+    ;;
+esac
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}file", so it can be a program name with args.
+set dummy ${ac_tool_prefix}file; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_FILECMD+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$FILECMD"; then
+  ac_cv_prog_FILECMD="$FILECMD" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_FILECMD="${ac_tool_prefix}file"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+FILECMD=$ac_cv_prog_FILECMD
+if test -n "$FILECMD"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $FILECMD" >&5
+printf "%s\n" "$FILECMD" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_FILECMD"; then
+  ac_ct_FILECMD=$FILECMD
+  # Extract the first word of "file", so it can be a program name with args.
+set dummy file; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_FILECMD+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_FILECMD"; then
+  ac_cv_prog_ac_ct_FILECMD="$ac_ct_FILECMD" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_FILECMD="file"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_FILECMD=$ac_cv_prog_ac_ct_FILECMD
+if test -n "$ac_ct_FILECMD"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_FILECMD" >&5
+printf "%s\n" "$ac_ct_FILECMD" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_FILECMD" = x; then
+    FILECMD=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    FILECMD=$ac_ct_FILECMD
+  fi
+else
+  FILECMD="$ac_cv_prog_FILECMD"
+fi
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args.
+set dummy ${ac_tool_prefix}objdump; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_OBJDUMP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$OBJDUMP"; then
+  ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OBJDUMP=$ac_cv_prog_OBJDUMP
+if test -n "$OBJDUMP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5
+printf "%s\n" "$OBJDUMP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OBJDUMP"; then
+  ac_ct_OBJDUMP=$OBJDUMP
+  # Extract the first word of "objdump", so it can be a program name with args.
+set dummy objdump; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_OBJDUMP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_OBJDUMP"; then
+  ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OBJDUMP="objdump"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
+if test -n "$ac_ct_OBJDUMP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5
+printf "%s\n" "$ac_ct_OBJDUMP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_OBJDUMP" = x; then
+    OBJDUMP="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OBJDUMP=$ac_ct_OBJDUMP
+  fi
+else
+  OBJDUMP="$ac_cv_prog_OBJDUMP"
+fi
+
+test -z "$OBJDUMP" && OBJDUMP=objdump
+
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
+printf %s "checking how to recognize dependent libraries... " >&6; }
+if test ${lt_cv_deplibs_check_method+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_file_magic_cmd='$MAGIC_CMD'
+lt_cv_file_magic_test_file=
+lt_cv_deplibs_check_method='unknown'
+# Need to set the preceding variable on all platforms that support
+# interlibrary dependencies.
+# 'none' -- dependencies not supported.
+# 'unknown' -- same as none, but documents that we really don't know.
+# 'pass_all' -- all dependencies passed with no checks.
+# 'test_compile' -- check by making test program.
+# 'file_magic [[regex]]' -- check by looking for files in library path
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
+
+case $host_os in
+aix[4-9]*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+beos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+bsdi[45]*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)'
+  lt_cv_file_magic_cmd='$FILECMD -L'
+  lt_cv_file_magic_test_file=/shlib/libc.so
+  ;;
+
+cygwin*)
+  # func_win32_libid is a shell function defined in ltmain.sh
+  lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+  lt_cv_file_magic_cmd='func_win32_libid'
+  ;;
+
+mingw* | pw32*)
+  # Base MSYS/MinGW do not provide the 'file' command needed by
+  # func_win32_libid shell function, so use a weaker test based on 'objdump',
+  # unless we find 'file', for example because we are cross-compiling.
+  if ( file / ) >/dev/null 2>&1; then
+    lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
+    lt_cv_file_magic_cmd='func_win32_libid'
+  else
+    # Keep this pattern in sync with the one in func_win32_libid.
+    lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)'
+    lt_cv_file_magic_cmd='$OBJDUMP -f'
+  fi
+  ;;
+
+cegcc*)
+  # use the weaker test based on 'objdump'. See mingw*.
+  lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?'
+  lt_cv_file_magic_cmd='$OBJDUMP -f'
+  ;;
+
+darwin* | rhapsody*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+freebsd* | dragonfly* | midnightbsd*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    case $host_cpu in
+    i*86 )
+      # Not sure whether the presence of OpenBSD here was a mistake.
+      # Let's accept both of them until this is cleared up.
+      lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library'
+      lt_cv_file_magic_cmd=$FILECMD
+      lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*`
+      ;;
+    esac
+  else
+    lt_cv_deplibs_check_method=pass_all
+  fi
+  ;;
+
+haiku*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+hpux10.20* | hpux11*)
+  lt_cv_file_magic_cmd=$FILECMD
+  case $host_cpu in
+  ia64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64'
+    lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so
+    ;;
+  hppa*64*)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'
+    lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl
+    ;;
+  *)
+    lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library'
+    lt_cv_file_magic_test_file=/usr/lib/libc.sl
+    ;;
+  esac
+  ;;
+
+interix[3-9]*)
+  # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here
+  lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$'
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $LD in
+  *-32|*"-32 ") libmagic=32-bit;;
+  *-n32|*"-n32 ") libmagic=N32;;
+  *-64|*"-64 ") libmagic=64-bit;;
+  *) libmagic=never-match;;
+  esac
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+netbsd*)
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$'
+  fi
+  ;;
+
+newos6*)
+  lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)'
+  lt_cv_file_magic_cmd=$FILECMD
+  lt_cv_file_magic_test_file=/usr/lib/libnls.so
+  ;;
+
+*nto* | *qnx*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+openbsd* | bitrig*)
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
+  else
+    lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
+  fi
+  ;;
+
+osf3* | osf4* | osf5*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+rdos*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+solaris*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+
+sysv4 | sysv4.3*)
+  case $host_vendor in
+  motorola)
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]'
+    lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*`
+    ;;
+  ncr)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  sequent)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )'
+    ;;
+  sni)
+    lt_cv_file_magic_cmd='/bin/file'
+    lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib"
+    lt_cv_file_magic_test_file=/lib/libc.so
+    ;;
+  siemens)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  pc)
+    lt_cv_deplibs_check_method=pass_all
+    ;;
+  esac
+  ;;
+
+tpf*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+os2*)
+  lt_cv_deplibs_check_method=pass_all
+  ;;
+esac
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
+printf "%s\n" "$lt_cv_deplibs_check_method" >&6; }
+
+file_magic_glob=
+want_nocaseglob=no
+if test "$build" = "$host"; then
+  case $host_os in
+  mingw* | pw32*)
+    if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then
+      want_nocaseglob=yes
+    else
+      file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"`
+    fi
+    ;;
+  esac
+fi
+
+file_magic_cmd=$lt_cv_file_magic_cmd
+deplibs_check_method=$lt_cv_deplibs_check_method
+test -z "$deplibs_check_method" && deplibs_check_method=unknown
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_DLLTOOL+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$DLLTOOL"; then
+  ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DLLTOOL=$ac_cv_prog_DLLTOOL
+if test -n "$DLLTOOL"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5
+printf "%s\n" "$DLLTOOL" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DLLTOOL"; then
+  ac_ct_DLLTOOL=$DLLTOOL
+  # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_DLLTOOL+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_DLLTOOL"; then
+  ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DLLTOOL="dlltool"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
+if test -n "$ac_ct_DLLTOOL"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5
+printf "%s\n" "$ac_ct_DLLTOOL" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_DLLTOOL" = x; then
+    DLLTOOL="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DLLTOOL=$ac_ct_DLLTOOL
+  fi
+else
+  DLLTOOL="$ac_cv_prog_DLLTOOL"
+fi
+
+test -z "$DLLTOOL" && DLLTOOL=dlltool
+
+
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5
+printf %s "checking how to associate runtime and link libraries... " >&6; }
+if test ${lt_cv_sharedlib_from_linklib_cmd+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_sharedlib_from_linklib_cmd='unknown'
+
+case $host_os in
+cygwin* | mingw* | pw32* | cegcc*)
+  # two different shell functions defined in ltmain.sh;
+  # decide which one to use based on capabilities of $DLLTOOL
+  case `$DLLTOOL --help 2>&1` in
+  *--identify-strict*)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
+    ;;
+  *)
+    lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback
+    ;;
+  esac
+  ;;
+*)
+  # fallback: assume linklib IS sharedlib
+  lt_cv_sharedlib_from_linklib_cmd=$ECHO
+  ;;
+esac
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5
+printf "%s\n" "$lt_cv_sharedlib_from_linklib_cmd" >&6; }
+sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd
+test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  for ac_prog in ar
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_AR+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$AR"; then
+  ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AR=$ac_cv_prog_AR
+if test -n "$AR"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+printf "%s\n" "$AR" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+    test -n "$AR" && break
+  done
+fi
+if test -z "$AR"; then
+  ac_ct_AR=$AR
+  for ac_prog in ar
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_AR+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_AR"; then
+  ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_AR="$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_AR=$ac_cv_prog_ac_ct_AR
+if test -n "$ac_ct_AR"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+printf "%s\n" "$ac_ct_AR" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_AR" && break
+done
+
+  if test "x$ac_ct_AR" = x; then
+    AR="false"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    AR=$ac_ct_AR
+  fi
+fi
+
+: ${AR=ar}
+
+
+
+
+
+
+# Use ARFLAGS variable as AR's operation code to sync the variable naming with
+# Automake.  If both AR_FLAGS and ARFLAGS are specified, AR_FLAGS should have
+# higher priority because thats what people were doing historically (setting
+# ARFLAGS for automake and AR_FLAGS for libtool).  FIXME: Make the AR_FLAGS
+# variable obsoleted/removed.
+
+test ${AR_FLAGS+y} || AR_FLAGS=${ARFLAGS-cr}
+lt_ar_flags=$AR_FLAGS
+
+
+
+
+
+
+# Make AR_FLAGS overridable by 'make ARFLAGS='.  Don't try to run-time override
+# by AR_FLAGS because that was never working and AR_FLAGS is about to die.
+
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5
+printf %s "checking for archiver @FILE support... " >&6; }
+if test ${lt_cv_ar_at_file+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_ar_at_file=no
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  echo conftest.$ac_objext > conftest.lst
+      lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5'
+      { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+  (eval $lt_ar_try) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+      if test 0 -eq "$ac_status"; then
+	# Ensure the archiver fails upon bogus file names.
+	rm -f conftest.$ac_objext libconftest.a
+	{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
+  (eval $lt_ar_try) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+	if test 0 -ne "$ac_status"; then
+          lt_cv_ar_at_file=@
+        fi
+      fi
+      rm -f conftest.* libconftest.a
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
+printf "%s\n" "$lt_cv_ar_at_file" >&6; }
+
+if test no = "$lt_cv_ar_at_file"; then
+  archiver_list_spec=
+else
+  archiver_list_spec=$lt_cv_ar_at_file
+fi
+
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_STRIP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+printf "%s\n" "$STRIP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_STRIP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+printf "%s\n" "$ac_ct_STRIP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+test -z "$STRIP" && STRIP=:
+
+
+
+
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_RANLIB+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$RANLIB"; then
+  ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+RANLIB=$ac_cv_prog_RANLIB
+if test -n "$RANLIB"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+printf "%s\n" "$RANLIB" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_RANLIB"; then
+  ac_ct_RANLIB=$RANLIB
+  # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_RANLIB+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_RANLIB"; then
+  ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_RANLIB="ranlib"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
+if test -n "$ac_ct_RANLIB"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+printf "%s\n" "$ac_ct_RANLIB" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_RANLIB" = x; then
+    RANLIB=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    RANLIB=$ac_ct_RANLIB
+  fi
+else
+  RANLIB="$ac_cv_prog_RANLIB"
+fi
+
+test -z "$RANLIB" && RANLIB=:
+
+
+
+
+
+
+# Determine commands to create old-style static archives.
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs'
+old_postinstall_cmds='chmod 644 $oldlib'
+old_postuninstall_cmds=
+
+if test -n "$RANLIB"; then
+  case $host_os in
+  bitrig* | openbsd*)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
+    ;;
+  *)
+    old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
+    ;;
+  esac
+  old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
+fi
+
+case $host_os in
+  darwin*)
+    lock_old_archive_extraction=yes ;;
+  *)
+    lock_old_archive_extraction=no ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_AWK+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+printf "%s\n" "$AWK" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  test -n "$AWK" && break
+done
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+
+# Check for command to grab the raw symbol name followed by C symbol from nm.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
+printf %s "checking command to parse $NM output from $compiler object... " >&6; }
+if test ${lt_cv_sys_global_symbol_pipe+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+
+# These are sane defaults that work on at least a few old systems.
+# [They come from Ultrix.  What could be older than Ultrix?!! ;)]
+
+# Character class describing NM global symbol codes.
+symcode='[BCDEGRST]'
+
+# Regexp to match symbols that can be accessed directly from C.
+sympat='\([_A-Za-z][_A-Za-z0-9]*\)'
+
+# Define system-specific variables.
+case $host_os in
+aix*)
+  symcode='[BCDT]'
+  ;;
+cygwin* | mingw* | pw32* | cegcc*)
+  symcode='[ABCDGISTW]'
+  ;;
+hpux*)
+  if test ia64 = "$host_cpu"; then
+    symcode='[ABCDEGRST]'
+  fi
+  ;;
+irix* | nonstopux*)
+  symcode='[BCDEGRST]'
+  ;;
+osf*)
+  symcode='[BCDEGQRST]'
+  ;;
+solaris*)
+  symcode='[BDRT]'
+  ;;
+sco3.2v5*)
+  symcode='[DT]'
+  ;;
+sysv4.2uw2*)
+  symcode='[DT]'
+  ;;
+sysv5* | sco5v6* | unixware* | OpenUNIX*)
+  symcode='[ABDT]'
+  ;;
+sysv4)
+  symcode='[DFNSTU]'
+  ;;
+esac
+
+# If we're using GNU nm, then use its standard symbol codes.
+case `$NM -V 2>&1` in
+*GNU* | *'with BFD'*)
+  symcode='[ABCDGIRSTW]' ;;
+esac
+
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+  # Gets list of data symbols to import.
+  lt_cv_sys_global_symbol_to_import="$SED -n -e 's/^I .* \(.*\)$/\1/p'"
+  # Adjust the below global symbol transforms to fixup imported variables.
+  lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+  lt_c_name_hook=" -e 's/^I .* \(.*\)$/  {\"\1\", (void *) 0},/p'"
+  lt_c_name_lib_hook="\
+  -e 's/^I .* \(lib.*\)$/  {\"\1\", (void *) 0},/p'\
+  -e 's/^I .* \(.*\)$/  {\"lib\1\", (void *) 0},/p'"
+else
+  # Disable hooks by default.
+  lt_cv_sys_global_symbol_to_import=
+  lt_cdecl_hook=
+  lt_c_name_hook=
+  lt_c_name_lib_hook=
+fi
+
+# Transform an extracted symbol line into a proper C declaration.
+# Some systems (esp. on ia64) link data and code symbols differently,
+# so use this general approach.
+lt_cv_sys_global_symbol_to_cdecl="$SED -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
+
+# Transform an extracted symbol line into symbol name and symbol address
+lt_cv_sys_global_symbol_to_c_name_address="$SED -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/  {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/  {\"\1\", (void *) \&\1},/p'"
+
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="$SED -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/  {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/  {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/  {\"lib\1\", (void *) \&\1},/p'"
+
+# Handle CRLF in mingw tool chain
+opt_cr=
+case $build_os in
+mingw*)
+  opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp
+  ;;
+esac
+
+# Try without a prefix underscore, then with it.
+for ac_symprfx in "" "_"; do
+
+  # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol.
+  symxfrm="\\1 $ac_symprfx\\2 \\2"
+
+  # Write the raw and C identifiers.
+  if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+    # Fake it for dumpbin and say T for any non-static function,
+    # D for any global variable and I for any imported variable.
+    # Also find C++ and __fastcall symbols from MSVC++ or ICC,
+    # which start with @ or ?.
+    lt_cv_sys_global_symbol_pipe="$AWK '"\
+"     {last_section=section; section=\$ 3};"\
+"     /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
+"     /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+"     /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+"     /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+"     /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
+"     \$ 0!~/External *\|/{next};"\
+"     / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
+"     {if(hide[section]) next};"\
+"     {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+"     {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+"     s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
+"     s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
+"     ' prfx=^$ac_symprfx"
+  else
+    lt_cv_sys_global_symbol_pipe="$SED -n -e 's/^.*[	 ]\($symcode$symcode*\)[	 ][	 ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
+  fi
+  lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | $SED '/ __gnu_lto/d'"
+
+  # Check to see that the pipe works correctly.
+  pipe_works=no
+
+  rm -f conftest*
+  cat > conftest.$ac_ext <<_LT_EOF
+#ifdef __cplusplus
+extern "C" {
+#endif
+char nm_test_var;
+void nm_test_func(void);
+void nm_test_func(void){}
+#ifdef __cplusplus
+}
+#endif
+int main(){nm_test_var='a';nm_test_func();return(0);}
+_LT_EOF
+
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    # Now try to grab the symbols.
+    nlist=conftest.nm
+    if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5
+  (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s "$nlist"; then
+      # Try sorting and uniquifying the output.
+      if sort "$nlist" | uniq > "$nlist"T; then
+	mv -f "$nlist"T "$nlist"
+      else
+	rm -f "$nlist"T
+      fi
+
+      # Make sure that we snagged all the symbols we need.
+      if $GREP ' nm_test_var$' "$nlist" >/dev/null; then
+	if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
+	  cat <<_LT_EOF > conftest.$ac_ext
+/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests.  */
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
+   relocations are performed -- see ld's documentation on pseudo-relocs.  */
+# define LT_DLSYM_CONST
+#elif defined __osf__
+/* This system does not cope well with relocations in const data.  */
+# define LT_DLSYM_CONST
+#else
+# define LT_DLSYM_CONST const
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+_LT_EOF
+	  # Now generate the symbol file.
+	  eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext'
+
+	  cat <<_LT_EOF >> conftest.$ac_ext
+
+/* The mapping between symbol names and symbols.  */
+LT_DLSYM_CONST struct {
+  const char *name;
+  void       *address;
+}
+lt__PROGRAM__LTX_preloaded_symbols[] =
+{
+  { "@PROGRAM@", (void *) 0 },
+_LT_EOF
+	  $SED "s/^$symcode$symcode* .* \(.*\)$/  {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+	  cat <<\_LT_EOF >> conftest.$ac_ext
+  {0, (void *) 0}
+};
+
+/* This works around a problem in FreeBSD linker */
+#ifdef FREEBSD_WORKAROUND
+static const void *lt_preloaded_setup() {
+  return lt__PROGRAM__LTX_preloaded_symbols;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+_LT_EOF
+	  # Now try linking the two files.
+	  mv conftest.$ac_objext conftstm.$ac_objext
+	  lt_globsym_save_LIBS=$LIBS
+	  lt_globsym_save_CFLAGS=$CFLAGS
+	  LIBS=conftstm.$ac_objext
+	  CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
+	  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s conftest$ac_exeext; then
+	    pipe_works=yes
+	  fi
+	  LIBS=$lt_globsym_save_LIBS
+	  CFLAGS=$lt_globsym_save_CFLAGS
+	else
+	  echo "cannot find nm_test_func in $nlist" >&5
+	fi
+      else
+	echo "cannot find nm_test_var in $nlist" >&5
+      fi
+    else
+      echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5
+    fi
+  else
+    echo "$progname: failed program was:" >&5
+    cat conftest.$ac_ext >&5
+  fi
+  rm -rf conftest* conftst*
+
+  # Do not use the global_symbol_pipe unless it works.
+  if test yes = "$pipe_works"; then
+    break
+  else
+    lt_cv_sys_global_symbol_pipe=
+  fi
+done
+
+fi
+
+if test -z "$lt_cv_sys_global_symbol_pipe"; then
+  lt_cv_sys_global_symbol_to_cdecl=
+fi
+if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+printf "%s\n" "failed" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ok" >&5
+printf "%s\n" "ok" >&6; }
+fi
+
+# Response file support.
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+  nm_file_list_spec='@'
+elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then
+  nm_file_list_spec='@'
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5
+printf %s "checking for sysroot... " >&6; }
+
+# Check whether --with-sysroot was given.
+if test ${with_sysroot+y}
+then :
+  withval=$with_sysroot;
+else $as_nop
+  with_sysroot=no
+fi
+
+
+lt_sysroot=
+case $with_sysroot in #(
+ yes)
+   if test yes = "$GCC"; then
+     lt_sysroot=`$CC --print-sysroot 2>/dev/null`
+   fi
+   ;; #(
+ /*)
+   lt_sysroot=`echo "$with_sysroot" | $SED -e "$sed_quote_subst"`
+   ;; #(
+ no|'')
+   ;; #(
+ *)
+   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5
+printf "%s\n" "$with_sysroot" >&6; }
+   as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5
+   ;;
+esac
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5
+printf "%s\n" "${lt_sysroot:-no}" >&6; }
+
+
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5
+printf %s "checking for a working dd... " >&6; }
+if test ${ac_cv_path_lt_DD+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+if test -z "$lt_DD"; then
+  ac_path_lt_DD_found=false
+  # Loop through the user's path and test for each of PROGNAME-LIST
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_prog in dd
+   do
+    for ac_exec_ext in '' $ac_executable_extensions; do
+      ac_path_lt_DD="$as_dir$ac_prog$ac_exec_ext"
+      as_fn_executable_p "$ac_path_lt_DD" || continue
+if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+  cmp -s conftest.i conftest.out \
+  && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi
+      $ac_path_lt_DD_found && break 3
+    done
+  done
+  done
+IFS=$as_save_IFS
+  if test -z "$ac_cv_path_lt_DD"; then
+    :
+  fi
+else
+  ac_cv_path_lt_DD=$lt_DD
+fi
+
+rm -f conftest.i conftest2.i conftest.out
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5
+printf "%s\n" "$ac_cv_path_lt_DD" >&6; }
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5
+printf %s "checking how to truncate binary pipes... " >&6; }
+if test ${lt_cv_truncate_bin+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+  cmp -s conftest.i conftest.out \
+  && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5
+printf "%s\n" "$lt_cv_truncate_bin" >&6; }
+
+
+
+
+
+
+
+# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+    for cc_temp in $*""; do
+      case $cc_temp in
+        compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+        distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+        \-*) ;;
+        *) break;;
+      esac
+    done
+    func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+
+# Check whether --enable-libtool-lock was given.
+if test ${enable_libtool_lock+y}
+then :
+  enableval=$enable_libtool_lock;
+fi
+
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
+
+# Some flags need to be propagated to the compiler or linker for good
+# libtool support.
+case $host in
+ia64-*-hpux*)
+  # Find out what ABI is being produced by ac_compile, and set mode
+  # options accordingly.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `$FILECMD conftest.$ac_objext` in
+      *ELF-32*)
+	HPUX_IA64_MODE=32
+	;;
+      *ELF-64*)
+	HPUX_IA64_MODE=64
+	;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+*-*-irix6*)
+  # Find out what ABI is being produced by ac_compile, and set linker
+  # options accordingly.
+  echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    if test yes = "$lt_cv_prog_gnu_ld"; then
+      case `$FILECMD conftest.$ac_objext` in
+	*32-bit*)
+	  LD="${LD-ld} -melf32bsmip"
+	  ;;
+	*N32*)
+	  LD="${LD-ld} -melf32bmipn32"
+	  ;;
+	*64-bit*)
+	  LD="${LD-ld} -melf64bmip"
+	;;
+      esac
+    else
+      case `$FILECMD conftest.$ac_objext` in
+	*32-bit*)
+	  LD="${LD-ld} -32"
+	  ;;
+	*N32*)
+	  LD="${LD-ld} -n32"
+	  ;;
+	*64-bit*)
+	  LD="${LD-ld} -64"
+	  ;;
+      esac
+    fi
+  fi
+  rm -rf conftest*
+  ;;
+
+mips64*-*linux*)
+  # Find out what ABI is being produced by ac_compile, and set linker
+  # options accordingly.
+  echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    emul=elf
+    case `$FILECMD conftest.$ac_objext` in
+      *32-bit*)
+	emul="${emul}32"
+	;;
+      *64-bit*)
+	emul="${emul}64"
+	;;
+    esac
+    case `$FILECMD conftest.$ac_objext` in
+      *MSB*)
+	emul="${emul}btsmip"
+	;;
+      *LSB*)
+	emul="${emul}ltsmip"
+	;;
+    esac
+    case `$FILECMD conftest.$ac_objext` in
+      *N32*)
+	emul="${emul}n32"
+	;;
+    esac
+    LD="${LD-ld} -m $emul"
+  fi
+  rm -rf conftest*
+  ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
+s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
+  # Find out what ABI is being produced by ac_compile, and set linker
+  # options accordingly.  Note that the listed cases only cover the
+  # situations where additional linker options are needed (such as when
+  # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+  # vice versa); the common cases where no linker options are needed do
+  # not appear in the list.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `$FILECMD conftest.o` in
+      *32-bit*)
+	case $host in
+	  x86_64-*kfreebsd*-gnu)
+	    LD="${LD-ld} -m elf_i386_fbsd"
+	    ;;
+	  x86_64-*linux*)
+	    case `$FILECMD conftest.o` in
+	      *x86-64*)
+		LD="${LD-ld} -m elf32_x86_64"
+		;;
+	      *)
+		LD="${LD-ld} -m elf_i386"
+		;;
+	    esac
+	    ;;
+	  powerpc64le-*linux*)
+	    LD="${LD-ld} -m elf32lppclinux"
+	    ;;
+	  powerpc64-*linux*)
+	    LD="${LD-ld} -m elf32ppclinux"
+	    ;;
+	  s390x-*linux*)
+	    LD="${LD-ld} -m elf_s390"
+	    ;;
+	  sparc64-*linux*)
+	    LD="${LD-ld} -m elf32_sparc"
+	    ;;
+	esac
+	;;
+      *64-bit*)
+	case $host in
+	  x86_64-*kfreebsd*-gnu)
+	    LD="${LD-ld} -m elf_x86_64_fbsd"
+	    ;;
+	  x86_64-*linux*)
+	    LD="${LD-ld} -m elf_x86_64"
+	    ;;
+	  powerpcle-*linux*)
+	    LD="${LD-ld} -m elf64lppc"
+	    ;;
+	  powerpc-*linux*)
+	    LD="${LD-ld} -m elf64ppc"
+	    ;;
+	  s390*-*linux*|s390*-*tpf*)
+	    LD="${LD-ld} -m elf64_s390"
+	    ;;
+	  sparc*-*linux*)
+	    LD="${LD-ld} -m elf64_sparc"
+	    ;;
+	esac
+	;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+
+*-*-sco3.2v5*)
+  # On SCO OpenServer 5, we need -belf to get full-featured binaries.
+  SAVE_CFLAGS=$CFLAGS
+  CFLAGS="$CFLAGS -belf"
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
+printf %s "checking whether the C compiler needs -belf... " >&6; }
+if test ${lt_cv_cc_needs_belf+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  lt_cv_cc_needs_belf=yes
+else $as_nop
+  lt_cv_cc_needs_belf=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+     ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
+printf "%s\n" "$lt_cv_cc_needs_belf" >&6; }
+  if test yes != "$lt_cv_cc_needs_belf"; then
+    # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
+    CFLAGS=$SAVE_CFLAGS
+  fi
+  ;;
+*-*solaris*)
+  # Find out what ABI is being produced by ac_compile, and set linker
+  # options accordingly.
+  echo 'int i;' > conftest.$ac_ext
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+    case `$FILECMD conftest.o` in
+    *64-bit*)
+      case $lt_cv_prog_gnu_ld in
+      yes*)
+        case $host in
+        i?86-*-solaris*|x86_64-*-solaris*)
+          LD="${LD-ld} -m elf_x86_64"
+          ;;
+        sparc*-*-solaris*)
+          LD="${LD-ld} -m elf64_sparc"
+          ;;
+        esac
+        # GNU ld 2.21 introduced _sol2 emulations.  Use them if available.
+        if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+          LD=${LD-ld}_sol2
+        fi
+        ;;
+      *)
+	if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
+	  LD="${LD-ld} -64"
+	fi
+	;;
+      esac
+      ;;
+    esac
+  fi
+  rm -rf conftest*
+  ;;
+esac
+
+need_locks=$enable_libtool_lock
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args.
+set dummy ${ac_tool_prefix}mt; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_MANIFEST_TOOL+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$MANIFEST_TOOL"; then
+  ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL
+if test -n "$MANIFEST_TOOL"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5
+printf "%s\n" "$MANIFEST_TOOL" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_MANIFEST_TOOL"; then
+  ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL
+  # Extract the first word of "mt", so it can be a program name with args.
+set dummy mt; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_MANIFEST_TOOL+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_MANIFEST_TOOL"; then
+  ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_MANIFEST_TOOL="mt"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL
+if test -n "$ac_ct_MANIFEST_TOOL"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5
+printf "%s\n" "$ac_ct_MANIFEST_TOOL" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_MANIFEST_TOOL" = x; then
+    MANIFEST_TOOL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL
+  fi
+else
+  MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL"
+fi
+
+test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5
+printf %s "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
+if test ${lt_cv_path_mainfest_tool+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_path_mainfest_tool=no
+  echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5
+  $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
+  cat conftest.err >&5
+  if $GREP 'Manifest Tool' conftest.out > /dev/null; then
+    lt_cv_path_mainfest_tool=yes
+  fi
+  rm -f conftest*
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
+printf "%s\n" "$lt_cv_path_mainfest_tool" >&6; }
+if test yes != "$lt_cv_path_mainfest_tool"; then
+  MANIFEST_TOOL=:
+fi
+
+
+
+
+
+
+  case $host_os in
+    rhapsody* | darwin*)
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_DSYMUTIL+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$DSYMUTIL"; then
+  ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+DSYMUTIL=$ac_cv_prog_DSYMUTIL
+if test -n "$DSYMUTIL"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5
+printf "%s\n" "$DSYMUTIL" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_DSYMUTIL"; then
+  ac_ct_DSYMUTIL=$DSYMUTIL
+  # Extract the first word of "dsymutil", so it can be a program name with args.
+set dummy dsymutil; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_DSYMUTIL+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_DSYMUTIL"; then
+  ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
+if test -n "$ac_ct_DSYMUTIL"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5
+printf "%s\n" "$ac_ct_DSYMUTIL" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_DSYMUTIL" = x; then
+    DSYMUTIL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    DSYMUTIL=$ac_ct_DSYMUTIL
+  fi
+else
+  DSYMUTIL="$ac_cv_prog_DSYMUTIL"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nmedit; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_NMEDIT+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$NMEDIT"; then
+  ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+NMEDIT=$ac_cv_prog_NMEDIT
+if test -n "$NMEDIT"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5
+printf "%s\n" "$NMEDIT" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_NMEDIT"; then
+  ac_ct_NMEDIT=$NMEDIT
+  # Extract the first word of "nmedit", so it can be a program name with args.
+set dummy nmedit; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_NMEDIT+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_NMEDIT"; then
+  ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_NMEDIT="nmedit"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
+if test -n "$ac_ct_NMEDIT"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5
+printf "%s\n" "$ac_ct_NMEDIT" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_NMEDIT" = x; then
+    NMEDIT=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    NMEDIT=$ac_ct_NMEDIT
+  fi
+else
+  NMEDIT="$ac_cv_prog_NMEDIT"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args.
+set dummy ${ac_tool_prefix}lipo; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_LIPO+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$LIPO"; then
+  ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+LIPO=$ac_cv_prog_LIPO
+if test -n "$LIPO"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5
+printf "%s\n" "$LIPO" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_LIPO"; then
+  ac_ct_LIPO=$LIPO
+  # Extract the first word of "lipo", so it can be a program name with args.
+set dummy lipo; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_LIPO+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_LIPO"; then
+  ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_LIPO="lipo"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
+if test -n "$ac_ct_LIPO"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5
+printf "%s\n" "$ac_ct_LIPO" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_LIPO" = x; then
+    LIPO=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    LIPO=$ac_ct_LIPO
+  fi
+else
+  LIPO="$ac_cv_prog_LIPO"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_OTOOL+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$OTOOL"; then
+  ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL=$ac_cv_prog_OTOOL
+if test -n "$OTOOL"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5
+printf "%s\n" "$OTOOL" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL"; then
+  ac_ct_OTOOL=$OTOOL
+  # Extract the first word of "otool", so it can be a program name with args.
+set dummy otool; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_OTOOL+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_OTOOL"; then
+  ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OTOOL="otool"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
+if test -n "$ac_ct_OTOOL"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5
+printf "%s\n" "$ac_ct_OTOOL" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_OTOOL" = x; then
+    OTOOL=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OTOOL=$ac_ct_OTOOL
+  fi
+else
+  OTOOL="$ac_cv_prog_OTOOL"
+fi
+
+    if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args.
+set dummy ${ac_tool_prefix}otool64; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_OTOOL64+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$OTOOL64"; then
+  ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+OTOOL64=$ac_cv_prog_OTOOL64
+if test -n "$OTOOL64"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5
+printf "%s\n" "$OTOOL64" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_OTOOL64"; then
+  ac_ct_OTOOL64=$OTOOL64
+  # Extract the first word of "otool64", so it can be a program name with args.
+set dummy otool64; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_OTOOL64+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_OTOOL64"; then
+  ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_OTOOL64="otool64"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
+if test -n "$ac_ct_OTOOL64"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5
+printf "%s\n" "$ac_ct_OTOOL64" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_OTOOL64" = x; then
+    OTOOL64=":"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    OTOOL64=$ac_ct_OTOOL64
+  fi
+else
+  OTOOL64="$ac_cv_prog_OTOOL64"
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
+printf %s "checking for -single_module linker flag... " >&6; }
+if test ${lt_cv_apple_cc_single_mod+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_apple_cc_single_mod=no
+      if test -z "$LT_MULTI_MODULE"; then
+	# By default we will add the -single_module flag. You can override
+	# by either setting the environment variable LT_MULTI_MODULE
+	# non-empty at configure time, or by adding -multi_module to the
+	# link flags.
+	rm -rf libconftest.dylib*
+	echo "int foo(void){return 1;}" > conftest.c
+	echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+-dynamiclib -Wl,-single_module conftest.c" >&5
+	$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
+	  -dynamiclib -Wl,-single_module conftest.c 2>conftest.err
+        _lt_result=$?
+	# If there is a non-empty error log, and "single_module"
+	# appears in it, assume the flag caused a linker warning
+        if test -s conftest.err && $GREP single_module conftest.err; then
+	  cat conftest.err >&5
+	# Otherwise, if the output was created with a 0 exit code from
+	# the compiler, it worked.
+	elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
+	  lt_cv_apple_cc_single_mod=yes
+	else
+	  cat conftest.err >&5
+	fi
+	rm -rf libconftest.dylib*
+	rm -f conftest.*
+      fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
+printf "%s\n" "$lt_cv_apple_cc_single_mod" >&6; }
+
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
+printf %s "checking for -exported_symbols_list linker flag... " >&6; }
+if test ${lt_cv_ld_exported_symbols_list+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_ld_exported_symbols_list=no
+      save_LDFLAGS=$LDFLAGS
+      echo "_main" > conftest.sym
+      LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  lt_cv_ld_exported_symbols_list=yes
+else $as_nop
+  lt_cv_ld_exported_symbols_list=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+	LDFLAGS=$save_LDFLAGS
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
+printf "%s\n" "$lt_cv_ld_exported_symbols_list" >&6; }
+
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5
+printf %s "checking for -force_load linker flag... " >&6; }
+if test ${lt_cv_ld_force_load+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_ld_force_load=no
+      cat > conftest.c << _LT_EOF
+int forced_loaded() { return 2;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5
+      $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5
+      echo "$AR $AR_FLAGS libconftest.a conftest.o" >&5
+      $AR $AR_FLAGS libconftest.a conftest.o 2>&5
+      echo "$RANLIB libconftest.a" >&5
+      $RANLIB libconftest.a 2>&5
+      cat > conftest.c << _LT_EOF
+int main() { return 0;}
+_LT_EOF
+      echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5
+      $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
+      _lt_result=$?
+      if test -s conftest.err && $GREP force_load conftest.err; then
+	cat conftest.err >&5
+      elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
+	lt_cv_ld_force_load=yes
+      else
+	cat conftest.err >&5
+      fi
+        rm -f conftest.err libconftest.a conftest conftest.c
+        rm -rf conftest.dSYM
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5
+printf "%s\n" "$lt_cv_ld_force_load" >&6; }
+    case $host_os in
+    rhapsody* | darwin1.[012])
+      _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
+    darwin1.*)
+      _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+    darwin*)
+      case $MACOSX_DEPLOYMENT_TARGET,$host in
+        10.[012],*|,*powerpc*-darwin[5-8]*)
+          _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
+        *)
+          _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+      esac
+    ;;
+  esac
+    if test yes = "$lt_cv_apple_cc_single_mod"; then
+      _lt_dar_single_mod='$single_module'
+    fi
+    if test yes = "$lt_cv_ld_exported_symbols_list"; then
+      _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
+    else
+      _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
+    fi
+    if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
+      _lt_dsymutil='~$DSYMUTIL $lib || :'
+    else
+      _lt_dsymutil=
+    fi
+    ;;
+  esac
+
+# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+#       string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+#       string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+#       string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+#       "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+#       VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+    case x$2 in
+    x)
+        ;;
+    *:)
+        eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
+        ;;
+    x:*)
+        eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
+        ;;
+    *::*)
+        eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+        eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
+        ;;
+    *)
+        eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
+        ;;
+    esac
+}
+
+ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
+"
+if test "x$ac_cv_header_dlfcn_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_DLFCN_H 1" >>confdefs.h
+
+fi
+
+
+
+
+
+# Set options
+
+
+
+        enable_dlopen=no
+
+
+
+            # Check whether --enable-shared was given.
+if test ${enable_shared+y}
+then :
+  enableval=$enable_shared; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_shared=yes ;;
+    no) enable_shared=no ;;
+    *)
+      enable_shared=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+      for pkg in $enableval; do
+	IFS=$lt_save_ifs
+	if test "X$pkg" = "X$p"; then
+	  enable_shared=yes
+	fi
+      done
+      IFS=$lt_save_ifs
+      ;;
+    esac
+else $as_nop
+  enable_shared=yes
+fi
+
+
+
+
+
+
+
+
+
+  # Check whether --enable-static was given.
+if test ${enable_static+y}
+then :
+  enableval=$enable_static; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_static=yes ;;
+    no) enable_static=no ;;
+    *)
+     enable_static=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+      for pkg in $enableval; do
+	IFS=$lt_save_ifs
+	if test "X$pkg" = "X$p"; then
+	  enable_static=yes
+	fi
+      done
+      IFS=$lt_save_ifs
+      ;;
+    esac
+else $as_nop
+  enable_static=yes
+fi
+
+
+
+
+
+
+
+
+
+
+# Check whether --with-pic was given.
+if test ${with_pic+y}
+then :
+  withval=$with_pic; lt_p=${PACKAGE-default}
+    case $withval in
+    yes|no) pic_mode=$withval ;;
+    *)
+      pic_mode=default
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+      for lt_pkg in $withval; do
+	IFS=$lt_save_ifs
+	if test "X$lt_pkg" = "X$lt_p"; then
+	  pic_mode=yes
+	fi
+      done
+      IFS=$lt_save_ifs
+      ;;
+    esac
+else $as_nop
+  pic_mode=default
+fi
+
+
+
+
+
+
+
+
+  # Check whether --enable-fast-install was given.
+if test ${enable_fast_install+y}
+then :
+  enableval=$enable_fast_install; p=${PACKAGE-default}
+    case $enableval in
+    yes) enable_fast_install=yes ;;
+    no) enable_fast_install=no ;;
+    *)
+      enable_fast_install=no
+      # Look at the argument we got.  We use all the common list separators.
+      lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+      for pkg in $enableval; do
+	IFS=$lt_save_ifs
+	if test "X$pkg" = "X$p"; then
+	  enable_fast_install=yes
+	fi
+      done
+      IFS=$lt_save_ifs
+      ;;
+    esac
+else $as_nop
+  enable_fast_install=yes
+fi
+
+
+
+
+
+
+
+
+  shared_archive_member_spec=
+case $host,$enable_shared in
+power*-*-aix[5-9]*,yes)
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5
+printf %s "checking which variant of shared library versioning to provide... " >&6; }
+
+# Check whether --with-aix-soname was given.
+if test ${with_aix_soname+y}
+then :
+  withval=$with_aix_soname; case $withval in
+    aix|svr4|both)
+      ;;
+    *)
+      as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5
+      ;;
+    esac
+    lt_cv_with_aix_soname=$with_aix_soname
+else $as_nop
+  if test ${lt_cv_with_aix_soname+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_with_aix_soname=aix
+fi
+
+    with_aix_soname=$lt_cv_with_aix_soname
+fi
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5
+printf "%s\n" "$with_aix_soname" >&6; }
+  if test aix != "$with_aix_soname"; then
+    # For the AIX way of multilib, we name the shared archive member
+    # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
+    # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
+    # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
+    # the AIX toolchain works better with OBJECT_MODE set (default 32).
+    if test 64 = "${OBJECT_MODE-32}"; then
+      shared_archive_member_spec=shr_64
+    else
+      shared_archive_member_spec=shr
+    fi
+  fi
+  ;;
+*)
+  with_aix_soname=aix
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+
+# This can be used to rebuild libtool when needed
+LIBTOOL_DEPS=$ltmain
+
+# Always use our own libtool.
+LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+test -z "$LN_S" && LN_S="ln -s"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+if test -n "${ZSH_VERSION+set}"; then
+   setopt NO_GLOB_SUBST
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
+printf %s "checking for objdir... " >&6; }
+if test ${lt_cv_objdir+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  rm -f .libs 2>/dev/null
+mkdir .libs 2>/dev/null
+if test -d .libs; then
+  lt_cv_objdir=.libs
+else
+  # MS-DOS does not allow filenames that begin with a dot.
+  lt_cv_objdir=_libs
+fi
+rmdir .libs 2>/dev/null
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
+printf "%s\n" "$lt_cv_objdir" >&6; }
+objdir=$lt_cv_objdir
+
+
+
+
+
+printf "%s\n" "#define LT_OBJDIR \"$lt_cv_objdir/\"" >>confdefs.h
+
+
+
+
+case $host_os in
+aix3*)
+  # AIX sometimes has problems with the GCC collect2 program.  For some
+  # reason, if we set the COLLECT_NAMES environment variable, the problems
+  # vanish in a puff of smoke.
+  if test set != "${COLLECT_NAMES+set}"; then
+    COLLECT_NAMES=
+    export COLLECT_NAMES
+  fi
+  ;;
+esac
+
+# Global variables:
+ofile=libtool
+can_build_shared=yes
+
+# All known linkers require a '.a' archive for static linking (except MSVC and
+# ICC, which need '.lib').
+libext=a
+
+with_gnu_ld=$lt_cv_prog_gnu_ld
+
+old_CC=$CC
+old_CFLAGS=$CFLAGS
+
+# Set sane defaults for various variables
+test -z "$CC" && CC=cc
+test -z "$LTCC" && LTCC=$CC
+test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
+test -z "$LD" && LD=ld
+test -z "$ac_objext" && ac_objext=o
+
+func_cc_basename $compiler
+cc_basename=$func_cc_basename_result
+
+
+# Only perform the check for file, if the check method requires it
+test -z "$MAGIC_CMD" && MAGIC_CMD=file
+case $deplibs_check_method in
+file_magic*)
+  if test "$file_magic_cmd" = '$MAGIC_CMD'; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
+printf %s "checking for ${ac_tool_prefix}file... " >&6; }
+if test ${lt_cv_path_MAGIC_CMD+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD=$MAGIC_CMD
+  lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS=$lt_save_ifs
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/${ac_tool_prefix}file"; then
+      lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+	  MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS=$lt_save_ifs
+  MAGIC_CMD=$lt_save_MAGIC_CMD
+  ;;
+esac
+fi
+
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+printf "%s\n" "$MAGIC_CMD" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+
+
+
+if test -z "$lt_cv_path_MAGIC_CMD"; then
+  if test -n "$ac_tool_prefix"; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for file" >&5
+printf %s "checking for file... " >&6; }
+if test ${lt_cv_path_MAGIC_CMD+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  case $MAGIC_CMD in
+[\\/*] |  ?:[\\/]*)
+  lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
+  ;;
+*)
+  lt_save_MAGIC_CMD=$MAGIC_CMD
+  lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
+  ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
+  for ac_dir in $ac_dummy; do
+    IFS=$lt_save_ifs
+    test -z "$ac_dir" && ac_dir=.
+    if test -f "$ac_dir/file"; then
+      lt_cv_path_MAGIC_CMD=$ac_dir/"file"
+      if test -n "$file_magic_test_file"; then
+	case $deplibs_check_method in
+	"file_magic "*)
+	  file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
+	  MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+	  if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
+	    $EGREP "$file_magic_regex" > /dev/null; then
+	    :
+	  else
+	    cat <<_LT_EOF 1>&2
+
+*** Warning: the command libtool uses to detect shared libraries,
+*** $file_magic_cmd, produces output that libtool cannot recognize.
+*** The result is that libtool may fail to recognize shared libraries
+*** as such.  This will affect the creation of libtool libraries that
+*** depend on shared libraries, but programs linked with such libtool
+*** libraries will work regardless of this problem.  Nevertheless, you
+*** may want to report the problem to your system manager and/or to
+*** bug-libtool@gnu.org
+
+_LT_EOF
+	  fi ;;
+	esac
+      fi
+      break
+    fi
+  done
+  IFS=$lt_save_ifs
+  MAGIC_CMD=$lt_save_MAGIC_CMD
+  ;;
+esac
+fi
+
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
+if test -n "$MAGIC_CMD"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
+printf "%s\n" "$MAGIC_CMD" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+  else
+    MAGIC_CMD=:
+  fi
+fi
+
+  fi
+  ;;
+esac
+
+# Use C for the default configuration in the libtool script
+
+lt_save_CC=$CC
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+# Source file extension for C test sources.
+ac_ext=c
+
+# Object file extension for compiled C test sources.
+objext=o
+objext=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="int some_variable = 0;"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='int main(){return(0);}'
+
+
+
+
+
+
+
+# If no C compiler was specified, use CC.
+LTCC=${LTCC-"$CC"}
+
+# If no C compiler flags were specified, use CFLAGS.
+LTCFLAGS=${LTCFLAGS-"$CFLAGS"}
+
+# Allow CC to be a program name with arguments.
+compiler=$CC
+
+# Save the default compiler, since it gets overwritten when the other
+# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP.
+compiler_DEFAULT=$CC
+
+# save warnings/boilerplate of simple test code
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_compile_test_code" >conftest.$ac_ext
+eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_compiler_boilerplate=`cat conftest.err`
+$RM conftest*
+
+ac_outfile=conftest.$ac_objext
+echo "$lt_simple_link_test_code" >conftest.$ac_ext
+eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err
+_lt_linker_boilerplate=`cat conftest.err`
+$RM -r conftest*
+
+
+## CAVEAT EMPTOR:
+## There is no encapsulation within the following macros, do not change
+## the running order or otherwise move them around unless you know exactly
+## what you are doing...
+if test -n "$compiler"; then
+
+lt_prog_compiler_no_builtin_flag=
+
+if test yes = "$GCC"; then
+  case $cc_basename in
+  nvcc*)
+    lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;
+  *)
+    lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;;
+  esac
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
+printf %s "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
+if test ${lt_cv_prog_compiler_rtti_exceptions+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_prog_compiler_rtti_exceptions=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="-fno-rtti -fno-exceptions"  ## exclude from sc_useless_quotes_in_assignment
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_rtti_exceptions=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
+printf "%s\n" "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then
+    lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
+else
+    :
+fi
+
+fi
+
+
+
+
+
+
+  lt_prog_compiler_wl=
+lt_prog_compiler_pic=
+lt_prog_compiler_static=
+
+
+  if test yes = "$GCC"; then
+    lt_prog_compiler_wl='-Wl,'
+    lt_prog_compiler_static='-static'
+
+    case $host_os in
+      aix*)
+      # All AIX code is PIC.
+      if test ia64 = "$host_cpu"; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static='-Bstatic'
+      fi
+      lt_prog_compiler_pic='-fPIC'
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            lt_prog_compiler_pic='-fPIC'
+        ;;
+      m68k)
+            # FIXME: we need at least 68020 code to build shared libraries, but
+            # adding the '-m68020' flag to GCC prevents building anything better,
+            # like '-m68040'.
+            lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
+        ;;
+      esac
+      ;;
+
+    beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*)
+      # PIC is the default for these OSes.
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      # Although the cygwin gcc ignores -fPIC, still need this for old-style
+      # (--disable-auto-import) libraries
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      case $host_os in
+      os2*)
+	lt_prog_compiler_static='$wl-static'
+	;;
+      esac
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic='-fno-common'
+      ;;
+
+    haiku*)
+      # PIC is the default for Haiku.
+      # The "-static" flag exists, but is broken.
+      lt_prog_compiler_static=
+      ;;
+
+    hpux*)
+      # PIC is the default for 64-bit PA HP-UX, but not for 32-bit
+      # PA HP-UX.  On IA64 HP-UX, PIC is the default but the pic flag
+      # sets the default TLS model and affects inlining.
+      case $host_cpu in
+      hppa*64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic='-fPIC'
+	;;
+      esac
+      ;;
+
+    interix[3-9]*)
+      # Interix 3.x gcc -fpic/-fPIC options generate broken code.
+      # Instead, we relocate shared libraries at runtime.
+      ;;
+
+    msdosdjgpp*)
+      # Just because we use GCC doesn't mean we suddenly get shared libraries
+      # on systems that don't support them.
+      lt_prog_compiler_can_build_shared=no
+      enable_shared=no
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic='-fPIC -shared'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	lt_prog_compiler_pic=-Kconform_pic
+      fi
+      ;;
+
+    *)
+      lt_prog_compiler_pic='-fPIC'
+      ;;
+    esac
+
+    case $cc_basename in
+    nvcc*) # Cuda Compiler Driver 2.2
+      lt_prog_compiler_wl='-Xlinker '
+      if test -n "$lt_prog_compiler_pic"; then
+        lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic"
+      fi
+      ;;
+    esac
+  else
+    # PORTME Check for flag to pass linker flags through the system compiler.
+    case $host_os in
+    aix*)
+      lt_prog_compiler_wl='-Wl,'
+      if test ia64 = "$host_cpu"; then
+	# AIX 5 now supports IA64 processor
+	lt_prog_compiler_static='-Bstatic'
+      else
+	lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp'
+      fi
+      ;;
+
+    darwin* | rhapsody*)
+      # PIC is the default on this platform
+      # Common symbols not allowed in MH_DYLIB files
+      lt_prog_compiler_pic='-fno-common'
+      case $cc_basename in
+      nagfor*)
+        # NAG Fortran compiler
+        lt_prog_compiler_wl='-Wl,-Wl,,'
+        lt_prog_compiler_pic='-PIC'
+        lt_prog_compiler_static='-Bstatic'
+        ;;
+      esac
+      ;;
+
+    mingw* | cygwin* | pw32* | os2* | cegcc*)
+      # This hack is so that the source file can tell whether it is being
+      # built for inclusion in a dll (and should export symbols for example).
+      lt_prog_compiler_pic='-DDLL_EXPORT'
+      case $host_os in
+      os2*)
+	lt_prog_compiler_static='$wl-static'
+	;;
+      esac
+      ;;
+
+    hpux9* | hpux10* | hpux11*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but
+      # not for PA HP-UX.
+      case $host_cpu in
+      hppa*64*|ia64*)
+	# +Z the default
+	;;
+      *)
+	lt_prog_compiler_pic='+Z'
+	;;
+      esac
+      # Is there a better lt_prog_compiler_static that works with the bundled CC?
+      lt_prog_compiler_static='$wl-a ${wl}archive'
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      lt_prog_compiler_wl='-Wl,'
+      # PIC (with -KPIC) is the default.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+      case $cc_basename in
+      # old Intel for x86_64, which still supported -KPIC.
+      ecc*)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-KPIC'
+	lt_prog_compiler_static='-static'
+        ;;
+      # icc used to be incompatible with GCC.
+      # ICC 10 doesn't accept -KPIC any more.
+      icc* | ifort*)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-fPIC'
+	lt_prog_compiler_static='-static'
+        ;;
+      # Lahey Fortran 8.1.
+      lf95*)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='--shared'
+	lt_prog_compiler_static='--static'
+	;;
+      nagfor*)
+	# NAG Fortran compiler
+	lt_prog_compiler_wl='-Wl,-Wl,,'
+	lt_prog_compiler_pic='-PIC'
+	lt_prog_compiler_static='-Bstatic'
+	;;
+      tcc*)
+	# Fabrice Bellard et al's Tiny C Compiler
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-fPIC'
+	lt_prog_compiler_static='-static'
+	;;
+      pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
+        # Portland Group compilers (*not* the Pentium gcc compiler,
+	# which looks to be a dead project)
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-fpic'
+	lt_prog_compiler_static='-Bstatic'
+        ;;
+      ccc*)
+        lt_prog_compiler_wl='-Wl,'
+        # All Alpha code is PIC.
+        lt_prog_compiler_static='-non_shared'
+        ;;
+      xl* | bgxl* | bgf* | mpixl*)
+	# IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene
+	lt_prog_compiler_wl='-Wl,'
+	lt_prog_compiler_pic='-qpic'
+	lt_prog_compiler_static='-qstaticlink'
+	;;
+      *)
+	case `$CC -V 2>&1 | $SED 5q` in
+	*Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*)
+	  # Sun Fortran 8.3 passes all unrecognized flags to the linker
+	  lt_prog_compiler_pic='-KPIC'
+	  lt_prog_compiler_static='-Bstatic'
+	  lt_prog_compiler_wl=''
+	  ;;
+	*Sun\ F* | *Sun*Fortran*)
+	  lt_prog_compiler_pic='-KPIC'
+	  lt_prog_compiler_static='-Bstatic'
+	  lt_prog_compiler_wl='-Qoption ld '
+	  ;;
+	*Sun\ C*)
+	  # Sun C 5.9
+	  lt_prog_compiler_pic='-KPIC'
+	  lt_prog_compiler_static='-Bstatic'
+	  lt_prog_compiler_wl='-Wl,'
+	  ;;
+        *Intel*\ [CF]*Compiler*)
+	  lt_prog_compiler_wl='-Wl,'
+	  lt_prog_compiler_pic='-fPIC'
+	  lt_prog_compiler_static='-static'
+	  ;;
+	*Portland\ Group*)
+	  lt_prog_compiler_wl='-Wl,'
+	  lt_prog_compiler_pic='-fpic'
+	  lt_prog_compiler_static='-Bstatic'
+	  ;;
+	esac
+	;;
+      esac
+      ;;
+
+    newsos6)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *nto* | *qnx*)
+      # QNX uses GNU C++, but need to define -shared option too, otherwise
+      # it will coredump.
+      lt_prog_compiler_pic='-fPIC -shared'
+      ;;
+
+    osf3* | osf4* | osf5*)
+      lt_prog_compiler_wl='-Wl,'
+      # All OSF/1 code is PIC.
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    rdos*)
+      lt_prog_compiler_static='-non_shared'
+      ;;
+
+    solaris*)
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      case $cc_basename in
+      f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
+	lt_prog_compiler_wl='-Qoption ld ';;
+      *)
+	lt_prog_compiler_wl='-Wl,';;
+      esac
+      ;;
+
+    sunos4*)
+      lt_prog_compiler_wl='-Qoption ld '
+      lt_prog_compiler_pic='-PIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	lt_prog_compiler_pic='-Kconform_pic'
+	lt_prog_compiler_static='-Bstatic'
+      fi
+      ;;
+
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_pic='-KPIC'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    unicos*)
+      lt_prog_compiler_wl='-Wl,'
+      lt_prog_compiler_can_build_shared=no
+      ;;
+
+    uts4*)
+      lt_prog_compiler_pic='-pic'
+      lt_prog_compiler_static='-Bstatic'
+      ;;
+
+    *)
+      lt_prog_compiler_can_build_shared=no
+      ;;
+    esac
+  fi
+
+case $host_os in
+  # For platforms that do not support PIC, -DPIC is meaningless:
+  *djgpp*)
+    lt_prog_compiler_pic=
+    ;;
+  *)
+    lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC"
+    ;;
+esac
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
+printf %s "checking for $compiler option to produce PIC... " >&6; }
+if test ${lt_cv_prog_compiler_pic+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5
+printf "%s\n" "$lt_cv_prog_compiler_pic" >&6; }
+lt_prog_compiler_pic=$lt_cv_prog_compiler_pic
+
+#
+# Check to make sure the PIC flag actually works.
+#
+if test -n "$lt_prog_compiler_pic"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
+printf %s "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
+if test ${lt_cv_prog_compiler_pic_works+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_prog_compiler_pic_works=no
+   ac_outfile=conftest.$ac_objext
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+   lt_compiler_flag="$lt_prog_compiler_pic -DPIC"  ## exclude from sc_useless_quotes_in_assignment
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   # The option is referenced via a variable to avoid confusing sed.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>conftest.err)
+   ac_status=$?
+   cat conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s "$ac_outfile"; then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings other than the usual output.
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp
+     $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+     if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_pic_works=yes
+     fi
+   fi
+   $RM conftest*
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
+printf "%s\n" "$lt_cv_prog_compiler_pic_works" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_pic_works"; then
+    case $lt_prog_compiler_pic in
+     "" | " "*) ;;
+     *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
+     esac
+else
+    lt_prog_compiler_pic=
+     lt_prog_compiler_can_build_shared=no
+fi
+
+fi
+
+
+
+
+
+
+
+
+
+
+
+#
+# Check to make sure the static flag actually works.
+#
+wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
+printf %s "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
+if test ${lt_cv_prog_compiler_static_works+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_prog_compiler_static_works=no
+   save_LDFLAGS=$LDFLAGS
+   LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         lt_cv_prog_compiler_static_works=yes
+       fi
+     else
+       lt_cv_prog_compiler_static_works=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS=$save_LDFLAGS
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
+printf "%s\n" "$lt_cv_prog_compiler_static_works" >&6; }
+
+if test yes = "$lt_cv_prog_compiler_static_works"; then
+    :
+else
+    lt_prog_compiler_static=
+fi
+
+
+
+
+
+
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if test ${lt_cv_prog_compiler_c_o+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_prog_compiler_c_o=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+printf "%s\n" "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
+printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
+if test ${lt_cv_prog_compiler_c_o+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_prog_compiler_c_o=no
+   $RM -r conftest 2>/dev/null
+   mkdir conftest
+   cd conftest
+   mkdir out
+   echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+   lt_compiler_flag="-o out/conftest2.$ac_objext"
+   # Insert the option either (1) after the last *FLAGS variable, or
+   # (2) before a word containing "conftest.", or (3) at the end.
+   # Note that $ac_compile itself does not contain backslashes and begins
+   # with a dollar sign (not a hyphen), so the echo should work correctly.
+   lt_compile=`echo "$ac_compile" | $SED \
+   -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
+   -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
+   -e 's:$: $lt_compiler_flag:'`
+   (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5)
+   (eval "$lt_compile" 2>out/conftest.err)
+   ac_status=$?
+   cat out/conftest.err >&5
+   echo "$as_me:$LINENO: \$? = $ac_status" >&5
+   if (exit $ac_status) && test -s out/conftest2.$ac_objext
+   then
+     # The compiler can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp
+     $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2
+     if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then
+       lt_cv_prog_compiler_c_o=yes
+     fi
+   fi
+   chmod u+w . 2>&5
+   $RM conftest*
+   # SGI C++ compiler will create directory out/ii_files/ for
+   # template instantiation
+   test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files
+   $RM out/* && rmdir out
+   cd ..
+   $RM -r conftest
+   $RM conftest*
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
+printf "%s\n" "$lt_cv_prog_compiler_c_o" >&6; }
+
+
+
+
+hard_links=nottested
+if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then
+  # do not overwrite the value of need_locks provided by the user
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
+printf %s "checking if we can lock with hard links... " >&6; }
+  hard_links=yes
+  $RM conftest*
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  touch conftest.a
+  ln conftest.a conftest.b 2>&5 || hard_links=no
+  ln conftest.a conftest.b 2>/dev/null && hard_links=no
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
+printf "%s\n" "$hard_links" >&6; }
+  if test no = "$hard_links"; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5
+printf "%s\n" "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;}
+    need_locks=warn
+  fi
+else
+  need_locks=no
+fi
+
+
+
+
+
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5
+printf %s "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; }
+
+  runpath_var=
+  allow_undefined_flag=
+  always_export_symbols=no
+  archive_cmds=
+  archive_expsym_cmds=
+  compiler_needs_object=no
+  enable_shared_with_static_runtimes=no
+  export_dynamic_flag_spec=
+  export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+  hardcode_automatic=no
+  hardcode_direct=no
+  hardcode_direct_absolute=no
+  hardcode_libdir_flag_spec=
+  hardcode_libdir_separator=
+  hardcode_minus_L=no
+  hardcode_shlibpath_var=unsupported
+  inherit_rpath=no
+  link_all_deplibs=unknown
+  module_cmds=
+  module_expsym_cmds=
+  old_archive_from_new_cmds=
+  old_archive_from_expsyms_cmds=
+  thread_safe_flag_spec=
+  whole_archive_flag_spec=
+  # include_expsyms should be a list of space-separated symbols to be *always*
+  # included in the symbol list
+  include_expsyms=
+  # exclude_expsyms can be an extended regexp of symbols to exclude
+  # it will be wrapped by ' (' and ')$', so one must not match beginning or
+  # end of line.  Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+  # as well as any symbol that contains 'd'.
+  exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+  # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
+  # platforms (ab)use it in PIC code, but their linkers get confused if
+  # the symbol is explicitly referenced.  Since portable code cannot
+  # rely on this symbol name, it's probably fine to never include it in
+  # preloaded symbol tables.
+  # Exclude shared library initialization/finalization symbols.
+  extract_expsyms_cmds=
+
+  case $host_os in
+  cygwin* | mingw* | pw32* | cegcc*)
+    # FIXME: the MSVC++ and ICC port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++ or Intel C++ Compiler.
+    if test yes != "$GCC"; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++ or ICC)
+    with_gnu_ld=yes
+    ;;
+  openbsd* | bitrig*)
+    with_gnu_ld=no
+    ;;
+  esac
+
+  ld_shlibs=yes
+
+  # On some targets, GNU ld is compatible enough with the native linker
+  # that we're better off using the native interface for both.
+  lt_use_gnu_ld_interface=no
+  if test yes = "$with_gnu_ld"; then
+    case $host_os in
+      aix*)
+	# The AIX port of GNU ld has always aspired to compatibility
+	# with the native linker.  However, as the warning in the GNU ld
+	# block says, versions before 2.19.5* couldn't really create working
+	# shared libraries, regardless of the interface used.
+	case `$LD -v 2>&1` in
+	  *\ \(GNU\ Binutils\)\ 2.19.5*) ;;
+	  *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;;
+	  *\ \(GNU\ Binutils\)\ [3-9]*) ;;
+	  *)
+	    lt_use_gnu_ld_interface=yes
+	    ;;
+	esac
+	;;
+      *)
+	lt_use_gnu_ld_interface=yes
+	;;
+    esac
+  fi
+
+  if test yes = "$lt_use_gnu_ld_interface"; then
+    # If archive_cmds runs LD, not CC, wlarc should be empty
+    wlarc='$wl'
+
+    # Set some defaults for GNU ld with shared library support. These
+    # are reset later if shared libraries are not supported. Putting them
+    # here allows them to be overridden if necessary.
+    runpath_var=LD_RUN_PATH
+    hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+    export_dynamic_flag_spec='$wl--export-dynamic'
+    # ancient GNU ld didn't support --whole-archive et. al.
+    if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
+      whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
+    else
+      whole_archive_flag_spec=
+    fi
+    supports_anon_versioning=no
+    case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in
+      *GNU\ gold*) supports_anon_versioning=yes ;;
+      *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
+      *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
+      *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
+      *\ 2.11.*) ;; # other 2.11 versions
+      *) supports_anon_versioning=yes ;;
+    esac
+
+    # See if GNU ld supports shared libraries.
+    case $host_os in
+    aix[3-9]*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test ia64 != "$host_cpu"; then
+	ld_shlibs=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: the GNU linker, at least up to release 2.19, is reported
+*** to be unable to reliably create shared libraries on AIX.
+*** Therefore, libtool is disabling shared libraries support.  If you
+*** really care for shared libraries, you may want to install binutils
+*** 2.20 or above, or modify your PATH so that a non-GNU linker is found.
+*** You will then need to restart the configuration process.
+
+_LT_EOF
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+            archive_expsym_cmds=''
+        ;;
+      m68k)
+            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_minus_L=yes
+        ;;
+      esac
+      ;;
+
+    beos*)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	allow_undefined_flag=unsupported
+	# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
+	# support --undefined.  This deserves some investigation.  FIXME
+	archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
+      # as there is no search path for DLLs.
+      hardcode_libdir_flag_spec='-L$libdir'
+      export_dynamic_flag_spec='$wl--export-all-symbols'
+      allow_undefined_flag=unsupported
+      always_export_symbols=no
+      enable_shared_with_static_runtimes=yes
+      export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
+      exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
+
+      if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
+        archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+	# If the export-symbols file already is a .def file, use it as
+	# is; otherwise, prepend EXPORTS...
+	archive_expsym_cmds='if   test DEF = "`$SED -n     -e '\''s/^[	 ]*//'\''     -e '\''/^\(;.*\)*$/d'\''     -e '\''s/^\(EXPORTS\|LIBRARY\)\([	 ].*\)*$/DEF/p'\''     -e q     $export_symbols`" ; then
+          cp $export_symbols $output_objdir/$soname.def;
+        else
+          echo EXPORTS > $output_objdir/$soname.def;
+          cat $export_symbols >> $output_objdir/$soname.def;
+        fi~
+        $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    haiku*)
+      archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+      link_all_deplibs=yes
+      ;;
+
+    os2*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      allow_undefined_flag=unsupported
+      shrext_cmds=.dll
+      archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+	$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+	$ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+	$ECHO EXPORTS >> $output_objdir/$libname.def~
+	emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+	$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+	emximp -o $lib $output_objdir/$libname.def'
+      archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+	$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+	$ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+	$ECHO EXPORTS >> $output_objdir/$libname.def~
+	prefix_cmds="$SED"~
+	if test EXPORTS = "`$SED 1q $export_symbols`"; then
+	  prefix_cmds="$prefix_cmds -e 1d";
+	fi~
+	prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+	cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+	$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+	emximp -o $lib $output_objdir/$libname.def'
+      old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+      enable_shared_with_static_runtimes=yes
+      file_list_spec='@'
+      ;;
+
+    interix[3-9]*)
+      hardcode_direct=no
+      hardcode_shlibpath_var=no
+      hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+      export_dynamic_flag_spec='$wl-E'
+      # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
+      # Instead, shared libraries are loaded at an image base (0x10000000 by
+      # default) and relocated if they conflict, which is a slow very memory
+      # consuming and fragmenting process.  To avoid this, we pick a random,
+      # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
+      # time.  Moving up from 0x10000000 also allows more sbrk(2) space.
+      archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      archive_expsym_cmds='$SED "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+      ;;
+
+    gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
+      tmp_diet=no
+      if test linux-dietlibc = "$host_os"; then
+	case $cc_basename in
+	  diet\ *) tmp_diet=yes;;	# linux-dietlibc with static linking (!diet-dyn)
+	esac
+      fi
+      if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
+	 && test no = "$tmp_diet"
+      then
+	tmp_addflag=' $pic_flag'
+	tmp_sharedflag='-shared'
+	case $cc_basename,$host_cpu in
+        pgcc*)				# Portland Group C compiler
+	  whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+	  tmp_addflag=' $pic_flag'
+	  ;;
+	pgf77* | pgf90* | pgf95* | pgfortran*)
+					# Portland Group f77 and f90 compilers
+	  whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+	  tmp_addflag=' $pic_flag -Mnomain' ;;
+	ecc*,ia64* | icc*,ia64*)	# Intel C compiler on ia64
+	  tmp_addflag=' -i_dynamic' ;;
+	efc*,ia64* | ifort*,ia64*)	# Intel Fortran compiler on ia64
+	  tmp_addflag=' -i_dynamic -nofor_main' ;;
+	ifc* | ifort*)			# Intel Fortran compiler
+	  tmp_addflag=' -nofor_main' ;;
+	lf95*)				# Lahey Fortran 8.1
+	  whole_archive_flag_spec=
+	  tmp_sharedflag='--shared' ;;
+        nagfor*)                        # NAGFOR 5.3
+          tmp_sharedflag='-Wl,-shared' ;;
+	xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
+	  tmp_sharedflag='-qmkshrobj'
+	  tmp_addflag= ;;
+	nvcc*)	# Cuda Compiler Driver 2.2
+	  whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+	  compiler_needs_object=yes
+	  ;;
+	esac
+	case `$CC -V 2>&1 | $SED 5q` in
+	*Sun\ C*)			# Sun C 5.9
+	  whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
+	  compiler_needs_object=yes
+	  tmp_sharedflag='-G' ;;
+	*Sun\ F*)			# Sun Fortran 8.3
+	  tmp_sharedflag='-G' ;;
+	esac
+	archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+
+        if test yes = "$supports_anon_versioning"; then
+          archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+            cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+            echo "local: *; };" >> $output_objdir/$libname.ver~
+            $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
+        fi
+
+	case $cc_basename in
+	tcc*)
+	  export_dynamic_flag_spec='-rdynamic'
+	  ;;
+	xlf* | bgf* | bgxlf* | mpixlf*)
+	  # IBM XL Fortran 10.1 on PPC cannot create shared libs itself
+	  whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
+	  hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+	  archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
+	  if test yes = "$supports_anon_versioning"; then
+	    archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+              cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+              echo "local: *; };" >> $output_objdir/$libname.ver~
+              $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+	  fi
+	  ;;
+	esac
+      else
+        ld_shlibs=no
+      fi
+      ;;
+
+    netbsd*)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
+	wlarc=
+      else
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+      fi
+      ;;
+
+    solaris*)
+      if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then
+	ld_shlibs=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: The releases 2.8.* of the GNU linker cannot reliably
+*** create shared libraries on Solaris systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.9.1 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+      elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+	ld_shlibs=no
+	cat <<_LT_EOF 1>&2
+
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
+*** reliably create shared libraries on SCO systems.  Therefore, libtool
+*** is disabling shared libraries support.  We urge you to upgrade GNU
+*** binutils to release 2.16.91.0.3 or newer.  Another option is to modify
+*** your PATH or compiler configuration so that the native linker is
+*** used, and then restart.
+
+_LT_EOF
+	;;
+	*)
+	  # For security reasons, it is highly recommended that you always
+	  # use absolute paths for naming shared libraries, and exclude the
+	  # DT_RUNPATH tag from executables and libraries.  But doing so
+	  # requires that you compile everything twice, which is a pain.
+	  if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	    hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+	    archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+	    archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+	  else
+	    ld_shlibs=no
+	  fi
+	;;
+      esac
+      ;;
+
+    sunos4*)
+      archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      wlarc=
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+	archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
+      else
+	ld_shlibs=no
+      fi
+      ;;
+    esac
+
+    if test no = "$ld_shlibs"; then
+      runpath_var=
+      hardcode_libdir_flag_spec=
+      export_dynamic_flag_spec=
+      whole_archive_flag_spec=
+    fi
+  else
+    # PORTME fill in a description of your system's linker (not GNU ld)
+    case $host_os in
+    aix3*)
+      allow_undefined_flag=unsupported
+      always_export_symbols=yes
+      archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname'
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      hardcode_minus_L=yes
+      if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
+	# Neither direct hardcoding nor static linking is supported with a
+	# broken collect2.
+	hardcode_direct=unsupported
+      fi
+      ;;
+
+    aix[4-9]*)
+      if test ia64 = "$host_cpu"; then
+	# On IA64, the linker does run time linking by default, so we don't
+	# have to do anything special.
+	aix_use_runtimelinking=no
+	exp_sym_flag='-Bexport'
+	no_entry_flag=
+      else
+	# If we're using GNU nm, then we don't want the "-C" option.
+	# -C means demangle to GNU nm, but means don't demangle to AIX nm.
+	# Without the "-l" option, or with the "-B" option, AIX nm treats
+	# weak defined symbols like other global defined symbols, whereas
+	# GNU nm marks them as "W".
+	# While the 'weak' keyword is ignored in the Export File, we need
+	# it in the Import File for the 'aix-soname' feature, so we have
+	# to replace the "-B" option with "-P" for AIX nm.
+	if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
+	  export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
+	else
+	  export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "L") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
+	fi
+	aix_use_runtimelinking=no
+
+	# Test if we are trying to use run time linking or normal
+	# AIX style linking. If -brtl is somewhere in LDFLAGS, we
+	# have runtime linking enabled, and use it for executables.
+	# For shared libraries, we enable/disable runtime linking
+	# depending on the kind of the shared library created -
+	# when "with_aix_soname,aix_use_runtimelinking" is:
+	# "aix,no"   lib.a(lib.so.V) shared, rtl:no,  for executables
+	# "aix,yes"  lib.so          shared, rtl:yes, for executables
+	#            lib.a           static archive
+	# "both,no"  lib.so.V(shr.o) shared, rtl:yes
+	#            lib.a(lib.so.V) shared, rtl:no,  for executables
+	# "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+	#            lib.a(lib.so.V) shared, rtl:no
+	# "svr4,*"   lib.so.V(shr.o) shared, rtl:yes, for executables
+	#            lib.a           static archive
+	case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
+	  for ld_flag in $LDFLAGS; do
+	  if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
+	    aix_use_runtimelinking=yes
+	    break
+	  fi
+	  done
+	  if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+	    # With aix-soname=svr4, we create the lib.so.V shared archives only,
+	    # so we don't have lib.a shared libs to link our executables.
+	    # We have to force runtime linking in this case.
+	    aix_use_runtimelinking=yes
+	    LDFLAGS="$LDFLAGS -Wl,-brtl"
+	  fi
+	  ;;
+	esac
+
+	exp_sym_flag='-bexport'
+	no_entry_flag='-bnoentry'
+      fi
+
+      # When large executables or shared objects are built, AIX ld can
+      # have problems creating the table of contents.  If linking a library
+      # or program results in "error TOC overflow" add -mminimal-toc to
+      # CXXFLAGS/CFLAGS for g++/gcc.  In the cases where that is not
+      # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS.
+
+      archive_cmds=''
+      hardcode_direct=yes
+      hardcode_direct_absolute=yes
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+      file_list_spec='$wl-f,'
+      case $with_aix_soname,$aix_use_runtimelinking in
+      aix,*) ;; # traditional, no import file
+      svr4,* | *,yes) # use import file
+	# The Import File defines what to hardcode.
+	hardcode_direct=no
+	hardcode_direct_absolute=no
+	;;
+      esac
+
+      if test yes = "$GCC"; then
+	case $host_os in aix4.[012]|aix4.[012].*)
+	# We only want to do this on AIX 4.2 and lower, the check
+	# below for broken collect2 doesn't work under 4.3+
+	  collect2name=`$CC -print-prog-name=collect2`
+	  if test -f "$collect2name" &&
+	   strings "$collect2name" | $GREP resolve_lib_name >/dev/null
+	  then
+	  # We have reworked collect2
+	  :
+	  else
+	  # We have old collect2
+	  hardcode_direct=unsupported
+	  # It fails to find uninstalled libraries when the uninstalled
+	  # path is not listed in the libpath.  Setting hardcode_minus_L
+	  # to unsupported forces relinking
+	  hardcode_minus_L=yes
+	  hardcode_libdir_flag_spec='-L$libdir'
+	  hardcode_libdir_separator=
+	  fi
+	  ;;
+	esac
+	shared_flag='-shared'
+	if test yes = "$aix_use_runtimelinking"; then
+	  shared_flag="$shared_flag "'$wl-G'
+	fi
+	# Need to ensure runtime linking is disabled for the traditional
+	# shared library, or the linker may eventually find shared libraries
+	# /with/ Import File - we do not want to mix them.
+	shared_flag_aix='-shared'
+	shared_flag_svr4='-shared $wl-G'
+      else
+	# not using gcc
+	if test ia64 = "$host_cpu"; then
+	# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
+	# chokes on -Wl,-G. The following line is correct:
+	  shared_flag='-G'
+	else
+	  if test yes = "$aix_use_runtimelinking"; then
+	    shared_flag='$wl-G'
+	  else
+	    shared_flag='$wl-bM:SRE'
+	  fi
+	  shared_flag_aix='$wl-bM:SRE'
+	  shared_flag_svr4='$wl-G'
+	fi
+      fi
+
+      export_dynamic_flag_spec='$wl-bexpall'
+      # It seems that -bexpall does not export symbols beginning with
+      # underscore (_), so it is better to generate a list of symbols to export.
+      always_export_symbols=yes
+      if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
+	# Warning - without using the other runtime loading flags (-brtl),
+	# -berok will link without error, but may produce a broken library.
+	allow_undefined_flag='-berok'
+        # Determine the default libpath from the value encoded in an
+        # empty executable.
+        if test set = "${lt_cv_aix_libpath+set}"; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  if test ${lt_cv_aix_libpath_+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+
+  lt_aix_libpath_sed='
+      /Import File Strings/,/^$/ {
+	  /^0/ {
+	      s/^0  *\([^ ]*\) *$/\1/
+	      p
+	  }
+      }'
+  lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_=/usr/lib:/lib
+  fi
+
+fi
+
+  aix_libpath=$lt_cv_aix_libpath_
+fi
+
+        hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
+        archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
+      else
+	if test ia64 = "$host_cpu"; then
+	  hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib'
+	  allow_undefined_flag="-z nodefs"
+	  archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
+	else
+	 # Determine the default libpath from the value encoded in an
+	 # empty executable.
+	 if test set = "${lt_cv_aix_libpath+set}"; then
+  aix_libpath=$lt_cv_aix_libpath
+else
+  if test ${lt_cv_aix_libpath_+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+
+  lt_aix_libpath_sed='
+      /Import File Strings/,/^$/ {
+	  /^0/ {
+	      s/^0  *\([^ ]*\) *$/\1/
+	      p
+	  }
+      }'
+  lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  # Check for a 64-bit object if we didn't find anything.
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
+  fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+  if test -z "$lt_cv_aix_libpath_"; then
+    lt_cv_aix_libpath_=/usr/lib:/lib
+  fi
+
+fi
+
+  aix_libpath=$lt_cv_aix_libpath_
+fi
+
+	 hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
+	  # Warning - without using the other run time loading flags,
+	  # -berok will link without error, but may produce a broken library.
+	  no_undefined_flag=' $wl-bernotok'
+	  allow_undefined_flag=' $wl-berok'
+	  if test yes = "$with_gnu_ld"; then
+	    # We only use this code for GNU lds that support --whole-archive.
+	    whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive'
+	  else
+	    # Exported symbols can be pulled into shared objects from archives
+	    whole_archive_flag_spec='$convenience'
+	  fi
+	  archive_cmds_need_lc=yes
+	  archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+	  # -brtl affects multiple linker settings, -berok does not and is overridden later
+	  compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`'
+	  if test svr4 != "$with_aix_soname"; then
+	    # This is similar to how AIX traditionally builds its shared libraries.
+	    archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+	  fi
+	  if test aix != "$with_aix_soname"; then
+	    archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+	  else
+	    # used by -dlpreopen to get the symbols
+	    archive_expsym_cmds="$archive_expsym_cmds"'~$MV  $output_objdir/$realname.d/$soname $output_objdir'
+	  fi
+	  archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d'
+	fi
+      fi
+      ;;
+
+    amigaos*)
+      case $host_cpu in
+      powerpc)
+            # see comment about AmigaOS4 .so support
+            archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+            archive_expsym_cmds=''
+        ;;
+      m68k)
+            archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)'
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_minus_L=yes
+        ;;
+      esac
+      ;;
+
+    bsdi[45]*)
+      export_dynamic_flag_spec=-rdynamic
+      ;;
+
+    cygwin* | mingw* | pw32* | cegcc*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++ or Intel C++ Compiler.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      case $cc_basename in
+      cl* | icl*)
+	# Native MSVC or ICC
+	hardcode_libdir_flag_spec=' '
+	allow_undefined_flag=unsupported
+	always_export_symbols=yes
+	file_list_spec='@'
+	# Tell ltmain to make .lib files, not .a files.
+	libext=lib
+	# Tell ltmain to make .dll files, not .so files.
+	shrext_cmds=.dll
+	# FIXME: Setting linknames here is a bad hack.
+	archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+	archive_expsym_cmds='if   test DEF = "`$SED -n     -e '\''s/^[	 ]*//'\''     -e '\''/^\(;.*\)*$/d'\''     -e '\''s/^\(EXPORTS\|LIBRARY\)\([	 ].*\)*$/DEF/p'\''     -e q     $export_symbols`" ; then
+            cp "$export_symbols" "$output_objdir/$soname.def";
+            echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+          else
+            $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+          fi~
+          $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+          linknames='
+	# The linker will not automatically build a static lib if we build a DLL.
+	# _LT_TAGVAR(old_archive_from_new_cmds, )='true'
+	enable_shared_with_static_runtimes=yes
+	exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+	export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
+	# Don't use ranlib
+	old_postinstall_cmds='chmod 644 $oldlib'
+	postlink_cmds='lt_outputfile="@OUTPUT@"~
+          lt_tool_outputfile="@TOOL_OUTPUT@"~
+          case $lt_outputfile in
+            *.exe|*.EXE) ;;
+            *)
+              lt_outputfile=$lt_outputfile.exe
+              lt_tool_outputfile=$lt_tool_outputfile.exe
+              ;;
+          esac~
+          if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+            $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+            $RM "$lt_outputfile.manifest";
+          fi'
+	;;
+      *)
+	# Assume MSVC and ICC wrapper
+	hardcode_libdir_flag_spec=' '
+	allow_undefined_flag=unsupported
+	# Tell ltmain to make .lib files, not .a files.
+	libext=lib
+	# Tell ltmain to make .dll files, not .so files.
+	shrext_cmds=.dll
+	# FIXME: Setting linknames here is a bad hack.
+	archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
+	# The linker will automatically build a .lib file if we build a DLL.
+	old_archive_from_new_cmds='true'
+	# FIXME: Should let the user specify the lib program.
+	old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs'
+	enable_shared_with_static_runtimes=yes
+	;;
+      esac
+      ;;
+
+    darwin* | rhapsody*)
+
+
+  archive_cmds_need_lc=no
+  hardcode_direct=no
+  hardcode_automatic=yes
+  hardcode_shlibpath_var=unsupported
+  if test yes = "$lt_cv_ld_force_load"; then
+    whole_archive_flag_spec='`for conv in $convenience\"\"; do test  -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+
+  else
+    whole_archive_flag_spec=''
+  fi
+  link_all_deplibs=yes
+  allow_undefined_flag=$_lt_dar_allow_undefined
+  case $cc_basename in
+     ifort*|nagfor*) _lt_dar_can_shared=yes ;;
+     *) _lt_dar_can_shared=$GCC ;;
+  esac
+  if test yes = "$_lt_dar_can_shared"; then
+    output_verbose_link_cmd=func_echo_all
+    archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+    module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+    archive_expsym_cmds="$SED 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+    module_expsym_cmds="$SED -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
+
+  else
+  ld_shlibs=no
+  fi
+
+      ;;
+
+    dgux*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
+    # support.  Future versions do this automatically, but an explicit c++rt0.o
+    # does not break anything, and helps significantly (at the cost of a little
+    # extra space).
+    freebsd2.2*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # Unfortunately, older versions of FreeBSD 2 do not have this feature.
+    freebsd2.*)
+      archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    # FreeBSD 3 and greater uses gcc -shared to do shared libraries.
+    freebsd* | dragonfly* | midnightbsd*)
+      archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    hpux9*)
+      if test yes = "$GCC"; then
+	archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+      else
+	archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
+      fi
+      hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+      hardcode_libdir_separator=:
+      hardcode_direct=yes
+
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      hardcode_minus_L=yes
+      export_dynamic_flag_spec='$wl-E'
+      ;;
+
+    hpux10*)
+      if test yes,no = "$GCC,$with_gnu_ld"; then
+	archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      if test no = "$with_gnu_ld"; then
+	hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+	hardcode_libdir_separator=:
+	hardcode_direct=yes
+	hardcode_direct_absolute=yes
+	export_dynamic_flag_spec='$wl-E'
+	# hardcode_minus_L: Not really in the search PATH,
+	# but as the default location of the library.
+	hardcode_minus_L=yes
+      fi
+      ;;
+
+    hpux11*)
+      if test yes,no = "$GCC,$with_gnu_ld"; then
+	case $host_cpu in
+	hppa*64*)
+	  archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	ia64*)
+	  archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+	  archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	esac
+      else
+	case $host_cpu in
+	hppa*64*)
+	  archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	ia64*)
+	  archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+	  ;;
+	*)
+
+	  # Older versions of the 11.00 compiler do not understand -b yet
+	  # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
+	  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5
+printf %s "checking if $CC understands -b... " >&6; }
+if test ${lt_cv_prog_compiler__b+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_prog_compiler__b=no
+   save_LDFLAGS=$LDFLAGS
+   LDFLAGS="$LDFLAGS -b"
+   echo "$lt_simple_link_test_code" > conftest.$ac_ext
+   if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
+     # The linker can only warn and ignore the option if not recognized
+     # So say no if there are warnings
+     if test -s conftest.err; then
+       # Append any errors to the config.log.
+       cat conftest.err 1>&5
+       $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp
+       $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2
+       if diff conftest.exp conftest.er2 >/dev/null; then
+         lt_cv_prog_compiler__b=yes
+       fi
+     else
+       lt_cv_prog_compiler__b=yes
+     fi
+   fi
+   $RM -r conftest*
+   LDFLAGS=$save_LDFLAGS
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
+printf "%s\n" "$lt_cv_prog_compiler__b" >&6; }
+
+if test yes = "$lt_cv_prog_compiler__b"; then
+    archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+else
+    archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
+fi
+
+	  ;;
+	esac
+      fi
+      if test no = "$with_gnu_ld"; then
+	hardcode_libdir_flag_spec='$wl+b $wl$libdir'
+	hardcode_libdir_separator=:
+
+	case $host_cpu in
+	hppa*64*|ia64*)
+	  hardcode_direct=no
+	  hardcode_shlibpath_var=no
+	  ;;
+	*)
+	  hardcode_direct=yes
+	  hardcode_direct_absolute=yes
+	  export_dynamic_flag_spec='$wl-E'
+
+	  # hardcode_minus_L: Not really in the search PATH,
+	  # but as the default location of the library.
+	  hardcode_minus_L=yes
+	  ;;
+	esac
+      fi
+      ;;
+
+    irix5* | irix6* | nonstopux*)
+      if test yes = "$GCC"; then
+	archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+	# Try to use the -exported_symbol ld option, if it does not
+	# work, assume that -exports_file does not work either and
+	# implicitly export all symbols.
+	# This should be the same for all languages, so no per-tag cache variable.
+	{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5
+printf %s "checking whether the $host_os linker accepts -exported_symbol... " >&6; }
+if test ${lt_cv_irix_exported_symbol+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  save_LDFLAGS=$LDFLAGS
+	   LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
+	   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+int foo (void) { return 0; }
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  lt_cv_irix_exported_symbol=yes
+else $as_nop
+  lt_cv_irix_exported_symbol=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+           LDFLAGS=$save_LDFLAGS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
+printf "%s\n" "$lt_cv_irix_exported_symbol" >&6; }
+	if test yes = "$lt_cv_irix_exported_symbol"; then
+          archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
+	fi
+      else
+	archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+	archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+      hardcode_libdir_separator=:
+      inherit_rpath=yes
+      link_all_deplibs=yes
+      ;;
+
+    linux*)
+      case $cc_basename in
+      tcc*)
+	# Fabrice Bellard et al's Tiny C Compiler
+	ld_shlibs=yes
+	archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	;;
+      esac
+      ;;
+
+    netbsd*)
+      if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+	archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'  # a.out
+      else
+	archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags'      # ELF
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    newsos6)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_direct=yes
+      hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+      hardcode_libdir_separator=:
+      hardcode_shlibpath_var=no
+      ;;
+
+    *nto* | *qnx*)
+      ;;
+
+    openbsd* | bitrig*)
+      if test -f /usr/libexec/ld.so; then
+	hardcode_direct=yes
+	hardcode_shlibpath_var=no
+	hardcode_direct_absolute=yes
+	if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+	  archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	  archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+	  hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+	  export_dynamic_flag_spec='$wl-E'
+	else
+	  archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+	  hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+	fi
+      else
+	ld_shlibs=no
+      fi
+      ;;
+
+    os2*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      allow_undefined_flag=unsupported
+      shrext_cmds=.dll
+      archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+	$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+	$ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+	$ECHO EXPORTS >> $output_objdir/$libname.def~
+	emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+	$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+	emximp -o $lib $output_objdir/$libname.def'
+      archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+	$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+	$ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+	$ECHO EXPORTS >> $output_objdir/$libname.def~
+	prefix_cmds="$SED"~
+	if test EXPORTS = "`$SED 1q $export_symbols`"; then
+	  prefix_cmds="$prefix_cmds -e 1d";
+	fi~
+	prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+	cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+	$CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+	emximp -o $lib $output_objdir/$libname.def'
+      old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+      enable_shared_with_static_runtimes=yes
+      file_list_spec='@'
+      ;;
+
+    osf3*)
+      if test yes = "$GCC"; then
+	allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+	archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+      else
+	allow_undefined_flag=' -expect_unresolved \*'
+	archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+      hardcode_libdir_separator=:
+      ;;
+
+    osf4* | osf5*)	# as osf3* with the addition of -msym flag
+      if test yes = "$GCC"; then
+	allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+	archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+	hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+      else
+	allow_undefined_flag=' -expect_unresolved \*'
+	archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+	archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
+          $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
+
+	# Both c and cxx compiler support -rpath directly
+	hardcode_libdir_flag_spec='-rpath $libdir'
+      fi
+      archive_cmds_need_lc='no'
+      hardcode_libdir_separator=:
+      ;;
+
+    solaris*)
+      no_undefined_flag=' -z defs'
+      if test yes = "$GCC"; then
+	wlarc='$wl'
+	archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+          $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+      else
+	case `$CC -V 2>&1` in
+	*"Compilers 5.0"*)
+	  wlarc=''
+	  archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+            $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+	  ;;
+	*)
+	  wlarc='$wl'
+	  archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+	  archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
+            $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+	  ;;
+	esac
+      fi
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_shlibpath_var=no
+      case $host_os in
+      solaris2.[0-5] | solaris2.[0-5].*) ;;
+      *)
+	# The compiler driver will combine and reorder linker options,
+	# but understands '-z linker_flag'.  GCC discards it without '$wl',
+	# but is careful enough not to reorder.
+	# Supported since Solaris 2.6 (maybe 2.5.1?)
+	if test yes = "$GCC"; then
+	  whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
+	else
+	  whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
+	fi
+	;;
+      esac
+      link_all_deplibs=yes
+      ;;
+
+    sunos4*)
+      if test sequent = "$host_vendor"; then
+	# Use $CC to link under sequent, because it throws in some extra .o
+	# files that make .init and .fini sections work.
+	archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
+      fi
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4)
+      case $host_vendor in
+	sni)
+	  archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct=yes # is this really true???
+	;;
+	siemens)
+	  ## LD is ld it makes a PLAMLIB
+	  ## CC just makes a GrossModule.
+	  archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags'
+	  reload_cmds='$CC -r -o $output$reload_objs'
+	  hardcode_direct=no
+        ;;
+	motorola)
+	  archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	  hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+	;;
+      esac
+      runpath_var='LD_RUN_PATH'
+      hardcode_shlibpath_var=no
+      ;;
+
+    sysv4.3*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_shlibpath_var=no
+      export_dynamic_flag_spec='-Bexport'
+      ;;
+
+    sysv4*MP*)
+      if test -d /usr/nec; then
+	archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+	hardcode_shlibpath_var=no
+	runpath_var=LD_RUN_PATH
+	hardcode_runpath_var=yes
+	ld_shlibs=yes
+      fi
+      ;;
+
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+      no_undefined_flag='$wl-z,text'
+      archive_cmds_need_lc=no
+      hardcode_shlibpath_var=no
+      runpath_var='LD_RUN_PATH'
+
+      if test yes = "$GCC"; then
+	archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    sysv5* | sco3.2v5* | sco5v6*)
+      # Note: We CANNOT use -z defs as we might desire, because we do not
+      # link with -lc, and that would cause any symbols used from libc to
+      # always be unresolved, which means just about no library would
+      # ever link correctly.  If we're not using GNU ld we use -z text
+      # though, which does catch some bad symbols but isn't as heavy-handed
+      # as -z defs.
+      no_undefined_flag='$wl-z,text'
+      allow_undefined_flag='$wl-z,nodefs'
+      archive_cmds_need_lc=no
+      hardcode_shlibpath_var=no
+      hardcode_libdir_flag_spec='$wl-R,$libdir'
+      hardcode_libdir_separator=':'
+      link_all_deplibs=yes
+      export_dynamic_flag_spec='$wl-Bexport'
+      runpath_var='LD_RUN_PATH'
+
+      if test yes = "$GCC"; then
+	archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      else
+	archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+	archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+      fi
+      ;;
+
+    uts4*)
+      archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_shlibpath_var=no
+      ;;
+
+    *)
+      ld_shlibs=no
+      ;;
+    esac
+
+    if test sni = "$host_vendor"; then
+      case $host in
+      sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
+	export_dynamic_flag_spec='$wl-Blargedynsym'
+	;;
+      esac
+    fi
+  fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
+printf "%s\n" "$ld_shlibs" >&6; }
+test no = "$ld_shlibs" && can_build_shared=no
+
+with_gnu_ld=$with_gnu_ld
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#
+# Do we need to explicitly link libc?
+#
+case "x$archive_cmds_need_lc" in
+x|xyes)
+  # Assume -lc should be added
+  archive_cmds_need_lc=yes
+
+  if test yes,yes = "$GCC,$enable_shared"; then
+    case $archive_cmds in
+    *'~'*)
+      # FIXME: we may have to deal with multi-command sequences.
+      ;;
+    '$CC '*)
+      # Test whether the compiler implicitly links with -lc since on some
+      # systems, -lgcc has to come before -lc. If gcc already passes -lc
+      # to ld, don't add -lc before -lgcc.
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
+printf %s "checking whether -lc should be explicitly linked in... " >&6; }
+if test ${lt_cv_archive_cmds_need_lc+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  $RM conftest*
+	echo "$lt_simple_compile_test_code" > conftest.$ac_ext
+
+	if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } 2>conftest.err; then
+	  soname=conftest
+	  lib=conftest
+	  libobjs=conftest.$ac_objext
+	  deplibs=
+	  wl=$lt_prog_compiler_wl
+	  pic_flag=$lt_prog_compiler_pic
+	  compiler_flags=-v
+	  linker_flags=-v
+	  verstring=
+	  output_objdir=.
+	  libname=conftest
+	  lt_save_allow_undefined_flag=$allow_undefined_flag
+	  allow_undefined_flag=
+	  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5
+  (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+	  then
+	    lt_cv_archive_cmds_need_lc=no
+	  else
+	    lt_cv_archive_cmds_need_lc=yes
+	  fi
+	  allow_undefined_flag=$lt_save_allow_undefined_flag
+	else
+	  cat conftest.err 1>&5
+	fi
+	$RM conftest*
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5
+printf "%s\n" "$lt_cv_archive_cmds_need_lc" >&6; }
+      archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc
+      ;;
+    esac
+  fi
+  ;;
+esac
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
+printf %s "checking dynamic linker characteristics... " >&6; }
+
+if test yes = "$GCC"; then
+  case $host_os in
+    darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+    *) lt_awk_arg='/^libraries:/' ;;
+  esac
+  case $host_os in
+    mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;;
+    *) lt_sed_strip_eq='s|=/|/|g' ;;
+  esac
+  lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
+  case $lt_search_path_spec in
+  *\;*)
+    # if the path contains ";" then we assume it to be the separator
+    # otherwise default to the standard path separator (i.e. ":") - it is
+    # assumed that no part of a normal pathname contains ";" but that should
+    # okay in the real world where ";" in dirpaths is itself problematic.
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'`
+    ;;
+  *)
+    lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"`
+    ;;
+  esac
+  # Ok, now we have the path, separated by spaces, we can step through it
+  # and add multilib dir if necessary...
+  lt_tmp_lt_search_path_spec=
+  lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+  # ...but if some path component already ends with the multilib dir we assume
+  # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+  case "$lt_multi_os_dir; $lt_search_path_spec " in
+  "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+    lt_multi_os_dir=
+    ;;
+  esac
+  for lt_sys_path in $lt_search_path_spec; do
+    if test -d "$lt_sys_path$lt_multi_os_dir"; then
+      lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
+    elif test -n "$lt_multi_os_dir"; then
+      test -d "$lt_sys_path" && \
+	lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
+    fi
+  done
+  lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
+BEGIN {RS = " "; FS = "/|\n";} {
+  lt_foo = "";
+  lt_count = 0;
+  for (lt_i = NF; lt_i > 0; lt_i--) {
+    if ($lt_i != "" && $lt_i != ".") {
+      if ($lt_i == "..") {
+        lt_count++;
+      } else {
+        if (lt_count == 0) {
+          lt_foo = "/" $lt_i lt_foo;
+        } else {
+          lt_count--;
+        }
+      }
+    }
+  }
+  if (lt_foo != "") { lt_freq[lt_foo]++; }
+  if (lt_freq[lt_foo] == 1) { print lt_foo; }
+}'`
+  # AWK program above erroneously prepends '/' to C:/dos/paths
+  # for these hosts.
+  case $host_os in
+    mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
+      $SED 's|/\([A-Za-z]:\)|\1|g'` ;;
+  esac
+  sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
+else
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+fi
+library_names_spec=
+libname_spec='lib$name'
+soname_spec=
+shrext_cmds=.so
+postinstall_cmds=
+postuninstall_cmds=
+finish_cmds=
+finish_eval=
+shlibpath_var=
+shlibpath_overrides_runpath=unknown
+version_type=none
+dynamic_linker="$host_os ld.so"
+sys_lib_dlsearch_path_spec="/lib /usr/lib"
+need_lib_prefix=unknown
+hardcode_into_libs=no
+
+# when you set need_version to no, make sure it does not cause -set_version
+# flags to be left without arguments
+need_version=unknown
+
+
+
+case $host_os in
+aix3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
+  shlibpath_var=LIBPATH
+
+  # AIX 3 has no versioning support, so we append a major version to the name.
+  soname_spec='$libname$release$shared_ext$major'
+  ;;
+
+aix[4-9]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  hardcode_into_libs=yes
+  if test ia64 = "$host_cpu"; then
+    # AIX 5 supports IA64
+    library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
+    shlibpath_var=LD_LIBRARY_PATH
+  else
+    # With GCC up to 2.95.x, collect2 would create an import file
+    # for dependence libraries.  The import file would start with
+    # the line '#! .'.  This would cause the generated library to
+    # depend on '.', always an invalid library.  This was fixed in
+    # development snapshots of GCC prior to 3.0.
+    case $host_os in
+      aix4 | aix4.[01] | aix4.[01].*)
+      if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
+	   echo ' yes '
+	   echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
+	:
+      else
+	can_build_shared=no
+      fi
+      ;;
+    esac
+    # Using Import Files as archive members, it is possible to support
+    # filename-based versioning of shared library archives on AIX. While
+    # this would work for both with and without runtime linking, it will
+    # prevent static linking of such archives. So we do filename-based
+    # shared library versioning with .so extension only, which is used
+    # when both runtime linking and shared linking is enabled.
+    # Unfortunately, runtime linking may impact performance, so we do
+    # not want this to be the default eventually. Also, we use the
+    # versioned .so libs for executables only if there is the -brtl
+    # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+    # To allow for filename-based versioning support, we need to create
+    # libNAME.so.V as an archive file, containing:
+    # *) an Import File, referring to the versioned filename of the
+    #    archive as well as the shared archive member, telling the
+    #    bitwidth (32 or 64) of that shared object, and providing the
+    #    list of exported symbols of that shared object, eventually
+    #    decorated with the 'weak' keyword
+    # *) the shared object with the F_LOADONLY flag set, to really avoid
+    #    it being seen by the linker.
+    # At run time we better use the real file rather than another symlink,
+    # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+    case $with_aix_soname,$aix_use_runtimelinking in
+    # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
+    # soname into executable. Probably we can add versioning support to
+    # collect2, so additional links can be useful in future.
+    aix,yes) # traditional libtool
+      dynamic_linker='AIX unversionable lib.so'
+      # If using run time linking (on AIX 4.2 or later) use lib<name>.so
+      # instead of lib<name>.a to let people know that these are not
+      # typical AIX shared libraries.
+      library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+      ;;
+    aix,no) # traditional AIX only
+      dynamic_linker='AIX lib.a(lib.so.V)'
+      # We preserve .a as extension for shared libraries through AIX4.2
+      # and later when we are not doing run time linking.
+      library_names_spec='$libname$release.a $libname.a'
+      soname_spec='$libname$release$shared_ext$major'
+      ;;
+    svr4,*) # full svr4 only
+      dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)"
+      library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+      # We do not specify a path in Import Files, so LIBPATH fires.
+      shlibpath_overrides_runpath=yes
+      ;;
+    *,yes) # both, prefer svr4
+      dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)"
+      library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+      # unpreferred sharedlib libNAME.a needs extra handling
+      postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+      postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
+      # We do not specify a path in Import Files, so LIBPATH fires.
+      shlibpath_overrides_runpath=yes
+      ;;
+    *,no) # both, prefer aix
+      dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)"
+      library_names_spec='$libname$release.a $libname.a'
+      soname_spec='$libname$release$shared_ext$major'
+      # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
+      postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+      postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+      ;;
+    esac
+    shlibpath_var=LIBPATH
+  fi
+  ;;
+
+amigaos*)
+  case $host_cpu in
+  powerpc)
+    # Since July 2007 AmigaOS4 officially supports .so libraries.
+    # When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
+    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+    ;;
+  m68k)
+    library_names_spec='$libname.ixlibrary $libname.a'
+    # Create ${libname}_ixlibrary.a entries in /sys/libs.
+    finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+    ;;
+  esac
+  ;;
+
+beos*)
+  library_names_spec='$libname$shared_ext'
+  dynamic_linker="$host_os ld.so"
+  shlibpath_var=LIBRARY_PATH
+  ;;
+
+bsdi[45]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib"
+  # the default ld.so.conf also contains /usr/contrib/lib and
+  # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow
+  # libtool to hard-code these into programs
+  ;;
+
+cygwin* | mingw* | pw32* | cegcc*)
+  version_type=windows
+  shrext_cmds=.dll
+  need_version=no
+  need_lib_prefix=no
+
+  case $GCC,$cc_basename in
+  yes,*)
+    # gcc
+    library_names_spec='$libname.dll.a'
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \$file`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname~
+      chmod a+x \$dldir/$dlname~
+      if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+        eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+      fi'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+
+    case $host_os in
+    cygwin*)
+      # Cygwin DLLs use 'cyg' prefix rather than 'lib'
+      soname_spec='`echo $libname | $SED -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+
+      sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"
+      ;;
+    mingw* | cegcc*)
+      # MinGW DLLs use traditional 'lib' prefix
+      soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+      ;;
+    pw32*)
+      # pw32 DLLs use 'pw' prefix rather than 'lib'
+      library_names_spec='`echo $libname | $SED -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+      ;;
+    esac
+    dynamic_linker='Win32 ld.exe'
+    ;;
+
+  *,cl* | *,icl*)
+    # Native MSVC or ICC
+    libname_spec='$name'
+    soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+    library_names_spec='$libname.dll.lib'
+
+    case $build_os in
+    mingw*)
+      sys_lib_search_path_spec=
+      lt_save_ifs=$IFS
+      IFS=';'
+      for lt_path in $LIB
+      do
+        IFS=$lt_save_ifs
+        # Let DOS variable expansion print the short 8.3 style file name.
+        lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"`
+        sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path"
+      done
+      IFS=$lt_save_ifs
+      # Convert to MSYS style.
+      sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'`
+      ;;
+    cygwin*)
+      # Convert to unix form, then to dos form, then back to unix form
+      # but this time dos style (no spaces!) so that the unix form looks
+      # like /cygdrive/c/PROGRA~1:/cygdr...
+      sys_lib_search_path_spec=`cygpath --path --unix "$LIB"`
+      sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null`
+      sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      ;;
+    *)
+      sys_lib_search_path_spec=$LIB
+      if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
+        # It is most probably a Windows format PATH.
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
+      else
+        sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
+      fi
+      # FIXME: find the short name or the path components, as spaces are
+      # common. (e.g. "Program Files" -> "PROGRA~1")
+      ;;
+    esac
+
+    # DLL is installed to $(libdir)/../bin by postinstall_cmds
+    postinstall_cmds='base_file=`basename \$file`~
+      dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
+      dldir=$destdir/`dirname \$dlpath`~
+      test -d \$dldir || mkdir -p \$dldir~
+      $install_prog $dir/$dlname \$dldir/$dlname'
+    postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~
+      dlpath=$dir/\$dldll~
+       $RM \$dlpath'
+    shlibpath_overrides_runpath=yes
+    dynamic_linker='Win32 link.exe'
+    ;;
+
+  *)
+    # Assume MSVC and ICC wrapper
+    library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'
+    dynamic_linker='Win32 ld.exe'
+    ;;
+  esac
+  # FIXME: first we should search . and the directory the executable is in
+  shlibpath_var=PATH
+  ;;
+
+darwin* | rhapsody*)
+  dynamic_linker="$host_os dyld"
+  version_type=darwin
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+  soname_spec='$libname$release$major$shared_ext'
+  shlibpath_overrides_runpath=yes
+  shlibpath_var=DYLD_LIBRARY_PATH
+  shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
+
+  sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"
+  sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib'
+  ;;
+
+dgux*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+freebsd* | dragonfly* | midnightbsd*)
+  # DragonFly does not have aout.  When/if they implement a new
+  # versioning mechanism, adjust this.
+  if test -x /usr/bin/objformat; then
+    objformat=`/usr/bin/objformat`
+  else
+    case $host_os in
+    freebsd[23].*) objformat=aout ;;
+    *) objformat=elf ;;
+    esac
+  fi
+  version_type=freebsd-$objformat
+  case $version_type in
+    freebsd-elf*)
+      library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+      soname_spec='$libname$release$shared_ext$major'
+      need_version=no
+      need_lib_prefix=no
+      ;;
+    freebsd-*)
+      library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+      need_version=yes
+      ;;
+  esac
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_os in
+  freebsd2.*)
+    shlibpath_overrides_runpath=yes
+    ;;
+  freebsd3.[01]* | freebsdelf3.[01]*)
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  freebsd3.[2-9]* | freebsdelf3.[2-9]* | \
+  freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1)
+    shlibpath_overrides_runpath=no
+    hardcode_into_libs=yes
+    ;;
+  *) # from 4.6 on, and DragonFly
+    shlibpath_overrides_runpath=yes
+    hardcode_into_libs=yes
+    ;;
+  esac
+  ;;
+
+haiku*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  dynamic_linker="$host_os runtime_loader"
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
+  hardcode_into_libs=yes
+  ;;
+
+hpux9* | hpux10* | hpux11*)
+  # Give a soname corresponding to the major version so that dld.sl refuses to
+  # link against other versions.
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  case $host_cpu in
+  ia64*)
+    shrext_cmds='.so'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.so"
+    shlibpath_var=LD_LIBRARY_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+    soname_spec='$libname$release$shared_ext$major'
+    if test 32 = "$HPUX_IA64_MODE"; then
+      sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+      sys_lib_dlsearch_path_spec=/usr/lib/hpux32
+    else
+      sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+      sys_lib_dlsearch_path_spec=/usr/lib/hpux64
+    fi
+    ;;
+  hppa*64*)
+    shrext_cmds='.sl'
+    hardcode_into_libs=yes
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
+    shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
+    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+    soname_spec='$libname$release$shared_ext$major'
+    sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
+    sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+    ;;
+  *)
+    shrext_cmds='.sl'
+    dynamic_linker="$host_os dld.sl"
+    shlibpath_var=SHLIB_PATH
+    shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
+    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+    soname_spec='$libname$release$shared_ext$major'
+    ;;
+  esac
+  # HP-UX runs *really* slowly unless shared libraries are mode 555, ...
+  postinstall_cmds='chmod 555 $lib'
+  # or fails outright, so override atomically:
+  install_override_mode=555
+  ;;
+
+interix[3-9]*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+irix5* | irix6* | nonstopux*)
+  case $host_os in
+    nonstopux*) version_type=nonstopux ;;
+    *)
+	if test yes = "$lt_cv_prog_gnu_ld"; then
+		version_type=linux # correct to gnu/linux during the next big refactor
+	else
+		version_type=irix
+	fi ;;
+  esac
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='$libname$release$shared_ext$major'
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
+  case $host_os in
+  irix5* | nonstopux*)
+    libsuff= shlibsuff=
+    ;;
+  *)
+    case $LD in # libtool.m4 will add one of these switches to LD
+    *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ")
+      libsuff= shlibsuff= libmagic=32-bit;;
+    *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ")
+      libsuff=32 shlibsuff=N32 libmagic=N32;;
+    *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ")
+      libsuff=64 shlibsuff=64 libmagic=64-bit;;
+    *) libsuff= shlibsuff= libmagic=never-match;;
+    esac
+    ;;
+  esac
+  shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
+  shlibpath_overrides_runpath=no
+  sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+  sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
+  hardcode_into_libs=yes
+  ;;
+
+# No shared lib support for Linux oldld, aout, or coff.
+linux*oldld* | linux*aout* | linux*coff*)
+  dynamic_linker=no
+  ;;
+
+linux*android*)
+  version_type=none # Android doesn't support versioned libraries.
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext'
+  soname_spec='$libname$release$shared_ext'
+  finish_cmds=
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  dynamic_linker='Android linker'
+  # Don't embed -rpath directories since the linker doesn't support them.
+  hardcode_libdir_flag_spec='-L$libdir'
+  ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+
+  # Some binutils ld are patched to set DT_RUNPATH
+  if test ${lt_cv_shlibpath_overrides_runpath+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  lt_cv_shlibpath_overrides_runpath=no
+    save_LDFLAGS=$LDFLAGS
+    save_libdir=$libdir
+    eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
+	 LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\""
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  if  ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null
+then :
+  lt_cv_shlibpath_overrides_runpath=yes
+fi
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+    LDFLAGS=$save_LDFLAGS
+    libdir=$save_libdir
+
+fi
+
+  shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
+
+  # This implies no fast_install, which is unacceptable.
+  # Some rework will be needed to allow for fast_install
+  # before this can be enabled.
+  hardcode_into_libs=yes
+
+  # Add ABI-specific directories to the system library path.
+  sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib"
+
+  # Ideally, we could use ldconfig to report *all* directores which are
+  # searched for libraries, however this is still not possible.  Aside from not
+  # being certain /sbin/ldconfig is available, command
+  # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+  # even though it is searched at run-time.  Try to do the best guess by
+  # appending ld.so.conf contents (and includes) to the search path.
+  if test -f /etc/ld.so.conf; then
+    lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[	 ]*hwcap[	 ]/d;s/[:,	]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
+    sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra"
+  fi
+
+  # We used to test for /lib/ld.so.1 and disable shared libraries on
+  # powerpc, because MkLinux only supported shared libraries with the
+  # GNU dynamic linker.  Since this was broken with cross compilers,
+  # most powerpc-linux boxes support dynamic linking these days and
+  # people can always --disable-shared, the test was removed, and we
+  # assume the GNU/Linux dynamic linker is in use.
+  dynamic_linker='GNU/Linux ld.so'
+  ;;
+
+netbsd*)
+  version_type=sunos
+  need_lib_prefix=no
+  need_version=no
+  if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
+    library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+    finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+    dynamic_linker='NetBSD (a.out) ld.so'
+  else
+    library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+    soname_spec='$libname$release$shared_ext$major'
+    dynamic_linker='NetBSD ld.elf_so'
+  fi
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  ;;
+
+newsos6)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+*nto* | *qnx*)
+  version_type=qnx
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  dynamic_linker='ldqnx.so'
+  ;;
+
+openbsd* | bitrig*)
+  version_type=sunos
+  sys_lib_dlsearch_path_spec=/usr/lib
+  need_lib_prefix=no
+  if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+    need_version=no
+  else
+    need_version=yes
+  fi
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+  finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  ;;
+
+os2*)
+  libname_spec='$name'
+  version_type=windows
+  shrext_cmds=.dll
+  need_version=no
+  need_lib_prefix=no
+  # OS/2 can only load a DLL with a base name of 8 characters or less.
+  soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+    v=$($ECHO $release$versuffix | tr -d .-);
+    n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+    $ECHO $n$v`$shared_ext'
+  library_names_spec='${libname}_dll.$libext'
+  dynamic_linker='OS/2 ld.exe'
+  shlibpath_var=BEGINLIBPATH
+  sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+  sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+  postinstall_cmds='base_file=`basename \$file`~
+    dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
+    dldir=$destdir/`dirname \$dlpath`~
+    test -d \$dldir || mkdir -p \$dldir~
+    $install_prog $dir/$dlname \$dldir/$dlname~
+    chmod a+x \$dldir/$dlname~
+    if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+      eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+    fi'
+  postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+    dlpath=$dir/\$dldll~
+    $RM \$dlpath'
+  ;;
+
+osf3* | osf4* | osf5*)
+  version_type=osf
+  need_lib_prefix=no
+  need_version=no
+  soname_spec='$libname$release$shared_ext$major'
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  shlibpath_var=LD_LIBRARY_PATH
+  sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
+  sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+  ;;
+
+rdos*)
+  dynamic_linker=no
+  ;;
+
+solaris*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  # ldd complains unless libraries are executable
+  postinstall_cmds='chmod +x $lib'
+  ;;
+
+sunos4*)
+  version_type=sunos
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+  finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  if test yes = "$with_gnu_ld"; then
+    need_lib_prefix=no
+  fi
+  need_version=yes
+  ;;
+
+sysv4 | sysv4.3*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  case $host_vendor in
+    sni)
+      shlibpath_overrides_runpath=no
+      need_lib_prefix=no
+      runpath_var=LD_RUN_PATH
+      ;;
+    siemens)
+      need_lib_prefix=no
+      ;;
+    motorola)
+      need_lib_prefix=no
+      need_version=no
+      shlibpath_overrides_runpath=no
+      sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib'
+      ;;
+  esac
+  ;;
+
+sysv4*MP*)
+  if test -d /usr/nec; then
+    version_type=linux # correct to gnu/linux during the next big refactor
+    library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+    soname_spec='$libname$shared_ext.$major'
+    shlibpath_var=LD_LIBRARY_PATH
+  fi
+  ;;
+
+sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+  version_type=sco
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=yes
+  hardcode_into_libs=yes
+  if test yes = "$with_gnu_ld"; then
+    sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
+  else
+    sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
+    case $host_os in
+      sco3.2v5*)
+        sys_lib_search_path_spec="$sys_lib_search_path_spec /lib"
+	;;
+    esac
+  fi
+  sys_lib_dlsearch_path_spec='/usr/lib'
+  ;;
+
+tpf*)
+  # TPF is a cross-target only.  Preferred cross-host = GNU/Linux.
+  version_type=linux # correct to gnu/linux during the next big refactor
+  need_lib_prefix=no
+  need_version=no
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  shlibpath_var=LD_LIBRARY_PATH
+  shlibpath_overrides_runpath=no
+  hardcode_into_libs=yes
+  ;;
+
+uts4*)
+  version_type=linux # correct to gnu/linux during the next big refactor
+  library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+  soname_spec='$libname$release$shared_ext$major'
+  shlibpath_var=LD_LIBRARY_PATH
+  ;;
+
+*)
+  dynamic_linker=no
+  ;;
+esac
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
+printf "%s\n" "$dynamic_linker" >&6; }
+test no = "$dynamic_linker" && can_build_shared=no
+
+variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
+if test yes = "$GCC"; then
+  variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
+fi
+
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+  sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
+fi
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+  sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
+fi
+
+# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5
+printf %s "checking how to hardcode library paths into programs... " >&6; }
+hardcode_action=
+if test -n "$hardcode_libdir_flag_spec" ||
+   test -n "$runpath_var" ||
+   test yes = "$hardcode_automatic"; then
+
+  # We can hardcode non-existent directories.
+  if test no != "$hardcode_direct" &&
+     # If the only mechanism to avoid hardcoding is shlibpath_var, we
+     # have to relink, otherwise we might link with an installed library
+     # when we should be linking with a yet-to-be-installed one
+     ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" &&
+     test no != "$hardcode_minus_L"; then
+    # Linking always hardcodes the temporary library directory.
+    hardcode_action=relink
+  else
+    # We can link without hardcoding, and we can hardcode nonexisting dirs.
+    hardcode_action=immediate
+  fi
+else
+  # We cannot hardcode anything, or else we can only hardcode existing
+  # directories.
+  hardcode_action=unsupported
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
+printf "%s\n" "$hardcode_action" >&6; }
+
+if test relink = "$hardcode_action" ||
+   test yes = "$inherit_rpath"; then
+  # Fast installation is not supported
+  enable_fast_install=no
+elif test yes = "$shlibpath_overrides_runpath" ||
+     test no = "$enable_shared"; then
+  # Fast installation is not necessary
+  enable_fast_install=needless
+fi
+
+
+
+
+
+
+  if test yes != "$enable_dlopen"; then
+  enable_dlopen=unknown
+  enable_dlopen_self=unknown
+  enable_dlopen_self_static=unknown
+else
+  lt_cv_dlopen=no
+  lt_cv_dlopen_libs=
+
+  case $host_os in
+  beos*)
+    lt_cv_dlopen=load_add_on
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+    ;;
+
+  mingw* | pw32* | cegcc*)
+    lt_cv_dlopen=LoadLibrary
+    lt_cv_dlopen_libs=
+    ;;
+
+  cygwin*)
+    lt_cv_dlopen=dlopen
+    lt_cv_dlopen_libs=
+    ;;
+
+  darwin*)
+    # if libdl is installed we need to link against it
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+printf %s "checking for dlopen in -ldl... " >&6; }
+if test ${ac_cv_lib_dl_dlopen+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main (void)
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_dl_dlopen=yes
+else $as_nop
+  ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes
+then :
+  lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
+else $as_nop
+
+    lt_cv_dlopen=dyld
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=yes
+
+fi
+
+    ;;
+
+  tpf*)
+    # Don't try to run any link tests for TPF.  We know it's impossible
+    # because TPF is a cross-compiler, and we know how we open DSOs.
+    lt_cv_dlopen=dlopen
+    lt_cv_dlopen_libs=
+    lt_cv_dlopen_self=no
+    ;;
+
+  *)
+    ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
+if test "x$ac_cv_func_shl_load" = xyes
+then :
+  lt_cv_dlopen=shl_load
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+printf %s "checking for shl_load in -ldld... " >&6; }
+if test ${ac_cv_lib_dld_shl_load+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char shl_load ();
+int
+main (void)
+{
+return shl_load ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_dld_shl_load=yes
+else $as_nop
+  ac_cv_lib_dld_shl_load=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
+printf "%s\n" "$ac_cv_lib_dld_shl_load" >&6; }
+if test "x$ac_cv_lib_dld_shl_load" = xyes
+then :
+  lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld
+else $as_nop
+  ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+if test "x$ac_cv_func_dlopen" = xyes
+then :
+  lt_cv_dlopen=dlopen
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+printf %s "checking for dlopen in -ldl... " >&6; }
+if test ${ac_cv_lib_dl_dlopen+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main (void)
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_dl_dlopen=yes
+else $as_nop
+  ac_cv_lib_dl_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
+printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; }
+if test "x$ac_cv_lib_dl_dlopen" = xyes
+then :
+  lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+printf %s "checking for dlopen in -lsvld... " >&6; }
+if test ${ac_cv_lib_svld_dlopen+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsvld  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char dlopen ();
+int
+main (void)
+{
+return dlopen ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_svld_dlopen=yes
+else $as_nop
+  ac_cv_lib_svld_dlopen=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
+printf "%s\n" "$ac_cv_lib_svld_dlopen" >&6; }
+if test "x$ac_cv_lib_svld_dlopen" = xyes
+then :
+  lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld
+else $as_nop
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+printf %s "checking for dld_link in -ldld... " >&6; }
+if test ${ac_cv_lib_dld_dld_link+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ldld  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char dld_link ();
+int
+main (void)
+{
+return dld_link ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_dld_dld_link=yes
+else $as_nop
+  ac_cv_lib_dld_dld_link=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
+printf "%s\n" "$ac_cv_lib_dld_dld_link" >&6; }
+if test "x$ac_cv_lib_dld_dld_link" = xyes
+then :
+  lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+
+fi
+
+    ;;
+  esac
+
+  if test no = "$lt_cv_dlopen"; then
+    enable_dlopen=no
+  else
+    enable_dlopen=yes
+  fi
+
+  case $lt_cv_dlopen in
+  dlopen)
+    save_CPPFLAGS=$CPPFLAGS
+    test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+
+    save_LDFLAGS=$LDFLAGS
+    wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
+
+    save_LIBS=$LIBS
+    LIBS="$lt_cv_dlopen_libs $LIBS"
+
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
+printf %s "checking whether a program can dlopen itself... " >&6; }
+if test ${lt_cv_dlopen_self+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  	  if test yes = "$cross_compiling"; then :
+  lt_cv_dlopen_self=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+	  if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+	}
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}
+_LT_EOF
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
+    (./conftest; exit; ) >&5 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;;
+      x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
+printf "%s\n" "$lt_cv_dlopen_self" >&6; }
+
+    if test yes = "$lt_cv_dlopen_self"; then
+      wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
+printf %s "checking whether a statically linked program can dlopen itself... " >&6; }
+if test ${lt_cv_dlopen_self_static+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  	  if test yes = "$cross_compiling"; then :
+  lt_cv_dlopen_self_static=cross
+else
+  lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
+  lt_status=$lt_dlunknown
+  cat > conftest.$ac_ext <<_LT_EOF
+#line $LINENO "configure"
+#include "confdefs.h"
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+#include <stdio.h>
+
+#ifdef RTLD_GLOBAL
+#  define LT_DLGLOBAL		RTLD_GLOBAL
+#else
+#  ifdef DL_GLOBAL
+#    define LT_DLGLOBAL		DL_GLOBAL
+#  else
+#    define LT_DLGLOBAL		0
+#  endif
+#endif
+
+/* We may have to define LT_DLLAZY_OR_NOW in the command line if we
+   find out it does not work in some platform. */
+#ifndef LT_DLLAZY_OR_NOW
+#  ifdef RTLD_LAZY
+#    define LT_DLLAZY_OR_NOW		RTLD_LAZY
+#  else
+#    ifdef DL_LAZY
+#      define LT_DLLAZY_OR_NOW		DL_LAZY
+#    else
+#      ifdef RTLD_NOW
+#        define LT_DLLAZY_OR_NOW	RTLD_NOW
+#      else
+#        ifdef DL_NOW
+#          define LT_DLLAZY_OR_NOW	DL_NOW
+#        else
+#          define LT_DLLAZY_OR_NOW	0
+#        endif
+#      endif
+#    endif
+#  endif
+#endif
+
+/* When -fvisibility=hidden is used, assume the code has been annotated
+   correspondingly for the symbols needed.  */
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+int fnord () __attribute__((visibility("default")));
+#endif
+
+int fnord () { return 42; }
+int main ()
+{
+  void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW);
+  int status = $lt_dlunknown;
+
+  if (self)
+    {
+      if (dlsym (self,"fnord"))       status = $lt_dlno_uscore;
+      else
+        {
+	  if (dlsym( self,"_fnord"))  status = $lt_dlneed_uscore;
+          else puts (dlerror ());
+	}
+      /* dlclose (self); */
+    }
+  else
+    puts (dlerror ());
+
+  return status;
+}
+_LT_EOF
+  if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
+    (./conftest; exit; ) >&5 2>/dev/null
+    lt_status=$?
+    case x$lt_status in
+      x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;;
+      x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;;
+    esac
+  else :
+    # compilation failed
+    lt_cv_dlopen_self_static=no
+  fi
+fi
+rm -fr conftest*
+
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
+printf "%s\n" "$lt_cv_dlopen_self_static" >&6; }
+    fi
+
+    CPPFLAGS=$save_CPPFLAGS
+    LDFLAGS=$save_LDFLAGS
+    LIBS=$save_LIBS
+    ;;
+  esac
+
+  case $lt_cv_dlopen_self in
+  yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;;
+  *) enable_dlopen_self=unknown ;;
+  esac
+
+  case $lt_cv_dlopen_self_static in
+  yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;;
+  *) enable_dlopen_self_static=unknown ;;
+  esac
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+striplib=
+old_striplib=
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5
+printf %s "checking whether stripping libraries is possible... " >&6; }
+if test -z "$STRIP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+else
+  if $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then
+    old_striplib="$STRIP --strip-debug"
+    striplib="$STRIP --strip-unneeded"
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+  else
+    case $host_os in
+    darwin*)
+      # FIXME - insert some real tests, host_os isn't really good enough
+      striplib="$STRIP -x"
+      old_striplib="$STRIP -S"
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+      ;;
+    freebsd*)
+      if $STRIP -V 2>&1 | $GREP "elftoolchain" >/dev/null; then
+        old_striplib="$STRIP --strip-debug"
+        striplib="$STRIP --strip-unneeded"
+        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+      else
+        { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+      fi
+      ;;
+    *)
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+      ;;
+    esac
+  fi
+fi
+
+
+
+
+
+
+
+
+
+
+
+
+  # Report what library types will actually be built
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
+printf %s "checking if libtool supports shared libraries... " >&6; }
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
+printf "%s\n" "$can_build_shared" >&6; }
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
+printf %s "checking whether to build shared libraries... " >&6; }
+  test no = "$can_build_shared" && enable_shared=no
+
+  # On AIX, shared libraries and static libraries use the same namespace, and
+  # are all built from PIC.
+  case $host_os in
+  aix3*)
+    test yes = "$enable_shared" && enable_static=no
+    if test -n "$RANLIB"; then
+      archive_cmds="$archive_cmds~\$RANLIB \$lib"
+      postinstall_cmds='$RANLIB $lib'
+    fi
+    ;;
+
+  aix[4-9]*)
+    if test ia64 != "$host_cpu"; then
+      case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+      yes,aix,yes) ;;			# shared object as lib.so file only
+      yes,svr4,*) ;;			# shared object as lib.so archive member only
+      yes,*) enable_static=no ;;	# shared object in lib.a archive as well
+      esac
+    fi
+    ;;
+  esac
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5
+printf "%s\n" "$enable_shared" >&6; }
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
+printf %s "checking whether to build static libraries... " >&6; }
+  # Make sure either enable_shared or enable_static is yes.
+  test yes = "$enable_shared" || enable_static=yes
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
+printf "%s\n" "$enable_static" >&6; }
+
+
+
+
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+CC=$lt_save_CC
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+        ac_config_commands="$ac_config_commands libtool"
+
+
+
+
+# Only expand once:
+
+
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_STRIP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5
+printf "%s\n" "$STRIP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_STRIP+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5
+printf "%s\n" "$ac_ct_STRIP" >&6; }
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+  if test "x$ac_ct_STRIP" = x; then
+    STRIP=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    STRIP=$ac_ct_STRIP
+  fi
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a race-free mkdir -p" >&5
+printf %s "checking for a race-free mkdir -p... " >&6; }
+if test -z "$MKDIR_P"; then
+  if test ${ac_cv_path_mkdir+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    for ac_prog in mkdir gmkdir; do
+	 for ac_exec_ext in '' $ac_executable_extensions; do
+	   as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext" || continue
+	   case `"$as_dir$ac_prog$ac_exec_ext" --version 2>&1` in #(
+	     'mkdir ('*'coreutils) '* | \
+	     'BusyBox '* | \
+	     'mkdir (fileutils) '4.1*)
+	       ac_cv_path_mkdir=$as_dir$ac_prog$ac_exec_ext
+	       break 3;;
+	   esac
+	 done
+       done
+  done
+IFS=$as_save_IFS
+
+fi
+
+  test -d ./--version && rmdir ./--version
+  if test ${ac_cv_path_mkdir+y}; then
+    MKDIR_P="$ac_cv_path_mkdir -p"
+  else
+    # As a last resort, use the slow shell script.  Don't cache a
+    # value for MKDIR_P within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the value is a relative name.
+    MKDIR_P="$ac_install_sh -d"
+  fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
+printf "%s\n" "$MKDIR_P" >&6; }
+
+
+
+# Set up defines needed before including any headers
+case $host in
+  *mingw* | *cygwin* | *msys*  )
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef _WIN32_WINNT
+     # error _WIN32_WINNT already defined
+     #endif
+
+int
+main (void)
+{
+;
+  ;
+  return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+
+printf "%s\n" "#define _WIN32_WINNT 0x0502" >>confdefs.h
+
+
+printf "%s\n" "#define NTDDI_VERSION 0x05020000" >>confdefs.h
+
+
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef WINVER
+     # error WINVER already defined
+     #endif
+
+int
+main (void)
+{
+;
+  ;
+  return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+
+printf "%s\n" "#define WINVER 0x0502" >>confdefs.h
+
+
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+  ;;
+esac
+
+# Checks for header files.
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+  as_ac_Header=`printf "%s\n" "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
+printf %s "checking for $ac_hdr that defines DIR... " >&6; }
+if eval test \${$as_ac_Header+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main (void)
+{
+if ((DIR *) 0)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  eval "$as_ac_Header=yes"
+else $as_nop
+  eval "$as_ac_Header=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+eval ac_res=\$$as_ac_Header
+	       { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+if eval test \"x\$"$as_ac_Header"\" = x"yes"
+then :
+  cat >>confdefs.h <<_ACEOF
+#define `printf "%s\n" "HAVE_$ac_hdr" | $as_tr_cpp` 1
+_ACEOF
+
+ac_header_dirent=$ac_hdr; break
+fi
+
+done
+# Two versions of opendir et al. are in -ldir and -lx on SCO Xenix.
+if test $ac_header_dirent = dirent.h; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+printf %s "checking for library containing opendir... " >&6; }
+if test ${ac_cv_search_opendir+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char opendir ();
+int
+main (void)
+{
+return opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' dir
+do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext
+  if test ${ac_cv_search_opendir+y}
+then :
+  break
+fi
+done
+if test ${ac_cv_search_opendir+y}
+then :
+
+else $as_nop
+  ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+printf "%s\n" "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no
+then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
+printf %s "checking for library containing opendir... " >&6; }
+if test ${ac_cv_search_opendir+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_func_search_save_LIBS=$LIBS
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char opendir ();
+int
+main (void)
+{
+return opendir ();
+  ;
+  return 0;
+}
+_ACEOF
+for ac_lib in '' x
+do
+  if test -z "$ac_lib"; then
+    ac_res="none required"
+  else
+    ac_res=-l$ac_lib
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+  fi
+  if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_search_opendir=$ac_res
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext
+  if test ${ac_cv_search_opendir+y}
+then :
+  break
+fi
+done
+if test ${ac_cv_search_opendir+y}
+then :
+
+else $as_nop
+  ac_cv_search_opendir=no
+fi
+rm conftest.$ac_ext
+LIBS=$ac_func_search_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_opendir" >&5
+printf "%s\n" "$ac_cv_search_opendir" >&6; }
+ac_res=$ac_cv_search_opendir
+if test "$ac_res" != no
+then :
+  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
+
+fi
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
+printf %s "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
+if test ${ac_cv_header_sys_wait_h+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/wait.h>
+#ifndef WEXITSTATUS
+# define WEXITSTATUS(stat_val) ((unsigned int) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+int
+main (void)
+{
+  int s;
+  wait (&s);
+  s = WIFEXITED (s) ? WEXITSTATUS (s) : 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_header_sys_wait_h=yes
+else $as_nop
+  ac_cv_header_sys_wait_h=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_sys_wait_h" >&5
+printf "%s\n" "$ac_cv_header_sys_wait_h" >&6; }
+if test $ac_cv_header_sys_wait_h = yes; then
+
+printf "%s\n" "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "acl/libacl.h" "ac_cv_header_acl_libacl_h" "$ac_includes_default"
+if test "x$ac_cv_header_acl_libacl_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_ACL_LIBACL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "attr/xattr.h" "ac_cv_header_attr_xattr_h" "$ac_includes_default"
+if test "x$ac_cv_header_attr_xattr_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_ATTR_XATTR_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "copyfile.h" "ac_cv_header_copyfile_h" "$ac_includes_default"
+if test "x$ac_cv_header_copyfile_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_COPYFILE_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "ctype.h" "ac_cv_header_ctype_h" "$ac_includes_default"
+if test "x$ac_cv_header_ctype_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_CTYPE_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "errno.h" "ac_cv_header_errno_h" "$ac_includes_default"
+if test "x$ac_cv_header_errno_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_ERRNO_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "ext2fs/ext2_fs.h" "ac_cv_header_ext2fs_ext2_fs_h" "$ac_includes_default"
+if test "x$ac_cv_header_ext2fs_ext2_fs_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_EXT2FS_EXT2_FS_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "fcntl.h" "ac_cv_header_fcntl_h" "$ac_includes_default"
+if test "x$ac_cv_header_fcntl_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_FCNTL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "fnmatch.h" "ac_cv_header_fnmatch_h" "$ac_includes_default"
+if test "x$ac_cv_header_fnmatch_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_FNMATCH_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "grp.h" "ac_cv_header_grp_h" "$ac_includes_default"
+if test "x$ac_cv_header_grp_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_GRP_H 1" >>confdefs.h
+
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether EXT2_IOC_GETFLAGS is usable" >&5
+printf %s "checking whether EXT2_IOC_GETFLAGS is usable... " >&6; }
+if test ${ac_cv_have_decl_EXT2_IOC_GETFLAGS+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/ioctl.h>
+#include <ext2fs/ext2_fs.h>
+int
+main (void)
+{
+int x = EXT2_IOC_GETFLAGS
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_have_decl_EXT2_IOC_GETFLAGS=yes
+else $as_nop
+  ac_cv_have_decl_EXT2_IOC_GETFLAGS=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_decl_EXT2_IOC_GETFLAGS" >&5
+printf "%s\n" "$ac_cv_have_decl_EXT2_IOC_GETFLAGS" >&6; }
+
+if test "x$ac_cv_have_decl_EXT2_IOC_GETFLAGS" = xyes
+then :
+
+printf "%s\n" "#define HAVE_WORKING_EXT2_IOC_GETFLAGS 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default"
+if test "x$ac_cv_header_inttypes_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_INTTYPES_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "io.h" "ac_cv_header_io_h" "$ac_includes_default"
+if test "x$ac_cv_header_io_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_IO_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "langinfo.h" "ac_cv_header_langinfo_h" "$ac_includes_default"
+if test "x$ac_cv_header_langinfo_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_LANGINFO_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default"
+if test "x$ac_cv_header_limits_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_LIMITS_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "linux/fiemap.h" "ac_cv_header_linux_fiemap_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_fiemap_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_LINUX_FIEMAP_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "linux/fs.h" "ac_cv_header_linux_fs_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_fs_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_LINUX_FS_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "linux/magic.h" "ac_cv_header_linux_magic_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_magic_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_LINUX_MAGIC_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "linux/types.h" "ac_cv_header_linux_types_h" "$ac_includes_default"
+if test "x$ac_cv_header_linux_types_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_LINUX_TYPES_H 1" >>confdefs.h
+
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether FS_IOC_GETFLAGS is usable" >&5
+printf %s "checking whether FS_IOC_GETFLAGS is usable... " >&6; }
+if test ${ac_cv_have_decl_FS_IOC_GETFLAGS+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+int
+main (void)
+{
+int x = FS_IOC_GETFLAGS
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_have_decl_FS_IOC_GETFLAGS=yes
+else $as_nop
+  ac_cv_have_decl_FS_IOC_GETFLAGS=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_have_decl_FS_IOC_GETFLAGS" >&5
+printf "%s\n" "$ac_cv_have_decl_FS_IOC_GETFLAGS" >&6; }
+
+if test "x$ac_cv_have_decl_FS_IOC_GETFLAGS" = xyes
+then :
+
+printf "%s\n" "#define HAVE_WORKING_FS_IOC_GETFLAGS 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "locale.h" "ac_cv_header_locale_h" "$ac_includes_default"
+if test "x$ac_cv_header_locale_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_LOCALE_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "membership.h" "ac_cv_header_membership_h" "$ac_includes_default"
+if test "x$ac_cv_header_membership_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_MEMBERSHIP_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "paths.h" "ac_cv_header_paths_h" "$ac_includes_default"
+if test "x$ac_cv_header_paths_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_PATHS_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "poll.h" "ac_cv_header_poll_h" "$ac_includes_default"
+if test "x$ac_cv_header_poll_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_POLL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
+if test "x$ac_cv_header_pthread_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_PTHREAD_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "pwd.h" "ac_cv_header_pwd_h" "$ac_includes_default"
+if test "x$ac_cv_header_pwd_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_PWD_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "readpassphrase.h" "ac_cv_header_readpassphrase_h" "$ac_includes_default"
+if test "x$ac_cv_header_readpassphrase_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_READPASSPHRASE_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "signal.h" "ac_cv_header_signal_h" "$ac_includes_default"
+if test "x$ac_cv_header_signal_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SIGNAL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "spawn.h" "ac_cv_header_spawn_h" "$ac_includes_default"
+if test "x$ac_cv_header_spawn_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SPAWN_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "stdarg.h" "ac_cv_header_stdarg_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdarg_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_STDARG_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdint_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_STDINT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default"
+if test "x$ac_cv_header_stdlib_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_STDLIB_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "string.h" "ac_cv_header_string_h" "$ac_includes_default"
+if test "x$ac_cv_header_string_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRING_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "sys/acl.h" "ac_cv_header_sys_acl_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_acl_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_ACL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/cdefs.h" "ac_cv_header_sys_cdefs_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_cdefs_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_CDEFS_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/ea.h" "ac_cv_header_sys_ea_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_ea_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_EA_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/extattr.h" "ac_cv_header_sys_extattr_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_extattr_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_EXTATTR_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "sys/ioctl.h" "ac_cv_header_sys_ioctl_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_ioctl_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_IOCTL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/mkdev.h" "ac_cv_header_sys_mkdev_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_mkdev_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_MKDEV_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/mount.h" "ac_cv_header_sys_mount_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_mount_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_MOUNT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/queue.h" "ac_cv_header_sys_queue_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_queue_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_QUEUE_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "sys/param.h" "ac_cv_header_sys_param_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_param_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_PARAM_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/poll.h" "ac_cv_header_sys_poll_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_poll_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_POLL_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/richacl.h" "ac_cv_header_sys_richacl_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_richacl_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_RICHACL_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "sys/select.h" "ac_cv_header_sys_select_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_select_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_SELECT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/statfs.h" "ac_cv_header_sys_statfs_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_statfs_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_STATFS_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/statvfs.h" "ac_cv_header_sys_statvfs_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_statvfs_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_STATVFS_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/sysmacros.h" "ac_cv_header_sys_sysmacros_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_sysmacros_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_SYSMACROS_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_time_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_TIME_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/utime.h" "ac_cv_header_sys_utime_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_utime_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_UTIME_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/utsname.h" "ac_cv_header_sys_utsname_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_utsname_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_UTSNAME_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/vfs.h" "ac_cv_header_sys_vfs_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_vfs_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_VFS_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "sys/xattr.h" "ac_cv_header_sys_xattr_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_xattr_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYS_XATTR_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "time.h" "ac_cv_header_time_h" "$ac_includes_default"
+if test "x$ac_cv_header_time_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_TIME_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default"
+if test "x$ac_cv_header_unistd_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_UNISTD_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "utime.h" "ac_cv_header_utime_h" "$ac_includes_default"
+if test "x$ac_cv_header_utime_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_UTIME_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "wchar.h" "ac_cv_header_wchar_h" "$ac_includes_default"
+if test "x$ac_cv_header_wchar_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_WCHAR_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "wctype.h" "ac_cv_header_wctype_h" "$ac_includes_default"
+if test "x$ac_cv_header_wctype_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_WCTYPE_H 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_header_compile "$LINENO" "windows.h" "ac_cv_header_windows_h" "$ac_includes_default"
+if test "x$ac_cv_header_windows_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_WINDOWS_H 1" >>confdefs.h
+
+fi
+
+# check windows.h first; the other headers require it.
+ac_fn_c_check_header_compile "$LINENO" "wincrypt.h" "ac_cv_header_wincrypt_h" "#ifdef HAVE_WINDOWS_H
+# include <windows.h>
+#endif
+
+"
+if test "x$ac_cv_header_wincrypt_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_WINCRYPT_H 1" >>confdefs.h
+
+fi
+ac_fn_c_check_header_compile "$LINENO" "winioctl.h" "ac_cv_header_winioctl_h" "#ifdef HAVE_WINDOWS_H
+# include <windows.h>
+#endif
+
+"
+if test "x$ac_cv_header_winioctl_h" = xyes
+then :
+  printf "%s\n" "#define HAVE_WINIOCTL_H 1" >>confdefs.h
+
+fi
+
+
+ if test "x$ac_cv_lib_b2_blake2sp_init" != "xyes"; then
+  INC_BLAKE2_TRUE=
+  INC_BLAKE2_FALSE='#'
+else
+  INC_BLAKE2_TRUE='#'
+  INC_BLAKE2_FALSE=
+fi
+
+
+
+# Checks for typedefs, structures, and compiler characteristics.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5
+printf %s "checking for an ANSI C-conforming const... " >&6; }
+if test ${ac_cv_c_const+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+
+#ifndef __cplusplus
+  /* Ultrix mips cc rejects this sort of thing.  */
+  typedef int charset[2];
+  const charset cs = { 0, 0 };
+  /* SunOS 4.1.1 cc rejects this.  */
+  char const *const *pcpcc;
+  char **ppc;
+  /* NEC SVR4.0.2 mips cc rejects this.  */
+  struct point {int x, y;};
+  static struct point const zero = {0,0};
+  /* IBM XL C 1.02.0.0 rejects this.
+     It does not let you subtract one const X* pointer from another in
+     an arm of an if-expression whose if-part is not a constant
+     expression */
+  const char *g = "string";
+  pcpcc = &g + (g ? g-g : 0);
+  /* HPUX 7.0 cc rejects these. */
+  ++pcpcc;
+  ppc = (char**) pcpcc;
+  pcpcc = (char const *const *) ppc;
+  { /* SCO 3.2v4 cc rejects this sort of thing.  */
+    char tx;
+    char *t = &tx;
+    char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+    *t++ = 0;
+    if (s) return 0;
+  }
+  { /* Someone thinks the Sun supposedly-ANSI compiler will reject this.  */
+    int x[] = {25, 17};
+    const int *foo = &x[0];
+    ++foo;
+  }
+  { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+    typedef const int *iptr;
+    iptr p = 0;
+    ++p;
+  }
+  { /* IBM XL C 1.02.0.0 rejects this sort of thing, saying
+       "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+    struct s { int j; const int *ap[3]; } bx;
+    struct s *b = &bx; b->j = 5;
+  }
+  { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
+    const int foo = 10;
+    if (!foo) return 0;
+  }
+  return !cs[0] && !zero.x;
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_c_const=yes
+else $as_nop
+  ac_cv_c_const=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5
+printf "%s\n" "$ac_cv_c_const" >&6; }
+if test $ac_cv_c_const = no; then
+
+printf "%s\n" "#define const /**/" >>confdefs.h
+
+fi
+
+# la_TYPE_UID_T defaults to "int", which is incorrect for MinGW
+# and MSVC. Use a customized version.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
+printf %s "checking for uid_t in sys/types.h... " >&6; }
+if test ${la_cv_type_uid_t+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "uid_t" >/dev/null 2>&1
+then :
+  la_cv_type_uid_t=yes
+else $as_nop
+  la_cv_type_uid_t=no
+fi
+rm -rf conftest*
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $la_cv_type_uid_t" >&5
+printf "%s\n" "$la_cv_type_uid_t" >&6; }
+if test $la_cv_type_uid_t = no; then
+  case $host in
+    *mingw*) def_uid_t=short ;;
+    *) def_uid_t=int ;;
+  esac
+
+printf "%s\n" "#define uid_t $def_uid_t" >>confdefs.h
+
+
+printf "%s\n" "#define gid_t $def_uid_t" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default"
+if test "x$ac_cv_type_mode_t" = xyes
+then :
+
+else $as_nop
+
+printf "%s\n" "#define mode_t int" >>confdefs.h
+
+fi
+
+# AC_TYPE_OFF_T defaults to "long", which limits us to 4GB files on
+# most systems... default to "long long" instead.
+ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
+if test "x$ac_cv_type_off_t" = xyes
+then :
+
+else $as_nop
+
+printf "%s\n" "#define off_t long long" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
+if test "x$ac_cv_type_size_t" = xyes
+then :
+
+else $as_nop
+
+printf "%s\n" "#define size_t unsigned int" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "id_t" "ac_cv_type_id_t" "$ac_includes_default"
+if test "x$ac_cv_type_id_t" = xyes
+then :
+
+else $as_nop
+
+printf "%s\n" "#define id_t unsigned long" >>confdefs.h
+
+fi
+
+ac_fn_c_check_type "$LINENO" "uintptr_t" "ac_cv_type_uintptr_t" "$ac_includes_default"
+if test "x$ac_cv_type_uintptr_t" = xyes
+then :
+
+else $as_nop
+
+printf "%s\n" "#define uintptr_t unsigned int" >>confdefs.h
+
+fi
+
+
+# Check for tm_gmtoff in struct tm
+ac_fn_c_check_member "$LINENO" "struct tm" "tm_gmtoff" "ac_cv_member_struct_tm_tm_gmtoff" "
+#include <time.h>
+
+"
+if test "x$ac_cv_member_struct_tm_tm_gmtoff" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_TM_TM_GMTOFF 1" >>confdefs.h
+
+
+fi
+ac_fn_c_check_member "$LINENO" "struct tm" "__tm_gmtoff" "ac_cv_member_struct_tm___tm_gmtoff" "
+#include <time.h>
+
+"
+if test "x$ac_cv_member_struct_tm___tm_gmtoff" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_TM___TM_GMTOFF 1" >>confdefs.h
+
+
+fi
+
+
+# Check for f_namemax in struct statfs
+ac_fn_c_check_member "$LINENO" "struct statfs" "f_namemax" "ac_cv_member_struct_statfs_f_namemax" "
+#include <sys/param.h>
+#include <sys/mount.h>
+
+"
+if test "x$ac_cv_member_struct_statfs_f_namemax" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STATFS_F_NAMEMAX 1" >>confdefs.h
+
+
+fi
+
+
+# Check for f_iosize in struct statfs
+ac_fn_c_check_member "$LINENO" "struct statfs" "f_iosize" "ac_cv_member_struct_statfs_f_iosize" "
+#include <sys/param.h>
+#include <sys/mount.h>
+
+"
+if test "x$ac_cv_member_struct_statfs_f_iosize" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STATFS_F_IOSIZE 1" >>confdefs.h
+
+
+fi
+
+
+# Check for f_iosize in struct statvfs
+ac_fn_c_check_member "$LINENO" "struct statvfs" "f_iosize" "ac_cv_member_struct_statvfs_f_iosize" "
+#include <sys/statvfs.h>
+
+"
+if test "x$ac_cv_member_struct_statvfs_f_iosize" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STATVFS_F_IOSIZE 1" >>confdefs.h
+
+
+fi
+
+
+# Check for birthtime in struct stat
+ac_fn_c_check_member "$LINENO" "struct stat" "st_birthtime" "ac_cv_member_struct_stat_st_birthtime" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_birthtime" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1" >>confdefs.h
+
+
+fi
+
+
+# Check for high-resolution timestamps in struct stat
+ac_fn_c_check_member "$LINENO" "struct stat" "st_birthtimespec.tv_nsec" "ac_cv_member_struct_stat_st_birthtimespec_tv_nsec" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_birthtimespec_tv_nsec" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC_TV_NSEC 1" >>confdefs.h
+
+
+fi
+
+ac_fn_c_check_member "$LINENO" "struct stat" "st_mtimespec.tv_nsec" "ac_cv_member_struct_stat_st_mtimespec_tv_nsec" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_mtimespec_tv_nsec" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1" >>confdefs.h
+
+
+fi
+
+ac_fn_c_check_member "$LINENO" "struct stat" "st_mtim.tv_nsec" "ac_cv_member_struct_stat_st_mtim_tv_nsec" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_mtim_tv_nsec" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC 1" >>confdefs.h
+
+
+fi
+
+ac_fn_c_check_member "$LINENO" "struct stat" "st_mtime_n" "ac_cv_member_struct_stat_st_mtime_n" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_mtime_n" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIME_N 1" >>confdefs.h
+
+
+fi
+ # AIX
+ac_fn_c_check_member "$LINENO" "struct stat" "st_umtime" "ac_cv_member_struct_stat_st_umtime" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_umtime" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_UMTIME 1" >>confdefs.h
+
+
+fi
+ # Tru64
+ac_fn_c_check_member "$LINENO" "struct stat" "st_mtime_usec" "ac_cv_member_struct_stat_st_mtime_usec" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_mtime_usec" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_MTIME_USEC 1" >>confdefs.h
+
+
+fi
+ # Hurd
+# Check for block size support in struct stat
+ac_fn_c_check_member "$LINENO" "struct stat" "st_blksize" "ac_cv_member_struct_stat_st_blksize" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_blksize" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_BLKSIZE 1" >>confdefs.h
+
+
+fi
+
+# Check for st_flags in struct stat (BSD fflags)
+ac_fn_c_check_member "$LINENO" "struct stat" "st_flags" "ac_cv_member_struct_stat_st_flags" "$ac_includes_default"
+if test "x$ac_cv_member_struct_stat_st_flags" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STAT_ST_FLAGS 1" >>confdefs.h
+
+
+fi
+
+
+# If you have uintmax_t, we assume printf supports %ju
+# If you have unsigned long long, we assume printf supports %llu
+# TODO: Check for %ju and %llu support directly.
+ac_fn_c_check_type "$LINENO" "uintmax_t" "ac_cv_type_uintmax_t" "$ac_includes_default"
+if test "x$ac_cv_type_uintmax_t" = xyes
+then :
+
+printf "%s\n" "#define HAVE_UINTMAX_T 1" >>confdefs.h
+
+
+fi
+ac_fn_c_check_type "$LINENO" "unsigned long long" "ac_cv_type_unsigned_long_long" "$ac_includes_default"
+if test "x$ac_cv_type_unsigned_long_long" = xyes
+then :
+
+printf "%s\n" "#define HAVE_UNSIGNED_LONG_LONG 1" >>confdefs.h
+
+
+fi
+
+
+# We use C99-style integer types
+# Declare them if the local platform doesn't already do so.
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for unsigned long long int" >&5
+printf %s "checking for unsigned long long int... " >&6; }
+if test ${ac_cv_type_unsigned_long_long_int+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_type_unsigned_long_long_int=yes
+     case $ac_prog_cc_stdc in
+       no | c89) ;;
+       *)
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+  /* For now, do not test the preprocessor; as of 2007 there are too many
+	 implementations with broken preprocessors.  Perhaps this can
+	 be revisited in 2012.  In the meantime, code should not expect
+	 #if to work with literals wider than 32 bits.  */
+      /* Test literals.  */
+      long long int ll = 9223372036854775807ll;
+      long long int nll = -9223372036854775807LL;
+      unsigned long long int ull = 18446744073709551615ULL;
+      /* Test constant expressions.   */
+      typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll)
+		     ? 1 : -1)];
+      typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1
+		     ? 1 : -1)];
+      int i = 63;
+int
+main (void)
+{
+/* Test availability of runtime routines for shift and division.  */
+      long long int llmax = 9223372036854775807ll;
+      unsigned long long int ullmax = 18446744073709551615ull;
+      return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i)
+	      | (llmax / ll) | (llmax % ll)
+	      | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i)
+	      | (ullmax / ull) | (ullmax % ull));
+  ;
+  return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+
+else $as_nop
+  ac_cv_type_unsigned_long_long_int=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext;;
+     esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_unsigned_long_long_int" >&5
+printf "%s\n" "$ac_cv_type_unsigned_long_long_int" >&6; }
+  if test $ac_cv_type_unsigned_long_long_int = yes; then
+
+printf "%s\n" "#define HAVE_UNSIGNED_LONG_LONG_INT 1" >>confdefs.h
+
+  fi
+
+
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for long long int" >&5
+printf %s "checking for long long int... " >&6; }
+if test ${ac_cv_type_long_long_int+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_type_long_long_int=yes
+      case $ac_prog_cc_stdc in
+	no | c89) ;;
+	*)
+	  ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int
+	  if test $ac_cv_type_long_long_int = yes; then
+	    	    	    	    if test "$cross_compiling" = yes
+then :
+  :
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <limits.h>
+		   #ifndef LLONG_MAX
+		   # define HALF \
+			    (1LL << (sizeof (long long int) * CHAR_BIT - 2))
+		   # define LLONG_MAX (HALF - 1 + HALF)
+		   #endif
+int
+main (void)
+{
+long long int n = 1;
+		   int i;
+		   for (i = 0; ; i++)
+		     {
+		       long long int m = n << i;
+		       if (m >> i != n)
+			 return 1;
+		       if (LLONG_MAX / 2 < m)
+			 break;
+		     }
+		   return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+
+else $as_nop
+  ac_cv_type_long_long_int=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+	  fi;;
+      esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_long_int" >&5
+printf "%s\n" "$ac_cv_type_long_long_int" >&6; }
+  if test $ac_cv_type_long_long_int = yes; then
+
+printf "%s\n" "#define HAVE_LONG_LONG_INT 1" >>confdefs.h
+
+  fi
+
+
+
+  ac_fn_c_check_type "$LINENO" "intmax_t" "ac_cv_type_intmax_t" "$ac_includes_default"
+if test "x$ac_cv_type_intmax_t" = xyes
+then :
+
+printf "%s\n" "#define HAVE_INTMAX_T 1" >>confdefs.h
+
+else $as_nop
+  test $ac_cv_type_long_long_int = yes \
+       && ac_type='long long int' \
+       || ac_type='long int'
+
+printf "%s\n" "#define intmax_t $ac_type" >>confdefs.h
+
+fi
+
+
+
+
+  ac_fn_c_check_type "$LINENO" "uintmax_t" "ac_cv_type_uintmax_t" "$ac_includes_default"
+if test "x$ac_cv_type_uintmax_t" = xyes
+then :
+
+printf "%s\n" "#define HAVE_UINTMAX_T 1" >>confdefs.h
+
+else $as_nop
+  test $ac_cv_type_unsigned_long_long_int = yes \
+       && ac_type='unsigned long long int' \
+       || ac_type='unsigned long int'
+
+printf "%s\n" "#define uintmax_t $ac_type" >>confdefs.h
+
+fi
+
+
+ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t"
+case $ac_cv_c_int64_t in #(
+  no|yes) ;; #(
+  *)
+
+printf "%s\n" "#define int64_t $ac_cv_c_int64_t" >>confdefs.h
+;;
+esac
+
+ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t"
+case $ac_cv_c_uint64_t in #(
+  no|yes) ;; #(
+  *)
+
+printf "%s\n" "#define _UINT64_T 1" >>confdefs.h
+
+
+printf "%s\n" "#define uint64_t $ac_cv_c_uint64_t" >>confdefs.h
+;;
+  esac
+
+ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t"
+case $ac_cv_c_int32_t in #(
+  no|yes) ;; #(
+  *)
+
+printf "%s\n" "#define int32_t $ac_cv_c_int32_t" >>confdefs.h
+;;
+esac
+
+ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t"
+case $ac_cv_c_uint32_t in #(
+  no|yes) ;; #(
+  *)
+
+printf "%s\n" "#define _UINT32_T 1" >>confdefs.h
+
+
+printf "%s\n" "#define uint32_t $ac_cv_c_uint32_t" >>confdefs.h
+;;
+  esac
+
+ac_fn_c_find_intX_t "$LINENO" "16" "ac_cv_c_int16_t"
+case $ac_cv_c_int16_t in #(
+  no|yes) ;; #(
+  *)
+
+printf "%s\n" "#define int16_t $ac_cv_c_int16_t" >>confdefs.h
+;;
+esac
+
+ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t"
+case $ac_cv_c_uint16_t in #(
+  no|yes) ;; #(
+  *)
+
+
+printf "%s\n" "#define uint16_t $ac_cv_c_uint16_t" >>confdefs.h
+;;
+  esac
+
+ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t"
+case $ac_cv_c_uint8_t in #(
+  no|yes) ;; #(
+  *)
+
+printf "%s\n" "#define _UINT8_T 1" >>confdefs.h
+
+
+printf "%s\n" "#define uint8_t $ac_cv_c_uint8_t" >>confdefs.h
+;;
+  esac
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5
+printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; }
+if test ${ac_cv_c_undeclared_builtin_options+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_save_CFLAGS=$CFLAGS
+   ac_cv_c_undeclared_builtin_options='cannot detect'
+   for ac_arg in '' -fno-builtin; do
+     CFLAGS="$ac_save_CFLAGS $ac_arg"
+     # This test program should *not* compile successfully.
+     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main (void)
+{
+(void) strchr;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+
+else $as_nop
+  # This test program should compile successfully.
+        # No library function is consistently available on
+        # freestanding implementations, so test against a dummy
+        # declaration.  Include always-available headers on the
+        # off chance that they somehow elicit warnings.
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <float.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+extern void ac_decl (int, char *);
+
+int
+main (void)
+{
+(void) ac_decl (0, (char *) 0);
+  (void) ac_decl;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  if test x"$ac_arg" = x
+then :
+  ac_cv_c_undeclared_builtin_options='none needed'
+else $as_nop
+  ac_cv_c_undeclared_builtin_options=$ac_arg
+fi
+          break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+    done
+    CFLAGS=$ac_save_CFLAGS
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5
+printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; }
+  case $ac_cv_c_undeclared_builtin_options in #(
+  'cannot detect') :
+    { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot make $CC report undeclared builtins
+See \`config.log' for more details" "$LINENO" 5; } ;; #(
+  'none needed') :
+    ac_c_undeclared_builtin_options='' ;; #(
+  *) :
+    ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;;
+esac
+
+ac_fn_check_decl "$LINENO" "SIZE_MAX" "ac_cv_have_decl_SIZE_MAX" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_SIZE_MAX" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_SIZE_MAX $ac_have_decl" >>confdefs.h
+ac_fn_check_decl "$LINENO" "INT32_MAX" "ac_cv_have_decl_INT32_MAX" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_INT32_MAX" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_INT32_MAX $ac_have_decl" >>confdefs.h
+ac_fn_check_decl "$LINENO" "INT32_MIN" "ac_cv_have_decl_INT32_MIN" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_INT32_MIN" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_INT32_MIN $ac_have_decl" >>confdefs.h
+
+ac_fn_check_decl "$LINENO" "INT64_MAX" "ac_cv_have_decl_INT64_MAX" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_INT64_MAX" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_INT64_MAX $ac_have_decl" >>confdefs.h
+ac_fn_check_decl "$LINENO" "INT64_MIN" "ac_cv_have_decl_INT64_MIN" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_INT64_MIN" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_INT64_MIN $ac_have_decl" >>confdefs.h
+ac_fn_check_decl "$LINENO" "UINT64_MAX" "ac_cv_have_decl_UINT64_MAX" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_UINT64_MAX" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_UINT64_MAX $ac_have_decl" >>confdefs.h
+ac_fn_check_decl "$LINENO" "UINT32_MAX" "ac_cv_have_decl_UINT32_MAX" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_UINT32_MAX" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_UINT32_MAX $ac_have_decl" >>confdefs.h
+
+ac_fn_check_decl "$LINENO" "INTMAX_MAX" "ac_cv_have_decl_INTMAX_MAX" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_INTMAX_MAX" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_INTMAX_MAX $ac_have_decl" >>confdefs.h
+ac_fn_check_decl "$LINENO" "INTMAX_MIN" "ac_cv_have_decl_INTMAX_MIN" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_INTMAX_MIN" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_INTMAX_MIN $ac_have_decl" >>confdefs.h
+ac_fn_check_decl "$LINENO" "UINTMAX_MAX" "ac_cv_have_decl_UINTMAX_MAX" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_UINTMAX_MAX" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_UINTMAX_MAX $ac_have_decl" >>confdefs.h
+
+
+ac_fn_check_decl "$LINENO" "SSIZE_MAX" "ac_cv_have_decl_SSIZE_MAX" "#include <limits.h>
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_SSIZE_MAX" = xyes
+then :
+
+printf "%s\n" "#define HAVE_DECL_SSIZE_MAX 1" >>confdefs.h
+
+fi
+
+ac_fn_check_decl "$LINENO" "EFTYPE" "ac_cv_have_decl_EFTYPE" "#include <errno.h>
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_EFTYPE" = xyes
+then :
+
+printf "%s\n" "#define HAVE_EFTYPE 1" >>confdefs.h
+
+fi
+ac_fn_check_decl "$LINENO" "EILSEQ" "ac_cv_have_decl_EILSEQ" "#include <errno.h>
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_EILSEQ" = xyes
+then :
+
+printf "%s\n" "#define HAVE_EILSEQ 1" >>confdefs.h
+
+fi
+ac_fn_c_check_type "$LINENO" "wchar_t" "ac_cv_type_wchar_t" "$ac_includes_default"
+if test "x$ac_cv_type_wchar_t" = xyes
+then :
+
+printf "%s\n" "#define HAVE_WCHAR_T 1" >>confdefs.h
+		# The cast to long int works around a bug in the HP C Compiler
+# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
+# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# This bug is HP SR number 8606223364.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of wchar_t" >&5
+printf %s "checking size of wchar_t... " >&6; }
+if test ${ac_cv_sizeof_wchar_t+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (wchar_t))" "ac_cv_sizeof_wchar_t"        "$ac_includes_default"
+then :
+
+else $as_nop
+  if test "$ac_cv_type_wchar_t" = yes; then
+     { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "cannot compute sizeof (wchar_t)
+See \`config.log' for more details" "$LINENO" 5; }
+   else
+     ac_cv_sizeof_wchar_t=0
+   fi
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_wchar_t" >&5
+printf "%s\n" "$ac_cv_sizeof_wchar_t" >&6; }
+
+
+
+printf "%s\n" "#define SIZEOF_WCHAR_T $ac_cv_sizeof_wchar_t" >>confdefs.h
+
+
+fi
+
+
+
+
+
+# Checks for library functions.
+if test $ac_cv_c_compiler_gnu = yes; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC needs -traditional" >&5
+printf %s "checking whether $CC needs -traditional... " >&6; }
+if test ${ac_cv_prog_gcc_traditional+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+    ac_pattern="Autoconf.*'x'"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sgtty.h>
+Autoconf TIOCGETP
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "$ac_pattern" >/dev/null 2>&1
+then :
+  ac_cv_prog_gcc_traditional=yes
+else $as_nop
+  ac_cv_prog_gcc_traditional=no
+fi
+rm -rf conftest*
+
+
+  if test $ac_cv_prog_gcc_traditional = no; then
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <termio.h>
+Autoconf TCGETA
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "$ac_pattern" >/dev/null 2>&1
+then :
+  ac_cv_prog_gcc_traditional=yes
+fi
+rm -rf conftest*
+
+  fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_gcc_traditional" >&5
+printf "%s\n" "$ac_cv_prog_gcc_traditional" >&6; }
+  if test $ac_cv_prog_gcc_traditional = yes; then
+    CC="$CC -traditional"
+  fi
+fi
+
+
+ac_fn_c_check_header_compile "$LINENO" "sys/mkdev.h" "ac_cv_header_sys_mkdev_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_mkdev_h" = xyes
+then :
+
+printf "%s\n" "#define MAJOR_IN_MKDEV 1" >>confdefs.h
+
+fi
+
+if test $ac_cv_header_sys_mkdev_h = no; then
+  ac_fn_c_check_header_compile "$LINENO" "sys/sysmacros.h" "ac_cv_header_sys_sysmacros_h" "$ac_includes_default"
+if test "x$ac_cv_header_sys_sysmacros_h" = xyes
+then :
+
+printf "%s\n" "#define MAJOR_IN_SYSMACROS 1" >>confdefs.h
+
+fi
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5
+printf %s "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; }
+if test ${ac_cv_sys_largefile_source+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  while :; do
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h> /* for off_t */
+     #include <stdio.h>
+int
+main (void)
+{
+int (*fp) (FILE *, off_t, int) = fseeko;
+     return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_sys_largefile_source=no; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#define _LARGEFILE_SOURCE 1
+#include <sys/types.h> /* for off_t */
+     #include <stdio.h>
+int
+main (void)
+{
+int (*fp) (FILE *, off_t, int) = fseeko;
+     return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_sys_largefile_source=1; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+  ac_cv_sys_largefile_source=unknown
+  break
+done
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_source" >&5
+printf "%s\n" "$ac_cv_sys_largefile_source" >&6; }
+case $ac_cv_sys_largefile_source in #(
+  no | unknown) ;;
+  *)
+printf "%s\n" "#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source" >>confdefs.h
+;;
+esac
+rm -rf conftest*
+
+# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug
+# in glibc 2.1.3, but that breaks too many other things.
+# If you want fseeko and ftello with glibc, upgrade to a fixed glibc.
+if test $ac_cv_sys_largefile_source != unknown; then
+
+printf "%s\n" "#define HAVE_FSEEKO 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for working memcmp" >&5
+printf %s "checking for working memcmp... " >&6; }
+if test ${ac_cv_func_memcmp_working+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test "$cross_compiling" = yes
+then :
+  ac_cv_func_memcmp_working=no
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main (void)
+{
+
+  /* Some versions of memcmp are not 8-bit clean.  */
+  char c0 = '\100', c1 = '\200', c2 = '\201';
+  if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0)
+    return 1;
+
+  /* The Next x86 OpenStep bug shows up only when comparing 16 bytes
+     or more and with at least one buffer not starting on a 4-byte boundary.
+     William Lewis provided this test program.   */
+  {
+    char foo[21];
+    char bar[21];
+    int i;
+    for (i = 0; i < 4; i++)
+      {
+	char *a = foo + i;
+	char *b = bar + i;
+	strcpy (a, "--------01111111");
+	strcpy (b, "--------10000000");
+	if (memcmp (a, b, 16) >= 0)
+	  return 1;
+      }
+    return 0;
+  }
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+  ac_cv_func_memcmp_working=yes
+else $as_nop
+  ac_cv_func_memcmp_working=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_memcmp_working" >&5
+printf "%s\n" "$ac_cv_func_memcmp_working" >&6; }
+test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in
+  *" memcmp.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS memcmp.$ac_objext"
+ ;;
+esac
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether lstat correctly handles trailing slash" >&5
+printf %s "checking whether lstat correctly handles trailing slash... " >&6; }
+if test ${ac_cv_func_lstat_dereferences_slashed_symlink+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  rm -f conftest.sym conftest.file
+echo >conftest.file
+if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then
+  if test "$cross_compiling" = yes
+then :
+  case "$host_os" in # ((
+			   # Guess yes on glibc systems.
+		   *-gnu*) ac_cv_func_lstat_dereferences_slashed_symlink=yes ;;
+			   # If we don't know, assume the worst.
+		   *)      ac_cv_func_lstat_dereferences_slashed_symlink=no ;;
+		 esac
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main (void)
+{
+struct stat sbuf;
+     /* Linux will dereference the symlink and fail, as required by POSIX.
+	That is better in the sense that it means we will not
+	have to compile and use the lstat wrapper.  */
+     return lstat ("conftest.sym/", &sbuf) == 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+  ac_cv_func_lstat_dereferences_slashed_symlink=yes
+else $as_nop
+  ac_cv_func_lstat_dereferences_slashed_symlink=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+else
+  # If the `ln -s' command failed, then we probably don't even
+  # have an lstat function.
+  ac_cv_func_lstat_dereferences_slashed_symlink=no
+fi
+rm -f conftest.sym conftest.file
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5
+printf "%s\n" "$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; }
+
+test $ac_cv_func_lstat_dereferences_slashed_symlink = yes &&
+
+printf "%s\n" "#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1" >>confdefs.h
+
+
+if test "x$ac_cv_func_lstat_dereferences_slashed_symlink" = xno; then
+  case " $LIBOBJS " in
+  *" lstat.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS lstat.$ac_objext"
+ ;;
+esac
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether lstat accepts an empty string" >&5
+printf %s "checking whether lstat accepts an empty string... " >&6; }
+if test ${ac_cv_func_lstat_empty_string_bug+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test "$cross_compiling" = yes
+then :
+  ac_cv_func_lstat_empty_string_bug=yes
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main (void)
+{
+struct stat sbuf;
+  return lstat ("", &sbuf) == 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+  ac_cv_func_lstat_empty_string_bug=no
+else $as_nop
+  ac_cv_func_lstat_empty_string_bug=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_lstat_empty_string_bug" >&5
+printf "%s\n" "$ac_cv_func_lstat_empty_string_bug" >&6; }
+if test $ac_cv_func_lstat_empty_string_bug = yes; then
+  case " $LIBOBJS " in
+  *" lstat.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS lstat.$ac_objext"
+ ;;
+esac
+
+
+printf "%s\n" "#define HAVE_LSTAT_EMPTY_STRING_BUG 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether stat accepts an empty string" >&5
+printf %s "checking whether stat accepts an empty string... " >&6; }
+if test ${ac_cv_func_stat_empty_string_bug+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  if test "$cross_compiling" = yes
+then :
+  ac_cv_func_stat_empty_string_bug=yes
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main (void)
+{
+struct stat sbuf;
+  return stat ("", &sbuf) == 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"
+then :
+  ac_cv_func_stat_empty_string_bug=no
+else $as_nop
+  ac_cv_func_stat_empty_string_bug=yes
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_stat_empty_string_bug" >&5
+printf "%s\n" "$ac_cv_func_stat_empty_string_bug" >&6; }
+if test $ac_cv_func_stat_empty_string_bug = yes; then
+  case " $LIBOBJS " in
+  *" stat.$ac_objext "* ) ;;
+  *) LIBOBJS="$LIBOBJS stat.$ac_objext"
+ ;;
+esac
+
+
+printf "%s\n" "#define HAVE_STAT_EMPTY_STRING_BUG 1" >>confdefs.h
+
+fi
+
+ac_fn_check_decl "$LINENO" "strerror_r" "ac_cv_have_decl_strerror_r" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_strerror_r" = xyes
+then :
+  ac_have_decl=1
+else $as_nop
+  ac_have_decl=0
+fi
+printf "%s\n" "#define HAVE_DECL_STRERROR_R $ac_have_decl" >>confdefs.h
+
+
+if test $ac_cv_have_decl_strerror_r = yes; then
+  # For backward compatibility's sake, define HAVE_STRERROR_R.
+  # (We used to run AC_CHECK_FUNCS_ONCE for strerror_r, as well
+  # as AC_CHECK_DECLS_ONCE.)
+
+printf "%s\n" "#define HAVE_STRERROR_R 1" >>confdefs.h
+
+fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char *" >&5
+printf %s "checking whether strerror_r returns char *... " >&6; }
+if test ${ac_cv_func_strerror_r_char_p+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+
+    ac_cv_func_strerror_r_char_p=no
+    if test $ac_cv_have_decl_strerror_r = yes; then
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <string.h>
+int
+main (void)
+{
+
+	  char buf[100];
+	  char x = *strerror_r (0, buf, sizeof buf);
+	  char *p = strerror_r (0, buf, sizeof buf);
+	  return !p || x;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_func_strerror_r_char_p=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+
+    fi
+
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_strerror_r_char_p" >&5
+printf "%s\n" "$ac_cv_func_strerror_r_char_p" >&6; }
+if test $ac_cv_func_strerror_r_char_p = yes; then
+
+printf "%s\n" "#define STRERROR_R_CHAR_P 1" >>confdefs.h
+
+fi
+
+
+  for ac_func in strftime
+do :
+  ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime"
+if test "x$ac_cv_func_strftime" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRFTIME 1" >>confdefs.h
+
+else $as_nop
+  # strftime is in -lintl on SCO UNIX.
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for strftime in -lintl" >&5
+printf %s "checking for strftime in -lintl... " >&6; }
+if test ${ac_cv_lib_intl_strftime+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lintl  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+char strftime ();
+int
+main (void)
+{
+return strftime ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_intl_strftime=yes
+else $as_nop
+  ac_cv_lib_intl_strftime=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_intl_strftime" >&5
+printf "%s\n" "$ac_cv_lib_intl_strftime" >&6; }
+if test "x$ac_cv_lib_intl_strftime" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRFTIME 1" >>confdefs.h
+
+LIBS="-lintl $LIBS"
+fi
+
+fi
+
+done
+ac_func=
+for ac_item in $ac_func_c_list
+do
+  if test $ac_func; then
+    ac_fn_c_check_func "$LINENO" $ac_func ac_cv_func_$ac_func
+    if eval test \"x\$ac_cv_func_$ac_func\" = xyes; then
+      echo "#define $ac_item 1" >> confdefs.h
+    fi
+    ac_func=
+  else
+    ac_func=$ac_item
+  fi
+done
+
+if test "x$ac_cv_func_vprintf" = xno
+then :
+  ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt"
+if test "x$ac_cv_func__doprnt" = xyes
+then :
+
+printf "%s\n" "#define HAVE_DOPRNT 1" >>confdefs.h
+
+fi
+
+fi
+# check for:
+#   CreateHardLinkA(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES)
+# To avoid necessity for including windows.h or special forward declaration
+# workarounds, we use 'void *' for 'struct SECURITY_ATTRIBUTES *'
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for CreateHardLinkA" >&5
+printf %s "checking for CreateHardLinkA... " >&6; }
+if test ${ac_cv_func_CreateHardLinkA+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char __stdcall CreateHardLinkA ( const char *, const char *, void * ) below.  */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char __stdcall CreateHardLinkA ( const char *, const char *, void * );
+char (*f) ( const char *, const char *, void * );
+
+int
+main (void)
+{
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_CreateHardLinkA) || defined (__stub___CreateHardLinkA)
+choke me
+#else
+f = CreateHardLinkA;
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_func_CreateHardLinkA=yes
+else $as_nop
+  ac_cv_func_CreateHardLinkA=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_CreateHardLinkA" >&5
+printf "%s\n" "$ac_cv_func_CreateHardLinkA" >&6; }
+if test $ac_cv_func_CreateHardLinkA = yes
+then :
+
+fi
+ac_fn_c_check_func "$LINENO" "arc4random_buf" "ac_cv_func_arc4random_buf"
+if test "x$ac_cv_func_arc4random_buf" = xyes
+then :
+  printf "%s\n" "#define HAVE_ARC4RANDOM_BUF 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "chflags" "ac_cv_func_chflags"
+if test "x$ac_cv_func_chflags" = xyes
+then :
+  printf "%s\n" "#define HAVE_CHFLAGS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "chown" "ac_cv_func_chown"
+if test "x$ac_cv_func_chown" = xyes
+then :
+  printf "%s\n" "#define HAVE_CHOWN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "chroot" "ac_cv_func_chroot"
+if test "x$ac_cv_func_chroot" = xyes
+then :
+  printf "%s\n" "#define HAVE_CHROOT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "ctime_r" "ac_cv_func_ctime_r"
+if test "x$ac_cv_func_ctime_r" = xyes
+then :
+  printf "%s\n" "#define HAVE_CTIME_R 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "fchdir" "ac_cv_func_fchdir"
+if test "x$ac_cv_func_fchdir" = xyes
+then :
+  printf "%s\n" "#define HAVE_FCHDIR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fchflags" "ac_cv_func_fchflags"
+if test "x$ac_cv_func_fchflags" = xyes
+then :
+  printf "%s\n" "#define HAVE_FCHFLAGS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fchmod" "ac_cv_func_fchmod"
+if test "x$ac_cv_func_fchmod" = xyes
+then :
+  printf "%s\n" "#define HAVE_FCHMOD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fchown" "ac_cv_func_fchown"
+if test "x$ac_cv_func_fchown" = xyes
+then :
+  printf "%s\n" "#define HAVE_FCHOWN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fcntl" "ac_cv_func_fcntl"
+if test "x$ac_cv_func_fcntl" = xyes
+then :
+  printf "%s\n" "#define HAVE_FCNTL 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fdopendir" "ac_cv_func_fdopendir"
+if test "x$ac_cv_func_fdopendir" = xyes
+then :
+  printf "%s\n" "#define HAVE_FDOPENDIR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fnmatch" "ac_cv_func_fnmatch"
+if test "x$ac_cv_func_fnmatch" = xyes
+then :
+  printf "%s\n" "#define HAVE_FNMATCH 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fork" "ac_cv_func_fork"
+if test "x$ac_cv_func_fork" = xyes
+then :
+  printf "%s\n" "#define HAVE_FORK 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "fstat" "ac_cv_func_fstat"
+if test "x$ac_cv_func_fstat" = xyes
+then :
+  printf "%s\n" "#define HAVE_FSTAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fstatat" "ac_cv_func_fstatat"
+if test "x$ac_cv_func_fstatat" = xyes
+then :
+  printf "%s\n" "#define HAVE_FSTATAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fstatfs" "ac_cv_func_fstatfs"
+if test "x$ac_cv_func_fstatfs" = xyes
+then :
+  printf "%s\n" "#define HAVE_FSTATFS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "fstatvfs" "ac_cv_func_fstatvfs"
+if test "x$ac_cv_func_fstatvfs" = xyes
+then :
+  printf "%s\n" "#define HAVE_FSTATVFS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "ftruncate" "ac_cv_func_ftruncate"
+if test "x$ac_cv_func_ftruncate" = xyes
+then :
+  printf "%s\n" "#define HAVE_FTRUNCATE 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "futimens" "ac_cv_func_futimens"
+if test "x$ac_cv_func_futimens" = xyes
+then :
+  printf "%s\n" "#define HAVE_FUTIMENS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "futimes" "ac_cv_func_futimes"
+if test "x$ac_cv_func_futimes" = xyes
+then :
+  printf "%s\n" "#define HAVE_FUTIMES 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "futimesat" "ac_cv_func_futimesat"
+if test "x$ac_cv_func_futimesat" = xyes
+then :
+  printf "%s\n" "#define HAVE_FUTIMESAT 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "geteuid" "ac_cv_func_geteuid"
+if test "x$ac_cv_func_geteuid" = xyes
+then :
+  printf "%s\n" "#define HAVE_GETEUID 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getline" "ac_cv_func_getline"
+if test "x$ac_cv_func_getline" = xyes
+then :
+  printf "%s\n" "#define HAVE_GETLINE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getpid" "ac_cv_func_getpid"
+if test "x$ac_cv_func_getpid" = xyes
+then :
+  printf "%s\n" "#define HAVE_GETPID 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getgrgid_r" "ac_cv_func_getgrgid_r"
+if test "x$ac_cv_func_getgrgid_r" = xyes
+then :
+  printf "%s\n" "#define HAVE_GETGRGID_R 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getgrnam_r" "ac_cv_func_getgrnam_r"
+if test "x$ac_cv_func_getgrnam_r" = xyes
+then :
+  printf "%s\n" "#define HAVE_GETGRNAM_R 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "getpwnam_r" "ac_cv_func_getpwnam_r"
+if test "x$ac_cv_func_getpwnam_r" = xyes
+then :
+  printf "%s\n" "#define HAVE_GETPWNAM_R 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getpwuid_r" "ac_cv_func_getpwuid_r"
+if test "x$ac_cv_func_getpwuid_r" = xyes
+then :
+  printf "%s\n" "#define HAVE_GETPWUID_R 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "getvfsbyname" "ac_cv_func_getvfsbyname"
+if test "x$ac_cv_func_getvfsbyname" = xyes
+then :
+  printf "%s\n" "#define HAVE_GETVFSBYNAME 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "gmtime_r" "ac_cv_func_gmtime_r"
+if test "x$ac_cv_func_gmtime_r" = xyes
+then :
+  printf "%s\n" "#define HAVE_GMTIME_R 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "lchflags" "ac_cv_func_lchflags"
+if test "x$ac_cv_func_lchflags" = xyes
+then :
+  printf "%s\n" "#define HAVE_LCHFLAGS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "lchmod" "ac_cv_func_lchmod"
+if test "x$ac_cv_func_lchmod" = xyes
+then :
+  printf "%s\n" "#define HAVE_LCHMOD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "lchown" "ac_cv_func_lchown"
+if test "x$ac_cv_func_lchown" = xyes
+then :
+  printf "%s\n" "#define HAVE_LCHOWN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "link" "ac_cv_func_link"
+if test "x$ac_cv_func_link" = xyes
+then :
+  printf "%s\n" "#define HAVE_LINK 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "linkat" "ac_cv_func_linkat"
+if test "x$ac_cv_func_linkat" = xyes
+then :
+  printf "%s\n" "#define HAVE_LINKAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "localtime_r" "ac_cv_func_localtime_r"
+if test "x$ac_cv_func_localtime_r" = xyes
+then :
+  printf "%s\n" "#define HAVE_LOCALTIME_R 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "lstat" "ac_cv_func_lstat"
+if test "x$ac_cv_func_lstat" = xyes
+then :
+  printf "%s\n" "#define HAVE_LSTAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "lutimes" "ac_cv_func_lutimes"
+if test "x$ac_cv_func_lutimes" = xyes
+then :
+  printf "%s\n" "#define HAVE_LUTIMES 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "mbrtowc" "ac_cv_func_mbrtowc"
+if test "x$ac_cv_func_mbrtowc" = xyes
+then :
+  printf "%s\n" "#define HAVE_MBRTOWC 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "memmove" "ac_cv_func_memmove"
+if test "x$ac_cv_func_memmove" = xyes
+then :
+  printf "%s\n" "#define HAVE_MEMMOVE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "memset" "ac_cv_func_memset"
+if test "x$ac_cv_func_memset" = xyes
+then :
+  printf "%s\n" "#define HAVE_MEMSET 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "mkdir" "ac_cv_func_mkdir"
+if test "x$ac_cv_func_mkdir" = xyes
+then :
+  printf "%s\n" "#define HAVE_MKDIR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mkfifo" "ac_cv_func_mkfifo"
+if test "x$ac_cv_func_mkfifo" = xyes
+then :
+  printf "%s\n" "#define HAVE_MKFIFO 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mknod" "ac_cv_func_mknod"
+if test "x$ac_cv_func_mknod" = xyes
+then :
+  printf "%s\n" "#define HAVE_MKNOD 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "mkstemp" "ac_cv_func_mkstemp"
+if test "x$ac_cv_func_mkstemp" = xyes
+then :
+  printf "%s\n" "#define HAVE_MKSTEMP 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "nl_langinfo" "ac_cv_func_nl_langinfo"
+if test "x$ac_cv_func_nl_langinfo" = xyes
+then :
+  printf "%s\n" "#define HAVE_NL_LANGINFO 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "openat" "ac_cv_func_openat"
+if test "x$ac_cv_func_openat" = xyes
+then :
+  printf "%s\n" "#define HAVE_OPENAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "pipe" "ac_cv_func_pipe"
+if test "x$ac_cv_func_pipe" = xyes
+then :
+  printf "%s\n" "#define HAVE_PIPE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "poll" "ac_cv_func_poll"
+if test "x$ac_cv_func_poll" = xyes
+then :
+  printf "%s\n" "#define HAVE_POLL 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "posix_spawnp" "ac_cv_func_posix_spawnp"
+if test "x$ac_cv_func_posix_spawnp" = xyes
+then :
+  printf "%s\n" "#define HAVE_POSIX_SPAWNP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "readlink" "ac_cv_func_readlink"
+if test "x$ac_cv_func_readlink" = xyes
+then :
+  printf "%s\n" "#define HAVE_READLINK 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "readlinkat" "ac_cv_func_readlinkat"
+if test "x$ac_cv_func_readlinkat" = xyes
+then :
+  printf "%s\n" "#define HAVE_READLINKAT 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "readpassphrase" "ac_cv_func_readpassphrase"
+if test "x$ac_cv_func_readpassphrase" = xyes
+then :
+  printf "%s\n" "#define HAVE_READPASSPHRASE 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "select" "ac_cv_func_select"
+if test "x$ac_cv_func_select" = xyes
+then :
+  printf "%s\n" "#define HAVE_SELECT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv"
+if test "x$ac_cv_func_setenv" = xyes
+then :
+  printf "%s\n" "#define HAVE_SETENV 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "setlocale" "ac_cv_func_setlocale"
+if test "x$ac_cv_func_setlocale" = xyes
+then :
+  printf "%s\n" "#define HAVE_SETLOCALE 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "sigaction" "ac_cv_func_sigaction"
+if test "x$ac_cv_func_sigaction" = xyes
+then :
+  printf "%s\n" "#define HAVE_SIGACTION 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "statfs" "ac_cv_func_statfs"
+if test "x$ac_cv_func_statfs" = xyes
+then :
+  printf "%s\n" "#define HAVE_STATFS 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "statvfs" "ac_cv_func_statvfs"
+if test "x$ac_cv_func_statvfs" = xyes
+then :
+  printf "%s\n" "#define HAVE_STATVFS 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "strchr" "ac_cv_func_strchr"
+if test "x$ac_cv_func_strchr" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRCHR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strdup" "ac_cv_func_strdup"
+if test "x$ac_cv_func_strdup" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRDUP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror"
+if test "x$ac_cv_func_strerror" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRERROR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strncpy_s" "ac_cv_func_strncpy_s"
+if test "x$ac_cv_func_strncpy_s" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRNCPY_S 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strnlen" "ac_cv_func_strnlen"
+if test "x$ac_cv_func_strnlen" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRNLEN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "strrchr" "ac_cv_func_strrchr"
+if test "x$ac_cv_func_strrchr" = xyes
+then :
+  printf "%s\n" "#define HAVE_STRRCHR 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "symlink" "ac_cv_func_symlink"
+if test "x$ac_cv_func_symlink" = xyes
+then :
+  printf "%s\n" "#define HAVE_SYMLINK 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "timegm" "ac_cv_func_timegm"
+if test "x$ac_cv_func_timegm" = xyes
+then :
+  printf "%s\n" "#define HAVE_TIMEGM 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "tzset" "ac_cv_func_tzset"
+if test "x$ac_cv_func_tzset" = xyes
+then :
+  printf "%s\n" "#define HAVE_TZSET 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "unlinkat" "ac_cv_func_unlinkat"
+if test "x$ac_cv_func_unlinkat" = xyes
+then :
+  printf "%s\n" "#define HAVE_UNLINKAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "unsetenv" "ac_cv_func_unsetenv"
+if test "x$ac_cv_func_unsetenv" = xyes
+then :
+  printf "%s\n" "#define HAVE_UNSETENV 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "utime" "ac_cv_func_utime"
+if test "x$ac_cv_func_utime" = xyes
+then :
+  printf "%s\n" "#define HAVE_UTIME 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "utimensat" "ac_cv_func_utimensat"
+if test "x$ac_cv_func_utimensat" = xyes
+then :
+  printf "%s\n" "#define HAVE_UTIMENSAT 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "utimes" "ac_cv_func_utimes"
+if test "x$ac_cv_func_utimes" = xyes
+then :
+  printf "%s\n" "#define HAVE_UTIMES 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "vfork" "ac_cv_func_vfork"
+if test "x$ac_cv_func_vfork" = xyes
+then :
+  printf "%s\n" "#define HAVE_VFORK 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "wcrtomb" "ac_cv_func_wcrtomb"
+if test "x$ac_cv_func_wcrtomb" = xyes
+then :
+  printf "%s\n" "#define HAVE_WCRTOMB 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "wcscmp" "ac_cv_func_wcscmp"
+if test "x$ac_cv_func_wcscmp" = xyes
+then :
+  printf "%s\n" "#define HAVE_WCSCMP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "wcscpy" "ac_cv_func_wcscpy"
+if test "x$ac_cv_func_wcscpy" = xyes
+then :
+  printf "%s\n" "#define HAVE_WCSCPY 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "wcslen" "ac_cv_func_wcslen"
+if test "x$ac_cv_func_wcslen" = xyes
+then :
+  printf "%s\n" "#define HAVE_WCSLEN 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "wctomb" "ac_cv_func_wctomb"
+if test "x$ac_cv_func_wctomb" = xyes
+then :
+  printf "%s\n" "#define HAVE_WCTOMB 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "wmemcmp" "ac_cv_func_wmemcmp"
+if test "x$ac_cv_func_wmemcmp" = xyes
+then :
+  printf "%s\n" "#define HAVE_WMEMCMP 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "wmemcpy" "ac_cv_func_wmemcpy"
+if test "x$ac_cv_func_wmemcpy" = xyes
+then :
+  printf "%s\n" "#define HAVE_WMEMCPY 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "wmemmove" "ac_cv_func_wmemmove"
+if test "x$ac_cv_func_wmemmove" = xyes
+then :
+  printf "%s\n" "#define HAVE_WMEMMOVE 1" >>confdefs.h
+
+fi
+
+ac_fn_c_check_func "$LINENO" "_fseeki64" "ac_cv_func__fseeki64"
+if test "x$ac_cv_func__fseeki64" = xyes
+then :
+  printf "%s\n" "#define HAVE__FSEEKI64 1" >>confdefs.h
+
+fi
+ac_fn_c_check_func "$LINENO" "_get_timezone" "ac_cv_func__get_timezone"
+if test "x$ac_cv_func__get_timezone" = xyes
+then :
+  printf "%s\n" "#define HAVE__GET_TIMEZONE 1" >>confdefs.h
+
+fi
+
+ac_fn_check_decl "$LINENO" "cmtime_s" "ac_cv_have_decl_cmtime_s" "#include <time.h>
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_cmtime_s" = xyes
+then :
+
+printf "%s\n" "#define HAVE_CMTIME_S 1" >>confdefs.h
+
+fi
+ac_fn_check_decl "$LINENO" "gmtime_s" "ac_cv_have_decl_gmtime_s" "#include <time.h>
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_gmtime_s" = xyes
+then :
+
+printf "%s\n" "#define HAVE_GMTIME_S 1" >>confdefs.h
+
+fi
+ac_fn_c_check_type "$LINENO" "localtime_s" "ac_cv_type_localtime_s" "#include <time.h>
+"
+if test "x$ac_cv_type_localtime_s" = xyes
+then :
+
+printf "%s\n" "#define HAVE_LOCALTIME_S 1" >>confdefs.h
+
+fi
+
+ac_fn_check_decl "$LINENO" "_mkgmtime" "ac_cv_have_decl__mkgmtime" "#include <time.h>
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl__mkgmtime" = xyes
+then :
+
+printf "%s\n" "#define HAVE__MKGMTIME 1" >>confdefs.h
+
+fi
+
+# detects cygwin-1.7, as opposed to older versions
+ac_fn_c_check_func "$LINENO" "cygwin_conv_path" "ac_cv_func_cygwin_conv_path"
+if test "x$ac_cv_func_cygwin_conv_path" = xyes
+then :
+  printf "%s\n" "#define HAVE_CYGWIN_CONV_PATH 1" >>confdefs.h
+
+fi
+
+
+# DragonFly uses vfsconf, FreeBSD xvfsconf.
+ac_fn_c_check_type "$LINENO" "struct vfsconf" "ac_cv_type_struct_vfsconf" "#if HAVE_SYS_TYPES_H
+	#include <sys/types.h>
+	#endif
+	#include <sys/mount.h>
+
+"
+if test "x$ac_cv_type_struct_vfsconf" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_VFSCONF 1" >>confdefs.h
+
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "struct xvfsconf" "ac_cv_type_struct_xvfsconf" "#if HAVE_SYS_TYPES_H
+	#include <sys/types.h>
+	#endif
+	#include <sys/mount.h>
+
+"
+if test "x$ac_cv_type_struct_xvfsconf" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_XVFSCONF 1" >>confdefs.h
+
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "struct statfs" "ac_cv_type_struct_statfs" "#if HAVE_SYS_TYPES_H
+	#include <sys/types.h>
+	#endif
+	#include <sys/mount.h>
+
+"
+if test "x$ac_cv_type_struct_statfs" = xyes
+then :
+
+printf "%s\n" "#define HAVE_STRUCT_STATFS 1" >>confdefs.h
+
+
+fi
+
+
+# There are several variants of readdir_r around; we only
+# accept the POSIX-compliant version.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <dirent.h>
+int
+main (void)
+{
+DIR *dir; struct dirent e, *r;
+		    return(readdir_r(dir, &e, &r));
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+
+printf "%s\n" "#define HAVE_READDIR_R 1" >>confdefs.h
+
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+# dirfd can be either a function or a macro.
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <dirent.h>
+                    DIR *dir;
+int
+main (void)
+{
+return(dirfd(dir));
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+
+printf "%s\n" "#define HAVE_DIRFD 1" >>confdefs.h
+
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+
+# FreeBSD's nl_langinfo supports an option to specify whether the
+# current locale uses month/day or day/month ordering.  It makes the
+# output a little prettier...
+ac_fn_check_decl "$LINENO" "D_MD_ORDER" "ac_cv_have_decl_D_MD_ORDER" "#if HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+
+" "$ac_c_undeclared_builtin_options" "CFLAGS"
+if test "x$ac_cv_have_decl_D_MD_ORDER" = xyes
+then :
+
+printf "%s\n" "#define HAVE_D_MD_ORDER 1" >>confdefs.h
+
+fi
+
+# Check for dirent.d_namlen field explicitly
+# (This is a bit more straightforward than, if not quite as portable as,
+# the recipe given by the autoconf maintainers.)
+ac_fn_c_check_member "$LINENO" "struct dirent" "d_namlen" "ac_cv_member_struct_dirent_d_namlen" "#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+"
+if test "x$ac_cv_member_struct_dirent_d_namlen" = xyes
+then :
+
+fi
+
+
+# Additional requirements
+# Check whether --enable-largefile was given.
+if test ${enable_largefile+y}
+then :
+  enableval=$enable_largefile;
+fi
+
+if test "$enable_largefile" != no; then
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5
+printf %s "checking for special C compiler options needed for large files... " >&6; }
+if test ${ac_cv_sys_largefile_CC+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  ac_cv_sys_largefile_CC=no
+     if test "$GCC" != yes; then
+       ac_save_CC=$CC
+       while :; do
+	 # IRIX 6.2 and later do not support large files by default,
+	 # so use the C compiler's -n32 option if that helps.
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+		       && LARGE_OFF_T % 2147483647 == 1)
+		      ? 1 : -1];
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+	 if ac_fn_c_try_compile "$LINENO"
+then :
+  break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+	 CC="$CC -n32"
+	 if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_sys_largefile_CC=' -n32'; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+	 break
+       done
+       CC=$ac_save_CC
+       rm -f conftest.$ac_ext
+    fi
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5
+printf "%s\n" "$ac_cv_sys_largefile_CC" >&6; }
+  if test "$ac_cv_sys_largefile_CC" != no; then
+    CC=$CC$ac_cv_sys_largefile_CC
+  fi
+
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5
+printf %s "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; }
+if test ${ac_cv_sys_file_offset_bits+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  while :; do
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+		       && LARGE_OFF_T % 2147483647 == 1)
+		      ? 1 : -1];
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_sys_file_offset_bits=no; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#define _FILE_OFFSET_BITS 64
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+		       && LARGE_OFF_T % 2147483647 == 1)
+		      ? 1 : -1];
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_sys_file_offset_bits=64; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  ac_cv_sys_file_offset_bits=unknown
+  break
+done
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5
+printf "%s\n" "$ac_cv_sys_file_offset_bits" >&6; }
+case $ac_cv_sys_file_offset_bits in #(
+  no | unknown) ;;
+  *)
+printf "%s\n" "#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits" >>confdefs.h
+;;
+esac
+rm -rf conftest*
+  if test $ac_cv_sys_file_offset_bits = unknown; then
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5
+printf %s "checking for _LARGE_FILES value needed for large files... " >&6; }
+if test ${ac_cv_sys_large_files+y}
+then :
+  printf %s "(cached) " >&6
+else $as_nop
+  while :; do
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+		       && LARGE_OFF_T % 2147483647 == 1)
+		      ? 1 : -1];
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_sys_large_files=no; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#define _LARGE_FILES 1
+#include <sys/types.h>
+ /* Check that off_t can represent 2**63 - 1 correctly.
+    We can't simply define LARGE_OFF_T to be 9223372036854775807,
+    since some C++ compilers masquerading as C compilers
+    incorrectly reject 9223372036854775807.  */
+#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
+  int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
+		       && LARGE_OFF_T % 2147483647 == 1)
+		      ? 1 : -1];
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  ac_cv_sys_large_files=1; break
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  ac_cv_sys_large_files=unknown
+  break
+done
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5
+printf "%s\n" "$ac_cv_sys_large_files" >&6; }
+case $ac_cv_sys_large_files in #(
+  no | unknown) ;;
+  *)
+printf "%s\n" "#define _LARGE_FILES $ac_cv_sys_large_files" >>confdefs.h
+;;
+esac
+rm -rf conftest*
+  fi
+fi
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+printf "%s\n" "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+	cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+	  mv -f confcache "$cache_file"$$ &&
+	  mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+	  mv -f confcache "$cache_file" ;;
+	esac
+      fi
+    fi
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+if test -z "${INC_WINDOWS_FILES_TRUE}" && test -z "${INC_WINDOWS_FILES_FALSE}"; then
+  as_fn_error $? "conditional \"INC_WINDOWS_FILES\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${INC_CYGWIN_FILES_TRUE}" && test -z "${INC_CYGWIN_FILES_FALSE}"; then
+  as_fn_error $? "conditional \"INC_CYGWIN_FILES\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${INC_BLAKE2_TRUE}" && test -z "${INC_BLAKE2_FALSE}"; then
+  as_fn_error $? "conditional \"INC_BLAKE2\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else $as_nop
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
+as_nl='
+'
+export as_nl
+IFS=" ""	$as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh).  This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2)            ; then :; else exec 2>/dev/null; fi
+
+# The user is always right.
+if ${PATH_SEPARATOR+false} :; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  case $as_dir in #(((
+    '') as_dir=./ ;;
+    */) ;;
+    *) as_dir=$as_dir/ ;;
+  esac
+    test -r "$as_dir$0" && as_myself=$as_dir$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  printf "%s\n" "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else $as_nop
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else $as_nop
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n.  New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+as_echo='printf %s\n'
+as_echo_n='printf %s'
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by Haskell libarchive-clib package $as_me 3.7.2, which was
+generated by GNU Autoconf 2.71.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_headers="$ac_config_headers"
+config_commands="$ac_config_commands"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --header=FILE[:TEMPLATE]
+                   instantiate the configuration header FILE
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <vamchale@gmail.com>."
+
+_ACEOF
+ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
+ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"`
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config='$ac_cs_config_escaped'
+ac_cs_version="\\
+Haskell libarchive-clib package config.status 3.7.2
+configured by $0, generated by GNU Autoconf 2.71,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2021 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+MKDIR_P='$MKDIR_P'
+AWK='$AWK'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    printf "%s\n" "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    printf "%s\n" "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    printf "%s\n" "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  printf "%s\n" "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+#
+# INIT-COMMANDS
+#
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='$sed_quote_subst'
+double_quote_subst='$double_quote_subst'
+delay_variable_subst='$delay_variable_subst'
+AS='`$ECHO "$AS" | $SED "$delay_single_quote_subst"`'
+DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`'
+OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`'
+macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`'
+macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`'
+enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
+enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
+pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
+enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
+shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`'
+SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`'
+ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`'
+PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`'
+host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`'
+host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`'
+host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`'
+build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`'
+build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`'
+build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`'
+SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`'
+Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`'
+GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`'
+EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`'
+FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`'
+LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`'
+NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`'
+LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`'
+max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`'
+ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`'
+exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`'
+lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`'
+lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`'
+lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`'
+lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`'
+reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`'
+reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`'
+FILECMD='`$ECHO "$FILECMD" | $SED "$delay_single_quote_subst"`'
+deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`'
+file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`'
+file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`'
+want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`'
+sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`'
+AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`'
+lt_ar_flags='`$ECHO "$lt_ar_flags" | $SED "$delay_single_quote_subst"`'
+AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`'
+archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`'
+STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`'
+RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`'
+old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`'
+lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`'
+CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`'
+CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`'
+compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`'
+GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`'
+lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`'
+nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`'
+lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`'
+lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`'
+objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`'
+MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`'
+lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`'
+lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`'
+need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`'
+MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`'
+DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`'
+NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`'
+LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`'
+OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`'
+OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`'
+libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`'
+shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`'
+extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`'
+enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`'
+export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`'
+whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`'
+compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`'
+old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`'
+old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`'
+archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`'
+archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`'
+module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`'
+with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`'
+allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`'
+no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`'
+hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`'
+hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`'
+hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`'
+hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`'
+hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`'
+hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`'
+inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`'
+link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`'
+always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`'
+export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`'
+exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`'
+include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`'
+prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`'
+postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`'
+file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`'
+variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`'
+need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`'
+need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`'
+version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`'
+runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`'
+shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`'
+libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`'
+library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`'
+soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`'
+install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`'
+postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`'
+postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`'
+finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`'
+finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`'
+hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`'
+sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`'
+configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`'
+configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`'
+hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`'
+enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`'
+enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`'
+old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`'
+striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`'
+
+LTCC='$LTCC'
+LTCFLAGS='$LTCFLAGS'
+compiler='$compiler_DEFAULT'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+  eval 'cat <<_LTECHO_EOF
+\$1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in AS \
+DLLTOOL \
+OBJDUMP \
+SHELL \
+ECHO \
+PATH_SEPARATOR \
+SED \
+GREP \
+EGREP \
+FGREP \
+LD \
+NM \
+LN_S \
+lt_SP2NL \
+lt_NL2SP \
+reload_flag \
+FILECMD \
+deplibs_check_method \
+file_magic_cmd \
+file_magic_glob \
+want_nocaseglob \
+sharedlib_from_linklib_cmd \
+AR \
+archiver_list_spec \
+STRIP \
+RANLIB \
+CC \
+CFLAGS \
+compiler \
+lt_cv_sys_global_symbol_pipe \
+lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_import \
+lt_cv_sys_global_symbol_to_c_name_address \
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+lt_cv_nm_interface \
+nm_file_list_spec \
+lt_cv_truncate_bin \
+lt_prog_compiler_no_builtin_flag \
+lt_prog_compiler_pic \
+lt_prog_compiler_wl \
+lt_prog_compiler_static \
+lt_cv_prog_compiler_c_o \
+need_locks \
+MANIFEST_TOOL \
+DSYMUTIL \
+NMEDIT \
+LIPO \
+OTOOL \
+OTOOL64 \
+shrext_cmds \
+export_dynamic_flag_spec \
+whole_archive_flag_spec \
+compiler_needs_object \
+with_gnu_ld \
+allow_undefined_flag \
+no_undefined_flag \
+hardcode_libdir_flag_spec \
+hardcode_libdir_separator \
+exclude_expsyms \
+include_expsyms \
+file_list_spec \
+variables_saved_for_relink \
+libname_spec \
+library_names_spec \
+soname_spec \
+install_override_mode \
+finish_eval \
+old_striplib \
+striplib; do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[\\\\\\\`\\"\\\$]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds \
+old_postinstall_cmds \
+old_postuninstall_cmds \
+old_archive_cmds \
+extract_expsyms_cmds \
+old_archive_from_new_cmds \
+old_archive_from_expsyms_cmds \
+archive_cmds \
+archive_expsym_cmds \
+module_cmds \
+module_expsym_cmds \
+export_symbols_cmds \
+prelink_cmds \
+postlink_cmds \
+postinstall_cmds \
+postuninstall_cmds \
+finish_cmds \
+sys_lib_search_path_spec \
+configure_time_dlsearch_path \
+configure_time_lt_sys_library_path; do
+    case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
+    *[\\\\\\\`\\"\\\$]*)
+      eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
+      ;;
+    *)
+      eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
+      ;;
+    esac
+done
+
+ac_aux_dir='$ac_aux_dir'
+
+# See if we are running on zsh, and set the options that allow our
+# commands through without removal of \ escapes INIT.
+if test -n "\${ZSH_VERSION+set}"; then
+   setopt NO_GLOB_SUBST
+fi
+
+
+    PACKAGE='$PACKAGE'
+    VERSION='$VERSION'
+    RM='$RM'
+    ofile='$ofile'
+
+
+
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "c/config.h") CONFIG_HEADERS="$CONFIG_HEADERS c/config.h" ;;
+    "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers
+  test ${CONFIG_COMMANDS+y} || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[	 ]*#[	 ]*define[	 ][	 ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[	 ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[	 ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = ""
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "    :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS"
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
+	 # because $ac_f cannot contain `:'.
+	 test -f "$ac_f" ||
+	   case $ac_f in
+	   [\\/$]*) false;;
+	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+	   esac ||
+	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+	  printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	`' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+printf "%s\n" "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`printf "%s\n" "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+printf "%s\n" X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      printf "%s\n" "/* $configure_input  */" >&1 \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+printf "%s\n" "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+	|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    printf "%s\n" "/* $configure_input  */" >&1 \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+
+  :C)  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+printf "%s\n" "$as_me: executing $ac_file commands" >&6;}
+ ;;
+  esac
+
+
+  case $ac_file$ac_mode in
+    "libtool":C)
+
+    # See if we are running on zsh, and set the options that allow our
+    # commands through without removal of \ escapes.
+    if test -n "${ZSH_VERSION+set}"; then
+      setopt NO_GLOB_SUBST
+    fi
+
+    cfgfile=${ofile}T
+    trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+    $RM "$cfgfile"
+
+    cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+# Generated automatically by $as_me ($PACKAGE) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions.  There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of of the License, or
+# (at your option) any later version.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the  same
+# distribution terms that you use for the rest of that program.
+#
+# GNU Libtool is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags=''
+
+# Configured defaults for sys_lib_dlsearch_path munging.
+: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Assembler program.
+AS=$lt_AS
+
+# DLL creation program.
+DLLTOOL=$lt_DLLTOOL
+
+# Object dumper program.
+OBJDUMP=$lt_OBJDUMP
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# Shared archive member basename,for filename based shared library versioning on AIX.
+shared_archive_member_spec=$shared_archive_member_spec
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that protects backslashes.
+ECHO=$lt_ECHO
+
+# The PATH separator for the build system.
+PATH_SEPARATOR=$lt_PATH_SEPARATOR
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# convert \$build file names to \$host format.
+to_host_file_cmd=$lt_cv_to_host_file_cmd
+
+# convert \$build files to toolchain format.
+to_tool_file_cmd=$lt_cv_to_tool_file_cmd
+
+# A file(cmd) program that detects file types.
+FILECMD=$lt_FILECMD
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method = "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# How to find potential files when deplibs_check_method = "file_magic".
+file_magic_glob=$lt_file_magic_glob
+
+# Find potential files using nocaseglob when deplibs_check_method = "file_magic".
+want_nocaseglob=$lt_want_nocaseglob
+
+# Command to associate shared and link libraries.
+sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd
+
+# The archiver.
+AR=$lt_AR
+
+# Flags to create an archive (by configure).
+lt_ar_flags=$lt_ar_flags
+
+# Flags to create an archive.
+AR_FLAGS=\${ARFLAGS-"\$lt_ar_flags"}
+
+# How to feed a file listing to the archiver.
+archiver_list_spec=$lt_archiver_list_spec
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Whether to use a lock for old archive extraction.
+lock_old_archive_extraction=$lock_old_archive_extraction
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm into a list of symbols to manually relocate.
+global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name lister interface.
+nm_interface=$lt_lt_cv_nm_interface
+
+# Specify filename containing input files for \$NM.
+nm_file_list_spec=$lt_nm_file_list_spec
+
+# The root where to search for dependent libraries,and where our libraries should be installed.
+lt_sysroot=$lt_sysroot
+
+# Command to truncate a binary pipe.
+lt_truncate_bin=$lt_lt_cv_truncate_bin
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Manifest tool.
+MANIFEST_TOOL=$lt_MANIFEST_TOOL
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names.  First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Permission mode override for installation of shared libraries.
+install_override_mode=$lt_install_override_mode
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Detected run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path
+
+# Explicit LT_SYS_LIBRARY_PATH set during ./configure time.
+configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \$shlibpath_var if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Commands necessary for finishing linking programs.
+postlink_cmds=$lt_postlink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+    cat <<'_LT_EOF' >> "$cfgfile"
+
+# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
+
+# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+#       string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+#       string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+#       string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+#       "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+#       VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+    case x$2 in
+    x)
+        ;;
+    *:)
+        eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
+        ;;
+    x:*)
+        eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
+        ;;
+    *::*)
+        eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+        eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
+        ;;
+    *)
+        eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
+        ;;
+    esac
+}
+
+
+# Calculate cc_basename.  Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+    for cc_temp in $*""; do
+      case $cc_temp in
+        compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+        distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+        \-*) ;;
+        *) break;;
+      esac
+    done
+    func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+
+
+# ### END FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_EOF
+
+  case $host_os in
+  aix3*)
+    cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program.  For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test set != "${COLLECT_NAMES+set}"; then
+  COLLECT_NAMES=
+  export COLLECT_NAMES
+fi
+_LT_EOF
+    ;;
+  esac
+
+
+
+ltmain=$ac_aux_dir/ltmain.sh
+
+
+  # We use sed instead of cat because bash on DJGPP gets confused if
+  # if finds mixed CR/LF and LF-only lines.  Since sed operates in
+  # text mode, it properly converts lines to CR/LF.  This bash problem
+  # is reportedly fixed, but why not run on old versions too?
+  $SED '$q' "$ltmain" >> "$cfgfile" \
+     || (rm -f "$cfgfile"; exit 1)
+
+   mv -f "$cfgfile" "$ofile" ||
+    (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+  chmod +x "$ofile"
+
+ ;;
+
+  esac
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,383 @@
+m4_define([LIBARCHIVE_VERSION_S],[3.7.2])
+m4_define([LIBARCHIVE_VERSION_N],[3007002])
+
+dnl bsdtar and bsdcpio versioning tracks libarchive
+m4_define([BSDTAR_VERSION_S],LIBARCHIVE_VERSION_S())
+m4_define([BSDCPIO_VERSION_S],LIBARCHIVE_VERSION_S())
+m4_define([BSDCAT_VERSION_S],LIBARCHIVE_VERSION_S())
+
+AC_PREREQ([2.69])
+AC_INIT([Haskell libarchive-clib package],[3.7.2],[vamchale@gmail.com],[libarchive-clib])
+
+#
+# Now starts the "real" configure script.
+#
+
+# Make sure the srcdir contains "libarchive" directory
+AC_CONFIG_SRCDIR([c])
+# Use auxiliary subscripts from this subdirectory (cleans up root)
+AC_CONFIG_AUX_DIR([build/autoconf])
+# M4 scripts
+AC_CONFIG_MACRO_DIR([build/autoconf])
+
+# Libtool's "interface version" can be computed from the libarchive version.
+
+# Libtool interface version bumps on any API change, so increments
+# whenever libarchive minor version does.
+ARCHIVE_MINOR=$(( (LIBARCHIVE_VERSION_N() / 1000) % 1000 ))
+# Libarchive 2.7 == libtool interface 9 = 2 + 7
+# Libarchive 2.8 == libtool interface 10 = 2 + 8
+# Libarchive 2.9 == libtool interface 11 = 2 + 8
+# Libarchive 3.0 == libtool interface 12
+# Libarchive 3.1 == libtool interface 13
+ARCHIVE_INTERFACE=`echo $((13 + ${ARCHIVE_MINOR}))`
+# Libarchive revision is bumped on any source change === libtool revision
+ARCHIVE_REVISION=$(( LIBARCHIVE_VERSION_N() % 1000 ))
+# Libarchive minor is bumped on any interface addition === libtool age
+ARCHIVE_LIBTOOL_VERSION=$ARCHIVE_INTERFACE:$ARCHIVE_REVISION:$ARCHIVE_MINOR
+
+# Stick the version numbers into config.h
+AC_DEFINE([__LIBARCHIVE_CONFIG_H_INCLUDED], [1],
+	[Internal macro for sanity checks])
+AC_DEFINE([LIBARCHIVE_VERSION_STRING],"LIBARCHIVE_VERSION_S()",
+	[Version number of libarchive])
+AC_DEFINE_UNQUOTED([LIBARCHIVE_VERSION_NUMBER],"LIBARCHIVE_VERSION_N()",
+	[Version number of libarchive as a single integer])
+AC_DEFINE([BSDCPIO_VERSION_STRING],"BSDCPIO_VERSION_S()",
+	[Version number of bsdcpio])
+AC_DEFINE([BSDTAR_VERSION_STRING],"BSDTAR_VERSION_S()",
+	[Version number of bsdtar])
+AC_DEFINE([BSDCAT_VERSION_STRING],"BSDTAR_VERSION_S()",
+	[Version number of bsdcat])
+
+# The shell variables here must be the same as the AC_SUBST() variables
+# below, but the shell variable names apparently cannot be the same as
+# the m4 macro names above.  Why?  Ask autoconf.
+BSDCPIO_VERSION_STRING=BSDCPIO_VERSION_S()
+BSDTAR_VERSION_STRING=BSDTAR_VERSION_S()
+BSDCAT_VERSION_STRING=BSDCAT_VERSION_S()
+LIBARCHIVE_VERSION_STRING=LIBARCHIVE_VERSION_S()
+LIBARCHIVE_VERSION_NUMBER=LIBARCHIVE_VERSION_N()
+
+# Substitute the above version numbers into the various files below.
+# Yes, I believe this is the fourth time we define what are essentially
+# the same symbols.  Why? Ask autoconf.
+AC_SUBST(ARCHIVE_LIBTOOL_VERSION)
+AC_SUBST(BSDCPIO_VERSION_STRING)
+AC_SUBST(BSDTAR_VERSION_STRING)
+AC_SUBST(BSDCAT_VERSION_STRING)
+AC_SUBST(LIBARCHIVE_VERSION_STRING)
+AC_SUBST(LIBARCHIVE_VERSION_NUMBER)
+
+AC_CONFIG_HEADERS([c/config.h])
+
+# Check for host type
+AC_CANONICAL_HOST
+
+dnl Compilation on mingw and Cygwin needs special Makefile rules
+inc_windows_files=no
+inc_cygwin_files=no
+case "$host_os" in
+  *mingw* ) inc_windows_files=yes ;;
+  *cygwin* | *msys*) inc_cygwin_files=yes ;;
+esac
+AM_CONDITIONAL([INC_WINDOWS_FILES], [test $inc_windows_files = yes])
+AM_CONDITIONAL([INC_CYGWIN_FILES], [test $inc_cygwin_files = yes])
+
+# Checks for programs.
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CPP
+AC_USE_SYSTEM_EXTENSIONS
+AC_LIBTOOL_WIN32_DLL
+AC_PROG_LIBTOOL
+AC_CHECK_TOOL([STRIP],[strip])
+AC_PROG_MKDIR_P
+
+
+# Set up defines needed before including any headers
+case $host in
+  *mingw* | *cygwin* | *msys*  )
+  AC_PREPROC_IFELSE([AC_LANG_PROGRAM(
+    [[#ifdef _WIN32_WINNT
+     # error _WIN32_WINNT already defined
+     #endif
+    ]],[[;]])
+  ],[
+    AC_DEFINE([_WIN32_WINNT], 0x0502, [Define to '0x0502' for Windows Server 2003 APIs.])
+    AC_DEFINE([NTDDI_VERSION], 0x05020000, [Define to '0x05020000' for Windows Server 2003 APIs.])
+  ])
+  AC_PREPROC_IFELSE([AC_LANG_PROGRAM(
+    [[#ifdef WINVER
+     # error WINVER already defined
+     #endif
+    ]],[[;]])
+  ],[
+    AC_DEFINE([WINVER], 0x0502, [Define to '0x0502' for Windows Server 2003 APIs.])
+  ])
+  ;;
+esac
+
+# Checks for header files.
+AC_HEADER_DIRENT
+AC_HEADER_SYS_WAIT
+AC_CHECK_HEADERS([acl/libacl.h attr/xattr.h])
+AC_CHECK_HEADERS([copyfile.h ctype.h])
+AC_CHECK_HEADERS([errno.h ext2fs/ext2_fs.h fcntl.h fnmatch.h grp.h])
+
+AC_CACHE_CHECK([whether EXT2_IOC_GETFLAGS is usable],
+    [ac_cv_have_decl_EXT2_IOC_GETFLAGS],
+    [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([@%:@include <sys/ioctl.h>
+@%:@include <ext2fs/ext2_fs.h>],
+                                   [int x = EXT2_IOC_GETFLAGS])],
+                  [AS_VAR_SET([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [yes])],
+                  [AS_VAR_SET([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [no])])])
+
+AS_VAR_IF([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [yes],
+    [AC_DEFINE_UNQUOTED([HAVE_WORKING_EXT2_IOC_GETFLAGS], [1],
+                    [Define to 1 if you have a working EXT2_IOC_GETFLAGS])])
+
+AC_CHECK_HEADERS([inttypes.h io.h langinfo.h limits.h])
+AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h linux/magic.h linux/types.h])
+
+AC_CACHE_CHECK([whether FS_IOC_GETFLAGS is usable],
+    [ac_cv_have_decl_FS_IOC_GETFLAGS],
+    [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([@%:@include <sys/ioctl.h>
+@%:@include <linux/fs.h>],
+                                   [int x = FS_IOC_GETFLAGS])],
+                  [AS_VAR_SET([ac_cv_have_decl_FS_IOC_GETFLAGS], [yes])],
+                  [AS_VAR_SET([ac_cv_have_decl_FS_IOC_GETFLAGS], [no])])])
+
+AS_VAR_IF([ac_cv_have_decl_FS_IOC_GETFLAGS], [yes],
+    [AC_DEFINE_UNQUOTED([HAVE_WORKING_FS_IOC_GETFLAGS], [1],
+                    [Define to 1 if you have a working FS_IOC_GETFLAGS])])
+
+AC_CHECK_HEADERS([locale.h membership.h paths.h poll.h pthread.h pwd.h])
+AC_CHECK_HEADERS([readpassphrase.h signal.h spawn.h])
+AC_CHECK_HEADERS([stdarg.h stdint.h stdlib.h string.h])
+AC_CHECK_HEADERS([sys/acl.h sys/cdefs.h sys/ea.h sys/extattr.h])
+AC_CHECK_HEADERS([sys/ioctl.h sys/mkdev.h sys/mount.h sys/queue.h])
+AC_CHECK_HEADERS([sys/param.h sys/poll.h sys/richacl.h])
+AC_CHECK_HEADERS([sys/select.h sys/statfs.h sys/statvfs.h sys/sysmacros.h])
+AC_CHECK_HEADERS([sys/time.h sys/utime.h sys/utsname.h sys/vfs.h sys/xattr.h])
+AC_CHECK_HEADERS([time.h unistd.h utime.h wchar.h wctype.h])
+AC_CHECK_HEADERS([windows.h])
+# check windows.h first; the other headers require it.
+AC_CHECK_HEADERS([wincrypt.h winioctl.h],[],[],
+[[#ifdef HAVE_WINDOWS_H
+# include <windows.h>
+#endif
+]])
+
+AM_CONDITIONAL([INC_BLAKE2], [test "x$ac_cv_lib_b2_blake2sp_init" != "xyes"])
+
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+# la_TYPE_UID_T defaults to "int", which is incorrect for MinGW
+# and MSVC. Use a customized version.
+la_TYPE_UID_T
+AC_TYPE_MODE_T
+# AC_TYPE_OFF_T defaults to "long", which limits us to 4GB files on
+# most systems... default to "long long" instead.
+AC_CHECK_TYPE(off_t, [long long])
+AC_TYPE_SIZE_T
+AC_CHECK_TYPE(id_t, [unsigned long])
+AC_CHECK_TYPE(uintptr_t, [unsigned int])
+
+# Check for tm_gmtoff in struct tm
+AC_CHECK_MEMBERS([struct tm.tm_gmtoff, struct tm.__tm_gmtoff],,,
+[
+#include <time.h>
+])
+
+# Check for f_namemax in struct statfs
+AC_CHECK_MEMBERS([struct statfs.f_namemax],,,
+[
+#include <sys/param.h>
+#include <sys/mount.h>
+])
+
+# Check for f_iosize in struct statfs
+AC_CHECK_MEMBERS([struct statfs.f_iosize],,,
+[
+#include <sys/param.h>
+#include <sys/mount.h>
+])
+
+# Check for f_iosize in struct statvfs
+AC_CHECK_MEMBERS([struct statvfs.f_iosize],,,
+[
+#include <sys/statvfs.h>
+])
+
+# Check for birthtime in struct stat
+AC_CHECK_MEMBERS([struct stat.st_birthtime])
+
+# Check for high-resolution timestamps in struct stat
+AC_CHECK_MEMBERS([struct stat.st_birthtimespec.tv_nsec])
+AC_CHECK_MEMBERS([struct stat.st_mtimespec.tv_nsec])
+AC_CHECK_MEMBERS([struct stat.st_mtim.tv_nsec])
+AC_CHECK_MEMBERS([struct stat.st_mtime_n]) # AIX
+AC_CHECK_MEMBERS([struct stat.st_umtime]) # Tru64
+AC_CHECK_MEMBERS([struct stat.st_mtime_usec]) # Hurd
+# Check for block size support in struct stat
+AC_CHECK_MEMBERS([struct stat.st_blksize])
+# Check for st_flags in struct stat (BSD fflags)
+AC_CHECK_MEMBERS([struct stat.st_flags])
+
+# If you have uintmax_t, we assume printf supports %ju
+# If you have unsigned long long, we assume printf supports %llu
+# TODO: Check for %ju and %llu support directly.
+AC_CHECK_TYPES([uintmax_t, unsigned long long])
+
+# We use C99-style integer types
+# Declare them if the local platform doesn't already do so.
+AC_TYPE_INTMAX_T
+AC_TYPE_UINTMAX_T
+AC_TYPE_INT64_T
+AC_TYPE_UINT64_T
+AC_TYPE_INT32_T
+AC_TYPE_UINT32_T
+AC_TYPE_INT16_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT8_T
+
+AC_CHECK_DECLS([SIZE_MAX, INT32_MAX, INT32_MIN])
+AC_CHECK_DECLS([INT64_MAX, INT64_MIN, UINT64_MAX, UINT32_MAX])
+AC_CHECK_DECLS([INTMAX_MAX, INTMAX_MIN, UINTMAX_MAX])
+
+AC_CHECK_DECL([SSIZE_MAX],
+		[AC_DEFINE(HAVE_DECL_SSIZE_MAX, 1, [Define to 1 if you have the declaration of `SSIZE_MAX', and to 0 if you don't.])],
+		[],
+		[#include <limits.h>])
+
+AC_CHECK_DECL([EFTYPE],
+		[AC_DEFINE(HAVE_EFTYPE, 1, [A possible errno value for invalid file format errors])],
+		[],
+		[#include <errno.h>])
+AC_CHECK_DECL([EILSEQ],
+		[AC_DEFINE(HAVE_EILSEQ, 1, [A possible errno value for invalid file format errors])],
+		[],
+		[#include <errno.h>])
+AC_CHECK_TYPE([wchar_t],
+	        [AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_[]wchar_t), 1, [Define to 1 if the system has the type `wchar_t'.])dnl
+		AC_CHECK_SIZEOF([wchar_t])],
+		[])
+
+AC_CHECK_HEADERS_ONCE([sys/time.h])
+
+# Checks for library functions.
+AC_PROG_GCC_TRADITIONAL
+AC_HEADER_MAJOR
+AC_FUNC_FSEEKO
+AC_FUNC_MEMCMP
+AC_FUNC_LSTAT
+AC_FUNC_STAT
+AC_FUNC_STRERROR_R
+AC_FUNC_STRFTIME
+AC_FUNC_VPRINTF
+# check for:
+#   CreateHardLinkA(LPCSTR, LPCSTR, LPSECURITY_ATTRIBUTES)
+# To avoid necessity for including windows.h or special forward declaration
+# workarounds, we use 'void *' for 'struct SECURITY_ATTRIBUTES *'
+AC_CHECK_STDCALL_FUNC([CreateHardLinkA],[const char *, const char *, void *])
+AC_CHECK_FUNCS([arc4random_buf chflags chown chroot ctime_r])
+AC_CHECK_FUNCS([fchdir fchflags fchmod fchown fcntl fdopendir fnmatch fork])
+AC_CHECK_FUNCS([fstat fstatat fstatfs fstatvfs ftruncate])
+AC_CHECK_FUNCS([futimens futimes futimesat])
+AC_CHECK_FUNCS([geteuid getline getpid getgrgid_r getgrnam_r])
+AC_CHECK_FUNCS([getpwnam_r getpwuid_r getvfsbyname gmtime_r])
+AC_CHECK_FUNCS([lchflags lchmod lchown link linkat localtime_r lstat lutimes])
+AC_CHECK_FUNCS([mbrtowc memmove memset])
+AC_CHECK_FUNCS([mkdir mkfifo mknod mkstemp])
+AC_CHECK_FUNCS([nl_langinfo openat pipe poll posix_spawnp readlink readlinkat])
+AC_CHECK_FUNCS([readpassphrase])
+AC_CHECK_FUNCS([select setenv setlocale sigaction statfs statvfs])
+AC_CHECK_FUNCS([strchr strdup strerror strncpy_s strnlen strrchr symlink])
+AC_CHECK_FUNCS([timegm tzset unlinkat unsetenv utime utimensat utimes vfork])
+AC_CHECK_FUNCS([wcrtomb wcscmp wcscpy wcslen wctomb wmemcmp wmemcpy wmemmove])
+AC_CHECK_FUNCS([_fseeki64 _get_timezone])
+AC_CHECK_DECL([cmtime_s],
+		[AC_DEFINE(HAVE_CMTIME_S, 1, [cmtime_s function])],
+		[],
+		[#include <time.h>])
+AC_CHECK_DECL([gmtime_s],
+		[AC_DEFINE(HAVE_GMTIME_S, 1, [gmtime_s function])],
+		[],
+		[#include <time.h>])
+AC_CHECK_TYPE([localtime_s],
+		[AC_DEFINE(HAVE_LOCALTIME_S, 1, [localtime_s function])],
+		[],
+		[#include <time.h>])
+AC_CHECK_DECL([_mkgmtime],
+		[AC_DEFINE(HAVE__MKGMTIME, 1, [_mkgmtime function])],
+		[],
+		[#include <time.h>])
+
+# detects cygwin-1.7, as opposed to older versions
+AC_CHECK_FUNCS([cygwin_conv_path])
+
+# DragonFly uses vfsconf, FreeBSD xvfsconf.
+AC_CHECK_TYPES(struct vfsconf,,,
+	[#if HAVE_SYS_TYPES_H
+	#include <sys/types.h>
+	#endif
+	#include <sys/mount.h>
+	])
+
+AC_CHECK_TYPES(struct xvfsconf,,,
+	[#if HAVE_SYS_TYPES_H
+	#include <sys/types.h>
+	#endif
+	#include <sys/mount.h>
+	])
+
+AC_CHECK_TYPES(struct statfs,,,
+	[#if HAVE_SYS_TYPES_H
+	#include <sys/types.h>
+	#endif
+	#include <sys/mount.h>
+	])
+
+# There are several variants of readdir_r around; we only
+# accept the POSIX-compliant version.
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([[#include <dirent.h>]],
+                  [[DIR *dir; struct dirent e, *r;
+		    return(readdir_r(dir, &e, &r));]])],
+ [AC_DEFINE(HAVE_READDIR_R,1,[Define to 1 if you have a POSIX compatible readdir_r])]
+)
+# dirfd can be either a function or a macro.
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM([[#include <dirent.h>
+                    DIR *dir;]],
+                  [[return(dirfd(dir));]])],
+ [AC_DEFINE(HAVE_DIRFD,1,[Define to 1 if you have a dirfd function or macro])]
+)
+
+# FreeBSD's nl_langinfo supports an option to specify whether the
+# current locale uses month/day or day/month ordering.  It makes the
+# output a little prettier...
+AC_CHECK_DECL([D_MD_ORDER],
+[AC_DEFINE(HAVE_D_MD_ORDER, 1, [Define to 1 if nl_langinfo supports D_MD_ORDER])],
+[],
+[#if HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+])
+
+# Check for dirent.d_namlen field explicitly
+# (This is a bit more straightforward than, if not quite as portable as,
+# the recipe given by the autoconf maintainers.)
+AC_CHECK_MEMBER(struct dirent.d_namlen,,,
+[#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+])
+
+# Additional requirements
+AC_SYS_LARGEFILE
+
+AC_OUTPUT
+
diff --git a/libarchive-clib.cabal b/libarchive-clib.cabal
new file mode 100644
--- /dev/null
+++ b/libarchive-clib.cabal
@@ -0,0 +1,182 @@
+cabal-version:      2.0
+name:               libarchive-clib
+version:            3.8.2
+license:            OtherLicense
+license-file:       LICENSE
+copyright:          Copyright: (c) 2018-2020 Vanessa McHale
+maintainer:         vamchale@gmail.com
+author:             Vanessa McHale
+
+bug-reports:        https://github.com/vmchale/libarchive/issues
+synopsis:           Haskell interface to libarchive (C sources)
+description:
+    Bundled libarchive C sources for use with the libarchive Haskell bindings.
+
+category:           Codec
+build-type:         Configure
+extra-source-files:
+    ./c/*.c
+    ./c/*.h
+    ./c/config.h.in
+    ./build/autoconf/config.guess
+    ./build/autoconf/config.sub
+    ./build/autoconf/install-sh
+    ./build/autoconf/ltmain.sh
+    configure
+    configure.ac
+
+source-repository head
+    type:     git
+    location: https://github.com/vmchale/libarchive
+
+library
+    default-language: Haskell2010
+    if os(windows)
+        cc-options: -U__GCC_ASM_FLAG_OUTPUTS__
+
+    if arch(i386)
+        cc-options: -D_Alignas(t)=__attribute__((__aligned__(t)))
+
+    cc-options:   -std=c11 -DHAVE_CONFIG_H
+    include-dirs: c
+    build-depends: zlib-clib
+    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_parse_date.c
+        ./c/archive_time.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_by_code.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_binary.c
+        ./c/archive_write_set_format_cpio_newc.c
+        ./c/archive_write_set_format_cpio_odc.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
+
+    install-includes:
+        archive.h
+        archive_entry.h
+
+    include-dirs: c/
+
