libarchive-clib (empty) → 3.8.2
raw patch · 182 files changed
+170355/−0 lines, 182 filesdep +zlib-clibsetup-changed
Dependencies added: zlib-clib
Files
- LICENSE +65/−0
- Setup.hs +2/−0
- build/autoconf/config.guess +1818/−0
- build/autoconf/config.sub +2364/−0
- build/autoconf/install-sh +541/−0
- build/autoconf/ltmain.sh +11429/−0
- c/archive.h +1265/−0
- c/archive_acl.c +2136/−0
- c/archive_acl_private.h +83/−0
- c/archive_blake2.h +197/−0
- c/archive_blake2_impl.h +161/−0
- c/archive_blake2s_ref.c +369/−0
- c/archive_blake2sp_ref.c +361/−0
- c/archive_check_magic.c +182/−0
- c/archive_cmdline.c +225/−0
- c/archive_cmdline_private.h +45/−0
- c/archive_crc32.h +86/−0
- c/archive_cryptor.c +534/−0
- c/archive_cryptor_private.h +205/−0
- c/archive_digest.c +1649/−0
- c/archive_digest_private.h +441/−0
- c/archive_disk_acl_darwin.c +559/−0
- c/archive_disk_acl_freebsd.c +712/−0
- c/archive_disk_acl_linux.c +760/−0
- c/archive_disk_acl_sunos.c +824/−0
- c/archive_endian.h +193/−0
- c/archive_entry.c +2293/−0
- c/archive_entry.h +763/−0
- c/archive_entry_copy_bhfi.c +55/−0
- c/archive_entry_copy_stat.c +82/−0
- c/archive_entry_link_resolver.c +469/−0
- c/archive_entry_locale.h +90/−0
- c/archive_entry_private.h +205/−0
- c/archive_entry_sparse.c +155/−0
- c/archive_entry_stat.c +121/−0
- c/archive_entry_strmode.c +86/−0
- c/archive_entry_xattr.c +155/−0
- c/archive_hmac.c +339/−0
- c/archive_hmac_private.h +119/−0
- c/archive_match.c +1846/−0
- c/archive_openssl_evp_private.h +54/−0
- c/archive_openssl_hmac_private.h +54/−0
- c/archive_options.c +217/−0
- c/archive_options_private.h +49/−0
- c/archive_pack_dev.c +330/−0
- c/archive_pack_dev.h +49/−0
- c/archive_parse_date.c +1145/−0
- c/archive_pathmatch.c +462/−0
- c/archive_pathmatch.h +50/−0
- c/archive_platform.h +213/−0
- c/archive_platform_acl.h +53/−0
- c/archive_platform_stat.h +45/−0
- c/archive_platform_xattr.h +45/−0
- c/archive_ppmd7.c +1173/−0
- c/archive_ppmd7_private.h +119/−0
- c/archive_ppmd8.c +1293/−0
- c/archive_ppmd8_private.h +148/−0
- c/archive_ppmd_private.h +159/−0
- c/archive_private.h +183/−0
- c/archive_random.c +300/−0
- c/archive_random_private.h +36/−0
- c/archive_rb.c +709/−0
- c/archive_rb.h +113/−0
- c/archive_read.c +1750/−0
- c/archive_read_add_passphrase.c +189/−0
- c/archive_read_append_filter.c +205/−0
- c/archive_read_data_into_fd.c +143/−0
- c/archive_read_disk_entry_from_file.c +1084/−0
- c/archive_read_disk_posix.c +2633/−0
- c/archive_read_disk_private.h +97/−0
- c/archive_read_disk_set_standard_lookup.c +312/−0
- c/archive_read_disk_windows.c +2549/−0
- c/archive_read_extract.c +59/−0
- c/archive_read_extract2.c +154/−0
- c/archive_read_open_fd.c +233/−0
- c/archive_read_open_file.c +254/−0
- c/archive_read_open_filename.c +648/−0
- c/archive_read_open_memory.c +185/−0
- c/archive_read_private.h +265/−0
- c/archive_read_set_format.c +116/−0
- c/archive_read_set_options.c +132/−0
- c/archive_read_support_filter_all.c +84/−0
- c/archive_read_support_filter_by_code.c +68/−0
- c/archive_read_support_filter_bzip2.c +363/−0
- c/archive_read_support_filter_compress.c +452/−0
- c/archive_read_support_filter_grzip.c +109/−0
- c/archive_read_support_filter_gzip.c +543/−0
- c/archive_read_support_filter_lrzip.c +119/−0
- c/archive_read_support_filter_lz4.c +739/−0
- c/archive_read_support_filter_lzop.c +498/−0
- c/archive_read_support_filter_none.c +51/−0
- c/archive_read_support_filter_program.c +492/−0
- c/archive_read_support_filter_rpm.c +294/−0
- c/archive_read_support_filter_uu.c +738/−0
- c/archive_read_support_filter_xz.c +803/−0
- c/archive_read_support_filter_zstd.c +295/−0
- c/archive_read_support_format_7zip.c +4526/−0
- c/archive_read_support_format_all.c +88/−0
- c/archive_read_support_format_ar.c +637/−0
- c/archive_read_support_format_by_code.c +76/−0
- c/archive_read_support_format_cab.c +3233/−0
- c/archive_read_support_format_cpio.c +1121/−0
- c/archive_read_support_format_empty.c +95/−0
- c/archive_read_support_format_iso9660.c +3534/−0
- c/archive_read_support_format_lha.c +2879/−0
- c/archive_read_support_format_mtree.c +2170/−0
- c/archive_read_support_format_rar.c +3918/−0
- c/archive_read_support_format_rar5.c +4411/−0
- c/archive_read_support_format_raw.c +191/−0
- c/archive_read_support_format_tar.c +3721/−0
- c/archive_read_support_format_warc.c +858/−0
- c/archive_read_support_format_xar.c +3667/−0
- c/archive_read_support_format_zip.c +4406/−0
- c/archive_string.c +4348/−0
- c/archive_string.h +240/−0
- c/archive_string_composition.h +2289/−0
- c/archive_string_sprintf.c +200/−0
- c/archive_time.c +163/−0
- c/archive_time_private.h +47/−0
- c/archive_util.c +686/−0
- c/archive_version_details.c +539/−0
- c/archive_virtual.c +162/−0
- c/archive_windows.c +1051/−0
- c/archive_windows.h +325/−0
- c/archive_write.c +873/−0
- c/archive_write_add_filter.c +71/−0
- c/archive_write_add_filter_b64encode.c +302/−0
- c/archive_write_add_filter_by_name.c +76/−0
- c/archive_write_add_filter_bzip2.c +402/−0
- c/archive_write_add_filter_compress.c +445/−0
- c/archive_write_add_filter_grzip.c +133/−0
- c/archive_write_add_filter_gzip.c +479/−0
- c/archive_write_add_filter_lrzip.c +195/−0
- c/archive_write_add_filter_lz4.c +698/−0
- c/archive_write_add_filter_lzop.c +477/−0
- c/archive_write_add_filter_none.c +42/−0
- c/archive_write_add_filter_program.c +389/−0
- c/archive_write_add_filter_uuencode.c +293/−0
- c/archive_write_add_filter_xz.c +542/−0
- c/archive_write_add_filter_zstd.c +572/−0
- c/archive_write_disk_posix.c +4764/−0
- c/archive_write_disk_private.h +43/−0
- c/archive_write_disk_set_standard_lookup.c +262/−0
- c/archive_write_disk_windows.c +2921/−0
- c/archive_write_open_fd.c +145/−0
- c/archive_write_open_file.c +106/−0
- c/archive_write_open_filename.c +270/−0
- c/archive_write_open_memory.c +114/−0
- c/archive_write_private.h +164/−0
- c/archive_write_set_format.c +124/−0
- c/archive_write_set_format_7zip.c +2584/−0
- c/archive_write_set_format_ar.c +569/−0
- c/archive_write_set_format_by_name.c +93/−0
- c/archive_write_set_format_cpio.c +11/−0
- c/archive_write_set_format_cpio_binary.c +612/−0
- c/archive_write_set_format_cpio_newc.c +456/−0
- c/archive_write_set_format_cpio_odc.c +502/−0
- c/archive_write_set_format_filter_by_ext.c +141/−0
- c/archive_write_set_format_gnutar.c +753/−0
- c/archive_write_set_format_iso9660.c +8172/−0
- c/archive_write_set_format_mtree.c +2296/−0
- c/archive_write_set_format_pax.c +2093/−0
- c/archive_write_set_format_private.h +40/−0
- c/archive_write_set_format_raw.c +125/−0
- c/archive_write_set_format_shar.c +648/−0
- c/archive_write_set_format_ustar.c +760/−0
- c/archive_write_set_format_v7tar.c +636/−0
- c/archive_write_set_format_warc.c +443/−0
- c/archive_write_set_format_xar.c +3698/−0
- c/archive_write_set_format_zip.c +2523/−0
- c/archive_write_set_options.c +129/−0
- c/archive_write_set_passphrase.c +95/−0
- c/archive_xxhash.h +48/−0
- c/config.h.in +884/−0
- c/config_freebsd.h +275/−0
- c/filter_fork.h +44/−0
- c/filter_fork_posix.c +269/−0
- c/filter_fork_windows.c +245/−0
- c/xxhash.c +529/−0
- configure +17995/−0
- configure.ac +383/−0
- libarchive-clib.cabal +182/−0
+ LICENSE view
@@ -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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMainWithHooks autoconfUserHooks
+ build/autoconf/config.guess view
@@ -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:
+ build/autoconf/config.sub view
@@ -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:
+ build/autoconf/install-sh view
@@ -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:
+ build/autoconf/ltmain.sh view
@@ -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 *) <_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:
+ c/archive.h view
@@ -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 */
+ c/archive_acl.c view
@@ -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)--;+ }+}
+ c/archive_acl_private.h view
@@ -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 */
+ c/archive_blake2.h view
@@ -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
+ c/archive_blake2_impl.h view
@@ -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
+ c/archive_blake2s_ref.c view
@@ -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
+ c/archive_blake2sp_ref.c view
@@ -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
+ c/archive_check_magic.c view
@@ -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);+}
+ c/archive_cmdline.c view
@@ -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);+}+
+ c/archive_cmdline_private.h view
@@ -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
+ c/archive_crc32.h view
@@ -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
+ c/archive_cryptor.c view
@@ -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,+};
+ c/archive_cryptor_private.h view
@@ -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
+ c/archive_digest.c view
@@ -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+};
+ c/archive_digest_private.h view
@@ -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
+ c/archive_disk_acl_darwin.c view
@@ -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 */
+ c/archive_disk_acl_freebsd.c view
@@ -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 */
+ c/archive_disk_acl_linux.c view
@@ -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 */
+ c/archive_disk_acl_sunos.c view
@@ -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 */
+ c/archive_endian.h view
@@ -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
+ c/archive_entry.c view
@@ -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
+ c/archive_entry.h view
@@ -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 */
+ c/archive_entry_copy_bhfi.c view
@@ -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
+ c/archive_entry_copy_stat.c view
@@ -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);+}
+ c/archive_entry_link_resolver.c view
@@ -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);+}
+ c/archive_entry_locale.h view
@@ -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 */
+ c/archive_entry_private.h view
@@ -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 */
+ c/archive_entry_sparse.c view
@@ -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+ */
+ c/archive_entry_stat.c view
@@ -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);+}
+ c/archive_entry_strmode.c view
@@ -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);+}
+ c/archive_entry_xattr.c view
@@ -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+ */
+ c/archive_hmac.c view
@@ -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,+};
+ c/archive_hmac_private.h view
@@ -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 */
+ c/archive_match.c view
@@ -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);+}
+ c/archive_openssl_evp_private.h view
@@ -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
+ c/archive_openssl_hmac_private.h view
@@ -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
+ c/archive_options.c view
@@ -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;+}+
+ c/archive_options_private.h view
@@ -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
+ c/archive_pack_dev.c view
@@ -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);+}
+ c/archive_pack_dev.h view
@@ -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 */
+ c/archive_parse_date.c view
@@ -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) */
+ c/archive_pathmatch.c view
@@ -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));+}
+ c/archive_pathmatch.h view
@@ -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
+ c/archive_platform.h view
@@ -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 */
+ c/archive_platform_acl.h view
@@ -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 */
+ c/archive_platform_stat.h view
@@ -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 */
+ c/archive_platform_xattr.h view
@@ -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 */
+ c/archive_ppmd7.c view
@@ -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+};
+ c/archive_ppmd7_private.h view
@@ -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
+ c/archive_ppmd8.c view
@@ -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,+};
+ c/archive_ppmd8_private.h view
@@ -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
+ c/archive_ppmd_private.h view
@@ -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
+ c/archive_private.h view
@@ -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
+ c/archive_random.c view
@@ -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 */
+ c/archive_random_private.h view
@@ -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 */
+ c/archive_rb.c view
@@ -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;+}
+ c/archive_rb.h view
@@ -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 */
+ c/archive_read.c view
@@ -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;+}
+ c/archive_read_add_passphrase.c view
@@ -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);+}
+ c/archive_read_append_filter.c view
@@ -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;+}
+ c/archive_read_data_into_fd.c view
@@ -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);+}
+ c/archive_read_disk_entry_from_file.c view
@@ -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, ©file_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__) */+
+ c/archive_read_disk_posix.c view
@@ -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
+ c/archive_read_disk_private.h view
@@ -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
+ c/archive_read_disk_set_standard_lookup.c view
@@ -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__) */
+ c/archive_read_disk_windows.c view
@@ -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
+ c/archive_read_extract.c view
@@ -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));+}
+ c/archive_read_extract2.c view
@@ -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);+ }+}
+ c/archive_read_open_fd.c view
@@ -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);+}
+ c/archive_read_open_file.c view
@@ -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);+}
+ c/archive_read_open_filename.c view
@@ -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);+}
+ c/archive_read_open_memory.c view
@@ -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);+}
+ c/archive_read_private.h view
@@ -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
+ c/archive_read_set_format.c view
@@ -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;+}
+ c/archive_read_set_options.c view
@@ -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);+}
+ c/archive_read_support_filter_all.c view
@@ -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);+}
+ c/archive_read_support_filter_by_code.c view
@@ -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);+}
+ c/archive_read_support_filter_bzip2.c view
@@ -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 */
+ c/archive_read_support_filter_compress.c view
@@ -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]);+}
+ c/archive_read_support_filter_grzip.c view
@@ -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);+}
+ c/archive_read_support_filter_gzip.c view
@@ -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 */
+ c/archive_read_support_filter_lrzip.c view
@@ -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);+}
+ c/archive_read_support_filter_lz4.c view
@@ -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 */
+ c/archive_read_support_filter_lzop.c view
@@ -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
+ c/archive_read_support_filter_none.c view
@@ -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);+}
+ c/archive_read_support_filter_program.c view
@@ -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);+}
+ c/archive_read_support_filter_rpm.c view
@@ -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);+}+
+ c/archive_read_support_filter_uu.c view
@@ -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);+}+
+ c/archive_read_support_filter_xz.c view
@@ -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 */
+ c/archive_read_support_filter_zstd.c view
@@ -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 */
+ c/archive_read_support_format_7zip.c view
@@ -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);+}
+ c/archive_read_support_format_all.c view
@@ -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);+}
+ c/archive_read_support_format_ar.c view
@@ -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);+}
+ c/archive_read_support_format_by_code.c view
@@ -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);+}
+ c/archive_read_support_format_cab.c view
@@ -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);+}
+ c/archive_read_support_format_cpio.c view
@@ -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);+}
+ c/archive_read_support_format_empty.c view
@@ -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);+}
+ c/archive_read_support_format_iso9660.c view
@@ -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
+ c/archive_read_support_format_lha.c view
@@ -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));+}
+ c/archive_read_support_format_mtree.c view
@@ -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;+ }+}
+ c/archive_read_support_format_rar.c view
@@ -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);+}
+ c/archive_read_support_format_rar5.c view
@@ -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;+}
+ c/archive_read_support_format_raw.c view
@@ -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);+}
+ c/archive_read_support_format_tar.c view
@@ -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);+}
+ c/archive_read_support_format_warc.c view
@@ -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 */
+ c/archive_read_support_format_xar.c view
@@ -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 */
+ c/archive_read_support_format_zip.c view
@@ -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:*/
+ c/archive_string.c view
@@ -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);+}
+ c/archive_string.h view
@@ -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
+ c/archive_string_composition.h view
@@ -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 */
+ c/archive_string_sprintf.c view
@@ -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);+ }+ }+}
+ c/archive_time.c view
@@ -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;+}
+ c/archive_time_private.h view
@@ -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 */
+ c/archive_util.c view
@@ -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
+ c/archive_version_details.c view
@@ -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+}
+ c/archive_virtual.c view
@@ -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));+}
+ c/archive_windows.c view
@@ -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__ */
+ c/archive_windows.h view
@@ -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 */
+ c/archive_write.c view
@@ -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;+}
+ c/archive_write_add_filter.c view
@@ -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);+}
+ c/archive_write_add_filter_b64encode.c view
@@ -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);+}+
+ c/archive_write_add_filter_by_name.c view
@@ -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);+}
+ c/archive_write_add_filter_bzip2.c view
@@ -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 */
+ c/archive_write_add_filter_compress.c view
@@ -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);+}
+ c/archive_write_add_filter_grzip.c view
@@ -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);+}
+ c/archive_write_add_filter_gzip.c view
@@ -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 */
+ c/archive_write_add_filter_lrzip.c view
@@ -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);+}
+ c/archive_write_add_filter_lz4.c view
@@ -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 */
+ c/archive_write_add_filter_lzop.c view
@@ -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
+ c/archive_write_add_filter_none.c view
@@ -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);+}
+ c/archive_write_add_filter_program.c view
@@ -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;+}+
+ c/archive_write_add_filter_uuencode.c view
@@ -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);+}+
+ c/archive_write_add_filter_xz.c view
@@ -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 */
+ c/archive_write_add_filter_zstd.c view
@@ -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 */
+ c/archive_write_disk_posix.c view
@@ -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, ×));++#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__ */+
+ c/archive_write_disk_private.h view
@@ -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
+ c/archive_write_disk_set_standard_lookup.c view
@@ -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;+}
+ c/archive_write_disk_windows.c view
@@ -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__ */
+ c/archive_write_open_fd.c view
@@ -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);+}
+ c/archive_write_open_file.c view
@@ -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);+}
+ c/archive_write_open_filename.c view
@@ -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);+}
+ c/archive_write_open_memory.c view
@@ -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);+}
+ c/archive_write_private.h view
@@ -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
+ c/archive_write_set_format.c view
@@ -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));+ }+}
+ c/archive_write_set_format_7zip.c view
@@ -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);+}
+ c/archive_write_set_format_ar.c view
@@ -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);+}
+ c/archive_write_set_format_by_name.c view
@@ -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);+}
+ c/archive_write_set_format_cpio.c view
@@ -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);+}
+ c/archive_write_set_format_cpio_binary.c view
@@ -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));+}
+ c/archive_write_set_format_cpio_newc.c view
@@ -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));+}
+ c/archive_write_set_format_cpio_odc.c view
@@ -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));+}
+ c/archive_write_set_format_filter_by_ext.c view
@@ -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);+}++++
+ c/archive_write_set_format_gnutar.c view
@@ -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);+}
+ c/archive_write_set_format_iso9660.c view
@@ -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 */+
+ c/archive_write_set_format_mtree.c view
@@ -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(®->digest.md5, entry->digest.md5,+ sizeof(reg->digest.md5));+ }+ if ((reg->compute_sum & F_RMD160)+ && (reg->mset_digest & AE_MSET_DIGEST_RMD160)) {++ memcpy(®->digest.rmd160, entry->digest.rmd160,+ sizeof(reg->digest.rmd160));+ }+ if ((reg->compute_sum & F_SHA1)+ && (reg->mset_digest & AE_MSET_DIGEST_SHA1)) {++ memcpy(®->digest.sha1, entry->digest.sha1,+ sizeof(reg->digest.sha1));+ }+ if ((reg->compute_sum & F_SHA256)+ && (reg->mset_digest & AE_MSET_DIGEST_SHA256)) {++ memcpy(®->digest.sha256, entry->digest.sha256,+ sizeof(reg->digest.sha256));+ }+ if ((reg->compute_sum & F_SHA384)+ && (reg->mset_digest & AE_MSET_DIGEST_SHA384)) {++ memcpy(®->digest.sha384, entry->digest.sha384,+ sizeof(reg->digest.sha384));+ }+ if ((reg->compute_sum & F_SHA512)+ && (reg->mset_digest & AE_MSET_DIGEST_SHA512)) {++ memcpy(®->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);+}
+ c/archive_write_set_format_pax.c view
@@ -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;+}
+ c/archive_write_set_format_private.h view
@@ -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
+ c/archive_write_set_format_raw.c view
@@ -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);+}
+ c/archive_write_set_format_shar.c view
@@ -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);+}
+ c/archive_write_set_format_ustar.c view
@@ -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, ©_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, ©_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, ©_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, ©_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, ©_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);+}
+ c/archive_write_set_format_v7tar.c view
@@ -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, ©_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, ©_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, ©_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);+}
+ c/archive_write_set_format_warc.c view
@@ -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 */
+ c/archive_write_set_format_xar.c view
@@ -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(×tr, 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 */
+ c/archive_write_set_format_zip.c view
@@ -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);+}
+ c/archive_write_set_options.c view
@@ -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);+}
+ c/archive_write_set_passphrase.c view
@@ -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);+}
+ c/archive_xxhash.h view
@@ -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
+ c/config.h.in view
@@ -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
+ c/config_freebsd.h view
@@ -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
+ c/filter_fork.h view
@@ -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
+ c/filter_fork_posix.c view
@@ -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) */
+ c/filter_fork_windows.c view
@@ -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__ */
+ c/xxhash.c view
@@ -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 */
+ configure view
@@ -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+++
+ configure.ac view
@@ -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+
+ libarchive-clib.cabal view
@@ -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/+